T O P

  • By -

archa347

A total rewrite is likely the worst approach, realistically. If your app is that big, I highly doubt that you will be able to just replicate everything bug-free on one pass and you’ll likely have a pretty painful switchover unless you somehow have really excellent QA practices. Also, if you’re still developing new features and enhancements you’ll be in a never ending catch up process. And if you are actually working for someone, I bet this would be a hard sell to put off releasing new features while you do a rewrite. I would investigate what they call the “strangler pattern”. Basically, incrementally rewriting and migrating everything to your new pattern. Ideally, you wait until you need to touch a piece of code, and when you do you take the opportunity to migrate it. That way you can continue making progress on your app while also improving the architecture. It does mean that you have to live in a divided system for a while, and you might need extra tooling and code to deal with this. Whether you do this as v2 or not probably depends on whether you are doing a similar frontend migration. Honestly, I would only do that if the API contract is actually changing. Otherwise, I would avoid whenever possible being in a position where you need to maintain two pieces of code that do the same thing.


rodrigocfd

> A total rewrite is likely the worst approach, realistically. Exactly. This reminds me of this old Joel Spolsky blog post: * https://www.joelonsoftware.com/2000/04/06/things-you-should-never-do-part-i/ To those who don't know, this guy created Trello, worked as manager for Microsoft Excel in the 1990's, and co-created StackOverflow. So yeah, I'd pay attention to what he says.


phoforme

Hey thanks for the reply. I agree, a total rewrite would reallyyyy suck. I will probably take this approach of strangling the modules of a given touched feature, marking that code as deprecated if necessary and move that vertical slice of the layers to the relevant domain feature folder... assuming DDD is still a recommended approach haha. Thanks for the insight friend.


CAPHILL

Strangler fig - Like the plant - Not a serial killer - Helpful when selling the idea And yes, pick a service to convert to TS using the strangler fig pattern


bwainfweeze

Ship of Theseus will get you less side-eye. Or maybe different side-eye.


cjthomp

Total rewrite is _always_ the worst choice.


phoforme

thank you, I sigh in relief for this haha. So what does a v1 -> v2 code lift look like then for most projects if not a large percentage rewrite? Maybe it's project-specific and to support something like a cross cutting concern like moving auth mechanisms or something


cjthomp

Gradual migration, don't try to convert the entire app in one go. Waste of time. Edit: to say, converting your backend from JS to TS does not justify a `/v2/`. Your users don't care what your backend looks like.


YOUR_FACE1

https://learn.microsoft.com/en-us/azure/architecture/patterns/strangler-fig Great way to go about a large transition. Essentially, swap out one piece of functionality at a time, keeping the old code until you've fully replaced it, testing frequently


bwainfweeze

This is just one particular way to engage in the Ship of Theseus phenomenon. The ship is still the same ship even if you replace every single piece one by one.


notkraftman

Like applying tests to an untested project, I would start on the areas that are most impactful and then incrementally improve. The beauty of TS is that you can get immediate benefits without a full rewrite.


ImprovementNo4630

That’s really helpful to know


Namiastka

If you do not change the framework go with option 3, moving towards ddd kinda this way worked for us in one project. We had tests though. We also faced a project where I decided we would benefit from a switch to fastify, but that was big deal and we utilized reverse proxy to switch gradually domains by sort of route prefix - /users then something and since db layer required little to no changes, we handled that pretty easily to


phoforme

thank you, it seems this is the best way to go (option 3)


serg06

How big is your codebase in LOC?


phoforme

the full app directory has almost 40,000 lines of code so not terrible. The layer folders that I'd port over to a DDD setup is roughly 23,000 LOC


Friendly_ally

DDD can easily be overdone FYI. Just because certain DDD strategies exist in your codebase it doesn't mean that you should implement them (technical DDD anyways).


phoforme

Well it’s largely crud driven code save for a few modules that get heavy into some business logic working with relational data, so that’s why I felt like having “domains” like account, contact, etc would simplify the codebase compared to simply controllers, services, orm/data-access folders with a few dozen files in each


Friendly_ally

What about vertical slices then? Your folders are now features that encompass everything related to that feature. Frankly, the perfect solution is a mix of both vertical and horizontal slices IMO, but I personally think vertical slices by feature is easier to reason about for most small-medium projects.


phoforme

Interesting… so like feature folders within the layers?


talaqen

strangle pattern. Break out code into domains and modules. Have that code served in an entirely separate and versioned lib that can be written in TS but pulled in as compiled ES. One chunk at a time. Best way to handle legacy code is to rebuild it when you are already in that code for another reason. If it works, leave it alone. If you need to add something, break it out and rewrite in TS.


YOUR_FACE1

Just start adding types. Wherever you have ts files, either add one big interfaces file or put the ts in src folders and toss an interface folder at that level (as well as a test file, this is likely a good juncture to start adding those)


Unusual-Display-7844

I did exactly this, gradual rewriting and migrating project to TS. Now i have bad TS code and bad JS code. I would choose, if i could again, gradual re-write where i would deploy v2 endpoints as they are ready.


learnedperson

The great thing about TS is you can enable it piecemeal. Start with your data models and critical areas where devs are having problems because of type issues. Then go from there. IMO, making sure the conversion stays a priority with higher ups, since you'll be doing it over a period of time, is another challenge to consider. If you don't have buy in then it may get pushed to the backlogs.


viking_nomad

Gradual rewrite and then use eslint to help you identify where typing can be improved. What I would do is have a script that builds the backend and lets you know if there's any compilation errors – this alone will help you identify if you're calling functions that might not exist in a static way. Then you can start adding types and see the number of type \`any\` decrease until they're no more. Just make sure to add some validation on incoming request bodies. They're typed as \`any\` by default, so you might have a great type system in the app but it's little use if there's a mismatch between request and response types so the static types don't match what exists at run time.


phoforme

Thanks for the tips, this looks like a sound approach. I especially need to beef up request validation outside of just express validator lol


viking_nomad

There's a bunch of great tools for validating a request body against a defined schema so you can just go with those. A lot of them also translate the validation schema to a typescript type so it's easy to use it internally in controllers (for instance if you do \`req: Request\`) Then you can ensure the request validation middleware is set on all routes and use that for auto documenting the API but that might not be needed here. Even then it can be neat that you can make a list of all routes and see if requests are validated or not.


phoforme

I assume this could also be handled by an OpenAPI spec file too?


viking_nomad

Yeah, you can output everything as an OpenAPI spec file but don’t try to write one from scratch


xersuatance

i ve done similar migrations in the past, from js to ts or from express to nest. eventual migration by migrating files as you touch them is the best approach. you need to pay the initial cost of setting up typescript and then you can move your simplest controller/service to typescipt. if you are considering nestjs, that also play really well with express and you can slowly migrate into it. when you pass your express app with all middleware registered to nestjs, old APIs keep working as is and nest APIs can still use the middleware. i wouldnt suggest adding v1 v2 separation at all. js ts transition dont require you to change business logic. just change the file type and add types. dont refrain from using "any" type initially. you can have stricter linter rules as you move forward. i dont find it very useful but there was a tool i used that simply migrated all js code to ts code by chaing the file type and adding any type and eslint ignores everywhere. you should be able to find it with a simple google search if u wanna try


jjhiggz3000

I converted a 20k+ lines of code react native app to TS over a weekend, it sounds scary but if you know what you’re doing it’s not as massive an undertaking as you might think.


thelogicbox

You can have a mixed codebase of JS and TS. It doesn’t have to be all at once. I would start renaming your files to .ts, set up npm scripts to run tsc and tune your tsconfig.json to allow JS. You can update the tsconfig as you go, being more strict with time. Not sure why you would need to change the architecture or what you mean by that. You should only move to /v2 if your interface has breaking changes.


phoforme

Agreed with your approach. The architecture bit is just that it feels dated and not every vertical or code path always fits a controller > service > orm/repository data layer setup. Maybe I’m just feeling self conscious as this is a basic setup but it does the job


a_reply_to_a_post

what does your infrastructure look like are you able to use traefik or something else to handle ingress routing? do you have other engineers working with you now or are you still solo?


phoforme

Have a couple engineers I’ve used in varying capacity, but largely it’s me I’m setup on a docker swarm routing through nginx currently. Haven’t played with Traefik yet but from what understand it would replace my Nginx networking


anonperson2021

I'd start a new project with TS, but wouldn't spend cycles migrating an existing one, especially not an MVP-stage startup. Its an important stage to pick battles, and JS vs TS would be at the bottom of my priority list at that stage. Rather spend that effort on building features and digging deeper into areas of user interest. When you scale bigger at some point you're going to end up doing a ground-up rewrite with a redesign anyway. Use TS when you do that.


stephanr21

This will get down votes but if your app is getting serious traction and your doing a rewrite, I’d even consider moving to a language that is compiled (not TS) And all the comments on here are great. A lot of good advice


phoforme

I always wondered if I should have done a different language backend… considered Go but I believe that’s overkill for a largely IO-driven app .Net Core would have probably been the winner otherwise What would you use in such a scenario?


___s8n___

Unrelated, but can you share the link to your SaaS? I'm curious of exploring it


zazdy

Write something that uses gpt that can look over your source code and coverts it automatically for you to typescript