Skip to main content
992

April 1st, 2026 ×

Migrating Legacy Code Just Got Easier

or
Topic 0 00:00

Transcript

Wes Bos

Welcome to Syntax. 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.

Wes Bos

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 ten years, maybe twelve, 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.

Wes Bos

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.

Scott Tolinski

I'm not sick. I'm not hurt. I am,

Wes Bos

a Yarn. An action. Win.

Scott Tolinski

Yeah. I wait. Bam, 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.

Scott Tolinski

I do do a number of these things, migrating things. I I migrated an app from Tori to Electron. It went great. 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

Wes Bos

probably a probably one of the best use cases of AI that I can think of is, like, it's very deterministic.

Wes Bos

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 other 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. 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 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.

Wes Bos

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 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 wanna do is all based on web standards. You know, like, what is that? It's Wes and response are standardized with the fetch API. Right? And it 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 Wes I first started it, everything was based on the standard of of connect, connect JS, connect middleware. You know? That was that was Express. That was what else was it built? Fastify uses was using that as well. Several other other these frameworks were using that sort of standard, and I wanted to move off of it. So that, I wanted to move off of Xpress. 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 they're not used anymore, but I still wanna keep the, like, landing page up. People still wanna 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 Node. Now it's called Pug.

Wes Bos

And, it's it's like a weird indentation based templating language. I was really into it at the time. I absolutely loved it. Thought it was so key. Script pnpm. Right? Yeah. It was it was I when I started this thing, it was all done in CoffeeScript.

Wes Bos

Wes talked about that maybe, like, six years ago. I moved from CoffeeScript to TypeScript, but I absolutely hate it Node, and I wanted to move off of it. So there's that. The emails Yarn written in Pug. The viewing experience and the admin experience, that's all been a React application for probably eight or nine years now.

Wes Bos

And I didn't touch any of that stuff. That's all gonna 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 auth, the middlewares, everything that's all done in, in express. And while 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 upfront, 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, Deno routes. So Hano is like, Scott of like the modern express, 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 Yeah. I I thought, like, yeah. Okay. The obviously, that's that's going to be the one for me. I'm not gonna move to something absolutely different. A lot of people are asking, like, why didn't you move it to, like like, SvelteKit 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 feature to feature parity. 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 Node myself with AI, sometimes I get impatient

Scott Tolinski

and I'm like, I'm doing something and whether that is a refactor.

Scott Tolinski

And I wanna start working on something else and it like, as a feature or I've noticed something about the refactor that I didn't 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.

Wes Bos

Do it all at once. It's so tempting, so tempting to do, but you'd it simply just makes a Wes. 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.

Wes Bos

Meaning that, like, I would love to have some routes in HONO 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 Wes you can run a HONO app inside of Express, I believe, but it I wanted the opposite, meaning that I wanna 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 JS anytime I added new code, I tried to do it in a way that was like web standards.

Wes Bos

So that means instead of sticking things onto, like, in middlewares 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.

Wes Bos

So you say Wes dot locals dot user or request dot 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 their Wes, 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 convert. Like, I rewrote my whole checkout recently. I redid a bunch of the auth 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 JS you gotta 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 just 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 Wes Scott, I don't know, whatever the weird things are that were nonstandard. I made sure that I wasn't using any of those, and I was just throwing them into variables before I went ahead and use them.

Wes Bos

And then other standards, form data, etcetera, etcetera.

Scott Tolinski

I will say, like, you you kinda 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.

Scott Tolinski

And being able to manually establish some patterns can go a long way into

Wes Bos

actually getting successful patterns in the long run. Yeah, exactly. 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 wanna move it over, it did like a huge scan of the code base 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 JS I wanted to realize that some of the newer stuff that had been written recently looks exactly how we want it to look.

Wes Bos

So there's that, and then also there's the the templating angle as well. Right? Like, I gotta 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 TSX JSX templating engine for Express, which, most bizarre thing ever. I might be the one of the only people in the world that needed this, but I, basically, in in Express, you can have different templating languages.

Wes Bos

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 Wes, but not like the type of React that you're thinking of Wes it gets hydrated on the on the client and and whatever.

Wes Bos

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 JS 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 dot pug to dot JS x, obviously, change the templating language in there, but I was able to use all the stuff that I was used to. I would like the locals, local variables that were passed in, parameters, and as well as any other React stuff that I wanted, which was worked really, really well. I was I was pretty happy with, that implementation.

Wes Bos

So the third step we have here JS, like, you gotta make a plan, and this JS probably took the second amount of time, most amount of time. The actual writing the Node, 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 wanna move this thing over. I've already done some of the moving over the controllers. I already have a JSX TSX templating ESLint, so, like, you can kinda see that this is this is the direction I wanna go in for the rest of it.

Wes Bos

And we need to go through every single controller, every single endpoint, every single piece of this code Bos, 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 and 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 auth, 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, a 150 check Bos of things that needed to be tested.

Wes Bos

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 a 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 gotta make sure that that is then rendered in the email properly.

Wes Bos

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, endpoints because all of that business logic of, like, what to do mostly stayed the same, you Node? 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, 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 the unit tests and all that stuff till the cows come home, but, like, really, this type of thing is kinda Scott. And Yeah. 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, because moving from Pug to JSX was not as straightforward.

Scott Tolinski

And if you want to see all of the errors in your application, you'll want to check out Sanity at century.io/syntax.

Scott Tolinski

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 century.io/syntax.

Scott Tolinski

Again, we've been using this tool for a long time, and it totally rules. Alright.

Wes Bos

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 to components.

Wes Bos

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 auth worked really well first try, which I was pretty happy with. So it took, well, 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 I felt comfortable deploying this thing. And then on a Monday morning, I I merged the PR, let the sucker go.

Wes Bos

Yes. And then immediately the Sentry emails start coming in.

Wes Bos

And that, 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 kinda back and forth.

Wes Bos

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, like, forward slash account forward slash progress, you know, tracking their progress. That's why I needed to make sure that the API API endpoints 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 Sanity, sentry.io forward slash syntax, 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.

Scott Tolinski

Yeah.

Scott Tolinski

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, having tools like that that can alert you to stuff going wrong is Yeah. Important.

Wes Bos

Now that it's now that it's, like, moved over, I've I'm, like, so excited because, like, now I the the world is my oyster in terms of, like now I'm thinking, 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 Node, and I feel like I can do a lot more with whatever features I wanna wanna 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 x. I probably eventually would have done it, but I was just I didn't wanna 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 Wes I wrote like this little converter function.

Wes Bos

And then

Scott Tolinski

once that was all done, I could delete that function from from the code Bos. 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 guardrails.

Scott Tolinski

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. Yep.

Scott Tolinski

You you can monitor the output. You could read the code if you know what you're doing. Yeah. And

Wes Bos

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 code Bos, or how many people are on, like, a like, still already have Sass, and they they wanna use some of the new CSS nesting features.

Wes Bos

Like, Sass 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 Sass features you're using, plan out what to do with those Sass features, you know, because it's not perfect one to one parity.

Wes Bos

How how are you gonna tackle how are you gonna reimplement this if it's that feature JS not supported in CSS nesting? But there's just so many different use cases where Node moving from one to another is such a easy thing. Yeah. Not super easy, obviously. Right? People have Effortless. Works first try. People have like 10 times, a 100 times bigger applications that are written in JavaScript, you Node, and you're not gonna just the one shot move that thing over, but it certainly is a viable solution versus having to try shoehorn

Scott Tolinski

streaming into an application that was never built for it. Sec. Well, I I loved hearing your process here, and some of the stuff is gonna go along with some some topics I wanna 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 guardrail 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 minded. So yeah. Well, thank you so much for sharing that, Wes. Let's get into sick picks and, shameless plugs. We haven't done shameless plugs in a while, but I've been feeling like shameless plugging something. So sick picks, I have a sick pick for you, which is WhisperFlow.

Scott Tolinski

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 this show. I had a crazy allergic reaction to my sinus surgery medication, and it, like, caused some major inflammation in my wrist where they were, like they felt like they were broken, and my wrist 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 a 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 Wes 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 show the Bos. Sometimes it wouldn't.

Scott Tolinski

People were like, oh, if it's slow, you gotta go in and and change which model you're using. It's like, I don't I don't care about models. I don't care about, like, settings. I wanna hit my voice dictation button, and I warp it to work, and I want it to work every time. I Wes suggested WhisperFlow as being a good option, and it really checks the boxes of just works.

Scott Tolinski

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 Node. I don't have to worry about any of that stuff. It's fast, it's efficient.

Scott Tolinski

So if you're doing voice dictation a lot, I found it to be much nicer obviously than the built in system wide one.

Scott Tolinski

And

Wes Bos

in general, it's it's been very reliable for me, which is something that I abs it it has never once not triggered, not pasted, not done my text, which JS, like, that that's how it's gotta work. Yeah. Yeah. Right. I've tried these things so many times, and I've always just go back to the built in macOS one. And, like Yeah. The macOS 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 because there's super whisper and whatever. Those things are way better at that. But I also I love that the built in macOS one will stream what you're saying so you can see it. I know. And then you can, like Node. 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.

Wes Bos

So, like, why why do you use these things versus the built in one?

Scott Tolinski

Simply because it's more accurate. I can say the word century, and it knows that I'm saying century and not century.

Scott Tolinski

I I it it can do my last name correctly, which isn't you Node? Right? My last name isn't even hard, but AI, it Scott up my last name or the, the dictation one. And I also just find, like, the the macOS dictation just be inaccurate enough that I kid you Node. 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 a 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.

Wes Bos

Interesting.

Wes Bos

Just more accurate. I need to I'll give them a shot again because I would like to provide a whole bunch of things that I often words that I often say, you Node? Or, like Yeah. 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, it could, like, look at your code Bos and, like, fill it with words that it sees on screen.

Scott Tolinski

That would be kinda cool. And whisper whisper flow has a cursor plug in 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. You like, macOS 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 Yeah. The whisper flow. And it Wes, like, the days when my wrists were at their worst, it was very usable, but, look, 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 Oh, no. Chaotic.

Wes Bos

Yeah. Alright. I'm gonna say pick another app as well.

Wes Bos

This is called DisplayPlacer.

Wes Bos

This is a CLI that allows you to save your display settings, and this is a problem that I have. If anyone uses a dock and a Mac, 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 pnpm time 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, 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 drove me nuts. So I Scott this little CLI called display placer, and then there's a a ray cast, like, UI for it.

Wes Bos

And basically, you can save your common things, your common layouts, which I really like because I have I have a desk at the Scott, I have a desk here, I have sometimes I have different displays on and off, sometimes I like a, like, a larger resolution, sometimes I like it zoomed a bit more up when I'm recording.

Wes Bos

So I have, like, six or seven different presets, and I can just type into it and hit presets. It's not perfect. It's, like, 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

Scott Tolinski

do my monitors every time I plug into my dock. Yeah. Man, what annoying little things, Real quick before we get out of here, I do have a shameless plug.

Scott Tolinski

My wife started a podcast. I've talked about it on here before, phases.fm.

Scott Tolinski

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, just quit or do something else or we wanna raise resilient kids. So, my wife is a doctor of psychology, and she has a new podcast about making parenting topics, scientific but also fun and easy. So check it out @phases.fm.

Scott Tolinski

And it's available on all podcast players, all that stuff. So check it out. Is a great

Wes Bos

Wes domain name. I can't believe you got that, phases.fm.

Scott Tolinski

Such a good name. Jumped on it so quickly. Yeah. I when I saw that that existed and the YouTube and all that, I just jumped on it as fast as possible.

Wes Bos

Sick faces. Alright. Thanks, everybody, for tuning in. Catch you later.

Share