refactoring-optimization
Purpose
Change the shape of working code without changing what it does. The input is code that is hard to
read, hard to change, or repeated in four places; the output is the same behavior in a better
structure, with the tests still green after every step and a diff a reviewer can actually follow.
The discipline is the whole point. Anyone can rewrite a function so it reads better; the hard part
is proving it still does the same thing. So the ordering here is always the same — establish a
safety net, make one small reversible change, verify, repeat — and the value of the skill is in
refusing to skip that, not in knowing clever refactorings.
This is the apply half of a pair. code-analyzer surveys a project and reports what is wrong;
this skill takes one of those findings and actually changes the code.
When to use
- A function has grown past what anyone can hold in their head and needs decomposing
- The same logic exists in three or four places and they have started to drift apart
- Code is unreachable or unreferenced and nobody dares delete it
- Names, magic numbers, or boolean flag parameters make call sites unreadable
- The code predates a language feature that would express it far more directly
- A refactoring is the prerequisite for a feature — untangle first, then add
Not this skill:
- Finding out what is wrong across a whole project →
code-analyzer, then come back here
- Fixing a bug, crash, or failing test →
error-debugging (that changes behavior, by design)
- Writing the tests this refactoring needs as a safety net →
test-generation, first
- Tidying code you just wrote in this session → the built-in
simplify skill is lighter weight
- Making code faster → measure first; see performance below
Route to a more specific skill first
Several stacks have dedicated skills for exactly the restructuring being asked for. When one
applies, invoke it rather than reimplementing its guidance; use this skill for the surrounding
discipline — safety net, ordering, verification, rollback.
| Situation |
Skill to invoke |
| Compose state in the wrong place, stateful screen mixed with UI |
compose-state-hoisting, compose-state-holder-ui-split |
| Compose component with boolean flags instead of content slots |
compose-slot-api-pattern |
| Compose modifier chains, layout wrappers, hardcoded roots |
compose-modifier-and-layout-style |
| Compose recomposing too often |
compose-recomposition-performance, compose-stability-diagnostics |
| Kotlin coroutine scope and cancellation structure |
kotlin-coroutines-structured-concurrency |
| Kotlin primitive obsession in domain types |
kotlin-types-value-class |
Dart mechanical lint fixes (dart fix --apply) |
dart-run-static-analysis |
| Dart if/else chains that want to be pattern matches |
dart-use-pattern-matching |
| Flutter widget tree restructuring, overflow, nesting |
flutter-fix-layout-issues |
| Flutter layering and state management cleanup |
flutter-apply-architecture-best-practices |
Swift, Rust, C++, and plain Kotlin/JVM have no dedicated skill — this one covers them directly.
Prerequisites
1. Is there a safety net? Refactoring without one is rewriting and hoping. Before the first
edit, three things need to be true:
- The tests pass now. A green baseline is what makes "the tests went red" mean something. If
they are already red, stop — fix or quarantine that first, or the signal is worthless.
- The working tree is clean and committed. Rollback is
git checkout only if there is
something to roll back to. Refactoring on top of uncommitted work destroys it.
- The code under change is covered. Not the whole project — the specific behavior being
restructured. If it is not, say so and offer to write characterization tests first
(
test-generation) rather than proceeding blind.
When no tests exist and the user wants to proceed anyway, that is their call — but make the risk
explicit, keep the steps smaller than usual, and lean harder on the compiler and the linter.
2. What stack is this? Detect from build files, not file extensions — the linter, the idioms,
and the reference-checking tools are declared there.
| Build file / signal |
Stack |
Reference |
build.gradle.kts with kotlin("multiplatform"), commonMain |
Kotlin Multiplatform / CMP |
kotlin-kmp.md |
build.gradle(.kts) with com.android.application |
Kotlin/JVM, Android |
kotlin-kmp.md |
pubspec.yaml |
Flutter / Dart |
flutter-dart.md |
Package.swift, *.xcodeproj, *.xcworkspace |
Swift (iOS/macOS) |
swift-apple.md |
Cargo.toml |
Rust |
rust-cpp.md |
CMakeLists.txt, conanfile.txt, *.cpp |
C / C++ |
rust-cpp.md |
package.json, pyproject.toml, go.mod, pom.xml |
JS/TS, Python, Go, Java |
other-languages.md |
Load exactly one stack reference. Mixing idioms from languages that are not in play produces
suggestions that read as wrong to everyone who works in the codebase.
3. Who else is in this file? A large refactoring on a file with open pull requests against it
turns into a merge conflict that costs more than the cleanup saved. Check before starting:
git log --since='2 weeks ago' --oneline -- <path> # recent churn
git branch -a --contains HEAD >/dev/null 2>&1 # and any open branches touching it
If the file is hot, prefer several small landed refactorings over one large one.
Workflow
Step 1: Find the smells with tools, not by reading
Every stack ships a linter that already knows its own smells, and it will be more thorough and
less opinionated than a manual read. Run it first and let it set the agenda — the stack reference
has the exact command and the rules worth enabling.
Then look for the things linters are bad at, which is mostly duplication with variation and
structure that is wrong rather than ugly:
| Smell |
What to look for |
Usual refactoring |
| Duplication |
The same 5+ lines in 3+ places, or near-copies that drifted |
Extract function/class, or parameterize the difference |
| Long function |
More than one reason to change; needs comments to be followable |
Extract the named steps |
| Deep nesting |
3+ levels of if/for; the happy path is at the bottom |
Guard clauses, early return, invert conditions |
| Long parameter list |
5+ parameters, or several booleans in a row |
Introduce a parameter object; split the function per flag |
| Magic values |
Unexplained literals, repeated string keys |
Named constant, enum, or sealed type |
| Misleading name |
The name says less or other than what the code does |
Rename — reference-aware, never by find/replace |
| Dead code |
Unreferenced symbols, unreachable branches, stale flags |
Delete, once references are actually proven absent |
| Primitive obsession |
String id, Int money, Long timestamp everywhere |
Value/wrapper type |
| Feature envy |
A function that mostly touches another type's data |
Move it to that type |
Smell-to-refactoring detail, and the mechanics of each refactoring, live in
refactoring-catalog.md — load it when the right move is not
obvious from the smell.
Step 2: Order the work safe → complex
Refactorings differ enormously in risk, and the order changes how much of the work survives review.
Do the mechanical ones first: they are individually verifiable, they shrink the code, and they often
make the structural problems visible enough that the harder decisions become obvious.
- Automated and reversible — formatter,
dart fix --apply, cargo clippy --fix, IDE-grade
renames. Machine-checked, and worth landing on their own so they do not drown the real diff.
- Local and contained — extract a function, introduce a constant, add guard clauses. One
file, no callers affected.
- Structural within a module — extract a class, move a method, introduce a parameter object.
Callers change, but all of them are in the repo and the compiler finds them.
- Cross-cutting — changing a public API, hoisting platform code into
commonMain, replacing
an abstraction. Needs a caller inventory before the first edit, and is usually worth splitting
across several changes.
Present this plan before editing anything past level 1. A refactoring plan the user rejects after
the fact is wasted work; one they redirect early is cheap.
Step 3: Apply one refactoring at a time
The unit of work is one refactoring, not one file and not one session. Between them the code
compiles and the tests pass — that invariant is what makes a mistake cost minutes instead of a day.
- Preserve behavior exactly, including the behavior nobody meant: error messages, ordering,
null/empty handling, overflow, logging that something else parses. If you believe a behavior is a
bug, that is a separate change — report it, do not quietly correct it under cover of a refactor.
- Change structure or behavior, never both in one step. Mixed diffs are unreviewable, and when
something breaks there is no way to tell which half did it.
- Use reference-aware tools for renames and moves (Serena's
rename_symbol, safe_delete_symbol,
find_referencing_symbols; the IDE's refactorings). Text search misses dynamic references and hits
unrelated matches in comments and strings.
- Do not reformat what you did not change. A whitespace-only change to 400 unrelated lines hides
the twelve that matter.
- Leave the code more consistent than you found it. A "better" pattern used once, differing from
the surrounding twenty call sites, makes the codebase harder to read, not easier.
Step 4: Verify after every step
Run the tests after each refactoring, not once at the end. The stack reference has the command;
keep the output filtered to failures.
Beyond green tests, three checks catch what tests miss:
- The compiler/linter is quiet — new warnings after a refactor usually mean something was left
half-moved.
- The diff is only what you intended. Read
git diff before moving on. Unintended edits are
common and cheap to catch here, expensive to catch in review.
- The public API is unchanged, or the change was deliberate and every caller is updated. For a
published library, callers outside the repo cannot be found by the compiler — check the stack
reference for the binary-compatibility tooling.
If a test goes red, revert that step rather than debugging forward. The step was small by
construction, so re-doing it correctly is cheaper than repairing it — and a refactoring that needs
debugging was not a refactoring.
Step 5: Report what changed and what you left alone
## Refactorings applied
<one line per refactoring: what moved/merged/was deleted, and why it is better>
## Verification
<command run, and the real result — after which step>
## Behavior preserved
<how you know: tests, compiler, caller inventory>
## Left alone
<smells found but not addressed — as `file:line` — with the reason>
## Found while refactoring
<bugs or surprises discovered, reported not fixed>
The last two sections are where the value accumulates. A refactoring that also silently fixed a bug
is a change nobody can review, and a smell you deliberately left is information the next person
needs.
A note on optimization
"Optimize" means two different things and they need different discipline. Structural cleanup —
everything above — is safe and reversible. Performance work is not: it trades readability for speed
and is often wrong about where the time goes.
So when the request is about speed, measure first. Profile, find the actual hot path, change that
one thing, and measure again. An optimization without a before-and-after number is a readability
regression with no proven benefit — and the stack references list each language's profiler for
exactly this reason. Algorithmic complexity in a hot loop is worth fixing on sight; everything else
waits for data.
Operations
| Operation |
User intent |
Output |
refactor (default) |
"clean this up", "refactor this function" |
Behavior-preserving edits, verified step by step |
plan |
"what should we refactor first" |
Ranked, ordered plan with risk per step; no edits |
duplicates |
"there is a lot of copy-paste here" |
Duplication located, shared abstraction extracted where it earns its place |
simplify |
"this function is too complex" |
Decomposition into named steps, nesting flattened |
deadcode |
"remove the unused code" |
Reference-verified deletions, with the dynamic-reference caveats stated |
rename |
"these names are confusing" |
Reference-aware renames across declarations and call sites |
modernize |
"make this idiomatic" |
Linter-driven idiom cleanups for the stack's current language version |
Critical constraints
- Never change behavior during a refactoring. This is the one rule the whole practice rests on:
if behavior may change, it is a rewrite, and it needs to be reviewed and tested as one.
- Never refactor on a red or unknown baseline. Without a green starting point there is no way to
attribute a failure, and the refactoring gets blamed for a bug it merely revealed.
- Never delete code you cannot prove is unreferenced. Reflection, dependency injection,
serialization, platform entry points, and build-flavor-specific code are invisible to a text
search — the stack reference lists what each one hides.
- Do not refactor and add a feature in the same change. Reviewers cannot separate them, and
neither can
git bisect.
- Do not touch generated code. Fix the generator or the template; regenerated output overwrites
edits and the diff misleads everyone.
- Do not extract an abstraction from two occurrences. Duplication is cheaper than the wrong
abstraction — the third occurrence is what reveals which parts actually vary.
- Do not optimize without a measurement. See above; unmeasured performance work usually costs
clarity and buys nothing.
- Do not chase a metric. Cyclomatic complexity and line counts point at code worth looking at;
they are not the goal, and code can be split into eight functions that are collectively worse.
- Do not restructure a public API without a caller inventory covering consumers outside the
repo. Inside the repo the compiler is the inventory; outside it, nothing is.
- Stop and ask when the "right" structure is a judgment call — layering, module boundaries, and
abstraction choices encode intent this skill cannot recover from the code alone.
References
- refactoring-catalog.md — smell-to-refactoring map, the mechanics of each refactoring, characterization tests, and rollback
- kotlin-kmp.md — Kotlin and Android idioms, detekt/ktlint, KMP
commonMain hoisting and expect/actual trimming, Compose routing, binary compatibility
- flutter-dart.md —
dart analyze/dart fix, widget extraction vs helper methods, const constructors, disposal, nesting
- swift-apple.md — SwiftLint, protocol extraction,
guard early returns, SwiftUI view decomposition, @MainActor and concurrency hygiene
- rust-cpp.md —
cargo clippy and --fix, iterator chains, ? over nested matches, trimming clone()/unwrap(); RAII, smart pointers, const-correctness, clang-tidy
- other-languages.md — JS/TS, Python, Go and Java tooling and idioms, for when a project needs them
1---2name: refactoring-optimization3description: Improve the structure of working code without changing what it does: extract duplication, break up complex functions, remove dead code, replace magic numbers, fix misleading names, and modernize to the language's idioms — each step verified against the tests. Detects the stack from its build files and leans on the project's own linter (detekt/ktlint, dart analyze, SwiftLint, cargo clippy, clang-tidy). Use when someone says 'refactor this', 'clean this up', 'this function is too long', 'there's a lot of copy-paste here', 'remove the dead code', 'simplify this', 'make this more idiomatic', 'reduce the complexity', or the Hungarian 'refaktoráld ezt', 'tisztítsd meg ezt a kódot', 'túl bonyolult ez a függvény', 'sok itt a duplikáció'. This is the apply counterpart to code-analyzer's detect: that skill finds what is wrong across a project, this one changes it. Not for fixing a bug or crash (that is error-debugging), nor for writing the tests a refactoring needs (that is test-generation).4---56# refactoring-optimization78## Purpose910Change the shape of working code without changing what it does. The input is code that is hard to11read, hard to change, or repeated in four places; the output is the same behavior in a better12structure, with the tests still green after every step and a diff a reviewer can actually follow.1314The discipline is the whole point. Anyone can rewrite a function so it reads better; the hard part15is proving it still does the same thing. So the ordering here is always the same — establish a16safety net, make one small reversible change, verify, repeat — and the value of the skill is in17refusing to skip that, not in knowing clever refactorings.1819This is the *apply* half of a pair. `code-analyzer` surveys a project and reports what is wrong;20this skill takes one of those findings and actually changes the code.2122## When to use2324- A function has grown past what anyone can hold in their head and needs decomposing25- The same logic exists in three or four places and they have started to drift apart26- Code is unreachable or unreferenced and nobody dares delete it27- Names, magic numbers, or boolean flag parameters make call sites unreadable28- The code predates a language feature that would express it far more directly29- A refactoring is the prerequisite for a feature — untangle first, then add3031Not this skill:3233- **Finding out what is wrong across a whole project** → `code-analyzer`, then come back here34- **Fixing a bug, crash, or failing test** → `error-debugging` (that changes behavior, by design)35- **Writing the tests this refactoring needs as a safety net** → `test-generation`, first36- **Tidying code you just wrote in this session** → the built-in `simplify` skill is lighter weight37- **Making code faster** → measure first; see [performance](#a-note-on-optimization) below3839## Route to a more specific skill first4041Several stacks have dedicated skills for exactly the restructuring being asked for. When one42applies, invoke it rather than reimplementing its guidance; use this skill for the surrounding43discipline — safety net, ordering, verification, rollback.4445| Situation | Skill to invoke |46|---|---|47| Compose state in the wrong place, stateful screen mixed with UI | `compose-state-hoisting`, `compose-state-holder-ui-split` |48| Compose component with boolean flags instead of content slots | `compose-slot-api-pattern` |49| Compose modifier chains, layout wrappers, hardcoded roots | `compose-modifier-and-layout-style` |50| Compose recomposing too often | `compose-recomposition-performance`, `compose-stability-diagnostics` |51| Kotlin coroutine scope and cancellation structure | `kotlin-coroutines-structured-concurrency` |52| Kotlin primitive obsession in domain types | `kotlin-types-value-class` |53| Dart mechanical lint fixes (`dart fix --apply`) | `dart-run-static-analysis` |54| Dart if/else chains that want to be pattern matches | `dart-use-pattern-matching` |55| Flutter widget tree restructuring, overflow, nesting | `flutter-fix-layout-issues` |56| Flutter layering and state management cleanup | `flutter-apply-architecture-best-practices` |5758Swift, Rust, C++, and plain Kotlin/JVM have no dedicated skill — this one covers them directly.5960## Prerequisites6162**1. Is there a safety net?** Refactoring without one is rewriting and hoping. Before the first63edit, three things need to be true:6465- **The tests pass now.** A green baseline is what makes "the tests went red" mean something. If66 they are already red, stop — fix or quarantine that first, or the signal is worthless.67- **The working tree is clean and committed.** Rollback is `git checkout` only if there is68 something to roll back to. Refactoring on top of uncommitted work destroys it.69- **The code under change is covered.** Not the whole project — the specific behavior being70 restructured. If it is not, say so and offer to write characterization tests first71 (`test-generation`) rather than proceeding blind.7273When no tests exist and the user wants to proceed anyway, that is their call — but make the risk74explicit, keep the steps smaller than usual, and lean harder on the compiler and the linter.7576**2. What stack is this?** Detect from build files, not file extensions — the linter, the idioms,77and the reference-checking tools are declared there.7879| Build file / signal | Stack | Reference |80|---|---|---|81| `build.gradle.kts` with `kotlin("multiplatform")`, `commonMain` | Kotlin Multiplatform / CMP | [kotlin-kmp.md](references/kotlin-kmp.md) |82| `build.gradle(.kts)` with `com.android.application` | Kotlin/JVM, Android | [kotlin-kmp.md](references/kotlin-kmp.md) |83| `pubspec.yaml` | Flutter / Dart | [flutter-dart.md](references/flutter-dart.md) |84| `Package.swift`, `*.xcodeproj`, `*.xcworkspace` | Swift (iOS/macOS) | [swift-apple.md](references/swift-apple.md) |85| `Cargo.toml` | Rust | [rust-cpp.md](references/rust-cpp.md) |86| `CMakeLists.txt`, `conanfile.txt`, `*.cpp` | C / C++ | [rust-cpp.md](references/rust-cpp.md) |87| `package.json`, `pyproject.toml`, `go.mod`, `pom.xml` | JS/TS, Python, Go, Java | [other-languages.md](references/other-languages.md) |8889Load exactly one stack reference. Mixing idioms from languages that are not in play produces90suggestions that read as wrong to everyone who works in the codebase.9192**3. Who else is in this file?** A large refactoring on a file with open pull requests against it93turns into a merge conflict that costs more than the cleanup saved. Check before starting:9495```bash96git log --since='2 weeks ago' --oneline -- <path> # recent churn97git branch -a --contains HEAD >/dev/null 2>&1 # and any open branches touching it98```99100If the file is hot, prefer several small landed refactorings over one large one.101102## Workflow103104### Step 1: Find the smells with tools, not by reading105106Every stack ships a linter that already knows its own smells, and it will be more thorough and107less opinionated than a manual read. Run it first and let it set the agenda — the stack reference108has the exact command and the rules worth enabling.109110Then look for the things linters are bad at, which is mostly duplication with variation and111structure that is wrong rather than ugly:112113| Smell | What to look for | Usual refactoring |114|---|---|---|115| Duplication | The same 5+ lines in 3+ places, or near-copies that drifted | Extract function/class, or parameterize the difference |116| Long function | More than one reason to change; needs comments to be followable | Extract the named steps |117| Deep nesting | 3+ levels of `if`/`for`; the happy path is at the bottom | Guard clauses, early return, invert conditions |118| Long parameter list | 5+ parameters, or several booleans in a row | Introduce a parameter object; split the function per flag |119| Magic values | Unexplained literals, repeated string keys | Named constant, enum, or sealed type |120| Misleading name | The name says less or other than what the code does | Rename — reference-aware, never by find/replace |121| Dead code | Unreferenced symbols, unreachable branches, stale flags | Delete, once references are actually proven absent |122| Primitive obsession | `String` id, `Int` money, `Long` timestamp everywhere | Value/wrapper type |123| Feature envy | A function that mostly touches another type's data | Move it to that type |124125Smell-to-refactoring detail, and the mechanics of each refactoring, live in126[refactoring-catalog.md](references/refactoring-catalog.md) — load it when the right move is not127obvious from the smell.128129### Step 2: Order the work safe → complex130131Refactorings differ enormously in risk, and the order changes how much of the work survives review.132Do the mechanical ones first: they are individually verifiable, they shrink the code, and they often133make the structural problems visible enough that the harder decisions become obvious.1341351. **Automated and reversible** — formatter, `dart fix --apply`, `cargo clippy --fix`, IDE-grade136 renames. Machine-checked, and worth landing on their own so they do not drown the real diff.1372. **Local and contained** — extract a function, introduce a constant, add guard clauses. One138 file, no callers affected.1393. **Structural within a module** — extract a class, move a method, introduce a parameter object.140 Callers change, but all of them are in the repo and the compiler finds them.1414. **Cross-cutting** — changing a public API, hoisting platform code into `commonMain`, replacing142 an abstraction. Needs a caller inventory before the first edit, and is usually worth splitting143 across several changes.144145Present this plan before editing anything past level 1. A refactoring plan the user rejects after146the fact is wasted work; one they redirect early is cheap.147148### Step 3: Apply one refactoring at a time149150The unit of work is one refactoring, not one file and not one session. Between them the code151compiles and the tests pass — that invariant is what makes a mistake cost minutes instead of a day.152153- **Preserve behavior exactly**, including the behavior nobody meant: error messages, ordering,154 null/empty handling, overflow, logging that something else parses. If you believe a behavior is a155 bug, that is a separate change — report it, do not quietly correct it under cover of a refactor.156- **Change structure or behavior, never both in one step.** Mixed diffs are unreviewable, and when157 something breaks there is no way to tell which half did it.158- **Use reference-aware tools** for renames and moves (Serena's `rename_symbol`, `safe_delete_symbol`,159 `find_referencing_symbols`; the IDE's refactorings). Text search misses dynamic references and hits160 unrelated matches in comments and strings.161- **Do not reformat what you did not change.** A whitespace-only change to 400 unrelated lines hides162 the twelve that matter.163- **Leave the code more consistent than you found it.** A "better" pattern used once, differing from164 the surrounding twenty call sites, makes the codebase harder to read, not easier.165166### Step 4: Verify after every step167168Run the tests after each refactoring, not once at the end. The stack reference has the command;169keep the output filtered to failures.170171Beyond green tests, three checks catch what tests miss:172173- **The compiler/linter is quiet** — new warnings after a refactor usually mean something was left174 half-moved.175- **The diff is only what you intended.** Read `git diff` before moving on. Unintended edits are176 common and cheap to catch here, expensive to catch in review.177- **The public API is unchanged**, or the change was deliberate and every caller is updated. For a178 published library, callers outside the repo cannot be found by the compiler — check the stack179 reference for the binary-compatibility tooling.180181If a test goes red, revert that step rather than debugging forward. The step was small by182construction, so re-doing it correctly is cheaper than repairing it — and a refactoring that needs183debugging was not a refactoring.184185### Step 5: Report what changed and what you left alone186187```markdown188## Refactorings applied189<one line per refactoring: what moved/merged/was deleted, and why it is better>190191## Verification192<command run, and the real result — after which step>193194## Behavior preserved195<how you know: tests, compiler, caller inventory>196197## Left alone198<smells found but not addressed — as `file:line` — with the reason>199200## Found while refactoring201<bugs or surprises discovered, reported not fixed>202```203204The last two sections are where the value accumulates. A refactoring that also silently fixed a bug205is a change nobody can review, and a smell you deliberately left is information the next person206needs.207208## A note on optimization209210"Optimize" means two different things and they need different discipline. Structural cleanup —211everything above — is safe and reversible. Performance work is not: it trades readability for speed212and is often wrong about where the time goes.213214So when the request is about speed, measure first. Profile, find the actual hot path, change that215one thing, and measure again. An optimization without a before-and-after number is a readability216regression with no proven benefit — and the stack references list each language's profiler for217exactly this reason. Algorithmic complexity in a hot loop is worth fixing on sight; everything else218waits for data.219220## Operations221222| Operation | User intent | Output |223|---|---|---|224| `refactor` (default) | "clean this up", "refactor this function" | Behavior-preserving edits, verified step by step |225| `plan` | "what should we refactor first" | Ranked, ordered plan with risk per step; no edits |226| `duplicates` | "there is a lot of copy-paste here" | Duplication located, shared abstraction extracted where it earns its place |227| `simplify` | "this function is too complex" | Decomposition into named steps, nesting flattened |228| `deadcode` | "remove the unused code" | Reference-verified deletions, with the dynamic-reference caveats stated |229| `rename` | "these names are confusing" | Reference-aware renames across declarations and call sites |230| `modernize` | "make this idiomatic" | Linter-driven idiom cleanups for the stack's current language version |231232## Critical constraints233234- **Never change behavior during a refactoring.** This is the one rule the whole practice rests on:235 if behavior may change, it is a rewrite, and it needs to be reviewed and tested as one.236- **Never refactor on a red or unknown baseline.** Without a green starting point there is no way to237 attribute a failure, and the refactoring gets blamed for a bug it merely revealed.238- **Never delete code you cannot prove is unreferenced.** Reflection, dependency injection,239 serialization, platform entry points, and build-flavor-specific code are invisible to a text240 search — the stack reference lists what each one hides.241- **Do not refactor and add a feature in the same change.** Reviewers cannot separate them, and242 neither can `git bisect`.243- **Do not touch generated code.** Fix the generator or the template; regenerated output overwrites244 edits and the diff misleads everyone.245- **Do not extract an abstraction from two occurrences.** Duplication is cheaper than the wrong246 abstraction — the third occurrence is what reveals which parts actually vary.247- **Do not optimize without a measurement.** See above; unmeasured performance work usually costs248 clarity and buys nothing.249- **Do not chase a metric.** Cyclomatic complexity and line counts point at code worth *looking* at;250 they are not the goal, and code can be split into eight functions that are collectively worse.251- **Do not restructure a public API without a caller inventory** covering consumers outside the252 repo. Inside the repo the compiler is the inventory; outside it, nothing is.253- **Stop and ask when the "right" structure is a judgment call** — layering, module boundaries, and254 abstraction choices encode intent this skill cannot recover from the code alone.255256## References257258- [refactoring-catalog.md](references/refactoring-catalog.md) — smell-to-refactoring map, the mechanics of each refactoring, characterization tests, and rollback259- [kotlin-kmp.md](references/kotlin-kmp.md) — Kotlin and Android idioms, detekt/ktlint, KMP `commonMain` hoisting and `expect`/`actual` trimming, Compose routing, binary compatibility260- [flutter-dart.md](references/flutter-dart.md) — `dart analyze`/`dart fix`, widget extraction vs helper methods, `const` constructors, disposal, nesting261- [swift-apple.md](references/swift-apple.md) — SwiftLint, protocol extraction, `guard` early returns, SwiftUI view decomposition, `@MainActor` and concurrency hygiene262- [rust-cpp.md](references/rust-cpp.md) — `cargo clippy` and `--fix`, iterator chains, `?` over nested matches, trimming `clone()`/`unwrap()`; RAII, smart pointers, `const`-correctness, clang-tidy263- [other-languages.md](references/other-languages.md) — JS/TS, Python, Go and Java tooling and idioms, for when a project needs them