Software Engineering
Refactoring: When and How
In 1999 Netscape decided to rewrite their browser from scratch. Internet Explorer enjoyed three years of monopoly. By the time Netscape 6 shipped, market share had collapsed from 80% to 7%. Joel Spolsky called it "the single worst strategic mistake that any software company can make." Refactoring was the alternative they refused.
- **Netscape 6 (2001)**: a from-scratch rewrite that cost three years and 73% of market share
- **Riot Games (2018)**: League of Legends client rewrite - two years, the product barely survived
- **Riot Games (2020)**: the same team migrated the renderer through refactoring - six months, zero downtime
- **Spotify Engineering**: the 20% rule - teams permanently spend a fifth of their time paying down technical debt
- **Stripe API**: evolution through refactoring since 2011 - 13 years without a breaking major version change
Code smells
A code smell is a surface-level symptom of a deeper structural problem. The code still works, but the shape will not survive the next requirement change. Fowler cataloged roughly 20 canonical smells, and each one has a matching refactoring recipe.
**Top five smells:** Long Method (>20 lines), Large Class (>200 lines, >7 fields), Long Parameter List (>4 parameters), Duplicated Code (identical blocks scattered across files), Feature Envy (a method reaches into another object more than into its own).
DeliveryService.calculatePrice() reads 8 fields from Order and zero from its own class. Which smell is this?
Refactoring catalog
Every smell maps to one or more named refactorings: typed transformations that preserve observable behavior. Modern IDEs apply them automatically, which is what makes the practice realistic in large codebases. Manual reshuffling of code by hand is what gets people into regression bugs.
**The basic six:** Extract Method (turn a long block into a function), Inline Method (the reverse), Rename, Move Method/Field, Extract Class (split a class along seams), Replace Conditional with Polymorphism (switch on type becomes a subclass hierarchy).
A class has 600 lines and four method groups that operate on disjoint field sets. Which refactoring fits?
Safe refactoring practice
The core rule is that refactoring must not change observable behavior. The only realistic way to enforce that rule is automated tests. Without coverage, refactoring drifts into rewriting and usually surfaces a regression in production two weeks later.
**Fowler's loop:** 1) Confirm tests are green. 2) Apply one tiny refactoring step. 3) Run tests. 4) Commit. 5) Repeat. If step 3 turns red, revert and split the step into something smaller.
**Antipattern:** the "big refactoring" - two weeks on a feature branch, a massive merge, and breakage everywhere. The proven alternative is the Mikado Method or the strangler fig pattern: gradual component replacement while the system stays alive.
When is refactoring safest to perform?
Technical debt
Ward Cunningham's metaphor: a compromise in architecture behaves like a loan. Taking it speeds up the next release, but interest in the form of slowdown accrues every week. Skip the payments long enough and the team hits bankruptcy: a rewrite from scratch.
**Fowler's quadrant:** Deliberate/Inadvertent crossed with Reckless/Prudent. The deadliest cell is Inadvertent-Reckless ("we did not know better and we did not think about it"). The healthiest is Deliberate-Prudent ("ship now, refactor next sprint, and the ticket is already on the board").
**Payment strategies:** the 20% rule (Spotify) - teams spend one fifth of capacity on debt. Refactoring Fridays - one day per week dedicated to cleanup. Boy Scout rule - every ticket leaves the code cleaner. Emergency mode - a dedicated tech-debt sprint once per quarter.
Refactoring means rewriting a module from scratch.
Refactoring is a series of micro-steps, after each of which the code compiles and the tests pass. A rewrite changes behavior and has no working intermediate state.
Riot Games rewrote the League of Legends client from scratch in 2018 - the project took two years and nearly died. The same team later migrated the renderer through refactoring - six months, zero downtime. Working intermediate states are the key difference: they allow rollback at any step.
A team reports: features get slower, tests fail intermittently, and writing new code feels dangerous. What is happening?
Key ideas
- A code smell is a surface symptom of a deeper problem. Top five: Long Method, Large Class, Long Parameter List, Duplicated Code, Feature Envy
- Every smell has a recipe. The basic six: Extract Method, Inline, Rename, Move, Extract Class, Replace Conditional with Polymorphism
- Safe refactoring is small steps, green tests after each step, commit. Mikado method or strangler fig instead of big bang
- Technical debt accrues interest. Payment strategies: 20% rule, Boy Scout rule, Refactoring Fridays. Ignoring it leads to bankruptcy
Related topics
Refactoring is impossible without a test safety net and without principled judgment about code quality:
- Property-Based and Mutation Testing — tests as a refactoring safety net
- Clean Code — the next level after refactoring
- Unit Testing — mandatory precondition for safe refactoring
- SOLID principles — criteria for evaluating refactoring outcomes
Вопросы для размышления
- Name three code smells in your current project. Which refactoring would you apply to each?
- When did technical debt last block a feature in your work? What payment was most urgent?
- Describe a safe refactoring path for an 800-line class with no tests. Where do you start?
- How is refactoring different from rewriting? When would you choose the rewrite?
Связанные уроки
- se-13 — Tests protect refactoring from regressions
- se-15 — Refactoring opens the path to design patterns
- alg-01-big-o — Understanding complexity helps assess refactoring impact
- ds-06-hash-intro — Replacing conditionals with hash tables - classic performance refactoring
- db-11-query-optimization — Query refactoring and code refactoring share the same readability principles
- comp-01-intro