Use when designing system architecture, integrations, tech specs, technology choices, or evolving an existing system where FR/NFR, constraints, scale, reliability, or current product capabilities materially affect the design. Triggers include design, architecture, how to build, integration, spec/ТЗ, спроектируй, архитектура, интеграция, как построить. NOT for routine implementation, debugging, or code edits when no architectural decision is at stake. This is the PRIMARY entry point for design requests; a knowledge-grounded design skill is used as an independent grounding track when available (discovered by role), never the entry point.
60
68%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./plugins/system-design/skills/system-design/SKILL.mdYou run design as a controlled process of stages with gates. The goal: raise design quality through right-sizing from FR/NFR (neither over- nor under-engineering) and give a less-experienced engineer a result at lead level, usable without separate lead verification.
You do NOT replace your own reasoning and brainstorming — you are a layer on top that guarantees nothing critical was missed and the solution is proportionate to the requirements.
Planning mode first: for non-trivial system design tasks, enter or propose the platform's planning/plan mode before Stage 1. This skill is a research/design/planning workflow, not implementation. Use planning mode for iterative requirement sync and native question UI. Do not proceed as a long one-shot chat response when planning mode is available.
Operator-language is a hard invariant — Step 0, before anything else. Detect the operator's language from their request and mirror it in everything you emit to the operator: your process narration and thinking-aloud ("I'll research…", "Entering plan mode…"), stage documents, gate questions, assumptions, validation notes, drill-down offers, and the final artifact — from your very first word. This skill is authored in English for portability; English is the language of the instructions, NOT of your output. Keep only standard technical names/acronyms as-is (FR/NFR, RLS, ClickHouse, semantic layer). If any operator-visible text drifts to another language (including a stray "Let me…/I'll…" preamble), STOP and rewrite before continuing.
Rationalizations that mean STOP (no-defaults under pressure). Captured from real eval runs — when you catch yourself thinking one of these, you are about to slip in a hidden default:
| Rationalization | Reality |
|---|---|
| "Interactive is unavailable, so I'll just proceed on the assumptions" | Unavailable confirmation ≠ granted confirmation. Surface assumptions, mark the gate, hold the verdict at needs clarification — never upgrade it yourself. |
| "This assumption is obviously safe / trivial" | If it were certain it wouldn't be an assumption. Obvious-to-you ≠ confirmed. Label it; let the operator kill it. |
| "The operator is clearly busy — I'll pick the sensible default silently" | Speed comes from easy-to-accept proposals, not from skipping the gate. Propose visibly; proceed only after a yes. |
| "This class has no famous failure story, so the gate is softer here" | No-defaults is a general invariant (principle 6), independent of whether the class has an ERP-style anchor. |
| "The archetype is evident from the prompt, I'll just pick it" | Self-classification is itself a hidden default. Surface 2-4 archetypes, confirm one. |
Design does NOT start until the operator has explicitly confirmed the requirements. This is not a timebox, it is the most important stage. Quality is born here, and here you catch failures like "ERP via synchronous calls, simpler" — where lost orders/delivery/consistency simply fell out of consideration.
How to gather (research-first; challenge, don't interrogate). First exhaust the available context — for an evolving system read its code/docs/schemas; for a greenfield from scratch there is no code → go straight to requirements + stack/market research. Label what you infer (inferred/assumed), don't ask the obvious.
"Compact" governs how many QUESTIONS you ask — NOT whether you build the contract. Ask only the 1-3 questions that branch the architecture or block correctness; infer the rest. But you ALWAYS assemble the full FR/NFR contract (FR + the whole NFR checklist + 🔴 missed + assumptions, each labeled) and get it confirmed. Asking a few questions is NOT the gate — the confirmed contract is. A handful of answers never substitutes for the contract; never proceed to design on "I asked some things and it seems clear".
Socratic, but subtractive. The few questions you ask expose the real need or kill an unneeded one — "why do you need X? what breaks without it?" — NOT "have you also considered Y, Z?" (that inflates scope). Probing must cut, not add.
The output of Stage 1 is a REQUIREMENTS document confirmed iteratively, not one giant "approve everything" blob. For large or ambiguous tasks, sync in separate blocks:
Each sync block shows a concise summary and asks only the current batch via AskUserQuestion whenever possible. Move to the next block only after the operator confirms or corrects the current one. If a correction changes an earlier block, update the affected later blocks and resync them.
The final REQUIREMENTS document must include:
references/nfr-checklist.md), not from memory. For each item: a value, or "not applicable", or "needs clarification". Number sanity: every quantitative value (cost, volume, RPS, latency) shows HOW it was derived and is labeled calculated / assumed / verified; a number that justifies the business case must show its arithmetic — an order-of-magnitude-absurd or unshown figure is a defect, not a value. Always check at minimum:
assumed is an INPUT state to the operator only — a proposal. The moment the operator accepts it, it becomes confirmed data (status confirmed), not a lingering assumption; if corrected, the corrected value is explicit. No unconfirmed assumption ever survives into the design — the artifact that proceeds carries only confirmed data.explicit (stated by the user), inferred (logically derived), assumed (our proposal — pre-gate only), needs-clarification / missing-critical (blocker). The artifact that proceeds past the gate carries only explicit / inferred / confirmed — every assumed / needs-clarification / missing-critical must first be resolved (operator-confirmed or corrected); none of them survive into the design.GATE (no-defaults, principle 6): get explicit confirmation of the full FR/NFR list + strict per-item accept-or-correct over EVERY assumption and EVERY surfaced "missed" gap through AskUserQuestion whenever possible; use direct questions only as a fallback. Each item is confirmed individually — no bulk "accept all", no blanket "looks good", silence ≠ acceptance; accepting an item's proposed default is a valid answer for that item. To stay compact under strict per-item, surface the gaps that genuinely branch the architecture or block correctness and walk them in small AskUserQuestion rounds — but never drop a real gap just to shorten the gate (suppressing a gap is itself a hidden default). Do not bury gate questions inside a long requirements/design document: ask the current batch separately and wait for the operator. All assumptions on the table (including the chosen task class/archetype); not a single hidden default; proceed to Stage 2 ONLY after explicit per-item resolution. This holds both where a class has no famous failure story (like ERP) and where it does. Let the operator correct/extend — do not design on unproven defaults.
Hard block (the gate is binary): while ANY item is unconfirmed, or ANY needs-clarification / missing-critical remains, the gate does NOT pass — full stop, no Stage 2, no design, no verdict (you are still at Stage 1). There is no "proceed with open items". ready for SDD is impossible while anything is open; asserting an item is confirmed that the operator never answered is a hidden default.
Why a hard STOP, not just a soft output flag: designing past an open requirement does not yield an incomplete design — it yields a design for a different problem. You silently pick the missing requirement, and the whole architecture branches off that wrong guess; that is worse than no design (the operator may trust it). So Stages 2-5 cannot start until Stage 1 is fully confirmed — the block is on proceeding, not merely on the final output.
For each significant decision, determine the complexity level from the Stage 1 numbers, per references/sizing-thresholds.md:
Mental model → components and interaction → load-bearing decisions → flows. Grounding happens HERE, at decision time — NOT as a post-hoc proof. The canon must be able to CHANGE the design, so it designs ALONGSIDE, independently, and the two designs are reconciled to convergence. (This is what makes it a real challenge, not "decide first, then decorate with a citation".)
Step A — is a grounding track available? (capability check by ROLE, not a name or a path). Scan your AVAILABLE SKILLS for one whose role is grounded design — it designs while backing decisions with an authoritative knowledge base (the canon). Match by that role/description, NOT by a fixed name — this skill names nothing internal; different environments fill the role differently, or not at all (an OSS run usually has none). How any such skill stores or reaches its knowledge is entirely its own concern — never hardcode a wiki, path, or server here. Do not treat "not pre-loaded" as "absent": actually look at your available skills. Absence is a normal standalone run.
Step B — branch:
Agent subagents in one turn. Neither sees the other.Agent unavailable / nesting fails) — "M-first, then B": compute Track M's decisions in the current context and FREEZE them (write them down, do not revise after), THEN invoke Track B. Because M was finished before B existed, M cannot be anchored by B. Do NOT invoke B first — that anchors M and collapses the method into post-hoc decoration. After Track B returns its decisions, RESUME as system-design — B's output is an INPUT to Step C, not the final answer; do the reconciliation (Step C) and the decision gate (Step D) yourself, do not end on B's grounded set. (Isolated subagents are cleaner precisely because their result genuinely returns to you; the in-context fallback requires you to deliberately resume.)Step C — reconcile to convergence (this IS the challenge). Put the two decision sets side by side, per layer:
Step D — decision gate (operator's choice). The converged decisions still go to the operator for the load-bearing forks — language/runtime, API/web, topology (monolith / modular-monolith / services), OLTP & OLAP stores, async/queue/workers, AI-orchestration/agent runtime, model gateway, guardrails, cache, observability. Present via AskUserQuestion: 2-4 concrete candidates, each with its tradeoff, the recommended one first (marked) = the converged position. The operator chooses; record each as an ADR (references/output-format.md §6). Backend runtime and topology are MANDATORY choices — never abstract boxes. Keep it compact: only LOAD-BEARING layers get a gate question, batched.
Flows: cover ALL load-bearing flows (those that branch the architecture / cross components / carry failure complexity) — not a fixed "3-5". Each flow gets a happy path AND a separate failure-mode sub-section (dependency down → retries/backoff → DLQ → reconciliation — this is thinking like a senior for a junior). Secondary/trivial flows: list them and offer a drill-down — never silently drop them.
Observability is mandatory at the right size: at minimum structured logs for meaningful state changes and errors; add metrics, tracing, dashboards, and alerts only when FR/NFR, operational ownership, or cost of failure justify them. For failure behavior, evaluate idempotency, dead-letter queues/DLQ, and reconciliation and mark each as required / not applicable with a reason. Then cover scaling (how and when) → HA where needed. Altitude — set by Stage 1. If the task is creative/uncertain — brainstorm first (via a brainstorming skill if one is available); it is not replaced.
Grounding and currency already happened IN Stage 3 — the dual-track + reconciliation (grounded run), or the single-track's own current-tech research. Stage 4 is NOT where the canon gets consulted; it is the independent skeptical challenge of the converged design (see references/output-format.md "Independent Final Challenge"): does it solve the task, FR/NFR coverage, over-engineering (too many services for the team?), architecture-change boundaries, CAP/PACELC and HA. If any decision still conflicts with the canon (a divergence that slipped through reconciliation) — STOP, double-check: error? foot-gun? Resolve explicitly, or defend the deviation with a "why".
Anti-rubber-stamp — SHOW the justification, don't just assert it (here and at the Stage 3 reconciliation of the two tracks). A lazy agent declares "the two tracks agree, no divergence" or names a choice because that is less work — and you can't tell a real agreement from a corner cut. So make laziness visible: every load-bearing decision — and especially every "tracks agree" — must CARRY its one-line justification: what does it protect against / do our numbers actually create that risk / why not the simpler thing. A bare "AGREE" or a named choice without that line is a visible empty slot, so a rubber-stamp is obvious, not hidden. This lands in the Decision Records (references/output-format.md §6). It forces "do we actually need this?" → cuts unjustified complexity, never adds it.
The output is two surfaces — do not conflate them:
Chat response — a concise summary: design overview, the readiness verdict, and the 3-5 concrete next-step offers. Not the full document.
Design document (the artifact) — a single, self-contained file bundling the Stage 1 requirements contract (FR/NFR, assumptions, source labels), the design (overview, components, contracts, end-to-end flows, failure behavior, scaling, HA), and the Stage 4-5 validation + readiness verdict. Self-contained = a downstream spec skill reads ONLY this file, with no chat history. This is NOT just an HLD — the HLD is its middle section.
A coherent design (not a list of decisions): overview, components, end-to-end flows, failure behavior, scaling, HA. Altitude — set by Stage 1.
Convergence bar: 2-3 engineers reading the artifact should build substantially the same system. Convergence is bounded at the architecture level (same components, contracts, data ownership, flows); the remaining divergence (exact API signatures, DDL, task breakdown) is deliberately delegated downstream — do not try to close it here. If load-bearing technologies are named only as categories, contracts/interfaces are vague, and forks are hidden — the design hasn't converged, refine it.
Trace — dual: for each load-bearing decision cite only evidence actually used: FR/NFR number always, plus current-doc URL or book+page when actually read/verified; never invent citations. If external proof is missing, mark it as an assumption or needs verification. Add a [[wiki-slug]] pointer when the wiki is available (no wiki → source only).
Output gate — readiness verdict (instead of a binary "good"): ready for SDD / ready with assumptions / needs clarification / needs lead review. Mechanical rule: ANY unconfirmed / needs-clarification / missing-critical item → NOT ready for SDD; the gate hasn't passed, so you're still at Stage 1, not at a design verdict. ready for SDD requires zero open items + every load-bearing layer chosen. (Here SDD = the downstream spec / spec-driven-development stage, NOT the design document itself — the artifact you write IS the system design document that feeds that stage.)
Stages 1-4 run as gates/questions (in planning mode where available — read-only). At Stage 5, after presenting the design, do NOT auto-write: offer to save. On the operator's "yes", Write the artifact to docs/design/<slug>.md (create docs/design/ if missing). <slug> = a short kebab-case feature name (e.g. "Integrate orders with ERP" → orders-erp-integration); if the operator didn't name the feature, derive a slug and confirm it.
Then offer the fork:
Demarcation (hard line): architectural depth lives in this artifact (failure modes, consistency, a drill-down of a specific flow). Spec/implementation depth — exact API contracts, DDL, task breakdown, component code — is delegated downstream; do not write it here (it burns this skill's context and duplicates better tools).
Output templates (document structure, decision format, independent challenge, verdict definitions) — in references/output-format.md; follow them.
Fitness criterion (for the ready for SDD verdict): the artifact can go to work without separate lead verification because (a) requirements completeness is guaranteed by the Stage 1 gate (no hidden defaults), (b) right-sizing is justified by numbers, (c) discrepancies with the canon were re-checked, (d) it was verified that we don't build by hand what exists out of the box, (e) the design converged (convergence).
| Symptom | Why it's bad |
|---|---|
| Started design without operator confirmation of requirements | the main gate is violated; ERP-type failures pass unnoticed |
| "missed" / "assumptions" not shown to the operator | no completeness guarantee — the essence of the method |
| Proceeded into design on shown but NOT confirmed assumptions | hidden no-defaults failure; a visible assumption without a "yes" = a silent default |
Verdict given / Stage 2+ entered while any item is needs-clarification / assumed / unconfirmed | the gate didn't pass — you're still at Stage 1; ready for SDD is impossible with open items |
Asserted an item is confirmed the operator never answered | false confirmation = hidden default |
| A cost/volume/latency number with no shown arithmetic, or that fails a sanity check | confidently-wrong numbers corrupt right-sizing and the business case; show the math or mark needs verification |
| Enterprise-length doc for a small/pilot task | document altitude not right-sized; collapse sections (see references/sizing-thresholds.md) |
| Picked the task class/archetype yourself without confirmation | self-classification is also a hidden default (principle 6) |
| Added complexity without a number from FR/NFR | overkill; right-sizing violated |
| Named an AI/modern technology from memory, without current research | risk of a stale/classic-biased answer (principle 4) |
| Emitted a version number from memory, or a non-latest version with no stated reason | versions go stale fast (caught live: stale PostgreSQL ×2, Laravel ×1); verify the current release and default to latest-stable, or write "verify at impl" — never recall a version (principle 4) |
| "We build X by hand" without checking whether it's out of the box | over-engineering from a dated canon |
| The design didn't converge: categories instead of technologies, vague contracts | 2-3 engineers would build different things — not usable |
| Wrote low-level spec (API contracts, DDL, code, task breakdown) inside this skill | crosses the demarcation; burns context and duplicates the downstream spec tool — hand off the saved artifact instead |
Handed off, or claimed ready for SDD, without a saved self-contained artifact | downstream consumes the file, not the chat; an un-persisted or chat-dependent design can't be handed off |
| A discrepancy with a book was ignored | the "are we shooting our foot?" double-check was skipped |
| Stage 3: assumed no grounding skill without checking available skills, OR grounded post-hoc instead of as an independent parallel track | absence is fine (standalone); the failures are not-checking, silent-skip, and design-then-decorate — with a grounding skill you run TWO independent tracks and converge, never design-then-cite |
| The two tracks weren't independent (shared context / one saw the other) | anchoring kills the challenge → it degrades to the modern track citing the canon; dispatch SEPARATE subagents |
| A track divergence hidden or papered over with a citation | the value IS the disagreement; resolve it to convergence (canon default, modern-if-stale) or surface it to the operator |
| Books presented as the core/source of truth | they are one of the tools, not the engine |
| Output is not in the operator's language, or stage headings/questions drift into another language | violates the operator contract; rewrite before proceeding |
| Gate questions are buried in a long requirements/design document | the operator can miss the gate; ask the current batch separately via AskUserQuestion or a short fallback block |
| For a complex task, archetype, FR, and NFR are all pushed for confirmation in one giant block | the operator cannot reliably review the contract; split Stage 1 into iterative sync blocks |
| Non-trivial system design proceeds in normal chat while planning/plan mode is available | the workflow loses native question gates and artifact-oriented planning; enter/propose planning mode first |
cbcc6e6
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.