You've heard of "tech debt sprints." Two weeks where the team stops shipping features and cleans up the mess. Management hates it. Developers hate it. And the codebase is messy again within a month.
There's a better way — and it doesn't require permission from anyone.
Two phases. Every feature.
Phase 1: Make it work (2 hours)
Get the feature functional. Tests passing. Deploy it. Resist every urge to chase perfection. The goal is working software in users' hands, not a museum piece.
Phase 2: Make it right (30 minutes, next morning)
Fresh eyes. Extract the pattern you've now seen three times. Rename the confusing variable. Add the missing type. Commit, move on.
The key insight: Phase 2 happens the next morning because you have fresh eyes and you're still in context. It's not a separate task — it's the end of the current one.
The Boy Scout Rule
Leave the campground cleaner than you found it.
Every file you touch, make one small improvement. Not a rewrite. Not a refactor. One better variable name. One extracted helper. One added type annotation.
Over weeks, this compounds. What starts as scattered small improvements becomes a fundamentally cleaner codebase — without ever scheduling a "cleanup sprint."
When to improve and when to skip
The decision is simpler than you think. Ask one question: Am I already paying the context-loading cost?
| Signal | Action | Budget |
|---|---|---|
| Tests pass, code is readable | Ship it | 0 min |
| Copied this pattern twice | Extract now (Rule of Three) | 30-60 min |
| Function is 200+ lines | Split before adding more | 20-40 min |
| Confusing variable names | Rename — you have context now | 5-10 min |
| Security or data integrity risk | Fix before shipping | Whatever it takes |
| Violation in a different module | Skip, note in debt register | 0 min |
If you're already in the file, improvement is cheap. If you'd have to context-switch to a different module, note it and move on.
The technical debt register
A simple markdown file that tracks debt worth paying down. Not a backlog that grows forever — a living document reviewed weekly.
### TD-012: Duplicate auth logic in 4 endpoints
- **Impact:** Medium
- **Trigger:** Next time we add an endpoint to this module
- **Estimate:** 1 hour
- **Status:** Open The trigger field is key. It tells you when to pay this debt — not "someday" but "next time you're already touching this code." That's when the context cost is lowest.
Real results
| Metric | Result |
|---|---|
| Features shipped (6 months) | 40 (no feature freeze) |
| Time lost to refactoring | 45 hours (spread across 6 months) |
| Code duplication at month 6 | 7% (vs 18% with big-bang sprints) |
| Velocity trend | Steady/increasing |
Compare that to the big-bang approach: 2-week freeze, 40+ introduced bugs, sawtooth velocity that drops every quarter when "tech debt sprint" hits the calendar.
The Rule of Three pays off
| Feature type | First time | Fifth time | Speedup |
|---|---|---|---|
| CRUD endpoint | 3 days | 4 hours | 6x |
| Filtered list | 2 days | 3 hours | 5x |
| Notification handler | 1 day | 45 min | 11x |
That 30-minute extraction on the third occurrence? It's paying dividends on every feature that follows.