refactoring-patterns — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited refactoring-patterns (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
A disciplined approach to improving the internal structure of existing code without changing its observable behavior. Every refactoring follows the same loop: verify tests pass, apply one small structural change, verify tests still pass.
Refactoring is not rewriting. It is a sequence of small, behavior-preserving transformations, each backed by tests. You never change what the code does — only how it is organized. Big-bang rewrites fail because they combine structural change with behavioral change, making it impossible to know which broke things.
The foundation: Bad code is a natural consequence of delivering under time pressure, not a character flaw. Code smells are objective signals of degraded structure; the smell catalog tells you where to look, and the refactoring catalog tells you what to do.
Goal: 10/10. Score structural quality by how many of the eight Quick Diagnostic rows pass — score = round(passed / 8 × 10), adjusting down when a single smell is severe. Bands:
Always state the current score, name the smells driving it down, and list the specific refactorings needed to reach 10/10.
Six areas of focus for systematically improving code structure:
Core concept: Code smells are surface indicators of deeper structural problems — not bugs, but signals that the design makes code harder to understand, extend, or maintain. Each smell maps to named refactorings that fix it.
Why it works: Named smells give teams objective criteria instead of subjective "I don't like this" — "This is Feature Envy" points directly at the fix.
Key insights:
Code applications:
| Context | Pattern | Example |
|---|---|---|
| Method > 10 lines | Extract Method | Pull loop body into calculateLineTotal() |
| One change touches many classes (Shotgun Surgery) | Move Method/Field | Gather the scattered behavior into one class |
| Same params in many methods | Introduce Parameter Object | startDate, endDate → DateRange |
| Copy-pasted logic | Extract Method + Pull Up Method | Share via common method or base class |
See references/smell-catalog.md when you need to name a smell and its fix — all five families (Bloaters, OO Abusers, Change Preventers, Dispensables, Couplers) with detection heuristics and the refactoring each maps to.
Core concept: Most refactoring starts here: break long methods into smaller, well-named pieces that read like prose — high-level steps delegating to clearly named helpers.
Why it works: Short methods with intention-revealing names eliminate comments, make bugs obvious at a glance, and enable reuse; a method call costs nothing to read when the name says everything.
Key insights:
Code applications:
| Context | Pattern | Example |
|---|---|---|
| Block with a comment | Extract Method | // check eligibility → isEligible() |
| Temp used once | Inline Variable | Drop const price = order.getPrice() |
| Trivial delegating method | Inline Method | Inline return deliveries > 5 if used once |
| Method with many tangled locals | Replace Method with Method Object | Locals become fields in a new class |
See references/composing-methods.md when applying any method-level transformation — step-by-step mechanics and before/after code for Extract/Inline Method, Extract/Inline Variable, Replace Temp with Query, Split Temporary Variable, and Replace Method with Method Object.
Core concept: The key OO design decision is where responsibilities live. When Feature Envy, excessive coupling, or unbalanced class sizes show a method or field is in the wrong class, move it where it belongs.
Why it works: A method placed away from the data it uses creates invisible cross-class dependencies, so one logical change ripples across many files — Shotgun Surgery. Co-locating method and data confines the change to one class.
Key insights:
Code applications:
| Context | Pattern | Example |
|---|---|---|
| Method envies another class | Move Method | calculateShipping() from Order to ShippingPolicy |
| God class 500+ lines | Extract Class | Pull Address fields/methods into own class |
Client calls a.getB().getC() | Hide Delegate | Add a.getCThroughB() |
| Class only forwards calls | Remove Middle Man | Let client call the delegate directly |
See references/moving-features.md when deciding where a responsibility belongs — mechanics for Move Method/Field, Extract/Inline Class, Hide Delegate, and Remove Middle Man.
Core concept: Raw data — magic numbers, exposed fields, integer type codes — creates subtle bugs and scatters domain knowledge. Replace primitives with objects that encapsulate behavior and enforce invariants.
Why it works: An int amount has no rounding rules or currency code; a Money object encapsulates all of it, so business rules live in one place and the type system catches errors at compile time.
Key insights:
EmailAddress, Money, Temperature)Customer, not copies)Code applications:
| Context | Pattern | Example |
|---|---|---|
if (status == 2) | Replace Magic Number | if (status == ORDER_SHIPPED) |
String email passed everywhere | Replace Data Value with Object | EmailAddress class with validation |
| Getter returns mutable list | Encapsulate Collection | Return Collections.unmodifiableList(items) |
int typeCode with switch | Replace Type Code with Subclasses | Employee → Engineer, Manager |
See references/organizing-data.md when replacing primitives with objects — mechanics for Replace Data Value with Object, Change Value to Reference, Replace Magic Number, Encapsulate Field/Collection, and the Replace Type Code variants.
Core concept: Deeply nested if/else trees, long switches, and scattered null checks are the hardest code to read and the most bug-prone. Named refactorings decompose, consolidate, and replace conditionals with clearer structures.
Why it works: A six-branch conditional forces readers to simulate every path mentally; well-named extracted branches are self-documenting, and polymorphism eliminates whole categories of "forgot this case" bugs.
Key insights:
if (x == null) checks; Introduce Assertion makes assumptions fail fastCode applications:
| Context | Pattern | Example |
|---|---|---|
Long if with complex condition | Decompose Conditional | Extract isSummer(date) and summerCharge() |
Deeply nested if/else | Replace with Guard Clauses | Edge cases first, return early, flat main path |
| Switch on object type | Replace Conditional with Polymorphism | Each type implements its own calculatePay() |
if (customer == null) everywhere | Introduce Special Case | NullCustomer with safe default behavior |
See references/simplifying-conditionals.md when untangling branches — before/after examples for Decompose/Consolidate Conditional, Guard Clauses, Replace Conditional with Polymorphism, Special Case, and Assertions.
Core concept: Refactoring is only safe when wrapped in tests. The workflow is mechanical: run tests (green), apply one small transformation, run tests (green), commit. If tests go red, revert — don't debug a broken refactoring.
Why it works: Small steps make the failure obvious (it was the last thing you did) and reverting costs seconds; debugging a failed big-bang rewrite costs days.
Key insights:
Code applications:
| Context | Pattern | Example |
|---|---|---|
| About to add a feature | Preparatory Refactoring | Clean the insertion point first |
| Third copy of same logic | Rule of Three | Extract shared logic now |
| Large API change in production | Branch by Abstraction | Add abstraction layer, migrate callers, remove old path |
| Renaming a widely-used method | Parallel Change | Add new, deprecate old, migrate, remove |
See references/refactoring-workflow.md before a large or risky refactoring — the full green-to-green cycle, when (not) to refactor, performance, Branch by Abstraction, and Parallel Change.
| Mistake | Why It Fails | Fix |
|---|---|---|
| Refactoring without tests | No safety net to detect behavior change | Write characterization tests first |
| Big-bang rewrite | Mixes structural and behavioral change; undebuggable | Smallest possible steps, tests after each |
| Refactoring while adding features | Two hats at once — neither change verifiable | Refactor first (commit), then add feature (commit) |
| Renaming without updating callers | Broken build or dead code | Use IDE rename; search all references |
| Extracting too many tiny methods | Indirection without clarity when names are poor | Each name must remove the need to read the body |
| Ignoring the smell catalog | Reinvents fixes instead of applying proven recipes | Learn named smells; each maps to refactorings |
| Refactoring doomed code | Polish on condemned code is waste | Check the code's lifespan justifies the investment |
| Optimizing while refactoring | Conflates clarity with performance | Clarity first, then profile, then optimize hot path |
| Question | If No | Action |
|---|---|---|
| Do tests pass before you start? | No safety net | Write or fix tests first — never refactor red |
| Can you name the smell you're fixing? | Refactoring by instinct, not catalog | Identify the smell, apply its prescribed refactoring |
| Is each method under ~10 lines? | Long Methods likely | Extract Method into named steps |
| Does each class have one reason to change? | Divergent Change or Large Class | Extract Class to separate responsibilities |
| Are there duplicated code blocks? | The most expensive smell | Extract shared logic into common method/base class |
| Do conditionals use polymorphism where apt? | Switch Statements remain | Replace Conditional with Polymorphism |
| Are you committing after each step? | Risk losing work, mixing changes | Commit after every green-to-green transformation |
| Is the code easier to read after your change? | Refactoring added complexity | Revert and try a different approach |
The definitive guides to improving existing code:
Martin Fowler is Chief Scientist at Thoughtworks, a signatory of the Agile Manifesto, and author of Refactoring: Improving the Design of Existing Code (1999; 2nd edition 2018), which introduced catalog-based, named refactorings to mainstream development. His catalog underpins the automated refactoring tools in every major IDE.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.