Migrating Legacy Code Just Got Easier

Syntax| 00:29:08|Apr 1, 2026
Chapters12
The chapter discusses using AI to migrate code between languages and frameworks, addressing the challenge of moving large monoliths and outlining approaches for testing and planning the migration.

AI-assisted migrations can be planned and executed gradually, moving from Express to Hono with a web-standards mindset and careful testing.

Summary

Wes and Scott of Syntax walk through a real-world migration story: moving a long-running Express.js-based course platform to a newer stack using AI as a guide, not a shortcut. Wes details how he kept the project moving in small, verifiable steps—first choosing a lateral, standards-aligned target (Hono) over a big-bang rewrite. He describes rewriting the core server-rendered templates from Pug to JSX via a custom TSX templating engine, and using Sync Local Storage to propagate data across middleware without Express-specific APIs. The duo emphasizes establishing repeatable patterns, planning every controller and endpoint with the aid of an LLM, and generating a concrete 150-item test checklist before “letting it rip.” The actual migration then unfolds over a few hours of automated refactoring, followed by careful testing and fast-falling Sentry alerts to catch edge cases. The result is not only a successful transition but a new sense of possibility: more options (Cloudflare, Drizzle, etc.) open up once the move is complete. Wes also shares practical tips on templating, gradual rollout, and the value of guard rails for AI-driven changes. The chat closes with a look at how this approach could apply to older Angular apps, SAS-to-CSS nesting, or even larger Java Spring migrations.

Key Takeaways

  • Choose a true lateral move (feature-for-feature parity) to minimize risk; Wes relies on Hono as a modern, standards-based Express alternative rather than jumping to a completely different stack.
  • Move core templating away from Pug by building a JSX/TSX templating layer, enabling a future React-based rendering path and smoother cross-over during migration.
  • Use Sync Local Storage to carry data through non-Express middleware, reducing coupling to Express-specific request objects and easing the migration of business logic.
  • Plan meticulously with the help of an LLM: map every controller/endpoint, identify edge cases (CSRF, JWT, rate limiting), and generate a 150-item testing checklist before deploying.
  • Begin with small, verifiable steps: migrate a handful of routes first, use Sentry for real-time visibility, and deploy only after a staged, confidence-boosting testing cycle.
  • Templates and UI can be modularized into reusable components rather than monolithic partials, speeding up future portability and replacement of templating languages.
  • Post-migration, new opportunities emerge (Cloudflare, Drizzle, etc.) because the system’s architecture is now aligned with modern web standards and tooling.

Who Is This For?

Essential viewing for frontend engineers and backend developers planning a large codebase migration, especially those weighing Express-to-Hono or similar middleware-alternative moves. It provides a practical blueprint for AI-assisted refactors with guard rails and testing.

Notable Quotes

"The cost of moving has never been lower with all of this stuff."
Introductory point about AI-assisted migration reducing perceived barriers.
"A lot of people are asking like, why didn't you move it to like spelt kit or like 10 stack start or or something like that? I I if you're doing a big move like this, it needs to be a lateral move, which is feature-to-feature parody."
Justifies choosing a lateral, standards-aligned path over flashy but riskier options.
"I wrote a TSXJSX templating engine for Express which uh most bizarre thing ever."
Highlights a creative solution to keep JSX templating while migrating.
"There was something weird where it wasn't loading the progress properly, but those were relatively easy fixes. But I was thankful to have Sentry sentry.io/sax to tell me what those issues were very quickly so I could fix them and not too many people lost their progress."
Emphasizes the importance of observability during migration.
"AI is so, so good for this type of stuff."
Wraps up the core value proposition of the episode.

Questions This Video Answers

  • How can I migrate an Express.js app to a modern framework without rewiring everything at once?
  • What is a practical way to move from Pug templates to JSX in a large codebase?
  • Can Sync Local Storage help in migrating middleware without touching core business logic?
  • What planning steps should I take before a big AI-assisted migration in a production app?
  • Which tools best support risk monitoring during a gradual framework migration?
JavaScriptNode.jsExpress.jsHonoWeb StandardsPugJSXTSXTemplatingSync Local Storage (SLS)」、「AI-assisted migration」、「Sentry (sentry.io)」、「Code refactor planning」
Full Transcript
Welcome to syntax. Today we're talking about migrating and moving languages or frameworks, moving your code to something else with the help of AI. How do you possibly even do that? You got a big monolith application that's been sitting around for many, many years and you've dreamt of of moving this thing over to something else. You simply cannot just type into the box, move it to X, Y, and Z, move it to a different language, move it to a different framework. It doesn't work that way just yet, at least. But the cost of moving has never been lower with all of this stuff. So, I recently moved my own course platform over from it was on Express for probably 10 years, maybe 12, and I moved it I finally moved it over to Hano, and then I also moved a whole bunch of other stuff at the same time. and we're going to explain like what the the approach is to to moving something like this over, which seems like maybe a bit of a daunting move and how to get into it, how to test it, how to plan for it, all that good stuff. Let's get on into it. My name is Wes. With me is Scott. How you doing? I'm doing good, man. Chilling. Uh, I'm not sick. I'm not hurt. I am a rare rare Scott win. Yeah. I Wait a minute. when you got when you got little kids, you're a action junkie like me, it's easy to to dive into any of those things. So, no, I'm ready to hear about this. Uh I do do a number of these things, migrating things. I I migrated an app from Tori to Electron. It went great. Um so, I think this is a perfect use case for AI as long as you keep a handle on how things are. It's a probably a ve probably one of the best use cases of AI that I can think of is like it's very deterministic. um it can be very it tested very well but certainly a lot can go wrong. So let let me explain it. So I have a course platform which I sell courses on. I have free courses on there's I think 10 or so different marketing websites that are on it. There's a whole administration area. There is a whole viewing experience. There's stats. There's an admin area. There's there's roles and all, you know, like there's there's everything you would expect from like a course platform. And I've been been building this thing for many many years and I've kept the stack relatively the same. Um I've upgraded lots of stuff over the years, but like the one piece that was sort of the backbone of it was it used Express.js JS as the like controller router sort of the whole thing lives as an express server rendered or API endpoints that it's it's been hitting and I have wanted to move that off of express for for many many years. I think I had something like 60 or 70 different like routes and endpoints and I've always just looked at it and I I think this is the reason why Express is is still so popular. I've always looked at it and go, that's a lot of work. That's a lot of repetitive work for something that doesn't have a whole lot of of benefit to me. You know, like at the end of the day, I would simply just be it would be exactly the same. But I'm at a point where I just like I want to upgrade it because a lot of the new stuff that I want to do is all based on web standards, you know, like what is that? It's request and response are standardized with the fetch API, right? and uses fetch API, uses web streaming, uses a sync local storage, uses form, all of these like web standards that we've been talking about for so many years, everything uses them. Whereas when I first started it, everything was based on the standard of of connectjs, connect middleware, you know, that was that was express that was what else was built? Fastify uses was using that as well. Several other other frameworks were using that sort of standard and I wanted to move off of it. So that I wanted to move off of Express. And then along that I was also all of these sites that I have done over the years. A lot of these courses are are deprecated. They're not used anymore, but I still want to keep the like landing page up. People still want to visit them. And all of those were written as Pug as a server template. Right? So Pug is a templating language. It used to be called Jade. Now it's called Pug. And it's it's like a weird indentationbased templating language. which I was really into it at the time. I absolutely loved it. Thought it was so cool. Coffee script era, right? Yeah, it was it was I when I started this thing, it was all done in coffecript. We talked about that maybe like six years ago. I moved from coffecript to Typescript, but I absolutely hate it now and I wanted to move off of it. So, there's that. The emails were written in Pug. The viewing experience and the admin experience, that's all been a React application for probably eight or nine years now. Um, and I didn't touch any of that stuff. That's all going to stay in in React. But the the core thing of much of the server rendered templating is in pug and all of the logic and controllers and stuff, the API endpoints, the off the middleares, everything that's all done in in Express and I needed to get a get away for that. So, let's talk about how how we approach how I approach a move like this. So, first thing is you need to make your tech choices up front, right? Investigate the possibility. Is this thing even doable? For the longest time, I was looking for something that would let me run like web standards HANA routes. So, HANA is like sort of like the modern Express. Um, and it's it's super fast. You can run it anywhere. It's a really nice API. And of course, most importantly, it's it's built on web standards and it's similar enough to express that I could see myself making like that lateral move from one to another. So I I thought like, yeah, okay, obviously that's that's going to be the one for me. I'm not going to move to something absolutely different. A lot of people are asking like, why didn't you move it to like like spelt kit or like 10 stack start or or something like that? And I I if you're doing a big move like this, it needs to be a lateral move, which is featureto feature parody. you you can't like also start introducing a whole bunch of new features at this point because when you try to migrate something and start to put new features in that's just way too big of a move in my opinion and that those types of projects never ever get shipped. I notice myself with AI sometimes I get impatient and I'm like I'm doing something and whether that is a refactor and I want to start working on something else and it like as a feature or I've noticed something about the refactor that I then want to change again and it it takes a tremendous amount of restraint to say no wait actually let's get this into a stable place and then modify and then stable and then modify rather than let's take everything out of the cupboard and then try to put it back in. Yeah. Do it all at once. It's so tempting. So tempting to do it, but you it simply just makes a mess and it's those types of projects will never ship. So I was like looking for a long time like a way that I could move this gradually. Um meaning that like I would love to have some routes in Hano and some routes in Express and sort of just move them over one at a time as I was going through it. And I never found anything like that. There is the opposite where you can run a Hono app inside of Express I believe but it I wanted the opposite meaning that I want to take all my express routes and turn them into Hono. What I have been doing over probably over the last year year and a half is anytime I added new code I tried to do it in a way that was like web standards. So that means instead of sticking things onto like in middleares in express the way that you sort of hot potato data from one middleware handler to the next like if you're populating a user you might have a middleware that populates a user the way that you do that is you stick it on the request. So you say request locals do user or request do user equals and you stick it on there and then you know the next the next like middleware down the line or the one that actually ends up rendering out your template. You know they'll they'll have access to that data because you stuck it in the request and they can then pull it out. I don't think they have that sort of concept in Hano. They might, but I move I've been moving everything over to using a sync local storage, which essentially is you can stick stuff in like a a local store and then any function that gets called down the line will then be able to pull that out of a sync local storage. It's sort of just out of the air. You can just pull it up and then you have access to the data you want. So, I've been working on using that API for over a year and I had probably four or five things converted. Like I rewrote my whole checkout recently. I redid a bunch of the O stuff. So all of that stuff was in a sync local storage and I had a and like that's sort of my next point of how you tackle this thing is you got to move over a few things yourself to figure out what this is going to look like. You need a couple really clear explanations as to how the new routes should look. So I moved all of those over to sync local storage. I made sure that any data that was being extracted, you know, like body, params, all of that stuff, I was made sure that it was extracted high up in the controller, meaning that you're not using any express specific APIs inside of your business logic. You know, you're not using like request, I don't whatever the weird things are that were non-standard. I made sure that I wasn't using any of those and I was just throwing them into variables before I went ahead and used them. Um, and then other standards, form data, etc., etc. I will say like you kind of briefly briefly hinted at this, something that you pointed out in another episode is that AI does really well when you have established patterns. Yep. And being able to manually establish some patterns can go a long way into actually getting successful patterns in the long run. Yeah, exactly. I I think the reason that this move was so easy for me was that I had been coding it myself. I have these patterns that I know this is exactly how I want it to look. And then when I did that first prompt of like let's start planning and I want to move it over. It did like a huge scan of the codebase and it says oh I see you have already started to move several of the routes over. And I was like perfect. That's exactly that's exactly what I wanted is I wanted it to realize that some of the newer stuff that has been written recently looks exactly how we want it to look. So there's that and then also there's the the templating angle as well, right? Like I got to get off of Pug. This stuff is is really frustrating to work on. So what I did again probably about a year ago as well is I wrote a TSXJSX templating engine for Express which uh most bizarre thing ever. I might be one of the only people in the world that needed this but I basically in in Express you can have different templating languages, right? You can have handlebars or pug or whatever. So I wrote a custom one that allowed me to use JSX and in fact it actually used React as well but not like the type of React that you're thinking of where it gets hydrated on the on the client and and whatever. Simply I just wanted to use JSX tags so that in the future I can move this whole thing over to a React application, you know, like some sort of meta framework. But I wanted to at least get to a point where I could just start using JSX TSX as I was already using it. And then by adding that I was able to simply go to any template I had, rename the extension from pug to.jsx. Obviously changed the templating language in there, but I was able to use all the stuff that I was used to. I like the locals, local variables that were passed in parameters and as well as any of the React stuff that I wanted which was worked really really well. I was I was pretty happy with um that implementation. So the third step we have here is like you got to make a plan and this is probably took the second amount of time most amount of time. the actual writing the code almost nothing but the actual planning and the the testing were the two things that took the longest. So what I did is I made a plan. I went into the LLM and I said like I want to move this thing over. I've already done some of the moving over the controllers. I already have a JSX TSX templating engine. So like you can kind of see that this is this is the direction I want to go in for the rest of it. Um, and we need to go through every single controller, every single endpoint, every single piece of this codebase and and make a plan for how we're going to do it. So, it it took quite a while. Went through every single instance, went through every single controller, looked through every single piece of middleware and scaffolded out a plan. I went back and forth, I don't know, probably 20 or 30 times making sure that we have everything, making sure we look at things like O rate limiting, CSRF tokens, JWT tokens, all of all of that kind of complicated stuff, going back and forth through it. Then once I felt like it had a pretty good idea of the entire surface area of the application that needed to be moved over, I asked it to make a manual checklist for testing. And I think I think that this was really important because it then kicked out a list of I think 150 checkboxes of things that needed to be tested and that was really good because I understood okay it understands the the surface area right you can look at the plan and it will say do X Y and Z but if you ask it to make yourself a checkbox of things to test and it kicks out 150 items you realize oh it actually does understand that when somebody wants to merge two accounts, then we create a token and then that token gets emailed to them with a link and you got to make sure that that is then rendered in the email properly and then when somebody clicks on that link, it will render out the new template. You know, like there's all these little edge cases of things that need to be covered. And at that point where it made that huge list for me, I felt pretty confident in letting it rip because it it understood all of the like nitty-gritty little pieces. Then I like you let it rip and it honestly just ran for I don't know maybe three or three or four hours which is not very long considering how much code it had to move over and rewrite. A lot of that was not the like end points because all of that business logic of like what to do mostly stayed the same. You know, send a password, reset email, approve somebody, buy something. All of those endpoints, none of the business logic there changed aside from taking in request parameters, bodies, whatever, doing the business logic, and then sending back uh the data. So, very little of that actually changed. It was more just about like what the actual route signatures looked like and making sure all the types and everything lined up. What did take a whole lot of time was actually testing it, right? Like you can have like unit tests and all that stuff till the cows come home, but like really this type of thing is kind of scary. And we had to go through every single feature of the entire platform and just go through one by one checking that it still worked, seeing if anything popped up. And there was, I don't know, maybe 20 or 30 little things that popped up here and there. A lot of them were just templating problems um because moving from Pug to JSX was not as straightforward. And if you want to see all of the errors in your application, you'll want to check out Sentry at centry.io/sax. You don't want a production application out there that, well, you have no visibility into in case something is blowing up and you might not even know it. So head on over to centry.io/sax. Again, we've been using this tool for a long time and it totally rules. All right. In a lot of the cases, my like pug templates were just huge things that covered everything and I I didn't want that to be in my JSX. I wanted to like convert them to components. So I I did make sure that a lot of it got moved into reusable components rather than just like partials and pug and whatnot. So, lots of work on the templating part. Surprisingly, most of the like business logic worked really, really well. All the O worked really well first try, which I was pretty happy with. So, it took while it took a couple hours for the code to go through, probably took maybe a week, week and a half to actually to get to a point, not like full-time work, but just to get to a point where we felt comfortable deploying this thing. And then on a Monday morning, I I merged the PR, let the sucker go. Yes. And then immediately the Sentry emails start coming in. Uh, and like that's always the most hilarious thing is that like the the emails from Sentry come in so quickly and I was able to luckily all of the emails, all of the problems were related to progress tracking. So as you're watching a video, there's progress that goes kind of back and forth. And there were people I I think there was like 40 people watching videos while I deployed it. So they were on like the the UI version of the old one and it was still sending API hits to the API um like forward slash account/progress, you know, tracking their progress. That's why I needed to make sure that the API API endpoint stayed the same. And there was something weird where it wasn't loading the progress properly, but those were relatively easy fixes. But I was thankful to have Sentry sentry.io/sax to tell me what those issues were very quickly so I could fix them and not too many people lost their progress in watching their video. Yeah, man. That's a scary migration to do in that kind of way. And I will say, man, I've done so many of these types of migrations by hand. It's scary to do it by hand, too. So, uh, having tools like that that can alert you to stuff going wrong is important. Now that it's now that it's like moved over, I I'm like so excited because like now I the the world is my oyster in terms of like now I'm thinking, hm, can I can I move it to Cloudflare? Can I move to Drizzle? Can I like there's so many more options that I have available to me now now that this thing this move has been done and I feel like I can do a lot more with whatever features I want to want to scaffold out. So AI is so so good for this type of stuff. I was so happy with it because I don't know if I would ever would have moved off of ex I probably eventually would have done it, but I was just I didn't want to do it for the longest time, you know? I didn't really didn't want to have to to do it. Oh, one more thing I forgot to say is that I wrote like a a middleware that converted the express request and converted it into a standard web request. And that's how I was able to move a lot of the stuff over beforehand where I wrote like this little converter function. Um, and then once that was all done, I could delete that function from from the codebase. How many code bases do you think are out there that could benefit from this type of thing? Like a serious process, you set up some guard rails. I think the the planning and the patterns are like the biggest keys to success and then the the testing and verification. You have all that. Yeah. you can monitor the output, you can read the code if you know what you're doing. And it's important to to be able to do stuff like this. Yeah. Like I wonder how many people are on like an Angular one codebase or how many people are on like a like still already have SAS and they they want to use some of the new CSS nesting features like SAS to CSS nesting. That would be easy. That would be an easy one, right? You'd have to plan a little bit. You'd have to see which SAS features you're using. plan out what to do with those SAS features, you know, because it's not perfect one to one parody. Um, how how are you going to tackle how are you going to reimplement this if it's that feature is not supported in CSS nesting, but there's just so many different use cases where now moving from one to another is such an easy thing. Not super easy obviously, right? People have works first try. people have like 10 times 100 times bigger applications that are written in Java Spring, you know, and you're not going to just a one shot move that thing over, but it certainly is a viable solution versus having to try shoehorn streaming into an application that was never built for it. Tech. Well, I I loved hearing your process here and some of the stuff is going to go along with some some topics I want to get to you talking about soon about using more deterministic tools for ensuring things like this work because I do feel like that is the place to get when you have a good testing suite that passes or you have these types of things that are guard rails for the AI writing quality or whatever it is. I think those are the types of things that are going to get us from a place of just slop slop slop to maintainable systems migrated. So, yeah. Well, thank you so much for sharing that, Wes. Let's get into sick picks and uh shameless plugs. We haven't done shameless plugs in a while, but I've been feeling like shameless plugging something. So, sick pics, I have a sick pick for you, which is Whisper Flow. I've been using a lot of voice dictation. I had a crazy allergic This is back to me being sick about something. I didn't even talk about this on the show. I had a crazy allergic reaction to my sinus surgery medication and it like caused some major inflammation in my wrists where they were like they felt like they were broken and my wrists are still recovering from that. Believe it or not, I got really bad RSI from it. So, I've been doing a lot I Dude, I'm I just Yeah, this is just my life. I I I've been doing a lot of voice to to text and I was getting just so frustrated with the voice to text in Super Whisper. And the Super Whisper folks are great because they've reached out to me and we're like, "Okay, how can we make this better?" So, I just was getting really frustrated with Super Whisper not being reliable for me. I would hit the button, sometimes it would show the bar, sometimes it wouldn't. Uh people were like, "Oh, if it's slow, you got to go in and and change which model that you're using." It's like, I don't I don't care about models. I don't care about like settings. I want to hit my voice dictation button and I want it to work and I want it to work every time. I was suggested whisper flow as being a good option. And it really checks the boxes of just works. You hit the button, it works. The little indicator is small. It's visible. It works every time. I don't have to worry about the model. I don't have to worry about any of that stuff. It's fast. It's efficient. So, if you're doing voice dictation a lot, I found it to be much nicer obviously than the built-in systemwide one. And in general, it's it's been very reliable for me, which is something that I abs it has never once not triggered, not pasted, not done my text, which is like that that's how it's got to work. Yeah. Right. I've tried these things so many times and I always just go back to the built-in Mac OS one and like the Mac OS one I don't like because I would love to be able to give it a dictionary of like common words that I say in coding cuz there's Super Whisper and whatever those things are way better at that. But I also I love that the built-in Mac OS one will stream what you're saying so you can see it. And then you can like, oh, if something is wrong, you can just click where you want to go. You can delete stuff, and then you can just keep talking. So, like, why why do you use these things versus the built-in one? Simply because it's more accurate. I can say the word century and it knows that I'm saying century and not century. I I it it can do my last name correctly, which is, you know, right? my last name isn't even hard, but AI, it goofs up my last name or the uh the dictation one. And I also just find like the the Mac OS dictation to just be inaccurate enough that I kid you not, I bet there are times when I'm sending you guys messages on Slack that it has just weird words in it. And those are almost 100% of the time because I'm doing it on my phone via the the dictation that's on the phone rather than something like whisper flow or super whisper which are getting like actual the actual words I'm really saying. Interesting. It's just more accurate. I need to I'll give them a shot again cuz I would like to provide a whole bunch of things that I often words that I often say, you know, or like you could even like what would be cool is if you could hook it up with like OCR as well. So like based on the app that you're on and it could like look at your codebase and like fill it with words that it sees on screen. That would be kind of cool. And Whisper Whisper Flow has a cursor plugin and apparently works well with coding, but I have never done that because I don't use cursor and there there isn't like something for me. I I don't know how well I would do with that actually coding with voice. I did get into the accessibility like Mac OS accessibility has like window control with voice and so I was like show numbers 42 enter and I'm like talking to my computer like that whatever and then dictating the whisper flow and it was like the days when my wrists were at their worst it was very usable but uh definitely definitely took some getting used to. I'll tell you that I accidentally like archived and deleted some open code sessions because I told it the wrong number at the wrong time and stuff. So, it's getting a little chaotic. Yeah. All right, I'm going to s pick another app as well. Um, this is called Display Placer. Um, this is a CLI that allows you to save your display settings. And this is a problem that I have. If if anyone uses a doc and a Mac um and you have multiple monitors, you may know that every time you plug into a different USB port, they ID them all your monitors separately and then anytime that they come online in a different order, they also ID them differently. So, I have this problem where every time I plug my dock in, it thinks that my monitors are always wrong and I have to go and turn them and replace them exactly where I want. And then I have this uh prompter which I have off a lot of the time and then I when I turn it on it thinks it's a different arrangement. It drives me nuts. So I got this little CLI called display placer and then there's a a raycast like UI for it. Um and basically you can save your common things um your common layouts which I really like because I have I have a desk at the cottage. I have a desk here. I have sometimes I have different displays on and off. Sometimes I like a like a a larger resolution. And sometimes I like it zoomed a bit more up when I'm recording. Um, so I have like six or seven different presets and I can just type into it and hit presets. It's not perfect as maybe once in the last couple weeks I've had to manually go and and change it again, but it is way better than having to like manually go in and do my monitors every time I plug into my dock. Yeah, man. What annoying little things, huh? Real quick before we get out of here, I do have a shameless plug. Uh, my wife started a podcast. I've talked about it on here before, phases.fm. Her most recent episode is growth mindset for kids. And it's really talking about how to raise resilient kids who don't give up because man, it's really easy that like when kids get frustrated with stuff that they can uh just quit or do something else or we want to raise resilient kids. So, my wife is a doctor of psychology and she has a new podcast about making parenting topics uh scientific but also fun and easy. So, check it out at phases.fm and it's available in all podcast players, all that stuff. So, check it out. That is a great web domain name. I can't believe you got that. Fm. Such a good name. I jumped on it so quickly. I when I saw that that existed and the YouTube and all that, I just jumped on it as fast as possible. Sick phases. All right, thanks everybody for tuning in. Catch you later. Peace.

Get daily recaps from
Syntax

AI-powered summaries delivered to your inbox. Save hours every week while staying fully informed.