When an existing product becomes slow to change, a rewrite can feel like the cleanest answer. It promises a better architecture without the compromises accumulated in the current system.
The difficult part is that a rewrite also replaces working behaviour, operational knowledge and years of edge cases. Before making that investment, I prefer to separate the visible frustration from the actual constraints on the business.
Define what “difficult” means
Different symptoms point to different remedies. Releases may be risky because tests are weak. Development may be slow because responsibilities are unclear. Incidents may come from one integration rather than the core architecture. Product decisions may be blocked by missing ownership instead of code quality.
Gather concrete examples:
- changes that took much longer than expected;
- recurring incidents and their causes;
- areas developers avoid modifying;
- manual deployment or recovery steps;
- dependencies that are unsupported or unsafe;
- product opportunities the system cannot support.
This turns “the system is bad” into a set of problems that can be prioritised.
Map business-critical flows
Not every part of a product deserves the same attention. Identify the workflows that create revenue, protect data, serve customers or keep operations running. Then trace the software and external services those flows depend on.
This map reveals where technical problems create business risk. It also shows which stable parts can remain untouched while higher-risk areas are improved.
Measure change risk, not code age
Old code is not automatically bad, and new code is not automatically easy to operate. Useful signals include how often an area changes, how reliably it can be tested, whether failures are observable and how many systems depend on its behaviour.
A stable component that rarely changes may need documentation, not replacement. A small but frequently modified component with weak boundaries may deserve attention first.
Compare three options
Most product recovery plans combine more than one of these approaches:
- Stabilise: add monitoring, tests, safer deployment and operational documentation around the current behaviour.
- Modernise incrementally: improve boundaries, replace a dependency or move one workflow while the product remains in use.
- Replace deliberately: rebuild a component or product when the current constraints make incremental change more expensive or unsafe.
The comparison should include delivery risk, migration, parallel operation, data movement, missing behaviour and the opportunity cost of pausing normal product work.
Create a staged recovery plan
Start with work that improves visibility and reduces immediate risk. That may mean observing failures, automating a fragile release, protecting a critical flow with tests or documenting an integration before changing it.
The next stages can address architecture and product capability with better evidence. Every stage should have a reason the business can understand: fewer dangerous releases, a shorter path to a needed feature, removal of an unsupported dependency or clearer ownership.
Keep the decision reversible
A useful modernisation plan creates checkpoints. After each stage, review what was learned and whether the original diagnosis still holds. If incremental work is succeeding, continue. If evidence shows that a boundary cannot be repaired responsibly, replace that part with a clear migration plan.
The goal is not to preserve old software at any cost. It is to regain control of the product while avoiding a larger, riskier project based only on frustration.