simplify
Reduce-time discipline: make existing code easier to read without changing what it does.
The goal is not fewer lines - it's code a new teammate understands faster. Every change must pass one test: would someone reading this for the first time grasp it quicker than the original? If not, it's churn, not simplification.
What this skill is - and isn't
| Skill |
When |
Output |
vd:simplify (this) |
Existing code works but reads heavy - reduce complexity, behavior unchanged |
Refactor commits, tests still green |
vd:simplify --aggressive |
Feature works but its shape is historical |
May delete proven-dead paths and collapse flags; intended flow frozen |
vd:cook |
Writing new code |
Simplicity is built in at write-time (Pragmatism rules), not a later pass |
vd:code-review |
Judging someone's diff |
Reports findings; never edits |
vd:fix |
Code is broken |
Changes behavior to fix a bug |
Use this when the code is correct but cluttered. --aggressive when the clutter is leftover architecture, not reading complexity. If it's buggy, that's vd:fix. If you're still writing it, that's vd:cook.
| Mode |
When |
Behavior |
| default |
Reads heavy, shape is right |
Behavior frozen; readability only |
--aggressive |
Shape is historical (compat flags, dead aliases) |
Follow references/aggressive.md |
--scan |
Want the candidate list first |
Same as aggressive, no edits |
When to use
- A feature passes tests but the implementation feels heavier than the problem.
- Code written under deadline accreted nesting, dead branches, or generic names.
- A review flagged readability and you're acting on it.
Not for: code that's already clean (don't simplify for its own sake), code you don't yet understand (comprehend first), hot paths where the simpler form is measurably slower, or a module you're about to rewrite anyway.
Hard rules
- Behavior is frozen. Same output for every input, same errors, same side effects and ordering. If you're unsure a change preserves behavior, don't make it.
- Tests are the proof. Run them after every single change. A simplification that needs a test edited to pass is a behavior change in disguise - stop and reconsider.
- One change at a time. Batching means you can't tell which edit broke something.
- Refactor commits stand alone. Never mix a
refactor: with a feat:/fix:. Two concerns = two commits (or two PRs).
- Scope to what changed. Default to recently modified code. Drive-by refactors of unrelated code create diff noise and regression risk - broaden scope only when asked.
Workflow
1. Understand before touching (Chesterton's Fence)
Don't remove a fence until you know why it's there. Before changing anything, answer:
- What is this code's responsibility? What calls it, what does it call?
- What are its edge cases and error paths? Which tests pin them?
- Why might it look this way - performance, a platform constraint, a historical reason? (
git blame / git log -p the lines.)
Can't answer? You're not ready. Read more context first.
2. Find the opportunities (signals, not vibes)
Structure
| Pattern |
Signal |
Simplification |
| Deep nesting (3+ levels) |
Control flow is hard to follow |
Guard clauses; extract helpers |
| Long function (50+ lines) |
Multiple responsibilities |
Split into focused, named functions |
| Nested ternaries |
Needs a mental stack to parse |
if/else, switch, or a lookup map |
Boolean flag params (f(true, false)) |
Opaque at the call site |
Options object or separate functions |
| Repeated conditional |
Same if in many places |
Extract a named predicate |
Naming & redundancy
| Pattern |
Signal |
Simplification |
Generic names (data, tmp, result) |
Says nothing about content |
Rename to the content (validationErrors) |
"What" comments (// increment over i++) |
Restates the code |
Delete - the code is the comment |
"Why" comments (// retry: API flakes under load) |
Carries intent code can't |
Keep |
| Duplicated logic (5+ lines, 2+ places) |
- |
Extract a shared function (Rule of Three) |
| Dead code (unreachable, unused, commented-out) |
- |
Remove after confirming it's truly dead |
| Wrong abstraction (factory-for-a-factory, 1-impl strategy) |
Indirection with no payoff |
Inline to the direct form |
3. Apply incrementally
For each simplification: make the change → run tests → green, continue; red, revert and reconsider. Commit refactors separately from any behavior change.
Rule of 500: if a refactor would touch more than ~500 lines, write the codemod (sed/AST transform), don't hand-edit. Manual edits at that scale are error-prone and exhausting to review.
4. Verify the whole
Step back: is it genuinely easier to understand? Did you introduce a pattern foreign to the codebase? Is the diff clean and reviewable? If the "simpler" version is harder to read or review - revert. Not every attempt succeeds, and that's fine.
Over-simplification traps (the failure mode)
- Inlining a helper that named a concept - the call site gets harder, not easier.
- Merging unrelated logic - two simple functions fused into one complex one is not simpler.
- Deleting an abstraction that existed for testability/extensibility, not for complexity.
- Optimizing for line count. Fewer lines ≠ clearer.
Rationalizations to catch in yourself
| Thought |
Reality |
| "I'll just clean up this nearby code too" |
Scope creep - that's a separate PR |
| "Fewer lines is better" |
Comprehension is the metric, not length |
| "This abstraction is pointless" |
Check why it exists before removing it (Fence) |
| "Tests fail but my version is clearer" |
Then it changed behavior - it's not a simplification |
Integration points
vd:cook - Step E surfaces complexity during a feature; bank the note and run vd:simplify as a separate follow-up commit, never tangled into the feature diff.
vd:code-review - review flags complexity (report-only); this skill is how you act on it.
vd:git - refactor commits stay isolated per the vd:git skill's references/commit-standards.md.
Future (out of scope for MVP)
- Language-specific codemod recipes beyond the Rule-of-500 pointer.
- An automatic complexity metric gate (cyclomatic/cognitive) - judgment-first for now.
1---2name: simplify3description: Reduce the complexity of existing code without changing behavior - deep nesting, long functions, dead code, unclear names, the wrong abstraction. Use after a feature works but reads heavier than it should, or to clean up code written under time pressure. Pass `--aggressive` to reshape a working feature into the form it should have had from day one (delete proven-dead compatibility paths). `--scan` lists aggressive candidates only. Triggers: 'simplify this', 'clean up this code', 'reduce complexity', 'zero tech debt', 'remove the compat layer', 'rebuild this as if from scratch'.4license: MIT5---67# simplify89> Reduce-time discipline: make existing code easier to read without changing what it does.1011The goal is **not fewer lines** - it's code a new teammate understands faster. Every change must pass one test: would someone reading this for the first time grasp it quicker than the original? If not, it's churn, not simplification.1213## What this skill is - and isn't1415| Skill | When | Output |16|---|---|---|17| **`vd:simplify`** (this) | Existing code works but reads heavy - reduce complexity, behavior unchanged | Refactor commits, tests still green |18| `vd:simplify --aggressive` | Feature works but its *shape* is historical | May delete proven-dead paths and collapse flags; intended flow frozen |19| `vd:cook` | Writing new code | Simplicity is built in at write-time (Pragmatism rules), not a later pass |20| `vd:code-review` | Judging someone's diff | Reports findings; never edits |21| `vd:fix` | Code is broken | Changes behavior to fix a bug |2223Use this when the code is *correct but cluttered*. `--aggressive` when the clutter is leftover architecture, not reading complexity. If it's buggy, that's `vd:fix`. If you're still writing it, that's `vd:cook`.2425| Mode | When | Behavior |26|---|---|---|27| **default** | Reads heavy, shape is right | Behavior frozen; readability only |28| `--aggressive` | Shape is historical (compat flags, dead aliases) | Follow [`references/aggressive.md`](references/aggressive.md) |29| `--scan` | Want the candidate list first | Same as aggressive, no edits |3031## When to use3233- A feature passes tests but the implementation feels heavier than the problem.34- Code written under deadline accreted nesting, dead branches, or generic names.35- A review flagged readability and you're acting on it.3637**Not for:** code that's already clean (don't simplify for its own sake), code you don't yet understand (comprehend first), hot paths where the simpler form is measurably slower, or a module you're about to rewrite anyway.3839## Hard rules40411. **Behavior is frozen.** Same output for every input, same errors, same side effects and ordering. If you're unsure a change preserves behavior, don't make it.422. **Tests are the proof.** Run them after every single change. A simplification that needs a test edited to pass is a behavior change in disguise - stop and reconsider.433. **One change at a time.** Batching means you can't tell which edit broke something.444. **Refactor commits stand alone.** Never mix a `refactor:` with a `feat:`/`fix:`. Two concerns = two commits (or two PRs).455. **Scope to what changed.** Default to recently modified code. Drive-by refactors of unrelated code create diff noise and regression risk - broaden scope only when asked.4647## Workflow4849### 1. Understand before touching (Chesterton's Fence)5051Don't remove a fence until you know why it's there. Before changing anything, answer:5253- What is this code's responsibility? What calls it, what does it call?54- What are its edge cases and error paths? Which tests pin them?55- Why might it look this way - performance, a platform constraint, a historical reason? (`git blame` / `git log -p` the lines.)5657Can't answer? You're not ready. Read more context first.5859### 2. Find the opportunities (signals, not vibes)6061**Structure**6263| Pattern | Signal | Simplification |64|---|---|---|65| Deep nesting (3+ levels) | Control flow is hard to follow | Guard clauses; extract helpers |66| Long function (50+ lines) | Multiple responsibilities | Split into focused, named functions |67| Nested ternaries | Needs a mental stack to parse | if/else, switch, or a lookup map |68| Boolean flag params (`f(true, false)`) | Opaque at the call site | Options object or separate functions |69| Repeated conditional | Same `if` in many places | Extract a named predicate |7071**Naming & redundancy**7273| Pattern | Signal | Simplification |74|---|---|---|75| Generic names (`data`, `tmp`, `result`) | Says nothing about content | Rename to the content (`validationErrors`) |76| "What" comments (`// increment` over `i++`) | Restates the code | Delete - the code is the comment |77| "Why" comments (`// retry: API flakes under load`) | Carries intent code can't | **Keep** |78| Duplicated logic (5+ lines, 2+ places) | - | Extract a shared function (Rule of Three) |79| Dead code (unreachable, unused, commented-out) | - | Remove after confirming it's truly dead |80| Wrong abstraction (factory-for-a-factory, 1-impl strategy) | Indirection with no payoff | Inline to the direct form |8182### 3. Apply incrementally8384For each simplification: make the change → run tests → green, continue; red, revert and reconsider. Commit refactors separately from any behavior change.8586**Rule of 500:** if a refactor would touch more than ~500 lines, write the codemod (sed/AST transform), don't hand-edit. Manual edits at that scale are error-prone and exhausting to review.8788### 4. Verify the whole8990Step back: is it genuinely easier to understand? Did you introduce a pattern foreign to the codebase? Is the diff clean and reviewable? If the "simpler" version is harder to read or review - **revert.** Not every attempt succeeds, and that's fine.9192## Over-simplification traps (the failure mode)9394- **Inlining a helper that named a concept** - the call site gets harder, not easier.95- **Merging unrelated logic** - two simple functions fused into one complex one is not simpler.96- **Deleting an abstraction that existed for testability/extensibility**, not for complexity.97- **Optimizing for line count.** Fewer lines ≠ clearer.9899## Rationalizations to catch in yourself100101| Thought | Reality |102|---|---|103| "I'll just clean up this nearby code too" | Scope creep - that's a separate PR |104| "Fewer lines is better" | Comprehension is the metric, not length |105| "This abstraction is pointless" | Check why it exists before removing it (Fence) |106| "Tests fail but my version is clearer" | Then it changed behavior - it's not a simplification |107108## Integration points109110- **`vd:cook`** - Step E surfaces complexity during a feature; bank the note and run `vd:simplify` as a *separate* follow-up commit, never tangled into the feature diff.111- **`vd:code-review`** - review flags complexity (report-only); this skill is how you act on it.112- **`vd:git`** - refactor commits stay isolated per the `vd:git` skill's `references/commit-standards.md`.113114## Future (out of scope for MVP)115116- Language-specific codemod recipes beyond the Rule-of-500 pointer.117- An automatic complexity metric gate (cyclomatic/cognitive) - judgment-first for now.