Module 2: Refactoring Techniques
Primary text: Refactoring: Improving the Design of Existing Code (Fowler, 2nd edition) Selective support: Working Effectively with Legacy Code ideas (seams, enabling points), Clean Code for readability pressure, Good Code, Bad Code for engineering judgment around rollout
This guide is the primary teacher. You do not need to read Fowler's catalog front-to-back to complete this module. You do need to become operational at naming a refactor move, executing it in small behavior-preserving steps, proving behavior is preserved by tests, and knowing when to stop.
Scope of This Module
This module is not a pattern-name trivia quiz. It is where "clean up the code" stops being vague and becomes a named, disciplined sequence of moves under a test harness.
What it covers in depth:
- the precise definition of refactoring as behavior preservation
- the two-hats rule that keeps refactoring commits separate from feature commits
- the three triggers: rule of three, preparatory, comprehension, and opportunistic
- characterization tests that pin current behavior before any structural change
- seams and enabling points from Working Effectively with Legacy Code
- the core Fowler moves: Extract/Inline Function, Extract Variable, Rename, Change Function Declaration, Move Function/Field, Split Phase, Encapsulate Record, Replace Primitive with Object
- conditional restructuring: Replace Conditional with Polymorphism, Decompose Conditional, Consolidate Conditional Expression
- API reshaping: Introduce Parameter Object, Preserve Whole Object
- large-scale strategies: Branch by Abstraction, Parallel Change, Strangler Fig
What it deliberately does not try to finish here:
- the full Fowler catalog of seventy-plus refactorings
- comprehensive legacy-code recovery techniques (Module 5 and future)
- design patterns as destinations (that is Modules 3 and 4)
- team-process choreography like feature branching and trunk-based development (Track B)
This is the operational core. If you can quote the names but cannot execute the moves without breaking tests, you are not done.
Before You Start
Answer these closed-book before starting the main path:
- What is the difference between "cleaning up code" and "refactoring" as Fowler defines it?
- Why is it a rule to never add a feature and refactor in the same commit?
- What does a characterization test assert, and why is that different from a regular unit test?
- If you cannot test a function because it calls the network, what structural change gives you a place to intervene?
- When is Replace Conditional with Polymorphism the wrong move?
Diagnostic Interpretation
4-5 solid answers
- You are ready for the full path.
2-3 solid answers
- Continue, but expect extra time in Cluster 2 (safety net) and Cluster 4 (conditional restructuring).
0-1 solid answers
- Revisit S3M1 smells first. Refactoring is the answer to smells; if you cannot name the smell, the move is arbitrary.
What This Module Is For
Refactoring is the mathematical language of safe change. Every real codebase asks questions like:
- how do I clean up this 300-line function without breaking the existing callers?
- how do I add a new feature when the shape of the existing code fights me?
- how do I get this untested legacy class under test before I change it?
- how do I replace a library dependency without a six-month branch?
- which of the ten code smells I just spotted should I fix first?
This module builds the change-safety skills needed for:
- Module 3 and 4, where patterns are often introduced by refactoring, not written from scratch
- Module 5, where code review depends on being able to read diffs move by move
- real production work, where the "big rewrite" almost always ends badly and the incremental replacement almost always wins
You are learning to reshape running code without stopping it.
Concept Map
How To Use This Module
Work in order. Later clusters assume the discipline and test habits from earlier ones.
Cluster 1: The Refactoring Discipline
| Order | Concept | Type | Focus |
|---|---|---|---|
| 1 | Refactoring Is Behavior Preservation | PRIMARY | Fowler's precise definition and why "any cleanup" is not refactoring |
| 2 | The Two Hats: Refactor or Feature, Never Both | PRIMARY | Kent Beck's metaphor as a commit-level discipline |
| 3 | When to Refactor: Rule of Three, Preparatory, Opportunistic, Comprehension | PRIMARY | The four triggers and why scheduled "refactoring sprints" usually fail |
Cluster mastery check: Can you explain why a commit labeled "refactor and fix bug" is a defect in the commit itself?
Cluster 2: Tests as a Safety Net
| Order | Concept | Type | Focus |
|---|---|---|---|
| 4 | Characterization Tests: Pin Behavior Before Change | PRIMARY | Capturing current behavior, including bugs, as the baseline |
| 5 | Legacy Seams and Enabling Points | PRIMARY | Feathers' vocabulary for breaking dependencies without editing everywhere |
| 6 | Test Granularity: Unit vs Integration During a Refactor | SUPPORTING | Which level of test actually protects which kind of move |
Cluster mastery check: Can you name a seam in code you wrote this week and the enabling point that would activate a stub?
Cluster 3: Fundamental Refactor Moves
| Order | Concept | Type | Focus |
|---|---|---|---|
| 7 | Extract Function and Inline Function | PRIMARY | The intention/implementation split and its inverse |
| 8 | Extract Variable, Rename, Change Function Declaration | PRIMARY | Naming as a refactor discipline, not a cosmetic |
| 9 | Move Function, Move Field, Split Phase | PRIMARY | Relocation moves that fix coupling and sequential-phase tangles |
| 10 | Encapsulate Record and Replace Primitive with Object | PRIMARY | Giving anonymous data a home and behavior |
Cluster mastery check: Can you execute Extract Function on a 40-line function in 10 minutes with every test still green?
Cluster 4: Reorganizing Data and Logic
| Order | Concept | Type | Focus |
|---|---|---|---|
| 11 | Replace Conditional with Polymorphism | PRIMARY | Turning recurring switch statements into type-dispatched behavior |
| 12 | Introduce Parameter Object and Preserve Whole Object | SUPPORTING | Shrinking parameter lists by letting data clumps become types |
| 13 | Decompose and Consolidate Conditional | SUPPORTING | Extracting the "why" from tangled branches; combining parallel branches |
Cluster mastery check: Can you decide between polymorphism, parameter object, and a decomposed conditional for a given smelly function, and justify the choice?
Cluster 5: Refactoring Large Changes and Rollout
| Order | Concept | Type | Focus |
|---|---|---|---|
| 14 | Branch by Abstraction and Parallel Change | PRIMARY | Keeping the system shippable through a multi-week change |
| 15 | Incremental Strangler Refactors in Production | SUPPORTING | Replacing a legacy component while users are still using it |
Cluster mastery check: Can you sketch how to replace a hand-rolled ORM with a library in a system shipped three times a week, without a two-month branch?
Then work these practice pages:
| Order | Practice path | Focus |
|---|---|---|
| 1 | Behavior Preservation Lab | Characterization tests and seam identification |
| 2 | Small Refactor Moves Workshop | Extract, inline, rename, move drills with tests green |
| 3 | Conditional and Data Restructuring Clinic | Polymorphism, parameter objects, decomposed conditionals |
| 4 | Code Katas | Six to eight refactor katas with real before/after code |
Use Module Quiz after the concept and practice path. Use Reference and Selective Reading and Learning Resources only for targeted reinforcement.
Learning Objectives
By the end of this module you should be able to:
- State Fowler's definition of refactoring and explain what "observable behavior" rules in and out.
- Keep refactor and feature commits separate and explain the cost of mixing them.
- Choose between rule-of-three, preparatory, opportunistic, and comprehension refactoring as triggers.
- Write characterization tests for a function whose current behavior is not documented.
- Identify seams in legacy code and name the enabling point for each seam.
- Execute Extract Function, Inline Function, Extract Variable, Rename, and Change Function Declaration without breaking tests.
- Execute Move Function, Move Field, and Split Phase on code where the module boundary was wrong.
- Execute Encapsulate Record and Replace Primitive with Object to give data a home.
- Replace a recurring type-switch with polymorphism, and know when not to.
- Introduce Parameter Object, Preserve Whole Object, Decompose Conditional, and Consolidate Conditional Expression deliberately.
- Plan a large-scale change using Branch by Abstraction, Parallel Change, or a Strangler Fig, keeping the system shippable throughout.
Outputs
- a refactoring journal with at least 15 named moves applied to real code, each with a before/after diff and a test log
- one characterization-test set for at least one function you did not write
- one seam-identification memo naming at least 5 seams and their enabling points in code you work with
- one mixed-commit-audit: find a PR that mixes refactor and feature, and rewrite its commit history into clean refactor-only and feature-only commits
- one "kata log" where each of the six to eight katas has been completed at least twice
- one large-change plan for a real system you use, choosing between Branch by Abstraction, Parallel Change, and Strangler Fig with written justification
- one mistake log naming at least 10 real refactor mistakes (e.g.,
changed behavior in a rename,introduced a bug during Extract Function because of aliasing,applied polymorphism with only one subclass)
Completion Standard
You have completed Module 2 when all of these are true:
- you can name the move before you do it
- you run tests between every refactor step, not just at the end
- your refactor commits do not contain feature code, and vice versa
- you can characterize a legacy function with tests before you touch it
- you can point to a seam in real code and say "here is the enabling point"
- you can talk someone out of Replace Conditional with Polymorphism when the conditional is not recurring
- you can propose a large-scale change without a multi-week dark branch
If you can do the moves but cannot explain why one move is better than another for a given smell, the module is not complete.
Reading Policy
- Concept pages are the main path.
- The Refactoring book chunks are selective reinforcement, not a second syllabus.
Read only if stuckmeans try the concept page, self-check, and drill first.Optional deep divemeans additional nuance or alternate examples, not required progression.- External Fowler articles are cited only where they add something the book chunks do not already carry.
Suggested Weekly Flow
| Day | Work |
|---|---|
| 1 | Concepts 1-3; write the two-hats contract and pin it to the repo |
| 2 | Concepts 4-6; characterize a function you did not write |
| 3 | Concepts 7-8; Extract Function / Rename katas |
| 4 | Concepts 9-10; Move Function / Split Phase drills |
| 5 | Concepts 11-13; conditional restructuring clinic |
| 6 | Concepts 14-15; large-change plan |
| 7 | Practice pages, quiz, mistake-log cleanup |
Reference
If you need exact links into the local chunked books, use Reference and Selective Reading.
Rich Learning Pages
Worked Examples | Guided Labs | Case Studies | Mistake Clinic | Reading Guide | Capstone Thread
Model Artifact Calibration
For reviewable refactoring evidence, compare your writeup to the refactor PR model artifact.