simplify
Review recently changed code for simplification, dead code, over-engineering, duplication, and low-value abstractions — then fix what you find. Use immediately after implementing a feature/fix, after
Files1
View on GitHubSKILL.md.claude/skills/simplify/SKILL.md
GitHub/simplify — Focused Cleanup Pass
You are reviewing code that just changed (not the whole codebase) and removing every line, abstraction, or indirection that does not pay for itself. The goal is a net-negative diff — fewer lines, lower cyclomatic complexity, clearer intent — without changing public behavior.
🎯 Prime Rule — Delete First, Then Refactor
The cheapest, fastest, most correct line of code is the one you don't write.
Before proposing any replacement, ask: "Can this just be deleted?" If yes, delete it. Only if the answer is no do you consider rewriting.
🚫 Hard Boundaries — Do NOT
- Do not change public API (exported functions, types, route signatures, CLI flags, DB schema). If simplifying requires an API change, stop and ask the user first.
- Do not modify test expectations. Tests describe the contract. Change the code to satisfy the tests, never the reverse.
- Do not touch code outside the current change's blast radius unless it is directly entangled. Do not freelance a cleanup of unrelated files.
- Do not reformat (whitespace, import ordering, quote style). That is the formatter's job, not yours.
- Do not "simplify" by adding new dependencies, frameworks, or abstractions. Removing an abstraction is simplification; adding one with a friendlier name is not.
- Do not silence errors to reduce code. If a try/catch exists because something can actually fail, it stays.
If any proposed simplification would break one of the above, discard it.
📋 Workflow
Follow these steps in order.
Step 1 — Define the Blast Radius
Determine which files to review. In priority order:
- Staged + unstaged changes (
git status,git diff,git diff --staged) — the primary target. - Files touched in the last commit (
git diff HEAD~1 HEAD --name-only) — if nothing is staged. - Files passed explicitly by the user or caller (e.g.,
/gopasses the list of files it verified). - Nothing else. Do not wander the repo.
Output the blast radius as a list. If it is empty, stop and say "nothing changed — nothing to simplify."
Step 2 — Read the Diff, Not the File
For each file in the blast radius, read the diff first (git diff <file>), then read surrounding context only for lines the diff touched. This keeps the review focused on what's new, not a general code audit.
Step 3 — Run the 8 Simplification Lenses
For every changed hunk, walk through these lenses in order. Note concrete findings (file:line → issue → proposed action).
| # | Lens | Question |
|---|---|---|
| 1 | Dead code | Is this branch/function/import reachable? Is it referenced anywhere? Remove unused imports, unreachable branches, commented-out code, TODOs that are actually DONE. |
| 2 | Redundant abstraction | Does this wrapper/helper/class add information, or does it just rename the underlying call? A one-line wrapper used once is almost always net-negative. |
| 3 | Duplication | Is the same shape repeated 2+ times in the diff? Extract only if the duplicates will actually co-evolve (avoid premature DRY — incidental duplication is fine). |
| 4 | Over-parameterization | Does this function take a flag/option that is only ever passed one value? Inline the value. Does it have dead parameters? Remove. |
| 5 | Speculative generality | Is there a base class, interface, or plugin seam with only one implementation? Collapse to the concrete class. |
| 6 | Nesting depth | More than 3 levels of if/for nesting? Invert guards (if (!x) return), early-return, or extract. |
| 7 | Comment-as-apology | Is a comment explaining why the code is confusing? Fix the code so the comment is unnecessary. Keep comments that explain why, not what. |
| 8 | Boolean-chain / null-dance | Long &&/` |
Step 4 — Propose Edits as a Diff Plan
Before editing, list every proposed change in this format:
<file>:<line> <lens #> — <one-line rationale>
BEFORE: <short snippet or summary>
AFTER: <short snippet or "DELETE">
Net: <lines removed> / <lines added>
Target: total net lines MUST be negative. If your plan has more additions than deletions, you are not simplifying — stop and reconsider.
Step 5 — Apply Edits
Apply the plan with Edit. After each file:
- Re-read the file to confirm no dangling references.
- If tests exist for the file, note them — they will be run in Step 6.
Step 6 — Verify Behavior Is Unchanged
This is non-negotiable. Before declaring success:
- Type check — run the project's type checker (
tsc --noEmit,mypy,cargo check,go vet, etc. — detect from the project). - Lint — run the project's linter. Do not auto-fix style; fail loudly if your simplification introduced lint errors.
- Tests — run the test suite scoped to the affected files if possible, full suite otherwise.
- If a /go verification script exists or was just run — re-run it.
If any of these fail, revert the change that caused the failure and note it in the report. A simplification that breaks behavior is not a simplification.
Step 7 — Report
Output a single report with this shape:
🧹 Simplify Report
Blast radius : <N files>
Findings : <N>
Applied : <N>
Skipped : <N> (reason each)
Net lines : -<X> (deletions − additions; MUST be ≤ 0)
Type check : PASS / FAIL
Lint : PASS / FAIL
Tests : PASS / FAIL / NOT RUN (<why>)
API changed : NO (if YES → you violated a boundary; revert)
Next step: <one of: "ship", "re-run /go", "review skipped findings with user">
🔁 When Called By /go
If the caller is /go, you will receive the verified file list explicitly. Use that as your blast radius (skip Step 1's discovery). Report back in the same format — /go will compose it into its own final report.
❌ When NOT to Run /simplify
- On a branch you did not change — there's no diff to review.
- On generated code (protobufs, OpenAPI clients, migration files) — those are owned by their generator.
- Mid-feature when the code is intentionally scaffolded and will be filled in next. Ask the user first.
- Immediately before a release freeze — simplification can introduce regressions; run it after the release, not before.
🧭 Philosophy — Why This Exists
Claude tends to produce slightly too much code: extra helpers, defensive guards, speculative flags, explanatory comments for confusing structure. A dedicated cleanup pass — run after the feature works, before the diff is committed — catches this drift. Over many iterations it compounds: the codebase stays lean instead of slowly bloating.
This is not a style pass. Style is the formatter's job. This is a structural pass: removing what does not earn its keep.