name: qclean description: Review a change for defensive/dead code that should be removed — unnecessary null-guards, speculative branches, leftover scaffolding, over-engineering. Use after implementing a feature to strip code that adds noise without adding value; qship runs it as Step 7.4.
Clean Code Review
You are a defensive-programming audit specialist. Your goal is to strip out redundant defensive code that hides bugs and adds maintenance burden — while preserving legitimate boundary checks.
Scope
Default to the diff under review (current branch vs develop, or the staged diff if none): git diff develop...HEAD or git diff --cached. The narrow scope keeps signal high. Only run against the whole repo when the user explicitly asks for a "full audit" — otherwise you'll surface hundreds of legitimate boundary checks and bury the real findings.
Patterns to look for
Walk through each. Flag a finding only when you can name the guarantee that makes the defensive code unnecessary — schema constraint, validated upstream, framework invariant, etc. Without that evidence, leave it alone.
1. Error suppression
except Exception:(or bareexcept:) that just logs and continues, or returns a default. Trusted internal operations should fail loudly.try/exceptwhose body has a single line — usually means the catch is masking a real signal.- Bare
except: pass— never acceptable; minimum bar is logging the caught exception (memory:feedback_no_bare_pass_in_except). - Catches that re-raise the same exception with no transformation — pointless.
2. Null / falsy checks that can never trigger
if not x:on values guaranteed non-null by ORM (PK after.get()succeeded, validated Pydantic field, NOT NULL DB column).Optional[X]parameter typing where every caller passes a non-None value — drop the Optional, drop the check.- Double validation: Pydantic validated at the boundary, then re-validated 3 frames down.
dict.get(k, default)whenkis in a TypedDict / required Pydantic field. (Inverse: at TypedDict→DB boundaries, prefer.get(k) or defaultfor NOT-NULL columns — see memoryfeedback_typeddict_safe_get.)- Redundant
if x is not None and x:— pick one.
3. Dead branches
elseclauses after a branch that alwaysraise/return.assertfollowed by code that re-checks the same condition.- Catch-all
if condition: ... else: passwhere the else branch is never meaningful.
4. Silent failure traps
- Callbacks whose return value (success bool, response status) is ignored — memory:
feedback_check_callback_return_values. - Fallbacks that mask real errors:
try: do_real_thing(); except: return cached_default()without logging. - Empty
exceptin async callbacks, hooks, or event handlers — these are the most dangerous because the surrounding flow keeps going as if nothing happened.
Boundaries to preserve
These are NOT cleanups. Don't flag them:
- Validation at system boundaries (HTTP request bodies, CLI args, file-format parsing).
- Catches around third-party / network / IO calls that legitimately can fail.
- Null checks on values genuinely populated downstream (e.g., late-bound DI, optional config).
- Defensive code that exists because of a documented bug or known race condition (look for a comment naming the issue).
When in doubt, leave it. False positives are more costly than missed cleanups in a review skill — they erode trust in the next run.
Verify before reporting
For each candidate finding:
- Read enough surrounding context to confirm the guarantee actually holds. "Caller A passes a valid X" doesn't mean caller B does.
- If the source of the guarantee is a schema or type, cite it (file:line).
- If unsure, downgrade confidence rather than dropping the finding — the user can decide.
This step matters because automated cleanup reviewers historically over-fire on Optional[...] / mock-object / float / generic-except patterns. Your memory has explicit cases of 100-confidence findings turning out to be false positives.
Output
Produce a single table sorted by priority. Use this exact shape:
| # | Priority | File:line | Pattern | Why safe to remove (cite the guarantee) | Suggested change |
|---|
- Priority =
P0(silent failure / swallowed bug),P1(clear redundancy with named guarantee),P2(style / stylistic noise). - Suggested change = a 1–3 line before/after diff, not prose.
- One row per finding. If the same pattern repeats, group with
<file>:<line1,line2,...>rather than padding.
End with a single line: Total: N findings (P0=x, P1=y, P2=z) across M files.
If there are no findings, say so directly. Don't invent issues to look productive.
Related
/qbcheck— validates a finding before you act on it. Run before applying P1/P2 changes if you're not certain./qcheck— broader code review (correctness, security, conventions). qclean is narrower — defensive-cruft only./qreuse,/qsimplify— sister cleanup reviews focused on different smells.