Rebuild a product without putting work on hold
Protect what already works
Old code is not automatically bad code. Some parts may be awkward to maintain but still serve customers every day. Write down the customer tasks, integrations, and operational habits that cannot break during the rebuild. They are constraints for the new work, even when the implementation behind them changes completely.
Name the cost of the current system
"The codebase is a mess" does not explain why a rebuild deserves time and money. Look for the cost instead. A release may take days longer than it should. The same area may cause repeated incidents, or a needed feature may keep slipping because nobody can change that part safely.
Use the evidence you already have. Hours lost, delayed releases, and recurring failures help distinguish a focused replacement from a wider rebuild.
Choose the first boundary
Start with a part that has a clear beginning and end, such as one page, service, or internal task. It should be possible to release that part while the rest of the product stays on the current system. A good boundary lets the team learn from production without committing the whole product to the same decision.
Treat the first release as evidence about this one boundary. It should work better than the part it replaces and coexist with the work around it.
Make every release reversible
Choose the return path before implementation begins. Use a feature flag, run the new and old paths in parallel, or keep the previous version ready to deploy until the replacement settles. The right method depends on the part being changed, but the team should know how to step back before it needs to.
Define done before the work begins
Write the acceptance conditions for each part and name the person who approves them. This prevents the rebuild from collecting unrelated improvements as it moves. New ideas can still be useful, but they should become deliberate follow-up work instead of quietly extending the current part.
Devloop