| > But writing code is the easy part. If it's inherently easier to write new code than maintaining old code, then we should do our best to also make it a good choice operationally. Maybe write a new service/module alongside the old thing and deploy that, instead of opting for a full rewrite immediately. What else are we supposed to do, suffer with codebases that decline over time, instead of using more modern tooling and frameworks? If adding a new module to a dated legacy Spring system would involve messing around for weeks with XML configuration mechanisms, brittle runtimes and mechanisms in the codebase and integrating everything with the existing solution whilst having risks in regards to overall stability, then you might as well bootstrap a new service in Spring Boot/Quarkus/whatever you're comfortable with and deploy it alongside the old thing. (using Java as an example here, because the old Spring projects I've seen got pretty bad sometimes) With reverse proxies or other gateway solutions, as well as containers and orchestration, deploying and routing traffic to this new service (even just for new endpoints in your existing domain/API) is not a big issue and many other concerns are covered, in addition to this new service being simpler to change and evolve over time, as well as replace altogether. However, the situation around the data and integrating various services with one another still is difficult - because with microservices your data model would be strewn across multiple separate databases, whereas with multiple services connecting to the same database you'd end up with something that's hard to reason about. Whereas if you call APIs directly, you're also introducing an unreliable network connection into the mix between the services, which has some overhead as well, or a complex message queue/pub-sub solution. I'm glad that we're far along enough for the former problems to be at least reasonably solved (things like 12 Factor Apps are great), but I don't think that there are all that many solutions for the latter group of issues, unfortunately. Even worse if you do a full rewrite and there's an expectation of the new thing to 1:1 match the logic and features of the old one, then you're setting yourself up for failure, unless the project is simple. |
You've captured the essence -- although we have solutions for when the impulse to rewrite an application occurs, we don't have solutions that don't bring their own problems.
Further, the natural human expectation for the software engineer to be like Chuck Norris, who can Fix Everything Without Changing Anything, needs to be considered and managed.
The opposite - where, as someone else termed it - the new version is based on a grab-bag/wishlist of everyone's hopes and dreams, generally doesn't work out either.
The true path does seem to be:
* setup integration tests that validate WHAT the system does (blackbox), rather than HOW the system works (vs whitebox).
* build the new system modularly, offloading one part of the old system's work onto the new system, one part at a time.
* keep the new and old system synchronized, using eventing/message-queues/etc (this is going to be difficult)
* continue replacing the old parts of the system with their new replacements until the old system no longer exists
* Now you've created the old system again, but the code and architecture are clean and capable of the changes that were not possible before
* Move forward confidently adding new features and capabilities to your new system which does not suffer from the problems of the old system.