Step 5
Progressive refactor · trade-offs
25 min
Progressive refactor · trade-offs
Big-bang rewrites almost always fail. Small steps and always-deployable branches win long-term.
1. Why rewrites fail
- Scope inflation
- Feature freeze during rewrite
- Hard to verify N existing behaviours
- Motivation decays after 6 months
Netscape · Twitter · Basecamp all warn against rewrites.
2. Principles
- Every commit deployable
- Place new code alongside old, switch gradually
- Use feature flags for rollback
- Track progress with measurable metrics
3. Strangler Fig
Phase 1: [Old] ← all traffic
Phase 2: [Router] → [Old] + [New] (partial routes)
Phase 3: [Router] → [New]
Phase 4: [Old] removed
pryzeet's /api/admin/* → admin app (2026-04-24) followed this.
4. Branch by Abstraction
interface EmailSender { send(to: string, body: string): Promise<void>; }
class SmtpSender implements EmailSender { ... }
class SesSender implements EmailSender { ... } // new
const sender = process.env.EMAIL_PROVIDER === "ses" ? new SesSender() : new SmtpSender();
Switch per environment, validate, promote.
5. Tests as safety net
Write characterisation tests before refactor. Refactor while tests stay green.
6. One-day PRs
A PR should take one day max.
- ≤ 500 lines
- CI green
- Rollback independently
7. Trade-offs on purpose
| Axis | Pick A | Pick B |
|---|---|---|
| Performance vs readability | optimised, commented | simple, slower |
| Strict types vs speed | TS strict | any, prototype |
| SSR vs CSR | fast initial, server load | slow initial, smooth interaction |
| ORM vs raw SQL | type-safe, productive | fast, transparent |
Record "why A over B" in comments or PR descriptions.
8. Anti-pattern — "rewrite everything"
Before yielding to this urge:
- Do you really need all of it rewritten?
- The current code works — that's worth something
- Will you repeat the same mistakes?
Prefer tests + partial refactor + new alongside + gradual switch.
9. Small consistency
Code style is team agreement, not truth. Pick function vs const, automate with Prettier/ESLint.
10. Sunk cost fallacy
"We already spent a month, so we must continue" is a trap. Re-ask "would I start this today?". Dropping is sometimes correct; the learning stays.
11. Gotchas
- Stopping in the middle of an N-step migration → permanent dual system
- Over-abstracting "just in case" → YAGNI
- Refactor without tests → late regressions
- Mixing refactor and new features → hard to isolate causes
Closing
The urge to "rewrite it all" is common and almost always a trap. A bit every day, tests, and conscious trade-offs are easier long-term.
Next
- 06-agent-friendly-docs