Better with Kent

One orchestrator prompt, six platform swaps, and a real production cutover on kentcdodds.com — the workflow, the review loop, the perf scare, and where the agents were wrong.

  • (00:00) - One prompt, 40,000 lines
  • (00:44) - The site and why migrate
  • (06:38) - PR #813 — 483 files
  • (07:09) - Kickoff prompt anatomy
  • (11:35) - Orchestrate with subagents
  • (16:13) - Steering + scope expansion
  • (21:33) - Three-layer review
  • (23:22) - Kody closes the loop
  • (25:24) - The performance scare
  • (30:22) - Merge and cutover
  • (33:51) - Cleanup waves
  • (37:44) - Was 40k lines worth it?
  • (39:40) - Homework: try orchestration

Kent walks through how AI agents moved kentcdodds.com from fly.io to Cloudflare Workers — database, ORM, images, email, MDX pipeline, and more — from a single orchestrator prompt plus steering, subagent swarms, three-layer review, and Kody closing every loop. Includes the performance scare that almost killed the migration, the post-cutover cleanup waves, and an honest rollup of four agent-driven PRs (about 40,000 lines changed).

Links

What is Better with Kent?

Solo episodes from Kent C. Dodds on durable skills for people who ship software: judgment, accountability, problem clarity, and what stays valuable as AI takes on more implementation. Kent teaches directly on camera — no guest, one idea at a time. Complements guest interviews on Become a Product Engineer (Chats with Kent). Episode 1 adapts The Last Software Engineer; later episodes cover traps like building the wrong thing faster and skills like making user pain visible. Subscribe for evergreen episodes as the series grows.

I submitted a single prompt to my agent and 40,000 lines of code change later. I went from fly.io to Cloudflare as my host. And I actually changed a whole bunch of other things. I moved from Prisma to Remix. I moved from Cloudinary to Cloudflare all in one giant, massive PR. And I wanna tell you the things that I learned in that process and some of the things that I did to make my agent successful in doing this.

So join me and buckle up. We've got some things to talk about. It's pretty wild stuff going on between Fable and Composer 2.5 and also even Grok 4.5 and a whole bunch of stuff. It's a lot. I'm excited to talk with you about this. So this is Better with Kent where you learn durable skills to improve your ability to build excellent products for your users.

And in this video, we're going to talk about the specifics around how I am using agents to accomplish really big production-scale agentic rewrites or re-infras of a pretty sizable application. So let's take a look at the application first. This is kentcdodds.com. And I built this from scratch using my own hands on the keyboard back in 2021.

And it was awesome. I had a whole launch video and everything at the time. And it's still awesome. And not a ton has changed. I've got some new courses and a couple of things, but lots of these things are still the same. But it was originally deployed on fly.io with Postgres and Redis. And then I moved those over to SQLite. I've got blog posts about all of this stuff. But one of the really unique and interesting things about this was how fast it is because it's running with SQLite.

And so it's right next to the database, right? But I also deployed that globally in many regions all over the world. And making that work with SQLite was kind of a fun trick. I'll save that for maybe another video. Let me know if you're interested. I do have blog posts about how that was structured as well. But basically it used a package or a tool called litefs that kind of acted as a little proxy between your application and SQLite.

And so then it could propagate all writes and everything. And yeah, it's pretty cool, pretty cool stuff. But the tools behind it for fly to support litefs are legacy tools and they are moving on. And so I have to move on. And so I decided, okay, well, now's the time. We're gonna use Cloudflare and see if we can make that work. And the interesting, like a couple interesting problems that I had to deal with with this is that my blog is very unique in that it's not just like a website with a blog.

It has a semantic search like testing library. And it performs that semantic search across all of my blog posts and all my content everywhere. And yeah, it's a pretty wild piece there. The talks here are just like links to all the talks. I've got courses. I've got Better with Kent. This integrates directly with my YouTube channel. The Discord also is all integrated.

And so you do an OAuth flow to join the Discord. The Call Kent podcast in particular is really interesting because you can actually record yourself asking me a question or talking to me. I will listen to that recording. If you don't want to record yourself or you wanna keep it private, we have an option to type your question and an AI generated voice will be used instead. So there's a lot to this. And I've got other things that aren't even on here like the chats with Kent podcast is also hosted on here, integrating with yet another podcast provider.

So a whole lot of things and of course, you've got user accounts in here as well. And I keep track of the things that you have favorited, the reads, the episodes where you're the caller, lots and lots of things. And on top of all of that also is this scoring system where we keep track of the blog posts that you've actually read and you are on a particular team and whoever has had the most number or percentage of their team read a blog post is winning and that changes the color of what the blog post is.

So here the red team is winning on this one, the yellow team is winning on this one. So there's a lot, it's super cool and it's super fun. So with that, I had really dug into this idea of having this all be hosted on fly.io with SQLite. And there are a lot of reasons that I did that because I can't just cache everything because it's all user specific. So if you scroll down here, you've got these blog post recommendations.

Well, every time you refresh the page, those blog post recommendations are different. Refresh again, different blog post recommendations. And so I can't just stick a CDN in front of this thing and make it lightning fast in that way. I have to make sure that it's really fast doing a full server render. And now what we're looking at now is actually coming from Cloudflare, but fly also was this fast using SQLite. And it was just really remarkable.

And it's all unique to you. So you've got this image here as well. This is server rendered. So you're not, we're not like doing a client side thing. So every single request is going to be server rendered specifically for you. And now because it's on Cloudflare, it's in over 300 regions all over the world. So wherever you are, you are within like 20 milliseconds of the closest edge node for Cloudflare. And so it should be fast for you everywhere in the world, which was one of the main constraints that I put on myself when I was building out my website.

So why did I move to Cloudflare? Well, it was because SQLite and the console, the service that I was using and LiteFS were kind of being de-emphasized. They're not unmaintained, but they're not really actively maintained anymore. And so I decided, hey, you know what? I like the global distributed characteristics of Cloudflare. I think that I can get that with this. But the problem is that like so much of my website assumed being on fly and using LiteFS with SQLite and the console service.

And so it was not a small lift. I've been thinking about doing this for a while and then Fable comes out and I think, you know what? I think Fable might be able to manage this one. And so that's what I did. And in the end, I'll just show you the end right now. I had a single pull request with 236 commits with 483 files changed. A total of 28,800 lines added and 12,000 removed.

So it added a whole bunch, but we'll talk a little bit about like where those lines came from and all of that. But yeah, a lot of commits. A lot of comments on this pull request. Just a ton of things going on in here, pretty wild. So the first prompt is actually not a massive thing here. I'm basically saying, hey, I've already started the migration. Like I had some services that I had moved off of my website onto Cloudflare.

So for example, like all of the podcast transcription or like the joining of your question and my answer on the call Kent podcast, I moved that over to Cloudflare already. I have the search was moved over to Cloudflare. So I've been like moving pieces over to Cloudflare already. And I had done a little bit of work to make some of the migration a little bit easier.

And so I'm saying, hey, I've kind of already done this. I don't necessarily want you to go all the way to like changing the DNS records or anything. But I do think that I wanna get the full version of the website running on Cloudflare. I want every feature at Feature Parity. Things should work exactly the same as far as the user is concerned. There will be some difference as far as admin is concerned. 'Cause on the original website, I had some admin pages for looking at the cache and looking at the database and the users and all.

Like there are a couple of things that would be a little bit different. And also there's caching that needs to be different. I want it to be as fast as possible, if not faster. And we need to have a solid mechanism for pushing updates to content that doesn't require rebuilding the whole site. That was on my previous site way back in like 2020. I had to rebuild the whole site every time I made a content change. I did not wanna have to do that again.

And so it had to figure out how we're gonna do that. Also, I want everything to be server rendered that is currently server rendered. And some of this was actually really, really challenging because all of these blog posts are MDX files. And to be able to compile this on demand and then of course cache it after the fact, but then make it so that I can make a typo change to this thing and push it out without redeploying.

I do have to have some mechanism for compiling this and turning it into some code that can be executed. But because it's MDX and I do have some things on here that are interactable or like here's a mermaid diagram that needs to be server rendered. This is in fact server render, refresh the page. There's no sort of like asynchronously loading or processing the mermaid diagram. It all is server rendered. And mermaid in particular is a tricky one.

Let me know if you want a video on how I server render mermaid diagrams. That one's kind of interesting. But this is really tricky because the way that I did it on fly was I would actually run MDX on demand. I would build it and you end up with the string of code. And then I would literally just eval that string of code on the server and then send it up to the client. And then when it would hydrate, it would eval again on the client. Now that's all well and good on the client and on the server as well in node.

But in Cloudflare, you cannot eval. That's not a feature that they support for security reasons and others. And so I knew that this was gonna be a problem. However, we recently in the last couple months got a new feature on Cloudflare called dynamic workers, which is basically like a super eval. You can send it a bunch of code and it will create a worker on the fly, compile it and then send the results back. And so I said, hey, you know what?

We should be able to do this with dynamic workers. And oh boy, it's pretty cool that this is working. So yeah, I know spoiler alert, we're already looking at the finish site, but it works and I'll talk about that a little bit too. So here then I say, hey, you've got a Kody MCP. If you aren't familiar with Kody, that's my MCP server that has integrations with just so many things. And one of those things is Cloudflare, also Cloudinary and a variety of other integrations that I would need for this, like cursor and GitHub and just like everything.

And so yeah, you can play around, test things out, whatever you need to do, just don't affect the production systems. You have free reign to deploy and test on other instances, whatever you want, just don't mess up with production. And it did a good job with that. I didn't have any problems with it messing with production. And I also said, you know what? I think as Fable requires a lot of tokens, I think it would actually be a really good orchestrator as well.

So I said, I don't want you to do this work by yourself. You want to, you need to be an orchestrator of many sub-agents using appropriate models. And I suggested Composer 2.5 as a highly capable and very fast and very cheap model. This was before we got Grok 4.5. In recent conversations similar to this, I've started encouraging the model to use Grok 4.5. Composer 2.5 is still really good.

I feel like they're about even, but Grok may be a little bit ahead in my own personal experimentation. The Kent bench, if you want to call it that. So I say, yeah, go ahead and use that one. If you think there's another reason to use another model, you feel free, you are the orchestrator, fan out to as many sub-agents as necessary to paralyze your work. One thing that I would change to this, oh, and actually another thing that I mentioned here too is you need to handle port collisions as needed.

So I've done this before and one problem that I had was the sub-agents would each try to start the server and they'd run into port collisions. So I mentioned that as an idea. That might be something to package up and some sort of orchestration skill or something to make this so I don't have to say that again. But yeah, so coordinate between those agents, identify a critical path and paralyze the work. And one other thing that I might say now is the orchestrator or the smart model should build out the primitives that the other sub-agents are going to use.

And so that way the sub-agents aren't just grabbing or making up things, but they have a common set of primitives. So if there are new primitives that need to exist that don't currently exist, then the smarter model can create those and then the sub-agents can simply use them. So that's something that I've learned even since this one. So calling out this is not a simple task necessarily, will require a lot of thought ahead of time. I'm pretty sure that does affect the output of the model.

So I don't think that that was a waste of stuff. But sometimes like I'm speaking to text to do this sort of thing. And so sometimes it's just the way I talk of like, I don't know, being nice to the model and stuff like that. I do think that even if it doesn't change output, it changes me to be nice. So yeah, and I said we did have some work in progress. You can take a look at some of those and reference those. But yeah, you should just do it yourself.

You're the smarter model anyway. And then yeah, verify your work as you go. So I'm telling it like, keep that agentic loop closed. That's one of the things that Kody helps with a ton is giving the agent the tools that it needs to be able to close the agentic loop and do things on its own. Be methodical about it. Send me a message on Discord using Kody when you're all done and just tell me that it's finished. So I didn't wanna have to open anything up or whatever.

And I said, I don't wanna open up the previews that you put on Cloudflare. Only to find that there are missing images or errors in the client or whatever. Do a quality check and feel free to use sub-agents for that as well. And when you're finished, what I expect is a URL that points to what is effectively a clone of my website, which doesn't rely on anything on fly.io. It definitely can and should rely on aspects of my website that are already running on Cloudflare.

I don't necessarily wanna expect changes to those. And if you need to make changes to those, then that's fine. Just make a clone of those as well. I don't want you to affect the production system. And yeah, when you're all finished, send, I want you to mark the PR as ready for review and enter this loop. I've actually since made this a skill. It's called Ship PR. And I even allow the agent sometimes to go through this loop and merge it itself and verify production on things that I'm confident that the agent is gonna get working just fine.

So yeah, wait on CI to finish the PR, address any failures, address any valid PR feedback. If there aren't any failures or valid feedback, then break out of the loop. Otherwise push your changes and repeat. And so that way, by the time I'm looking at the PR, it's already gone through a bunch of feedback with an AI reviewer and we're in a pretty solid place for me to actually look at things. In this case, I actually really didn't look at much of the code.

It was a lot of code guys, come on. So it worked, it ended up working. I did do quite a bit of checking of the results and everything. And yeah, when you're all done, send me that message on Discord. This is the only prompt you're going to receive from me about this. This job is yours now, you can do it. And then I sent it off and I went over to play practice and I started singing and dancing on stage. I pulled out my phone a half hour later and I realized, oh, you're having trouble connecting to Kody because Kody was disconnected somehow from Cursor and so I said, well, let me fix that up.

I fixed it and I said, okay, Kody is connected now, please continue. And then 17 minutes later, I noticed it got stuck on some sub-agent and so I said, hey, I think you're stuck, get unstuck. So I'm checking back in to see how things are going. And then in the process, I decided, hey, you need to use your sub-agents a little bit better. I don't really have to do that as much anymore. This is just a few days ago, but I feel like it's gotten better at doing this.

And yeah, I also wanna make sure that we don't have anything left over, that we don't need to keep. Get rid of old crafts like agents. Agents have a really hard time deleting code. They just are adding code all the time. So I actually say this several times in our conversation. And then an hour and a half later, it comes back with a video recording. This is all in Cursor. So Cursor has the, like your agent is actually running in a machine, so this is what the machine looks like.

And so this is it looking at the staging deployment and scrolling the page and taking us through all of this. And I'm watching this on my phone and thinking, hey, wow, it actually did a pretty good job. Now, I can't see where those images are and make sure that they are correct. But here's interactive piece of content inside of MDX. So this is like, okay, it actually is running properly.

So this is an interactive piece and it's server rendering and all of that stuff. So this is really cool. Even login was working. So like, wow, that's pretty good from what honestly is effectively a single prompt with just a couple little like nudges here and there. I was feeling pretty good about that. And it gave me the preview URL. I was able to run through all of that. And then we talked about performance and then a whole bunch of other stuff.

So this like ended up as I was going through all of this, I decided, you know what? How about instead of just doing the one thing, just getting it onto Cloudflare, let's like blow up the scope of this work considerably. And let's go to Cloudflare native. Like I want the OG image to be generated with Satori, which is a really awesome package for generating OG images in particular, really just generating images out of React components.

And so like, let's generate those from Satori. Let's also just get off of Cloudinary entirely. So like we've already gotten off of, you know, SQLite and LiteFS. We moved over to D1 with read replication. And then why don't we just get rid of Cloudinary as well? And later on, I'm saying, hey, you know what? Why don't we get rid of Prisma too? And we can use Remix version three.

They've got this data table module that you can use. And we'll just have our own little D1 adapter for the data table. And we can get rid of Prisma as well, which actually simplified quite a lot because the stuff you have to do to get Prisma running on Cloudflare, it like it's a paved path. But yeah, Prisma ORM on Cloudflare is not my favorite thing. And so moving over to Remix data table with like a little adapter, that worked out really well.

And then I also said, how about we get rid of Mailgun and we can just use Cloudflare email. Really, I wanted feature parity, but I wanted to just go all in a Cloudflare native stuff and get rid of all these other extra dependencies. And my goodness, it worked. And it's so cool having my website working and running on Cloudflare Infra from really a single pull request.

It's amazing. And like my favorite thing is just every single time you come to one of these pages, if it's not cached, which most of the time it's gonna be cached, but if it's not, it's taking the code for the MDX of this page. And it is sticking it into a dynamic worker to compile that on demand. And I just, that's so cool.

So it takes that code, sends it into a dynamic worker, compiles it, gets you back your HTML, and that is what is rendered. We're doing React server rendering inside of a dynamic worker, a worker that's spun up at the moment. And that's being handled inside of another worker. So you make a request that comes to my Cloudflare worker. And then that goes into another worker to do the server rendering. And it comes out, we cache it in KV, and then it ultimately comes out again.

And that's actually, it just is mind blowing. It's so cool. So really excited about that. And on top of that, that works really nice when you are like an anonymous user. So if I'm an anonymous user, I'm not gonna get all the customizations and stuff. But if you're a logged in user, then it actually is doing that every time. And I just, it's so cool, so cool.

And this feedback loop between the reviewer agents in the PR and the agent that's actually working on it is really powerful. So here you can see it's saying, okay, I'm gonna verify, commit, push feedback fixes. And then it's gonna wait for CI to run and the reviewers to come back with updated review. And it's gonna check, okay, what did the CI look like? What are the new comments? Oh, I don't have any new comments, awesome. So then I can continue and think about this.

Oh wait, no, CodeRabbit had some actionable feedback. So let's go through and implement those fixes for CodeRabbit and this happens all throughout this. Okay, so we're just iterating. We've got BugBot fixes that we needed to make based on feedback that we got from BugBot. And so this is, I love that I'm able to say, go ahead and iterate with the other AI. And so by the time I come around to it, it's already had a lot of iterations and I can either just merge it 'cause I'm happy with it or I can check on stuff and I don't have to nitpick on the things that are obviously wrong.

And the AI reviewers actually caught some really good problems. So we had a page cache privacy leak on the blog page. And we also even had actually an entirely new feature before anybody could use my Cloudinary to generate any image they wanted 'cause I didn't bother with signing of those images. But I decided, you know what, for this, like, yeah, sure, let's go ahead and implement that. Fable, you got this. And so as a part of that, it discovered a server-side request forgery.

And so we locked down that, a completely new feature. So just the most wild things that this was able to accomplish. And I'm honestly floored. Fable with a bunch of sub-agents nailed this and did such a good job. And really it was Kody that closed the loop on this and made the agent so successful as well because it was able to work with Cloudflare to deploy the test environments and actually do testing, make sure the performance was good.

It could actually look in the DNS and make sure that that was set up correctly. And when I finally did do the switchover, it handled that. It also, when I decided, okay, let's go from Cloudinary over to Cloudflare, it was able to grab all the images from Cloudinary and put them into Cloudflare because it had Kody, which like, that is so, so cool. And Kody is also a pretty important part in the process of iterating with the AI reviewers.

So Kody was absolutely a valuable part of this process. So thank you, Kody. And then, you know, I didn't stop there. I didn't want to just have this agent and the reviewers to be in isolation. I wanted like a bigger picture view of this, but it's so much code. I don't think that I could have read all of that in any reasonable amount of time. And so I just got another agent to review it for me. In fact, I used two agents to do that.

One of them was Fable, another Fable instance. And the other one was Vega, which is the code name for Grok 4.5. And so that's why you see Vega right here. And so I just thought, hey, like, why don't you take a look at the pull request, take a look at the cursor agent. It can look at both of these because of Kody and do an extensive review on this pull request to make sure that, like, we're doing good things.

We're not missing anything important. And, you know, it kind of put Vega to the test. And also I had Fable do this as well. And it did find a number of things. So this actually was the unauthenticated OG signing that was caught by Vega here. And a number of other things like with caching and things like that too. And so I was actually able to tell it, hey, you know what, like, let's take those and fold those back into this implementation.

And so the agent was able to actually do that too. Another thing I was really concerned about was performance. So let's back up a little bit. My original website was running LidFS with using the console service for determining who's the primary and stuff. And sometimes that console service would kind of mess up and we'd end up in these really terrible situations where my site comes all the way down. And this was getting worse and worse. Eventually I just decided, you know what, I'm gonna turn off LidFS.

We're gonna do single region. And that worked great except that anybody who was in the opposite side of the world from the region that I decided on was gonna get a slow experience, which is the entire reason why I had LidFS and the console service in place in the first place so that I could have regions all over the world that could serve traffic. Well, the side effect of this also was that anytime I would do a deploy, because it's SQLite and it's on a volume, that volume is mounted into a machine and it can't be mounted into the next machine, which is getting booted up for a new deploy.

And so we have to take the whole site down, the new one comes up, and then we mount the volume to that new one. And that could be several seconds of downtime, which is like, okay, yeah, but ah, come on. Like, we could do better than that. And so this was really irritating to me. And so having Cloudflare meant that I can just, they handle all of that. I don't have to worry about it. I don't even have to worry about blue-green deploys or whatever, like that is all handled for me. So that's beautiful.

However, I'm not really interested in things being super slow for whatever reason. Like, we're doing some major architectural changes here, in particular with the way that we're rendering MDX files and the code for that MDX in a dynamic worker. I was worried that like, oh, maybe that's gonna be super slow, especially because it's all dynamic. And so I asked this agent here to like take a look at some metrics and perf stuff so we can do a before and after.

And so, and here's the pull request, here's the agent that's working on it and use Kody to access that. And Cursor is actually able to talk to its own agents and stuff in some situations. But yeah, Kody, I just like using for this sort of thing. So yeah, it did before, after snapshots and it looked at a couple of things and the results really kind of scared me, actually.

I was looking at some of this stuff and thinking, okay, so Cloudflare, when we miss, that's two seconds. Two seconds to get my blog post. I was actually really concerned about this and it had a problem with Kody. So okay, use Kody, see if you can find more information and it did find some more information. But ultimately, it looked like there was a problem here where Cloudflare is gonna be worse. And so I said, hey, it seems like this is gonna make things a lot worse.

And then the bot is like, and this is Composer 2.5, the model said, no, not necessarily. Like we're talking about one region and you're going all over the world when you're with Cloudflare. And then is it really going to be a cache miss all the time? One of my favorite things that Ryan has said in the past, Ryan Florence, is he said, if cold starts are a problem for you, your problem isn't cold starts, it's marketing.

So you should have warm instances ready to serve traffic all the time. And if you don't, it's because you don't have enough people visiting your site. And so, okay, yeah, I'll take that. I do have plenty of people visiting my site all the time, but I still not super jazzed about this at all. And so here it decides like, hey, do you want me to run a primed cache benchmark and see what happens, what if the cache is all there?

And things looked a lot better in that instance. But then, I actually decided, hey, let's take this a little bit further. Let's ask another model. Let's ask Fable what Fable thinks. And so I gave that same agent back to the, that had done this analysis to Fable. And I said, hey, why don't you evaluate that agent's analysis? And of course I could have changed the model. Like I could send a new message with the new model, but sometimes it's just nice to be like, you are not that, that was its own thing.

And now you are a new thing and you're evaluating that. Like it just kind of feels right to me. And this worked out really well. So it helped me really think about whether this is going to result in a lot worse performance or if it's actually going to end up being better. And we actually do end up being fly in the cached instance, which is good.

And in particular, when we consider the global nature of my website and where everybody is coming from, it actually is going to be faster on the whole for everybody all over the world. And so that's good. Another thing that I did before merging was I said, hey, you know what, let's make a backup of the SQLite database on fly, just in case. So we backed it up. I ended up not needing this, but it was nice to have just in case. So then the day came, this is just yesterday actually.

And I told the agent that had worked on it. I said, let's merge this thing. Anything we need to do or should we just merge it? I'm ready to switch over and nothing's blocked, merge it. And it gave me like a sequence of things that we need to do to like get data migrated from the SQLite database over to the D1 database and Cloudflare. And that process, it had already established and everything. So it's just telling me what scripts I need to run and all of that stuff.

And so I did finally merge and I said, hey, you do everything yourself and I'll be watching. It worked for 16 minutes to do a handful of things. But then I said, hey, you know what? I had another agent manual do the DNS flipping. And so here it's like doing some stuff with Kody and it's actually making some code changes and committing and pushing. But I did have that other agent do the DNS flip. And here's that agent. I actually ran it as a local agent in a cursor because I wanted it to have access to things that I have on my local machine.

And so I explained, hey, here's an agent that's responsible for migrating from Fly to Cloudflare. We just merged a pull request. There's a manual migration step we need to execute. So take a look at that and be prepared to execute it when the time is right. So it reviewed that and knew what it needed to do. And so then I said, OK, yeah, let's not worry about the traffic window. We're going to have some downtime. It's fine. Go ahead and make it happen. And it did. It flipped the DNS using Kody and its integration with Cloudflare.

And it said, OK, yeah, site is now on workers. In 11 minutes, I didn't have to do anything. I actually did this on stream if you want to go look up my live streams on my channel. But yeah, so we got everything over. And while that was happening, it actually did take some time because we needed to migrate data. And so I actually freaked out a little bit. There was just a temporary freak out because things didn't quite work right away.

And I actually ended up rate limiting myself as well. But eventually, things settled. Here I was saying, oh, it's stalling forever. Oh, no. And that was just during the manual migration process. And so I'm explaining, hey, I've got this error 1101. Like, what is that? I'm freaking out. But eventually realized, nope, we kind of-- oh, and I also thought that like, oh, maybe this could be some cookies. Like, I thought we used the same session secret so that people wouldn't have to relog in.

And it turns out, nope, it was all just part of the process of getting all of the data where it needed to go. And it did a verification of all that data. And it wasn't like a little bit of data. This was a lot of-- the biggest table, of course, is the post reads, how many people are actually reading. And by the way, that's not just like page views. But they stayed on the page long enough to have probably read it and scrolled the entire length.

So that has happened almost a million times on my blog in the last-- since I started measuring it back in 2021, so five years. It's kind of wild. And so yeah, I copied all the users, their passwords, pass keys, sessions, everything. So everything was exactly right. Nobody had to relog in or anything like that. So that was a pretty cool migration process. It happened in just a few minutes, and the agents did it all for me.

So what did we find out after that? Well, I was really kind of concerned about how much code was added as a part of this. And I knew that there would be some migratory code. And so I wanted to clean up stuff. And so I actually had an agent spawn this other agent, using Kody, of course, to handle housekeeping, cleaning up everything. There was a moment in time where the agents thought that we needed to have a different mechanism for our two secrets and stuff, and I fixed that.

So we had to close those things and go get rid of Cloudinary and Cloudflare resources we no longer needed, all of that stuff. And so it ran through and made a bunch of changes to, hey, let's change some rate limits and stuff like that. And then I did another one. Let's clean up some more. And so this one deleted a bunch of lines. It actually changed a ton, and most of these lines ended up being package JSON, or the package lock.

So we ended up deleting a ton of dependencies. This is another agent that was spawned by a separate agent using Kody, because I don't write my prompts like this, but the agents do, and they seem fine with it. And then we did yet another cleanup. So we did a handful of cleanups to get rid of a bunch of stuff, and we did end up removing a ton of code. And actually, as a part of this, Kody had opened an issue because it noticed that there were some problem with a lot of duplication around how we're mocking things with moving from Express and all of the services that we were using before over to just Cloudflare for everything.

And so I noticed this and said, OK, well, let's clean that up. And that ended up removing a whole bunch of stuff here as well to consolidate our mocking situation, and that saved us a bunch of code too. And then I was still concerned about how much code was added. So I was talking with agents, and ultimately, it's 17,000 line addition, a net addition of lines is what we ended up with even after cleaning up everything.

So I'm like, there's got to be some more stuff that we can get rid of because the net change is 17,000 lines, even excluding the package lock. And this agent-- this is Fable going through this. It explained to me where everything is coming from. And where it came from was primarily Prisma. Going from Prisma to D1 ORM layer, that was like a custom adapter using Remix data-table.

And so that added some lines. And then Cloudinary going to in-house OG images and media, that added a whole bunch of lines. Because of course, Cloudflare does have transformation stuff, but I was using Cloudinary for generating OG images, and now we're using Satori. And so that added a whole bunch of lines too. We also had the problem with Workerd forbidding evals, and that's the Cloudflare workers platform.

And so now we have this build time MDX pipeline. So in GitHub Actions, it will actually build the MDX files into the JavaScript version of those things. So it basically compiles the MDX, and then it uploads those to Cloudflare to run inside of the dynamic workers. So there's a lot of extra code for that. This no MSW in Workerd, we ended up changing out how all of that works as well.

So yeah, we also have some stuff on the cold starts and making that faster, moving from the express server to our two worker topology. So a bunch of stuff. We actually ended up consolidating some of that stuff later as well. And we went from deploy config, that was very declarative, to deploy code, which added a whole bunch of lines as well. And then we also added some tests and documentation and stuff too.

So yes, we added a bunch of lines, but I think lots of them were pretty justified. So ultimately, it was like 40,000 lines changed in the process of this whole experience, which is just wild to me. And I merged it, and there were no issues. Just that short period of time where I was like, wait, what's going on? I can't log in. It's stalling, whatever. And that was just the migration process.

I wasn't watching closely enough. And when all that data ended up in the right place, we have had exactly zero issues. There were some optimizations that could be made, and I did make some of those. But as far as actual things working, people are still signing up on my Discord, so that integration works well. The transistor integration for the call Kent podcast is working well. The simplecast integration for the chats with Kent podcast is working well.

All of that is still working. So what do we get from this? What do we take away from this experience? I would say that the biggest thing that I learned from this is that Wow, Fable is really good, and it's also quite expensive. So this definitely did cost a pretty penny. But at the end of the day, I'm ultimately going to end up saving money because Cloudflare is going to be cheaper for me.

I'm going to have to spend less time worrying about my infra, and the end product is so much better. And so this is the sort of thing I might have done myself, and there's the opportunity cost of my time, or I might have paid somebody else to do. So if you factor those things in, this was actually a lot cheaper than that. So yes, Fable, it's kind of slow, and it's really expensive, but it's really effective. And if you get it, some simpler, cheaper, faster models to work with to do the grunt work, once the groundwork has been laid, and now you can just plow through and implement stuff, it's pretty good at that.

It's really good at that. And then using agents to evaluate other agents' work, that's really valuable. And so that's actually going to be your homework for today, is I want you to try orchestration. Try to use a smart model to orchestrate other models. Tell it that it can first create the primitives that need to exist, or evaluate what primitives need to exist, so that when it talks to the subagents, they're all using the same primitives and doing the right thing.

Make sure that the task that you have is something that subagents can effectively do. It's not necessarily about being faster and parallelizing, though that can be helpful sometimes as well. Sometimes it's just about having somebody that steps in your place, has a really good understanding of the system, and then drives this other subagent. And that worked out really well for me. And there were situations where I was able to parallelize a lot of work and get it done a little bit faster.

But ultimately, it was about getting it done cheaper. Really, that was ultimately the goal for that. So it worked super well. I'm honestly thrilled. And I hope that you enjoy kentcdodds.com. If you see any problems, please let me know. It's open source. You can go take a look at everything. Feel free to roast me for not reading all of the code for this migration. And seriously, go into the code and find something that you're like, oh my gosh, Kent, I can't believe that you actually merged this.

Look at this garbage, and we can talk about it. So yeah. But your homework is to try orchestration. That's what I want you to do. And with that, I just want to say thank you for watching Better with Kent, where we learn durable skills together so that we can continue to make the world better and get better ourselves. So thank you so much, and please like, comment, subscribe, and share this with your friends so we can all get better Together, thank you for getting better with me.