The 90‑Minute Refactor: How Tiny Code Cleanup Sessions Transform Messy Projects Over Time
How short, focused refactoring sessions can steadily transform messy legacy code into a maintainable, high‑quality system—without blowing up your delivery schedule.
The 90‑Minute Refactor: How Tiny Code Cleanup Sessions Transform Messy Projects Over Time
You don’t need a three‑month “rewrite” to fix a messy system.
You need 90 minutes.
Not once—repeatedly.
Short, time‑boxed refactoring sessions can quietly transform a painful, legacy codebase into something your team can understand, extend, and ship from confidently. Done right, this doesn’t derail delivery. It accelerates it.
This post walks through how 90‑minute refactor sessions work, why they’re so effective, and practical patterns to apply safely, even in fragile systems.
Why Time‑Boxed Refactoring Works
Refactoring is often postponed because it feels big and risky. Managers worry: “If we let developers refactor, we’ll never ship.” Developers worry: “If I touch this, I’ll break everything.”
Time‑boxing—e.g., 90‑minute refactor blocks—addresses both fears.
1. It prevents quality work from killing delivery
A 90‑minute timebox is long enough to do something meaningful, but short enough not to derail a sprint.
You can:
- Schedule one or two 90‑minute sessions per week
- Attach them to existing work (e.g., as part of a feature or bugfix)
- Keep them visible on the board (e.g., dedicated “Refactor” tasks)
This keeps refactoring from turning into an open‑ended “we’ll fix everything” marathon. You stop when the timer stops. If the change isn’t complete, you:
- Commit a small, safe improvement
- Leave a
TODOor ticket for follow‑ups
Over weeks, these sessions add up to real structural improvements—without asking stakeholders for a giant, risky refactor project.
2. It lowers the psychological barrier to starting
“Make the payment system less awful” is overwhelming.
“Spend 90 minutes making just this method less awful” is manageable.
Time‑boxing reframes refactoring as a routine maintenance activity, not a dramatic event. That shift in mindset is crucial to actually doing it.
Incremental Refactoring: Evolving Legacy Code Safely
Throw‑away rewrites are seductive and dangerous. Incremental refactoring is boring and powerful.
Incremental refactoring means:
- Many small, deliberate changes
- Each step keeps the system working
- No “big bang” switch‑overs
Instead of:
“We’ll replace the whole module in 3 months.”
You do:
“We’ll make this module slightly less painful every week—and never stop shipping.”
Characteristics of good incremental changes
In a 90‑minute session, a “good” refactor is:
- Behavior‑preserving – The system should do the same thing before and after.
- Scoped – Touch a small area; don’t chase every problem you see.
- Reversible – Easy to roll back if tests or production reveal issues.
Examples of safe, incremental steps:
- Rename a confusing method and update call sites
- Extract a 200‑line method into smaller, named helper methods
- Replace a magic number or string with a named constant or configuration
- Introduce a small, focused class to encapsulate a messy responsibility
Over time, these tiny steps reshape even the ugliest legacy code into something coherent.
Testing: Your Safety Net for Refactoring
Refactoring without tests is like surgery without scans.
Strong testing practices turn refactoring from a gamble into a controlled, repeatable process.
What “strong testing” means in this context
You don’t need 100% coverage, but you do need:
- Fast, automated tests – So you can run them repeatedly during a 90‑minute session.
- Coverage of critical behavior – Focus on flows where failure is expensive: payments, authentication, data integrity.
- Confidence indicators – When tests pass, you’re reasonably sure you didn’t break core behavior.
If you’re starting with poor tests
In truly messy systems, you often start refactoring by… adding tests.
A good pattern:
- Identify a specific behavior or bug.
- Write a test that captures the current (even if ugly) behavior.
- Run the test and watch it pass or fail.
- Refactor in small steps, re‑running the test suite constantly.
You might spend the first 90‑minute session only on characterization tests: tests that document what the code does today, before you clean it up.
Those tests then protect you as you reshape the code in the next sessions.
Practical Refactoring Patterns for Messy Code
Here are simple patterns well‑suited to 90‑minute refactor blocks.
1. Rename for clarity
Goal: Make the code read like an explanation of the domain.
Examples:
doStuff()→calculateOrderTotal()flag→isEligibleForDiscountdataMap→customerIdToSubscription
Renaming is low risk but high impact for comprehension. Modern IDEs make it safe and mechanical.
2. Extract method
Goal: Break down long functions into named, reusable building blocks.
Example before:
void processOrder(Order order) { // validate // apply discounts // calculate totals // send confirmation email }
After (over a few sessions):
void processOrder(Order order) { validate(order); applyDiscounts(order); calculateTotals(order); sendConfirmation(order); }
Each extracted method gets its own tests or is covered indirectly by existing tests.
3. Extract class
Goal: Move a cluster of related behavior and data into its own type.
When a class or module “does everything,” look for seams:
- Payment logic
- Email notifications
- Price calculations
Example: Move pricing logic out of OrderService into PriceCalculator. Start with one or two methods in a 90‑minute block; migrate the rest over time.
4. Introduce parameter or configuration
Goal: Remove magic values and hard‑coded decisions.
Example:
- Replace
if (country == "US")scattered everywhere with a strategy or configuration lookup. - Use
MAX_RETRY_ATTEMPTSinstead of bare3in multiple places.
You can often do this gradually: introduce the constant and update call sites incrementally.
5. Wrap external dependencies
Goal: Create a stable seam around APIs and libraries.
Wrap direct usages like:
axios.post("https://service/endpoint", data)
With a local abstraction:
paymentClient.chargeCustomer(customerId, amount)
Later sessions can refactor paymentClient without touching the rest of the codebase—and tests can mock it more easily.
Refactoring as a Path to “Quality at Speed”
Modern software teams are under constant pressure: ship faster, adapt faster, recover faster.
Messy code fights that goal. It slows onboarding, complicates debugging, and makes changes risky. That’s where disciplined, regular refactoring shines.
How 90‑minute refactors improve speed
Over time, teams that prioritize code quality and refactoring habits often see significant productivity gains—on the order of 25% or more:
- Fewer bugs – Clearer, better‑structured code reduces unintended side effects.
- Faster changes – Well‑factored modules are easier to modify safely.
- Lower cognitive load – Developers spend less time deciphering and more time building.
- Easier onboarding – New team members get productive faster when the code explains itself.
This is “quality at speed” in practice: continuous improvement of internal code quality that supports, rather than competes with, delivery.
Building a Culture of Ongoing Refactoring
You can’t rely on heroics or one‑off cleanup projects. The real payoff comes when refactoring becomes a standard, expected practice for the whole team.
1. Normalize refactoring in the workflow
- Add refactoring tasks to the backlog as first‑class work items.
- Allow small, opportunistic refactors as part of feature work: “Leave the campsite cleaner than you found it.”
- Include refactoring in Definition of Done when appropriate (e.g., “touched code is covered by tests and reasonably clean”).
2. Protect time explicitly
If you rely only on “spare time” for cleanup, it won’t happen.
- Reserve recurring 90‑minute blocks per sprint or week.
- Use them to pay down the most painful hotspots (guided by error rates, change frequency, or team complaints).
3. Make quality visible
- Track and celebrate improvements: reduced cyclomatic complexity, fewer bugs in a module, faster build times.
- Use code reviews to encourage small, continuous improvements.
- Share before/after examples in team meetings to show tangible benefits.
4. Lead by example
Tech leads and senior engineers should:
- Use their own 90‑minute blocks and talk about what they did
- Push back on shortcuts that create long‑term mess for short‑term wins
- Mentor others in safe refactoring patterns and test‑driven changes
A culture where refactoring is “just how we work” amplifies gains across the entire team.
Putting the 90‑Minute Refactor into Practice
You can start this week.
- Pick a pain point. A file or module everyone avoids or complains about.
- Schedule one 90‑minute block. Put it on the calendar like a meeting.
- Write or improve tests first. Even a couple of key tests are better than none.
- Choose one small pattern. Rename, extract method, or wrap a dependency.
- Commit a safe, incremental improvement. Don’t chase perfection.
- Repeat next week. Refine based on what you learned.
After a month, you’ll notice:
- The worst hotspots are less scary.
- Code reviews feel smoother.
- Changes ship with fewer surprises.
After six months, your “legacy” system may still be old—but it will be understandable, testable, and far less painful to work with.
Conclusion
You don’t fix messy projects with one heroic rewrite. You fix them with dozens—maybe hundreds—of small, safe, 90‑minute refactors.
Time‑boxed, incremental refactoring, backed by solid tests and practical patterns, lets you steadily improve internal quality while still delivering features. Over time, the payoff is real: higher productivity, fewer bugs, and a codebase your team isn’t afraid to touch.
Start small. Protect 90 minutes. Improve one rough edge.
Then do it again next week.
That’s how messy projects quietly become maintainable systems—one tiny refactor at a time.