SuperAPI AI Directory

arch-design

Design & creation

View repository

Updated 10-15 17:24

About this skill

Architecture design

The skill for the planning phase, does onlyarchitecture-level high-level design. The spec defines "what to do"; this skill defines "how to do it" — but only down to the architecture level (modules, dependencies, layering, seams), not sinking into code details. Afterward, incremental-impl turns the design slices into implementation.

spec-design          →   arch-design          →   incremental-impl
(为什么 + 做什么)         (怎么做 —— 本 skill)        (切片 + 实现)

Why this step is needed:Software has two kinds of value — its current behavior, and how cheap it is to change in the future. The latter is architecture. The purpose of architecture design is not "it can run now," but to keep the system "soft." This stance simultaneously opposes two things:rushed non-design(the debt incurred is repaid double in the future) andover-design(piling up abstractions for imagined requirements, each layer charging interest every time the code is read later). Every rule of this skill serves to find the "just enough" design between these two.

When to use

Two independent entry points both enter this skill, both atarchitecture altitude:

Entry point Trigger Upstream input
New feature architecture design A new feature goes through spec-design, and scale triage determines that it crosses boundaries, or has more than 5 acceptance criteria, or spans modules, requiring a real design docs/specs/<topic>/spec.md + validation-contract.md
Existing system architecture upgrade A piece of existing code has structural problems and you want to improve its architecture, butwithout changing externally visible behavior Target code + one specific structural complaint

Also triggers when the user says "technical plan / architecture design / architecture upgrade / refactoring plan / how to split modules / how to layer / how should this architecture be changed," or when you are about to start a cross-module change and the "how to do it" is not obvious.

Not for:

  • Requirement clarification—— if "what to do" is still vague, go back to spec-design. This skill assumes "what to do" is already fixed.
  • Slicing and execution details—— scale triage, slicing, distribution, and the implement-test-verify-commit loop belong to incremental-impl。
  • Bugs with reproduction steps—— the reproduction steps are themselves the requirement, go to systematic-debugging。
  • Code-level cleanup inside a module or file—— local cleanup such as naming, nesting, and dead code belongs to code-simplify。
  • Trivial single-module changes—— if all three criteria of the fast development workflow hold (single module, acceptance criteria ≤ 5, no cross-boundary interface changes), there is no architecture to design, go straight to implementation. Do not create a design process just to appear thorough.

Four iron rules

One: design is "how to do it," not "what to do." This skill does not reopen the motivation and scope of the requirement. If while polishing the design you find that "what to do" is actually still vague, stop, go back to spec-design—— do not fill the hole in the requirement with an arbitrary design decision. A requirement hole filled by a design decision is equivalent to quietly inventing a requirement.

Two: architecture upgrades do not change externally visible behavior. The existing-system upgrade path keeps everything observable to users, external callers, interface consumers, and downstream file formats. If a structural changewouldchange observable behavior, that part is a new feature—split it out and route it through spec-design; only behavior-preserving structural changes stay here. (This is exactly Fowler's definition of "refactoring".)

3. The recommended option is the simplest option that satisfies the design drivers. Choosing a heavier option is allowed only when you canname a specific design driverthat requires it. "Might need it later" is not a driver. This is auriga's "do not multiply entities beyond necessity" applied to the design phase—every layer, every abstraction, every indirection added here must be repaid by every future person who reads the code.

4. Abstractions must have a clear reason. Introducing any abstraction (interface, indirection layer, adapter) must come with a clear, specific reason that holds right now—usually "the two sides of the seam really are a real boundary between different rates of change" (regional differences, third-party replaceability, volatile versus stable). If you cannot state a clear reason, do not abstract. The criterion iswhether the rates of change on the two sides of the seam really differ, not "whether there might be a second implementation later".

  • Supporting rule: when there is only one implementation, you mostly do not need to extract an interface for it—one adapter is just an imagined seam; two make a real seam.
  • Exception: if the interface buys "dependency direction" or "change isolation" (for example, business logic should not depend directly on a particular database), it is worth it even if there is only one implementation right now—what it buys is not "multiple implementations" but keeping irreversible decisions behind a boundary.

Process

A. 调研        →  B. 设计候选        →  C. 选型与记录        →  D. 自检与交接
   (摸清现状)       (出方案、做权衡)       (选方向、磨细节、落库)     (自检与交接)

A Research

A1. Receive input.

  • New feature: read spec.md + validation-contract.mdin full. Pay attention to the "how to do it" that the user mentioned in passing during the spec-design phase—spec-design deliberately keeps implementation hints out of the spec, and they resurface here as design input.
  • Existing system: read the target code itself, not a summary of it. Open the files. Have the user state the structural complaint in one sentence.
  • Both entry points: collect project-level architecture rules—search for specifications under docs/rules/arch/ relevant to this design (layering rules, dependency direction, module boundary conventions, etc.; these aredesign constraints, not the accumulated decisions of ADRs). Anchor the directory to the repository root: use git rev-parse --show-toplevel to resolve the repository root, then search <仓库根>/docs/rules/arch/; when the cwd is not at the repository root and there is a closer docs/rules/arch/(a monorepo subpackage) between the cwd and the repository root, collect both levels—treat subpackage-level rules as supplements/narrowings of the repository-level ones, and when they conflict, the subpackage level wins; for non-git repositories, fall back to searching relative to the cwd. If the directory does not exist or has no relevant files, record "no project-specific architecture rules". Rules that are read go into the "Constraints and Invariants" of B1, and candidate options must not violate them.

A2. Understand the current state.

  • New feature: where the new code connects—which modules, which interfaces, how data goes in and out, which existing code will be touched.
  • Existing system: draw the current architecture of the affected area—modules, dependency direction, layering, data flow. Be specific to file paths; do not use adjectives.

A3. (Existing system) Precisely characterize the structural problem. Refactoring a problem that hasn't been named is aimless polishing. Use diagnostic tools (see 'Diagnostic Tools' below) to give it a character: is it a god module, a circular dependency, a shallow-module pass-through layer, a misplaced layer, or a boundary not aligned with the axis of change.Once named, the problem is the acceptance criterion— whether this design succeeds or fails depends on whether it eliminatesthatproblem, not on whether the code 'looks smoother.'

A4. Scale exit—if it's too small, exit. With the information now at hand, rerun the three criteria of the rapid development process: single module, acceptance criteria ≤ 5, no cross-boundary interface changes. If all three hold, there is no architecture to design—state this, hand it back for direct implementation, and exit. The scale gate is a gate; don't fabricate design work just to look thorough.

B Design Candidates

B1. Frame the design problem. Before generating options, lay out the decision space clearly:

  • Design drivers—pick out the 2–4 quality attributes that truly matter here (see 'Design Driver Vocabulary'). Don't score against all attributes; a framework where 'everything matters' equals no decision.
  • Constraints and invariants—whatcannotchange: validation contracts (new features), external behavior contracts (existing systems), public interfaces, performance budgets, platform limits.
  • When splitting modules,think about boundaries along 'axes of change': whatever changes together for the same reason at the same time, put together; whatever changes for different reasons, split apart. This is the truly actionable version of 'single responsibility.'

B2. Produce ≥ 2 genuine candidates. Each candidate must be an option a competent engineer would genuinely defend—don't build straw men meant only to lose. Always includethe simplest candidate(the minimal structural change that satisfies the design drivers); on the existing-system path, 'do nothing / make a small local fix' is a reasonable simplest candidate. For each candidate, sketch out: module shape, key interfaces, data flow.

B3. Trade-off comparison. Candidates × drivers, make a table. In each cell writeconcrete consequences, not ratings ('touches 3 modules + 2 test suites,' not 'blast radius: medium'). This table is meant to make the cost of rejected options visible, not to mechanically compute a winner.

B4. Simplest review. Before deciding: is the leading candidate the simplest option that satisfies the drivers? If the winner is the heavier one, write in one sentence which driver justifies that extra weight; if you can't write it, the simpler one wins. Also judgereal duplication versus false duplication: two pieces of code looking similar now doesn't mean they should be merged into one module—if they evolve for different reasons, forcing an extraction will create coupling in the future.

C Selection and Recording

C1. User chooses direction + polish together. Lay out the candidates and the trade-off table for the user,the user chooses the direction. Once chosen, move into polishing: walk the design tree together with the user—dependency direction, where the seams go, what the interfaces look like, which tests can stay—settling them one by one. Architecture decisions shouldn't be made by the agent head-down alone, but they also shouldn't be like spec-design with its one-question-one-answer rhythm (that pace is too fragmented for 'picking a direction').

C2. Define the migration path and safety net.

  • Existing system: definemigration shape—Which structural strategy to use to get from the current state to the target state (see "Migration Shape" below).
  • Existing system: fixedBehavior safety net—Before changing any structure, the affected external behavior must be covered by tests. If already covered, note which suite; if not, add characterization tests first. Characterization tests are the "red" phase of the refactored version: they don't specify new behavior, they lock down existing behavior so refactoring can't silently change it. When the behavioral surface isn't obvious, the design of characterization tests can draw on test-designer。
  • New feature: if the existing structure is found to be in the way, the design itself must include a segment ofpreparatory refactoring—first make the change easy, then make this easy change, rather than forcing in an awkward design to make do.
  • Does not produce an implementation order: in what order and at what granularity each part lands is the job of incremental-impl ; arch-design delivers the design, not the execution ordering.

C3. arch_design.md gate. See "arch_design.md gate" below to decide whether to persist it. If persisting, write references/arch-design-template.md according to docs/specs/<topic>/arch_design.md. If unsure, ask the user.

D Self-check and handoff

D1. Self-check—Sit in the seat of the person who will implement this design, and go through the "Self-check List" below. Fix on the spot; don't redo the earlier phases.

D2. Provide an architecture overview (only when arch_design.md has been persisted). After the gate triggers and the document is written, use AskUserQuestion / request_user_input to give the user three options, so they can quickly glance through this architecture—the order is fixed as skip → playground → static HTML:

  • Skip—Go straight to D3. The default when the design is small and the user has already seen it clearly in the conversation.
  • playground—Dispatch playground:playground(Anthropic official plugin) with arch_design.md as input to generate an interactive HTML that visually browses the candidates, trade-offs, and dependency graph. When the playground plugin isn't installed, hide this option.
  • Static HTML overview—Generate a self-contained docs/specs/<topic>/arch-overview.html, render arch_design.md : draw the Mermaid module diagram, directory tree, and candidate trade-off table, then open it. Doesn't depend on any plugin.

When the gate isn't triggered (decided in conversation, no document written), skip this step—there's no document to overview.

D3. Handoff.

  • The design (the persisted arch_design.md, or the in-conversation decision when the gate isn't triggered) is handed off as planning input to incremental-impl。
  • The existing-system path additionally hands off a safety-net requirement: characterization tests must exist and be greenbeforethe first structural commit.
  • If it was already sent back to direct implementation at A4, then there's nothing to hand off—this is a legitimate outcome.

Diagnostic tools (for upgrading existing systems)

Judging "whether this piece of architecture actually has problems" must be actionable, not rely on "I think it's messy." Two kinds of tools work together—the checklist is responsible for breadth (don't miss anything), the deletion test and deep/shallow modules are responsible for accuracy (don't wrongly accuse)。

Deletion test: Imagine deleting a module and inlining its code directly into all callers.

  • The complexity just disappears → it was an unnecessary pass-through layer to begin with, and should be deleted.
  • The complexity re-emerges at several callers, and is duplicated → it's doing work, it's a good module, don't touch it.

Deep modules / shallow modulesDeep module = narrow interface, lots of behavior behind it; trade a little interface for a lot of functionality. Shallow module = the interface is almost as complex as the implementation; it wraps a layer but doesn't save the caller any effort.

Recall checklist (architecture-level problem signals):

  • Circular dependency— a cycle appears in the module dependency graph.
  • Inverted dependency direction— a stable / high-level module depends on a volatile / low-level module.
  • Wrong layer— a piece of logic sits in a layer where it doesn't belong.
  • God module— one module manages everything.
  • Shallow module / pass-through layer— it wraps a layer but doesn't save any effort (confirm with the deletion test).
  • Leaky seam— implementation details leak through the interface and the caller ends up depending on them.
  • Divergent change— one module changes for several different reasons; the boundary is too coarse and should be split.
  • Shotgun surgery— one change requires touching many modules at once; related things have been cut apart and should be brought together.
  • Feature envy— a piece of logic keeps operating on another module's data; it's in the wrong module.

The last three are direct signals that "module boundaries aren't aligned with the axes of change," and they're two sides of the same coin as B1's "cut modules along the axes of change."

True seam rule: one adapter is only an imagined seam; two make a true seam. See Iron Law Four.

Design principles (from Clean Architecture)

Guidelines for cutting modules and setting dependency direction:

  • Dependency rule— source-code dependencies always point toward the more stable, more abstract, higher-level-policy side. Business rules don't depend on frameworks, databases, or UIs.Frameworks, databases, UIs, and third parties are all "details", placed in the outer ring of the architectural boundary; core business policy doesn't import them.
  • Draw boundaries at the fault lines of rates of change— separate the fast-changing (frameworks, databases, UIs, third parties) from the slow-changing (business policy).
  • Cut modules along the axes of change— a module is responsible for only one "source of change." Note: a source of change, not "does only one thing."
  • Acyclic dependencies— there must be no cycles in the module dependency graph.
  • Drawing boundaries is about deferring decisions— good architecture makes as few irreversible decisions as possible, isolating the irreversible ones (choosing a database, choosing a framework) behind boundaries, deferring them whenever possible, and preserving options.
  • (Optional)Organize directories by business capability, not by technical layering—so people can see at a glance "what kind of system this is."

If you need to divide components / modules (especially when the project is already a large multi-component project), read references/component-design.md— it contains principles of component cohesion and coupling, plus practical tips for mobile modularization.

Interface design (four architecture-level points)

Cutting modules means defining theseamsbetween modules, that is, the interfaces. Part of interface design is an architectural decision and can't be avoided—at the architecture level, take these four points:

  1. Narrow interface / information hiding——Anything observable at a seam will eventually be depended on by callers (Hyrum's Law). An interface should expose only what callers truly need and hide the implementation. This is really just another way of saying "deep module."
  2. Contract before implementation——A seam's interface is settled at the design-tree step; don't let it grow as you write.
  3. Evolve by adding, not changing——When an interface must change, prefer adding optional items; don't change signatures or delete fields. This directly relates to the blast radius.
  4. One-Version——Keep only one version of the same dependency and the same set of interfaces in a system; multiple coexisting versions cause diamond dependencies and mess up the dependency graph, belonging to the same category of problem as "acyclic dependencies."

Implementation details when landing an interface—error semantics, boundary validation, REST resource shape, naming conventions, TypeScript type patterns—see references/interface-design.md. That handbook focuses on implementation details; when defining interface contracts, its first half's fundamental mindset and evolution sections are also useful.

Design-driven vocabulary

Pick the 2–4 that truly matter here. Naming them is what turns "I like this" into a defensible choice.

Driver What it asks
Simplicity The fewest moving parts, the fewest concepts a reader must hold at once
Blast radius How many modules, tests, and call sites a change touches
Testability Can the new structure be tested in isolation; does it add or remove test seams.Hard to test is itself an architectural problem——If a design can only be verified end-to-end, the seams are in the wrong place
Reversibility How expensive it is to back out if the choice is wrong. Good architecture makes as few irreversible decisions as possible
Consistency Whether it follows patterns already in the codebase
Migration cost How many steps to reach the target state, whether there's an intermediate broken state, whether coordination is needed
Performance Latency / memory / throughput——Only when there's an explicit budgetdoes it serve as a driver
Extensibility The cost of the next change that is "already on the roadmap and definitely going to happen"——never a hypothetical change

Extensibility is a trap: at the abstract level it can justify almost any abstraction. Only allow it as a driver when a concrete, near-term, already-defined change names it.

Migration forms (upgrading an existing system)

For an existing system moving from the current state to the target state, choose a migration approach and write it into arch_design.md 's "Decision & Rationale." The three forms are distinguished by "what is replaced" and "scale":

Migration form What is replaced Old-new relationship How to transition Typical scenario
Parallel change One interface / contract Old and new interfaces coexist Add the new interface first → migrate callers one by one → delete the old interface Changing function signatures, interface shapes, data structures; especially when callers are not under your control
Branch by abstraction One module's implementation Old and new implementations satisfythe same interface, interchangeable Insert an intermediate layer → build a new implementation behind it → flip the intermediate layer → delete the old implementation Replace the implementation of a module within a single codebase (swap libraries, swap algorithms)
Strangler Fig An entire system / subsystem New systemIndependently rebuilt, the shape may differ Add routing in front to split traffic → rebuild functionality block by block in the new system and shift traffic over → delete the old system once it is emptied out Replacing a large legacy system, cannot afford downtime, dare not make a big-bang change all at once

How to choose: if what changes is the interface → parallel change; if you are swapping a module's implementation and old and new can fit the same interface → abstract branch; if you are replacing the entire system and old and new are not the same shape → strangler fig.

Regardless of which one, every step must keep the system compilable, testable, and releasable, and independently rollback-able (extended from git-workflow 's atomic commit rule).

arch_design.md gate

arch_design.md is a versioned design document, and its value lies in lettinganother agent (or you a few months later) execute or extend this design without having to re-derive it. This value only holds if the design itself is non-trivial and will outlive the current round of conversation. So it isconditionaloutput.

If any one holds, write arch_design.md:

  • The design crosses module or subsystem boundaries—an agent touching one part needs to see the whole picture.
  • There are ≥ 2 candidates with substantive trade-offs—recording the rejected candidates and thereasonscan stop future agents from re-litigating a decision that has already been made.
  • Introduces a new subsystem, a new layering, or changes the structure of a shared module / public interface.
  • The implementation will be sliced and distributed (incremental-impl 's L size / parallel)—each slice needs a shared design reference.
  • (Existing system) migration spans multiple commits, and an agent taking over midway needs to see the target state and the current step.

Skip the document and just decide in-conversation when:

  • Single module, the solution is unique and obvious (this should have exited at A4).
  • The design can be explained in two or three sentences, andthe same agent will implement it right away. A document no one will ever come back to read is pure burden.

Not sure—ask the user. When the design is of medium weight and you genuinely cannot judge, use AskUserQuestion / request_user_input: offer "write arch_design.md(recommended when it will be handed off or reviewed later)" versus "proceed in-conversation (faster, leaves no document to maintain)", and explain the trade-offs. Do not silently pick a side.

Location and lifecycle. The document lands in docs/specs/<topic>/arch_design.md—the same temporary workspace used by spec-design ,pr-ready-guard will force it to have a destination before PR Ready. At the PR Readiness stage, handle it per the "documentation conventions" in CLAUDE.md / AGENTS.md: if it is a long-term reference (module layout, data flow—exactly what that directory holds) then distill it into docs/architecture/, otherwise archive it into the worklog or delete it.

Its "candidate comparison" sectionborrows the ADR template's style—each candidate carries pros / cons / rejection reason, and the final section writes "consequences". For the template see references/arch-design-template.md。

Language. arch_design.md is a planning document—write the body in the language used in the conversation (if the conversation is in Chinese, write in Chinese). Keep section headings as the template gives them (English anchor + Chinese hint), and keep structural keywords in English.

An ADR is an optional peripheral output, not a routine action. Only when a decision is "expensive to reverse" (choosing a certain database, settling on a certain authentication architecture, and the like) should you ask the user at wrap-up whether to solidify it into an ADR—an ADR is an archive, serving as an architectural guide for subsequent development. This solidifying actionis handed to documentation-and-adrs skill; this skill does not reinvent the whole ADR apparatus itself.arch_design.md is a solution during development, while an ADR is a long-term archive; the division of labor between the two does not overlap.

Self-check list (D1)

Sit in the seat of the agent who will implement this design and read it through. Fix on the spot; do not redo the phase.

  • states the design problem and its drivers—not just the chosen solution.
  • genuinely considered ≥ 2 candidates; the rejected ones have recorded reasons.
  • the recommended solution is the simplest one that satisfies the drivers; otherwise it names the driver that justifies the extra weight.
  • every invariant / constraint in the spec or behavior contract is preserved by the chosen design.
  • every relevant rule in the project-level architecture rules (docs/rules/arch/) is not violated by the chosen design; when A1 found no rules, "no project-specific architecture rules" has been recorded.
  • the structural problem named by A3 is truly eliminated by this design—not just moved elsewhere.
  • the behavior protection net is specified before the first structural step.
  • does not smuggle in an implementation order or slicing plan—that belongs to incremental-impl .
  • does not smuggle in "what to do"-level decisions (new behavior, scope changes) as design choices.
  • if arch_design.mdis written: no TBD / placeholders; a zero-context agent can execute it.

Anti-patterns

  • ❌ Starting design before "what to do" is settled—if the spec is vague, go back to spec-design; do not plug the hole with a casual design decision.
  • ❌ Smuggling behavior changes into refactoring—upgrading an existing system does not change behavior; a behavior change is a new feature and must go through the spec.
  • ❌ Creating straw-man candidates—candidates made only to lose turn "comparison" into theater. Every candidate must be able to stand on its own.
  • ❌ Missing the simplest candidate—if there is no "do less" option on the table, the design will default to being longer and heavier.
  • ❌ Scoring against all drivers—a framework where "everything matters" equals no decision. Pick 2–4.
  • ❌ Defending structure with hypothetical future needs—extensibility must be named by a concrete, near-term, already-clarified change.
  • ❌ Writing arch_design.mdfor a trivial single-module change—the gate is a door; a document no one reads again is a burden.
  • ❌ A cross-module design that will be distributed in slices but skips arch_design.md—the slices will each re-derive the design and then drift.
  • ❌ Producing an implementation order or slicing plan here—arch-design delivers the design; order, slicing, and distribution belong to incremental-impl .
  • ❌ Refactoring without a named structural problem (A3)—"make it cleaner" is not a goal, and the design cannot determine completion.
  • ❌ Upgrading an existing system, touching the first structural commit before the characterization tests are green—refactoring can silently change behavior with nothing to catch it.

Relationship with other skills

  • spec-design—the upstream entry point for new features, providing spec.md + validation-contract.md. It deliberately blocks the "how" outside the spec, which is the input to this skill.
  • incremental-impl—the downstream consumer, taking this skill's design and handling scale triage, slicing, distribution, implementation order, and the single-slice loop.
  • test-designer—the characterization tests for the existing system's safety net, designed by it when the behavioral surface is not obvious; for new features, it designs failing tests against the verification contract.
  • playground:playground(Anthropic official, soft dependency)—one of the auxiliary options for the D2 architecture overview; if not installed, only a static HTML overview is provided.