The short answer
Refactoring improves existing code in small steps while the product keeps working; rewriting replaces it from scratch. Rewrites are tempting and frequently go badly: they take longer than expected, lose years of small fixes, and freeze the product while competitors move. Usually the better path is to refactor the worst parts, or replace the system piece by piece, an approach Martin Fowler calls the strangler fig. A full rewrite makes sense mainly when the product is small, or the technology truly cannot meet its needs.
Key points
- Refactor in small steps while the product keeps shipping.
- Rewrites take longer than planned and lose hidden fixes.
- Replace piece by piece where you can: the strangler fig approach.
- Small MVPs and dead-end technology can justify a rewrite.
- Add tests around code before changing it.
Why rewrites are risky
In 2000, Joel Spolsky wrote "Things You Should Never Do", arguing that rewriting working code from scratch is one of the worst strategic mistakes a software company can make. His example was Netscape, which went nearly three years between major releases after deciding to rewrite its browser. Old code looks messy partly because it contains years of fixes for real problems that nobody remembers.
- The new system must catch up with every feature of the old one before it is useful.
- Customers get no improvements while the rewrite happens.
- Edge cases the old code handled quietly come back as new bugs.
- The new code accumulates its own shortcuts under deadline pressure.
The options
| Option | What it means | Best when |
|---|---|---|
| Refactor | Improve structure in small steps, behavior unchanged | The code works but is hard to change |
| Strangler fig | Build new parts beside the old and move features across one at a time | Large systems, or a change of technology |
| Rewrite | Replace everything at once | Small products, or technology that cannot meet the need |
Martin Fowler describes the strangler fig as gradually building a new system around the edges of the old one until the old one can be retired, and notes it is less risky than a wholesale rewrite because value arrives early.
How to decide
- List the concrete problems: slow changes, frequent bugs, performance, security, hiring.
- Check whether each could be fixed in place.
- Estimate the real size of the system, including every edge case and integration.
- For an AI-built or prototype MVP with few users, a careful rebuild of the core can be reasonable. See Vibe coding to production.
- For a product with paying customers, prefer refactoring or a gradual replacement.
Changing code safely
- Add tests around the behavior you are about to change.
- Change one area at a time, and release often.
- Keep the product shipping improvements alongside the cleanup.
- Track the debt you are paying down. See What is technical debt?.
Monolith-to-services moves follow the same rule. See Monolith vs microservices. If you are weighing this for an early product, see MVP development.
Sources
Checked when this guide was written.