Migrating My Old Blog Posts With AI
My About page has quietly pointed to an old Blogger subdomain since I moved here, propping up my real post count. This week I finally brought all of it home.
Years ago, I had moved a handful of posts from WordPress to Blogger when I merged my two blogs, and even that had taken quite some time. Typically, the process is manual, involving copy-paste, post-after-post, loading images, formatting text etc. While export - import options exist, they aren’t infallible, and given that I currently use markdown files it wasn’t an option for me. With the availability of AI, I decided to give it a try. When asked for advice, I was suggested to provide the exported ZIP and it would take care of the rest. It sounded simple, but as it started to work on the migration, I realized that it was going through setting up widgets, some of them that I had used to source content (Zemanta, does anyone remember?!), RSS feeds and what not. I quickly instructed to ignore all that and focus just on the posts and images.
It got further than I expected, and not in a straight line. The first pass - exporting everything from Blogger via Google Takeout, parsing the export, downloading every embedded image, rewriting paths, converting the markup - came together fast in like 15 minutes. Then I started actually reading the converted posts, and that’s where it got interesting. I soon figured out that since the posts were composed with a WYSIWYG editor, there was a lot of <div>, <br>, <iframe> tag soup and inline styles spread across the files. A handful of posts had simply lost content: an old YouTube embed, a run of tweets, a data table, all silently dropped. Nothing about that failure was loud. The posts looked complete unless you happened to remember what used to be there.
That became the actual shape of the work: convert, build, read, notice something’s off, figure out why, fix it properly instead of patching the symptom, rebuild, repeat. A good chunk of what surfaced turned out to be relics of the old web - Flash-based video players, a defunct Twitter curation tool, CartoDB embeds pointing at subdomains that don’t resolve anymore. Other things very much were fixable, and some of the more interesting bugs only showed up because I went and looked - reading a post, essentially, and catching a sentence that had two paragraphs mashed into it with not even a space between them. Once I flagged one, the pattern rarely stayed contained to a single post: that same broken-paragraph-boundary bug turned up in half a dozen more places once it got scanned for properly instead of fixed one at a time, a stripped-out Twitter widget that had silently killed rich tweet embeds showed up in eleven more posts, and a handful of video embeds had gone missing the exact same way, across posts I hadn’t even looked at yet.
The one that would’ve cost me the most time by hand showed up almost at the end, after I thought the hard part was over. I went back and sorted the imported posts into categories - had AI do that pass too, matching each post’s actual content - a purely cosmetic change, I assumed. It wasn’t. This site’s permalink scheme quietly uses category as a URL prefix, so the moment a post picked up a category, its actual address changed. That silently broke every internal link between old posts that I’d already gone through and carefully fixed earlier in the process - link text still looked right, the posts they pointed to still existed, just at a different address now. Nothing about it would throw an error or fail a build; it would just 404 for a reader clicking through, discovered later, if ever.
I don’t think I’d have finished this without AI, not realistically. Not because any individual step was hard, but because there were ninety-two posts and each one could fail in its own small way, and doing that kind of repetitive judgment call by hand, post after post, is exactly the sort of task I tend to abandon halfway. What made it work wasn’t handing the whole thing over and walking away, though. It was closer to pairing with something fast enough to do the tedious parts at scale, while I stayed in the loop to actually read the output and push back when something looked wrong. The moment I stopped doing that - even briefly - is exactly when a real bug slipped through.
All those old posts live locally now, and my About page can finally stop sending people to a subdomain I don’t control anymore. Best of all, I can look back now and see how my own writing has evolved since then.
🔗If You’re Considering a Similar Migration
Mine was Blogger to a Jekyll site on GitHub, but none of this is really specific to that pair. The same shape of problem shows up moving WordPress to Medium, Medium to Ghost, or off any platform where you don’t fully control the export format. A few things I’d tell myself going in:
- Silent content loss is the real risk, not visible breakage. A clean build or a successful import tells you nothing about what got quietly dropped along the way. You have to actually read the output, post by post, not just trust that no errors means nothing’s missing.
- Anything embedding a service you don’t control from the old era is gone for good. Flash players, defunct widgets, retired APIs. Budget time to remove that dead weight cleanly instead of chasing a fix that doesn’t exist.
- Late structural changes can silently break things you already fixed. Adding categories, renaming a taxonomy, restructuring folders - any of it can change URLs out from under links you verified earlier. Re-check link integrity after every such change, not just once at the start.
- Link to posts by reference, not by hardcoded URL, if your platform supports it. Jekyll’s
{% link %}tag (most static site generators have an equivalent) resolves to a post’s actual current URL at build time, instead of one you typed once and hoped stays valid. I learned this after the fact, once the categories change above had already broken every internal link I’d hand-fixed earlier - switching those links to{% link %}afterward means they’ll survive the next structural change on their own. - Keep the original export around until you’re genuinely done. You’ll want to diff against it more than once, usually right when you’re convinced you’re finished.
None of this needs to be done alone by hand, and it doesn’t need to be handed off blindly either. What worked for me was staying in the loop - reading the actual output, pushing back when something looked off - while letting the tedious, repetitive parts run at a pace no manual pass could match.