codebase-architecture

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Codebase Architecture

代码库架构

Decide a TypeScript codebase's structure, improve it where change has become expensive, and make it hold. The target is a codebase a reader can hold in their head: few surfaces, one canonical way to do each job, and behaviour where you would first look for it.
  • IS: folder structures, module contracts, middleware pipelines, frontend/backend boundaries; architecture briefs; domain language and decision records; domain-informed deepening; guardrail tooling, CI gates, and agent wayfinding.
  • IS NOT: scaffolding a new repo (
    scaffold-nextjs
    for a Next.js turborepo,
    scaffold-cli
    for a TypeScript CLI), multi-tenant domain/isolation/routing (
    multi-tenant-architecture
    ), the content of AGENTS.md itself (
    agents-md
    ), a diff-scoped cleanup pass (
    tidy
    ), or structural review of a local diff (
    pr-reviewer
    ).
确定TypeScript代码库的结构,在变更成本过高的地方进行优化,并维持架构的稳定性。目标是打造一个易于理解的代码库:对外暴露的接口少,每个任务都有标准实现方式,代码行为符合直觉预期。
  • 包含内容: 文件夹结构、模块契约、中间件管道、前后端边界;架构文档;领域语言与决策记录;基于领域知识的代码深化;守卫工具、CI门禁和Agent导航指引。
  • 不包含内容: 搭建新仓库(Next.js turborepo请使用
    scaffold-nextjs
    ,TypeScript CLI请使用
    scaffold-cli
    )、多租户领域/隔离/路由(请使用
    multi-tenant-architecture
    )、AGENTS.md文件本身的内容(请使用
    agents-md
    )、针对差异的清理工作(请使用
    tidy
    )、本地差异的结构审查(请使用
    pr-reviewer
    )。

Contents

目录

  • Modes
  • References
  • Design mode (new codebase)
  • Deepen mode (existing codebase)
  • Harden mode (make it stick)
  • Validation loop
  • Output template
  • Excuses
  • Gotchas
  • 模式
  • 参考文档
  • 设计模式(新代码库)
  • 深化模式(现有代码库)
  • 加固模式(维持架构稳定)
  • 验证循环
  • 输出模板
  • 常见借口及反驳
  • 注意事项

Modes

模式

Pick by the problem, not by the artifact.
ModeYou are here whenOutput
DesignStarting a new app, service, or surface, and the structure is not decided yetAn architecture brief
DeepenThe code works, but change is expensive: concepts scattered, seams leaking, one idea under three namesRanked opportunities, then one migrated slice
HardenThe structure is decided and keeps decaying, or agents keep doing the wrong thing in this repoWired checks, markers, and recipes
Modes compose, and running more than one is normal. Design ends in Harden, because a contract with no check is a suggestion. Deepen ends in Harden, so the new seam cannot decay back. Harden runs alone when the structure is already right and only the enforcement is missing, which is the common case in a repo that agents work in.
When the two look equally right, prefer Deepen. A user reporting "agents keep using the old pattern" is describing a Harden symptom, but if the cause is one concept living in two places, quarantine only freezes the duplicate in place: Deepen deletes it and Harden holds the line until that lands. Harden alone is the right answer when the old thing genuinely has to stay.
When you cannot write to the repo (no checkout, read-only request, or the user asked a question rather than for a change), each mode's output degrades to its plan: the brief, the ranked opportunities, or the named checks with their rungs. Say which checks remain unproven, since none of them are wired.
As simple as possible, no simpler. Every mode cuts: surfaces in Design, concepts in Deepen, dual paths and dormant config in Harden. The floor does not get cut for simplicity: validation at trust boundaries, error handling that prevents data loss, security, accessibility, observability on anything deployed, and whatever was explicitly asked for. A simplification that reaches one of those is a bug, not a simplification. Where a corner is genuinely cut on purpose, mark it with its ceiling and upgrade path rather than leaving the next reader to guess whether it is finished.
根据问题选择模式,而非根据产物。
模式适用场景输出结果
设计模式启动新应用、服务或接口,且架构结构尚未确定架构文档
深化模式代码可正常运行,但变更成本极高:概念分散、边界泄露、同一概念对应多个名称优先级排序的优化机会,以及首个迁移的代码切片
加固模式架构已确定但持续退化,或是Agent在仓库中频繁执行错误操作配置完成的检查规则、标记和操作指南
模式可组合使用,同时运行多种模式是常见情况。 设计模式最终会进入加固模式,因为没有检查机制的契约只是建议。深化模式最终也会进入加固模式,确保新的代码边界不会再次退化。当架构本身合理但缺乏执行机制时,可单独运行加固模式,这也是Agent工作的仓库中的常见情况。
当两种模式看起来同样合适时,优先选择深化模式。 用户反馈“Agent持续使用旧模式”是加固模式的症状,但如果原因是同一概念存在于两个位置,隔离只会固化重复内容:而深化模式会删除重复内容,再由加固模式维持规范。只有当旧内容确实必须保留时,单独使用加固模式才是正确选择。
当无法写入仓库时(无检出权限、只读请求,或用户仅提问而非要求变更),每种模式的输出会降级为对应的计划:架构文档大纲、优先级排序的优化机会,或带执行层级的命名检查规则。需说明哪些检查尚未验证,因为它们均未配置生效。
保持极简,但不能过度简化。 每种模式都会做减法:设计模式减少对外接口,深化模式精简概念,加固模式移除双路径和休眠配置。但以下内容不能简化:信任边界的验证、防止数据丢失的错误处理、安全、可访问性、已部署服务的可观测性,以及用户明确要求的内容。如果简化触及这些内容,那是漏洞而非优化。若确实需要刻意简化某个细节,需标记其上限和升级路径,避免后续开发者猜测是否已完成。

References

参考文档

Load only when the condition applies.
ReferenceModeRead when
references/stack-defaults.mdDesignChoosing libraries, tooling, or deploy targets
references/api-design.mdDesign, DeepenDesigning endpoints, module contracts, request context, or error shapes
references/distributed-correctness.mdDesign, DeepenThe work provably touches an external system, webhook, retry, audit trail, or money. In Deepen you can grep for it; in Design it is a question about requirements, so confirm before loading rather than inferring it from the product's domain
references/craftsmanship.mdDesignWriting the team-conventions, testing, or quality-bar sections
references/shipping-practices.mdDesignWriting the rollout and rollback section
references/deepening-existing.mdDeepenRunning Deepen: vocabulary, opportunity patterns, output template
references/domain-language.mdDeepenWriting or fixing a glossary, resolving naming divergence, recording a decision
references/enforcement-ladder.mdHardenAdding any check to a repo that already violates it
references/guardrail-tooling.mdHardenChoosing and wiring the actual checks
references/wayfinding.mdHardenAgents cannot find things, or keep re-deriving the same path
references/contagion-markers.mdHardenThe repo has legacy, generated, or dual-path code
references/verification-tiers.mdHardenDefining which commands an agent should run, and when
references/agent-runtime.mdHardenConfiguring session hooks, permissions, or review gating
references/evaluation-scenarios.mdnoneChanging this skill. Never loads during a user task; it is the author's rubric
仅在符合条件时加载对应文档。
参考文档适用模式加载场景
references/stack-defaults.md设计模式选择库、工具或部署目标时
references/api-design.md设计模式、深化模式设计端点、模块契约、请求上下文或错误格式时
references/distributed-correctness.md设计模式、深化模式工作涉及外部系统、webhook、重试、审计追踪或资金处理时。在深化模式中可通过grep查找相关代码;在设计模式中需先确认需求,而非从产品领域推断
references/craftsmanship.md设计模式撰写团队规范、测试或质量标准部分时
references/shipping-practices.md设计模式撰写发布与回滚部分时
references/deepening-existing.md深化模式运行深化模式:术语、优化机会模式、输出模板
references/domain-language.md深化模式撰写或修复术语表、解决命名分歧、记录决策时
references/enforcement-ladder.md加固模式为已存在违规的仓库添加检查规则时
references/guardrail-tooling.md加固模式选择并配置具体检查工具时
references/wayfinding.md加固模式Agent无法找到内容,或持续重复推导相同路径时
references/contagion-markers.md加固模式仓库包含遗留代码、生成代码或双路径代码时
references/verification-tiers.md加固模式定义Agent应执行的命令及执行时机时
references/agent-runtime.md加固模式配置会话钩子、权限或审查门禁时
references/evaluation-scenarios.md修改本技能时使用。用户任务中不会加载,仅作为作者的评估标准

Design mode (new codebase)

设计模式(新代码库)

Before any of this, ask whether each surface needs to exist. A module, service, app, or entrypoint that could be a folder in something that already ships is the cheapest architecture decision available, and the only one that stays cheap. Every surface you do accept pays the relationship cost in
craftsmanship.md
's surface-area budget: name its owner, tests, observability, and deletion path before it goes in the brief.
  1. Constraints first: product scope, team size, compliance/security, expected scale, deploy targets, required integrations, and quality bar. A one-line request supplies none of these, so assume the common case, state every assumption you made in the brief's first section, and invite correction. Ask outright only where a wrong guess would restructure the brief rather than extend it, which in practice is multi-tenancy and whether the API is public.
  2. Choose repo shape:
    • apps/
      for deployable surfaces (
      api
      ,
      web
      ,
      admin
      ).
    • packages/
      for shared libraries (
      shared
      ,
      ui
      ,
      icons
      ,
      auth
      ,
      proto
      ).
  3. Define backend module contracts, each naming its enforcement (lint boundary rule or type check):
    • handler
      : transport only.
    • service
      : business orchestration.
    • dao
      : database access only.
    • mapper
      : DB/proto/domain transformations.
    • constants
      and
      types
      : module-local contracts.
  4. Define request context and middleware:
    • Carry
      tenantId
      ,
      userId
      , and
      traceId
      in an AsyncLocalStorage-backed
      RequestContext
      , initialized in every entrypoint (RPC, HTTP, jobs, CLI) and read via
      getContext()
      ; never thread a ctx parameter through business functions. Implementation in references/api-design.md.
    • Require explicit auth policy per RPC method at registration; a method without one fails registration, never defaults to open.
    • Keep auth, logging, errors, and context in shared middleware, not per-handler code.
  5. Define frontend boundaries:
    • Default to Server Components; add
      "use client"
      only at leaf components needing interactivity.
    • Server state in TanStack/Connect Query; client state in component state; MobX only for cross-cutting client state that fits neither.
    • Each piece of data has exactly one owner. Never mirror server data into
      useState
      or sync two stores with
      useEffect
      ; both are the red flag that ownership is unclear.
  6. Testing and release:
    • Unit tests stay DB-free; integration/E2E run in parallel with dynamically generated IDs so runs never collide on fixtures.
    • Release in small, complete, reversible vertical slices with a rollback plan per change.
    • A slice is complete only when reliability, error paths, observability, and user-facing states are covered; do not defer them to a later polish pass.
  7. Every contract in the brief names the lint rule, type check, or test that catches its violation, then continue into Harden mode to wire them.
在开始之前,先确认每个接口是否真的需要存在。如果某个模块、服务、应用或入口点可以作为现有已发布项目中的一个文件夹,这是成本最低的架构决策,也是唯一能持续保持低成本的选择。你所接受的每个接口都要消耗
craftsmanship.md
中定义的接口面积预算:在将其加入文档前,需指定其所有者、测试方案、可观测性配置和删除路径。
  1. 先明确约束条件:产品范围、团队规模、合规/安全要求、预期规模、部署目标、必需集成项和质量标准。一行式请求不会提供这些信息,因此需假设常见情况,在文档的第一部分说明所有假设,并邀请用户修正。仅当错误猜测会重构文档而非扩展文档时,才直接询问用户,实际场景中通常是多租户需求和API是否公开。
  2. 选择仓库结构:
    • apps/
      用于存放可部署接口(如
      api
      web
      admin
      )。
    • packages/
      用于存放共享库(如
      shared
      ui
      icons
      auth
      proto
      )。
  3. 定义后端模块契约,每个契约需指定其执行机制(lint边界规则或类型检查):
    • handler
      :仅负责传输层。
    • service
      :业务编排。
    • dao
      :仅负责数据库访问。
    • mapper
      :数据库/proto/领域模型转换。
    • constants
      types
      :模块本地契约。
  4. 定义请求上下文和中间件:
    • 在基于AsyncLocalStorage的
      RequestContext
      中携带
      tenantId
      userId
      traceId
      ,在每个入口点(RPC、HTTP、任务、CLI)初始化,并通过
      getContext()
      读取;绝不要在业务函数中传递ctx参数。具体实现见references/api-design.md
    • 要求每个RPC方法在注册时显式指定认证策略;未指定认证策略的方法会注册失败,绝不默认开放。
    • 将认证、日志、错误处理和上下文管理放在共享中间件中,而非每个handler的代码里。
  5. 定义前端边界:
    • 默认使用Server Components;仅在需要交互性的叶子组件中添加
      "use client"
    • 服务端状态使用TanStack/Connect Query管理;客户端状态使用组件状态管理;仅当跨组件客户端状态不适用上述两种方式时,才使用MobX。
    • 每份数据有且仅有一个所有者。绝不要将服务端数据镜像到
      useState
      中,或使用
      useEffect
      同步两个状态库;这两种情况都是所有权不清晰的信号。
  6. 测试与发布:
    • 单元测试不依赖数据库;集成/E2E测试使用动态生成的ID并行运行,避免测试 fixture 冲突。
    • 以小型、完整、可逆的垂直切片方式发布,每个变更都有对应的回滚计划。
    • 切片仅当覆盖可靠性、错误路径、可观测性和用户可见状态时才算完整;不要将这些内容推迟到后续的优化阶段。
  7. 文档中的每个契约都要指定用于捕获违规的lint规则、类型检查或测试,然后进入加固模式配置这些规则。

Deepen mode (existing codebase)

深化模式(现有代码库)

Goal: domain-informed deepening, not a rewrite. Load references/deepening-existing.md for the analysis method, opportunity patterns, and output template.
  1. Map the domain language and decisions. Read
    CONTEXT.md
    ,
    docs/adr/
    , or local equivalents if present, then read the code for entities, actions, and contexts as the team names them. Note divergence (one concept, three names; or one name, three concepts). Format and ADR rules in references/domain-language.md.
  2. Scope the scan by where change lands. Deepening pays off on code that keeps changing, so
    git log --oneline
    over a good stretch of history first and weight the files that keep coming up. An unscoped scan drifts into speculative cleanup.
  3. Find deepening opportunities. Look for anemic concepts, shallow modules, leaking seams, naming divergence, duplicated concepts, primitive obsession, misplaced logic, and tests forced past the public interface. Record each with file paths, never a vague smell. Check deletion first on every candidate: a concept with no live caller, a flag whose branch never runs, a layer with one implementation. Deleting it is the deepening, and it is the only move that cannot make the codebase harder to read.
  4. Rank by leverage. Prefer opportunities that pass the deletion test, localize named future changes, have low churn, meet a current requirement, and have a viable testing seam. Rank candidates before designing target interfaces; drop speculative cleanups.
  5. Migrate one vertical slice first. Prove the highest-leverage move end to end through one slice before generalizing.
  6. Enforce the new seam with lint, type, or test checks so it cannot decay, then roll out module by module. Continue into Harden mode for the enforcement rung and the check-bites test.
目标:基于领域知识深化代码,而非重写。加载references/deepening-existing.md获取分析方法、优化机会模式和输出模板。
  1. 梳理领域语言与决策。 阅读
    CONTEXT.md
    docs/adr/
    或本地等效文件(如果存在),然后从代码中读取团队命名的实体、操作和上下文。记录分歧点(同一概念对应多个名称;或同一名称对应多个概念)。格式和ADR规则见references/domain-language.md
  2. 根据变更热点范围扫描。 代码深化在频繁变更的代码上才会产生价值,因此先通过
    git log --oneline
    查看一段时期的历史记录,重点关注频繁出现的文件。无范围的扫描会演变为投机性清理。
  3. 寻找深化机会。 查找贫血概念、浅层模块、边界泄露、命名分歧、重复概念、原始类型过度使用、逻辑错位以及被迫绕过公共接口的测试。每个机会都要记录文件路径,绝不使用模糊描述。对每个候选项先检查是否可删除:无调用者的概念、分支从未执行的标志、仅有一个实现的层级。删除这些内容就是深化,也是唯一不会让代码库更难理解的操作。
  4. 按价值优先级排序。 优先选择符合删除条件、可定位未来变更、低 churn、满足当前需求且有可行测试边界的机会。在设计目标接口前先对候选项排序,剔除投机性清理项。
  5. 先迁移一个垂直切片。 在推广前,先通过一个切片端到端验证最高价值的变更。
  6. 通过lint、类型或测试检查强制执行新边界,防止其退化,然后逐个模块推广。进入加固模式配置执行层级和检查生效测试。

Harden mode (make it stick)

加固模式(维持架构稳定)

Two halves: guardrails stop the wrong thing landing, wayfinding makes the right thing cheap to find. Both exist because agents arrive by grep, not by reading docs, so the warning has to live where they land and the rule has to be an exit code rather than a sentence someone might recall.
Steps 1 to 3 always run. Steps 4 to 6 run only when their condition holds, and a request to add one check stops at step 3. Running all six for every request loads most of the bundle and is the failure this mode is most prone to.
  1. Survey what exists. Package scripts, CI steps, hook config, lint config, the instruction file, the docs index. Find two things: checks that run locally but do not gate the merge, and dormant config nobody invokes. Wire the first, delete the second (references/contagion-markers.md).
  2. Choose checks by the failure they prevent, never by tool popularity. Categories and tools in references/guardrail-tooling.md. Pick the two or three failures this repo actually exhibits; installing the full set at once forces the weakest enforcement rung on all of them.
  3. Install each check: pick an enforcement rung for the violations that already exist (references/enforcement-ladder.md), ship it green, then prove it bites (run it, break it on purpose, watch it fail with a message naming the fix, revert). Wire it into both a pre-commit hook and CI.
  4. Wayfinding per references/wayfinding.md: naming and locality, the add-a-new-X recipe file, the trust-labeled docs index, one canonical instruction file.
  5. Contagion markers per references/contagion-markers.md: anything an agent must not copy or must not edit gets a greppable marker at the code site naming what to use instead.
  6. Runtime ergonomics: verification tiers in references/verification-tiers.md; session hooks, permission allowlists, and review gating in references/agent-runtime.md.
分为两部分:守卫规则阻止错误代码提交,导航指引降低正确操作的成本。两者存在的原因是Agent通过grep定位代码,而非阅读文档,因此警告必须出现在Agent定位到的代码位置,规则必须是退出码而非可能被遗忘的文字说明。
步骤1至3必须执行。步骤4至6仅在符合条件时执行,若请求仅添加一个检查规则,执行到步骤3即可。对每个请求都执行全部六个步骤会加载大部分资源,是本模式最容易出现的错误。
  1. 调查现有配置。 包脚本、CI步骤、钩子配置、lint配置、说明文件、文档索引。找到两类内容:本地运行但不阻止合并的检查规则,以及无人调用的休眠配置。将前者配置生效,删除后者(见references/contagion-markers.md)。
  2. 根据要预防的故障选择检查规则,而非根据工具流行度。分类和工具见references/guardrail-tooling.md。选择仓库实际存在的两到三类故障;一次性安装全套规则会迫使所有规则使用最弱的执行层级。
  3. 安装每个检查规则: 针对已存在的违规选择执行层级(见references/enforcement-ladder.md),确保规则初始状态为通过,然后验证其能捕获违规(运行规则,故意触发违规,观察其返回包含修复建议的失败信息,再恢复)。将其配置到预提交钩子和CI中。
  4. 导航指引references/wayfinding.md:命名与位置规范、新增X操作的指南文件、带信任标记的文档索引、标准说明文件。
  5. 传染标记references/contagion-markers.md:Agent不得复制或编辑的内容,需在代码位置添加可grep的标记,说明应使用的替代方案。
  6. 运行时 ergonomics: 验证层级见references/verification-tiers.md;会话钩子、权限白名单和审查门禁见references/agent-runtime.md

Validation loop

验证循环

Run the items matching the modes you ran, and record results in the output. Each needs evidence; "looks consistent" is not a pass. An item that cannot execute yet, because nothing is installed or the repo is not writable, is recorded N/A with that reason. Silently passing it is how an unenforced contract ships looking verified.
  1. Consistency (Design, Deepen): naming, module contracts, and middleware rules read the same across every service. Evidence: a contradiction scan with zero findings.
  2. Enforceability (all): every contract names its lint rule, type check, or test. Evidence: an enforcement note per contract, and for any check actually installed, the pass, then fail on a deliberate violation, then pass after revert.
  3. Operability (Design): observability, health checks, and a rollback path per deployable surface. Evidence: the rollout section names each.
  4. Quality gates (whenever code changed): the repo's lint, type-check, and targeted tests (
    npm run lint
    ,
    npm run check-types
    ,
    npm run test --workspace=<pkg>
    or equivalents). Evidence: passing output.
  5. CI and local agree (Harden): the CI step invokes the same umbrella command a developer runs, or the difference is deliberate and stated.
  6. No dangling pointers, and a recipe works cold (Harden): a grep proving every path named in the docs index and instruction file exists, plus a fresh-context agent following one add-a-new-X recipe end to end with no further guidance.
  7. Net simplicity (all): the result leaves a reader less to hold, not more. Evidence: the net change in files, surfaces, and exported names, with every increase named and paid for by what it removed elsewhere; plus, for each layer, port, or indirection introduced, the second caller or implementation that made it real. An architecture pass that only adds has failed this check even when every other item passes.
On failure: fix the brief, the conventions, or the wiring, then re-run the loop.
执行与所选模式匹配的验证项,并将结果记录到输出中。每个验证项都需要证据;“看起来一致”不算通过。若因未安装工具或仓库只读导致无法执行,需标记为N/A并说明原因。默认通过未执行的验证项会导致未强制执行的契约被视为已验证。
  1. 一致性(设计模式、深化模式):命名、模块契约和中间件规则在所有服务中保持一致。证据:矛盾扫描结果为零。
  2. 可执行性(所有模式):每个契约都指定了对应的lint规则、类型检查或测试。证据:每个契约都有执行说明,对于已安装的检查规则,需提供通过记录、故意触发违规的失败记录,以及修复后的通过记录。
  3. 可操作性(设计模式):每个可部署接口都有可观测性配置、健康检查和回滚路径。证据:发布部分明确列出这些内容。
  4. 质量门禁(代码变更时):仓库的lint、类型检查和针对性测试(
    npm run lint
    npm run check-types
    npm run test --workspace=<pkg>
    或等效命令)。证据:通过的输出结果。
  5. CI与本地一致(加固模式):CI步骤调用的命令与开发者本地运行的命令相同,或差异是故意设置并已说明。
  6. 无悬空指针,指南可直接生效(加固模式):通过grep验证文档索引和说明文件中提到的所有路径都存在,且全新上下文的Agent可遵循一个新增X操作的指南完成全流程,无需额外指导。
  7. 净简化(所有模式):结果让开发者需要理解的内容更少,而非更多。证据:文件、接口和导出名称的净变化量,每个增加项都需说明其替换的内容;对于新增的层级、端口或间接层,需说明第二个调用者或实现使其具备实际价值。仅做加法的架构优化即使通过其他所有验证项,也未通过此项检查。
若验证失败:修改文档、规范或配置,然后重新运行验证循环。

Output template

输出模板

Design mode produces this brief. Deepen mode's ranked-opportunity template is in
references/deepening-existing.md
. Harden mode's output is the wiring itself plus the loop's evidence, not a document.
markdown
undefined
设计模式生成以下文档。深化模式的优先级排序优化机会模板见
references/deepening-existing.md
。加固模式的输出为配置好的规则及验证循环的证据,而非文档。
markdown
undefined

Architecture brief

架构文档

Context and constraints

背景与约束

Repo shape

仓库结构

Backend module contracts

后端模块契约

Request context and middleware policy

请求上下文与中间件策略

Frontend boundaries

前端边界

Testing strategy

测试策略

Quality bar and surface-area budget

质量标准与接口面积预算

Rollout and rollback plan

发布与回滚计划

Open risks and follow-ups

未解决风险与后续工作

undefined
undefined

Excuses

常见借口及反驳

Each rebuttal redirects to the step being skipped; none of them repeat a gotcha below.
ExcuseRebuttal
"The check is obviously configured right."You have not watched it fail. A misconfigured gate passes on everything and reads as coverage.
"There are too many existing violations to fix."That is what the enforcement ladder is for. Pick a rung and land it green today rather than a perfect rule next quarter.
"AGENTS.md already says not to do that."A prompt rule decays under context pressure. If a static tool can check it, it belongs in tooling.
"We'll add the enforcement in a follow-up."The follow-up is the deadline's first casualty, and the contract decays from the day it ships unenforced.
每个反驳都指向被跳过的步骤;以下内容不重复后续的注意事项。
借口反驳
“检查规则显然配置正确。”你还未观察到它捕获违规的情况。配置错误的门禁会放行所有内容,却显示为已覆盖。
“现有违规太多,无法修复。”这正是执行层级存在的意义。选择一个层级,今天就让规则处于通过状态,而非等到下个季度再设置完美规则。
“AGENTS.md已经说明不能这么做。”提示规则会在上下文压力下退化。如果静态工具可以检查,就应该配置到工具中。
“我们会在后续工作中添加执行机制。”后续工作通常会成为截止日期的第一个牺牲品,契约从发布之日起就会开始退化,因为没有强制执行机制。

Gotchas

注意事项

Design and Deepen

设计模式与深化模式

  • Don't default to microservices for teams under 5: each service adds a deploy pipeline, contract versioning, and on-call surface. Start with a modular monorepo; split when a boundary is proven by team or scale pressure.
  • Don't put app-level deps in a monorepo's root
    package.json
    : hoisting hides missing declarations, so an app builds locally but breaks deploying alone. Each app owns its deps.
  • Don't define module contracts (handler/service/dao) without enforcement: an unenforced contract decays at the first deadline. Add an import-boundary lint rule (e.g.
    dao
    may not import
    handler
    ) the day you write it.
  • Don't thread a ctx parameter through business functions instead of AsyncLocalStorage: every signature grows, and adding one field later touches hundreds of call sites.
  • Don't place
    "use client"
    at page or layout level: it converts the whole subtree to client rendering and forfeits streaming and direct server data access. Push it to leaves.
  • Don't propose a big-bang rewrite in Deepen mode: migrate one vertical slice, verify it, then generalize.
  • Don't extract to
    packages/
    early: wait until 3+ apps need the same code; a premature shared package couples release cycles for nothing. The exception is the contract two surfaces already share (generated types, the RPC schema, branded IDs): that is not speculative reuse, it is the interface between them, and it belongs in a package at two apps.
  • Don't finalize a brief without a rollback plan per change: an irreversible decision needs a documented fallback before it ships.
  • Don't dual-write to a database and a queue/webhook without an outbox (or CDC): one side commits, the other fails, and you silently lose or fabricate a notification. See
    references/distributed-correctness.md
    .
  • Don't enforce an externally-forceable invariant by construction (unsigned type, hard CHECK): when the outside world forces the state, the system crashes or clamps instead of recording it. Represent it, detect it post-factum, recover explicitly.
  • Don't scan a whole codebase for deepening opportunities: without git hot-spot scoping the list fills with modules nobody touches, and every entry on it is speculative by definition.
  • Don't write a glossary entry for a general programming concept: a glossary earns its keep on the terms this product argues about, and padding it with "timeout" and "retry" trains everyone to skim it.
  • 团队规模不足5人时,不要默认选择微服务:每个服务都会增加部署流水线、契约版本管理和运维接口。从模块化monorepo开始;当边界被团队或规模压力验证后再拆分。
  • 不要在monorepo的根
    package.json
    中添加应用级依赖:依赖提升会隐藏缺失的声明,导致应用在本地可构建,但单独部署时失败。每个应用应管理自己的依赖。
  • 不要在没有执行机制的情况下定义模块契约(handler/service/dao):未强制执行的契约会在第一个截止日期到来时退化。在撰写契约的当天就添加导入边界lint规则(例如
    dao
    不得导入
    handler
    )。
  • 不要在业务函数中传递ctx参数,而应使用AsyncLocalStorage:每个函数签名都会变长,后续添加字段时会修改数百个调用点。
  • 不要在页面或布局级别添加
    "use client"
    :这会将整个子树转换为客户端渲染,失去流式传输和直接服务端数据访问的能力。将其推送到叶子组件。
  • 在深化模式中不要提议大规模重写:先迁移一个垂直切片,验证后再推广。
  • 不要过早提取代码到
    packages/
    :等到3个以上应用需要相同代码时再提取;过早的共享包会毫无意义地耦合发布周期。例外情况是两个接口已共享的契约(生成的类型、RPC schema、品牌化ID):这不是投机性复用,而是它们之间的接口,当有两个应用使用时就应放入包中。
  • 不要在没有每个变更的回滚计划的情况下最终确定文档:不可逆的决策在发布前需要有记录的 fallback 方案。
  • 不要在没有outbox(或CDC)的情况下同时写入数据库和队列/webhook:一方提交成功,另一方失败,会导致通知被静默丢失或伪造。详见
    references/distributed-correctness.md
  • 不要通过构造方式强制执行外部可触发的不变量(无符号类型、硬CHECK):当外部世界强制修改状态时,系统会崩溃或限制操作,而非记录状态。应表示状态、事后检测并显式恢复。
  • 不要扫描整个代码库寻找深化机会:如果没有通过git热点范围限制,列表会充满无人触碰的模块,每个条目本质上都是投机性的。
  • 不要为通用编程概念撰写术语表条目:术语表的价值在于记录产品特有的争议术语,添加“timeout”和“retry”等通用术语会让所有人养成 skim 的习惯。

Harden

加固模式

  • Don't hand-roll a shrink-only baseline (
    *-ratchet.mjs
    plus
    *.baseline.json
    ): it was built once and deleted, because knip, eslint, madge, and jscpd all ship native ignore, allowlist, and warn mechanisms that do the same job with no code to maintain.
  • Don't land a rule red: agents and humans both learn the check is noise, and the next person adds
    --no-verify
    instead of a fix.
  • Don't leave dormant config or an unused devDep in place: an agent reads it as live convention and extends it, and one pointing at a missing file yields a confident wrong answer instead of an error.
  • Don't index a doc that does not exist: agents cite confidently, so a dangling pointer is worse than a missing one.
  • Don't write add-a-new-X recipes from memory: a recipe naming a moved file sends the agent somewhere wrong, and it will not doubt the doc.
  • Don't rely on pre-commit hooks alone: they are not guaranteed installed on a fresh clone or in a worktree, which is exactly where agents run.
  • Don't put a marker only in
    docs/legacy.md
    : an agent that arrived by grep never opens it. The marker goes in the frozen file.
  • Don't generate the instruction file wholesale: generated files mostly restate documentation the agent can already read, cost 20 to 23% more per task, and measurably lose to hand-written ones. Hand-curate it and update it in the PR that changes the convention.
  • 不要手动实现仅收缩的基线(
    *-ratchet.mjs
    *.baseline.json
    ):这种方案曾被实现但已被删除,因为knip、eslint、madge和jscpd都原生支持忽略、白名单和警告机制,可完成相同工作且无需维护额外代码。
  • 不要让规则处于失败状态:Agent和开发者都会认为该检查是噪音,下一个人会添加
    --no-verify
    而非修复问题。
  • 不要保留休眠配置或未使用的devDep:Agent会将其视为有效规范并扩展,指向不存在文件的配置会给出自信的错误答案而非报错。
  • 不要索引不存在的文档:Agent会自信地引用它,悬空指针比缺失文档更糟糕。
  • 不要凭记忆撰写新增X操作的指南:指南中提到已移动的文件会将Agent引导至错误位置,且Agent不会怀疑文档的正确性。
  • 不要仅依赖预提交钩子:在全新克隆或工作树中,预提交钩子不一定会被安装,而这正是Agent运行的场景。
  • 不要仅在
    docs/legacy.md
    中添加标记:通过grep定位的Agent永远不会打开该文档。标记应放在冻结的文件中。
  • 不要自动生成说明文件:生成的文件大多重复Agent已能读取的文档,每个任务会多花费20-23%的时间,且效果明显不如手写文档。应手动整理说明文件,并在修改规范的PR中更新它。