A diff that compiles, passes tests, and matches the requirement can still be twice as long as it should be — missed reuse opportunities, over-abstraction, defensive code for impossible cases. /simplify is a focused review pass that names those specific smells and rewrites them. Run it before code review, not after.
When an agent finishes a feature, the natural stopping condition is "tests pass and it does the thing." That's the functional bar. The quality bar — minimal, idiomatic, no dead weight — usually isn't checked.
The result, repeatable across enough projects to be a pattern:
/simplify is a separate review pass with a checklist for these specific failure modes. It reads the diff, names what's wrong by category, and either rewrites it or proposes the rewrite for your approval. Treat it as a step in the workflow, not an optional polish.
The right moment is after "tests pass," before "open a PR." Earlier and you're simplifying code that's still in flux. Later and the smells are already in review and your reviewer is the one finding them.
The five categories /simplify reviews against, in priority order:
if (count < 0) on an unsigned int. Catch-and-rethrow that adds nothing. Delete.// increment counter above counter += 1 is noise; // rate-limited to 5/s by upstream is signal. Keep the second kind, drop the first.In the LingCode chat, type:
/simplify
With no arguments, it scopes to your current uncommitted changes (git diff HEAD). LingCode reads the diff, runs the checklist, and produces a structured report.
To scope explicitly:
# Just one file.
/simplify Sources/Auth/LoginViewModel.swift
# A range of commits — useful before a force-push.
/simplify since main
# A specific PR-shaped scope.
/simplify HEAD~3..HEAD
The report comes back as a list of findings grouped by category, each with a file/line reference and a suggested rewrite. Findings without a confident rewrite are flagged as "consider" rather than "fix."
Edited for shape, with realistic content:
### Reuse (1)
- LoginViewModel.swift:42 — added formatPhoneNumber(). The same
function exists at Utilities/PhoneFormatter.swift:18 and is
already imported in this file. Replace the local helper with a
call to PhoneFormatter.format(_:).
### Over-abstraction (2)
- AuthService.swift:8 — protocol AuthProviding has one conformer
(RealAuthService) and is only injected in one place. Inline:
delete the protocol, depend on RealAuthService directly. Re-add
the protocol if and when a second conformer arrives.
- LoginCoordinator.swift:30 — generic NavigationDestination<T>
is instantiated only as NavigationDestination<LoginRoute>.
Specialize to NavigationDestinationLogin.
### Defensive code (1)
- LoginViewModel.swift:67 — guard let email = email else { ... }
where `email` is typed `String` (non-optional). The branch is
unreachable. Delete.
### Consider (1)
- AuthService.swift:55 — three-argument `login(email:password:
remember:)` defaults `remember = false` and no caller passes
`true`. Consider dropping the parameter, re-adding when needed.
Apply fixes? [y/N]
Categories separate the "obviously delete this" findings from the "judgment call" findings. The "Consider" section is where you push back; the rest is usually safe to apply.
Answer y and LingCode rewrites the files. The standard verify-before-claiming-done loop kicks in: it re-runs the test suite after the changes, and reports test status as part of the result.
If a fix breaks a test, LingCode reverts that specific fix and tells you which finding was unsafe. The other fixes stay applied. You end up with the diff that simplified cleanly.
Boundaries worth knowing:
The natural workflow:
/simplify — strip the over-build.Step 4 is where a lot of "simplification" workflows leak — fixes get applied, no one reruns the suite, regressions ship. /simplify runs the suite itself; if you skip it via manual edits, run the suite by hand. See verify before claiming done for the discipline.
Package the workflow as a skill so LingCode applies the right checklist every time you ask it to simplify:
---
name: simplify
description: Review changed code for reuse, quality, and efficiency, then fix any issues found. Triggers: 'simplify this', 'clean up the code', 'reduce', 'too verbose', 'review my diff', 'is there a simpler way', 'shorter version', 'refactor for clarity'. Actions: scan diff for five categories — missed reuse opportunities, over-abstraction, defensive code for impossible cases, dead branches, narrating comments. Output: structured report + edits. Explicitly NOT: behavior changes, performance optimization, taste rewrites, test changes.
---
Review the current diff (default: git diff HEAD; or the scope the
user names) against five categories, in priority order:
1. Reuse — does this diff add code that already exists elsewhere
in the codebase? Grep for similar shapes before accepting new
helpers. Prefer call-site updates over duplicate utilities.
2. Over-abstraction — protocols with one conformer, factories
with one product, generics with one concrete type. Inline.
Re-add the abstraction when a second use case appears.
3. Defensive code for impossible inputs — guard clauses on
non-nullable types, range checks the type system already
enforces, catch-and-rethrow that adds nothing. Delete.
4. Dead branches and unused parameters — error cases that can't
reach, params no caller passes, "for future use" knobs.
Delete.
5. Comments that narrate code — "// increment counter" above
"counter += 1" is noise. Keep comments that explain *why* (a
constraint, a non-obvious invariant), drop comments that
describe *what*.
Group findings by category. For each, give file:line, the smell,
and a concrete rewrite. Use a "Consider" bucket for judgment
calls.
After applying fixes, re-run the test suite. Revert any fix that
breaks a test; keep the rest.
Do not change behavior. Do not optimize for performance. Do not
rewrite working code that's just verbose by taste. Do not touch
test files.
Save as ~/.lingcode/skills/simplify/SKILL.md — see Install a skill for the exact location and how skills get discovered.