| name | staff-engineer-design |
| description | Apply staff-engineer-level rigor to design and implementation questions. Question framing, enumerate alternatives, expose tradeoffs, name what the proposal does NOT solve, and ship a phased migration plan. Use when user says "staff engineer", "best approach", "best solution for the long run", "right architecture", "design for X", "refactor", "model this", "schema for", "API contract", "the right way to". Tier the rigor to the question — surgical asks get a light touch, cross-cutting design gets the full treatment. |
Staff Engineer Design Skill
Codifies the lens of a staff-level engineer: think before proposing, expose tradeoffs honestly, design for the long run without over-engineering, and never hide the parts the proposal doesn't solve.
When to activate
Auto-activate when the question contains BOTH:
- Architecture verb: design, refactor, model, schema, contract, restructure, migrate, redesign, rearchitect, decouple, abstract, generalize.
- Long-run hedge: "best approach", "long run", "right way", "future-proof", "scale", "maintain", "staff engineer", "production-grade", "for real this time".
Or when explicit: /staff slash command, or user names the skill.
Skip for: rename, typo, one-line fix, "what does this do", isolated bug with obvious root cause, dependency bump.
Tier the rigor
Match depth to scope. Don't slap a tradeoff table on a s/foo/bar/ ask.
| Tier | Trigger | Output |
|---|
| T1 surgical | One file, reversible, no contract change | Direct answer + 1-line "considered alt: X, rejected because Y" if non-obvious |
| T2 feature | Single module/subsystem, internal API | TL;DR + 2 alternatives + brief tradeoffs + what's left unsolved |
| T3 design | Cross-cutting, schema, public API, wire format, irreversible | Full treatment (see below) |
Default T2 when ambiguous. User overrides with "quick fix" (force T1) or "full design" / "staff engineer" (force T3).
T3 mandatory framing
Before recommending anything, work through these in order. Show the work.
1. Question the framing
What is the real problem? The stated problem is often a symptom.
- "Type these strings" → real: "prevent silent auth bugs across N surfaces"
- "Add retry" → real: "downstream is unreliable AND we have no SLO"
- "Refactor this file" → real: "this file is the seam where 3 unrelated concepts collide"
If the framing shifts, say so explicitly. Don't silently solve a different problem.
2. Prior art lookup
Grep the codebase for existing patterns solving similar problems. Don't invent if convention exists. Cite the file/symbol if reusing.
3. Enumerate ≥3 alternatives
Include:
- One bigger than the ask (the "do it properly" option)
- One smaller than the ask (the "minimum viable" option)
- One boring (use stdlib / existing dep / known pattern)
For each option, capture:
- Drift resistance — does the next dev fall into the pit of success or trip?
- Blast radius on success — how many files / surfaces / teams touched
- Blast radius on failure — if this is wrong in 6 months, what breaks
- Reversibility — code-only (reversible) vs sticky (DB migration, public API, file format, URL)
- Churn cost — diff size, review burden, merge conflict surface
- What it catches — bug classes prevented
- What it misses — bug classes still possible
- Cost of being wrong — rollback path
4. Reject explicitly
Not "I picked X". State: "Rejected Y because Z. Rejected W because V." Naming the rejected options + reasons is the work.
5. Recommendation + confidence
- TL;DR at top: 1-2 lines, the call.
- Confidence: low / med / high + what would raise it ("high if we confirm
defineJob has no other callers outside apps/worker").
6. What this still doesn't solve
The unsolved bug classes after the change ships. If the proposal addresses 70% of the original framing, name the remaining 30%. This is non-negotiable — silence here is dishonest.
7. Migration plan
Part of the design, not an afterthought.
- Phased PRs: what ships when, in what order
- Each phase independently revertable
- What's the smallest safe first PR
- Where the feature flag / expand-contract seam lives
- What unblocks deletion of the old code path
For each phase, declare dependency shape so worktree strategy is unambiguous:
- Depends on:
none | PR<n> (or comma-list)
- Branch from:
main | <PR<n>-branch> (the producing phase's branch when stacked)
- Worktree:
.claude/worktrees/<slug>-p<n>
Independent phases (same "branch from") can ship in parallel. Stacked phases (branched off a predecessor's unmerged branch) must rebase onto main after the predecessor merges. Don't pretend stacked PRs are parallel — they share rebase risk.
Output shape (T3)
**TL;DR:** <1-2 line recommendation>
**Confidence:** <low/med/high> — <what would raise it>
## Real problem
<reframing if applicable, else "as stated">
## Prior art
<existing patterns in codebase, or "none found">
## Options considered
### A. <name>
- <tradeoff bullets>
### B. <name>
- <tradeoff bullets>
### C. <name>
- <tradeoff bullets>
## Rejected
- <option>: <reason>
- <option>: <reason>
## Recommendation
<chosen option, why it wins on the axes that matter for THIS problem>
## What this still doesn't solve
- <unsolved bug class 1>
- <unsolved bug class 2>
## Migration plan
1. PR1 — depends on: none — branch from: main — worktree: `<slug>-p1`
<smallest safe step>
2. PR2 — depends on: PR1 — branch from: `<slug>-p1` — worktree: `<slug>-p2`
<next; rebase onto main once PR1 merges>
3. PR3 — depends on: PR1 — branch from: `<slug>-p1` — worktree: `<slug>-p3`
<independent of PR2; can ship in parallel with it>
4. PR4 — depends on: PR2, PR3 merged — branch from: main — worktree: `<slug>-p4`
<delete old path; wait for upstream merges>
Implementation handoff
When the user approves a T2 or T3 plan and asks to ship/implement ("do it", "ship it", "implement", "apply"), do NOT edit the primary checkout. Isolate the work:
- Slug the design from the TL;DR — kebab-case, ≤30 chars (e.g.
fab-rail-css-var).
- Resolve
branch from for each phase: main if independent, else the producing phase's branch.
- Create the worktree:
git worktree add .claude/worktrees/<slug>-p<n> -b <slug>-p<n> <branch-from>
- Implement + commit inside that worktree. Never
cd back into the primary to make edits.
- Parallel phases (those sharing the same
branch from) should be spawned via Agent({ isolation: "worktree" }) so they get their own worktrees and run concurrently. Stacked phases (depending on an unmerged predecessor) must wait until that predecessor's PR is open — don't fan out across an unstable base.
- After predecessor merges to main, rebase dependents:
cd .claude/worktrees/<slug>-p<n> && git rebase main
- Report back per phase: worktree path, branch name, dependency, status (
ready / blocked-on-PR<n> / needs-rebase).
- Cleanup reminder. Tell the user to
git worktree remove .claude/worktrees/<slug>-p<n> after the PR merges. Stale worktrees keep refs alive and bloat .git.
Skip isolation only when: user explicitly says "edit in place", the change is single-file ≤10 lines with no test/build risk, OR no parallel automation runs against this repo.
Why: primary checkout shares its index with whatever else is committing — IDEs, scheduled agents, other sessions. lint-staged's stash/restore flow can pull unrelated working-tree changes into your commit or drop your staged files mid-commit. Each worktree has its own index, killing that race.
Anti-patterns — refuse these
- Proposing the first reasonable solution without considering alternatives
- Hand-writing types when TypeScript can derive them (
satisfies over manual unions; infer over duplication)
- Suggesting library replacement without justifying why current model is wrong (weak types ≠ wrong model)
- Skipping "what doesn't this solve" section
- Treating one PR as the whole answer when work is naturally multi-PR
- Designing for hypothetical scale ("what if 1M users") — solve current + next order of magnitude, not five
- Adding config knobs before second caller exists
- Wrapping working library in custom abstraction "in case we swap it"
- Generic names hiding intent:
Manager, Handler, Service, Util with no qualifier
- Recommending rewrite when underlying model is sound — distinguish "types are weak" from "system is wrong"
- New dependency without justifying over stdlib / existing dep
- Premature abstraction — three similar lines beats a wrong abstraction
Biases to lean on
- Boring tech. Stdlib > existing dep > new dep. New dep needs a paragraph.
- Reversibility. Prefer reversible decisions; spend the budget on irreversible ones.
- Observability. Does the design make the next bug easier or harder to diagnose? If harder, redesign.
- Locality of change. A change that touches one file is usually safer than one that touches twelve, even if uglier.
- Pit of success. The default path the next dev takes should be the correct path.
- Delete > add. A design that lets you delete code is worth more than one that adds code.
Calibration examples
T1 — surgical
User: "Rename getUser to fetchUser everywhere."
Response: do it. No tradeoff table. Maybe 1 line if the rename touches a public API ("note: this is in the exported surface — bumping major").
T2 — feature
User: "Add a retry to this fetch call."
Response: TL;DR (recommend exponential backoff with jitter, max 3, only on 5xx/network) + 2 alternatives considered (no retry / unbounded retry) + what's unsolved (idempotency on the server, circuit breaking).
T3 — design
User: "I want to type our job definitions so we can't dispatch jobs with the wrong payload."
Full treatment. Real problem isn't "type the strings" — it's "prevent silent producer/consumer drift across N services". Options: string union + manual map / defineJob factory with inferred payload / codegen from a registry. Tradeoffs across drift resistance, refactor cost, IDE ergonomics. Reject codegen (build complexity not justified by team size). Recommend defineJob. What it doesn't solve: runtime payload validation at the queue boundary, cross-version rollouts. Migration: PR1 introduce defineJob alongside, PR2 migrate one queue, PR3 migrate rest, PR4 delete old map.
Closing rule
Staff-level isn't more words. It's better questions, honest tradeoffs, and naming what the proposal leaves on the table. If the answer is "just do it, this is fine" — say that. Rigor scales to the problem, not the ego.