关于 codebase-architecture
codebase-architecture 是用于设计 TypeScript 代码库结构并降低变更成本的技能。它涵盖新代码库架构简报、现有代码的针对性改进,以及防止既定结构逐渐失效的约束机制。
主要功能
- 01为新代码库设计架构简报
- 02排序并迁移现有代码改进
- 03定义模块与接口边界
- 04添加 lint、类型和 CI 约束
- 05改善 Agent 在仓库中的导航
适用场景
- 01为新的 TypeScript 应用或服务选择结构
- 02减少概念分散和边界泄漏
- 03在 CI 中强制执行既定模块边界
- 04帮助 Agent 在仓库中找到规范模式
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mblode/ agent-skills --skill codebase-architecture安装后在 Claude Code 中输入/codebase-architecture确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
evals/evals.jsonevaluation-scenarios.mdreferences/agent-runtime.mdapi-design.mdbrief-conventions.mdcontagion-markers.mddeepening-existing.mddistributed-correctness.mddomain-language.mdenforcement-ladder.mdguardrail-tooling.mdstack-defaults.mdverification-tiers.mdvocabulary.mdwayfinding.md详细介绍
代码库架构
确定 TypeScript 代码库的结构,在变更成本变得高昂时加以改进,并确保结构保持稳定。目标是打造一个读者能在脑中把握全貌的代码库:界面少,每项工作都有一种规范做法,行为也出现在你最先会去查找的地方。
- 是: 文件夹结构、模块接口、请求上下文和中间件管道、前后端边界;架构简报;领域语言和决策记录;融入领域知识的深化;护栏工具、CI 门禁和 Agent 导航指引。
- 不是: 搭建新仓库(Next.js turborepo 用
scaffold-nextjs,TypeScript CLI 用scaffold-cli)、多租户领域/隔离/路由(multi-tenant-architecture)、AGENTS.md 本身的内容(agents-md)、单个功能的计划(planning)、限定于 diff 的清理(tidy)、对本地 diff 进行结构审查(tidy),或产品自身的 verify CLI、doctor 命令和功能图(app-verification,请按名称调用)。
目录
- 模式
- 参考资料
- Design 模式(新代码库)
- Deepen 模式(现有代码库)
- Harden 模式(确保结构持久)
- 验证循环
- 输出模板
- 注意事项
- 相关技能
模式
根据问题而不是产物来选择模式,并说明你选择了哪一种。
| 模式 | 适用情况 | 输出 |
|---|---|---|
| Design | 开始构建新的应用、服务或界面,尚未确定结构 | 架构简报 |
| Deepen | 代码可以正常工作,但变更成本高昂:概念分散、边界泄漏、同一个想法有三种叫法 | 排序后的改进机会,然后迁移一个切片 |
| Harden | 结构已经确定,却总在退化,或者 Agent 在此仓库中总是做错事 | 接入检查、标记和操作指南 |
模式可以组合,同时运行多个模式也很正常。 Design 以 Harden 收尾,因为没有检查的契约只是建议。Deepen 也以 Harden 收尾,这样新边界就不会再次退化。若结构已经正确,只缺少执行机制,则单独运行 Harden 即可;在 Agent 参与工作的仓库中,这种情况最常见。
两个模式看起来同样合适时,优先选择 Deepen。 “Agent 总在用旧模式”听起来像是 Harden 的问题,但如果原因是一个概念同时存在于两个地方,那么隔离只会冻结重复内容:Deepen 会删除重复内容,而 Harden 会守住底线,直到改动落地。只有旧内容确实必须保留时,才适合单独运行 Harden。
无法修改仓库时(没有检出代码、请求为只读,或用户提出的是问题而非修改),各模式的输出会退化为计划:简报、排序后的改进机会,或列出名称及等级的检查项。请说明哪些检查尚未得到验证,因为它们都还未接入。
尽可能简单,但不要过度简化。 每种模式都会做减法:Design 减少界面,Deepen 精简概念,Harden 消除双路径和休眠配置。但底线不能削减:信任边界处的验证、防止数据丢失的错误处理、安全性、无障碍性、任何已部署内容的可观测性,以及明确提出的要求。简化若触及其中任何一项,就是缺陷。若有意在某个角落做了取舍,请标明其上限和升级路径,不要让后续读者猜测它是否已经完成。
复制以下内容以跟踪进度,并删除未运行模式对应的行:
Codebase architecture progress:
- [ ] Modes chosen and stated (Design / Deepen / Harden)
- [ ] Design: assumptions stated, repo shape, every contract names its check, brief written
- [ ] Deepen: git hot spots, glossary, ranked opportunities with paths, one slice migrated
- [ ] Harden: existing checks surveyed, checks picked by failure, each landed green and proven to bite
- [ ] Validation loop run for the modes used; evidence recorded, N/A items named
参考资料
仅在条件适用时加载。
| 参考资料 | 模式 | 阅读时机 |
|---|---|---|
| references/vocabulary.md | 全部 | 在任何输出中为模块、接口、接缝或适配器命名之前;该文档定义了删除测试 |
| references/stack-defaults.md | 设计 | 选择库、工具或部署目标时 |
| references/api-design.md | 设计、深入 | 设计端点、模块接口、请求上下文、错误格式或转发凭证时 |
| references/distributed-correctness.md | 设计、深入 | 工作可证实地涉及外部系统、Webhook、重试、审计跟踪或资金时。在深入模式下,可以搜索确认;在设计模式下,这属于需求问题,因此应先确认再加载,不要根据产品领域推断 |
| references/brief-conventions.md | 设计 | 编写 brief 中的约定、测试、质量标准或发布与回滚部分时 |
| references/deepening-existing.md | 深入 | 运行深入流程时:机会模式、设计两次、排序、衡量面向 Agent 的变更、输出模板 |
| 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 | 加固 | 配置编辑后钩子、权限允许列表,或配置由 Agent 无人值守地起草修复或分流警报的任务时 |
设计模式(新代码库)
在做以下任何事情之前,先确认每个界面是否都确有存在的必要。可以作为现有交付内容中的一个文件夹的模块、服务、应用或入口点,是成本最低的架构决策,也是唯一能始终保持低成本的决策。你接受的每个界面都会消耗界面范围预算(brief-conventions.md)中的关系成本:将其纳入简报前,先明确其负责人、测试、可观测性和删除路径。
- 先明确约束:产品范围、团队规模、合规性/安全性、预期规模、部署目标、必需的集成,以及质量标准。一句话的需求不会提供这些信息,因此请按常见情况假设,在简报第一部分列明所有假设,并邀请对方纠正。只有当错误的猜测会重构简报,而非仅仅扩展简报时,才直接提问;实际需要确认的通常只有多租户和 API 是否公开。
- 选择代码仓库结构:
apps/用于可部署的界面(api、web、admin)。packages/用于共享库(shared、ui、icons、auth、proto)。
- 定义后端模块接口,并为每项注明如何强制执行(导入边界 lint 或类型检查):
handler:仅负责传输。service:业务编排。dao:仅负责数据库访问;只有 DAO 可以导入 db 包,每张表由一个模块负责,其服务是唯一的写入方。查询规则见 references/api-design.md 中的“Data Access”。mapper:DB/proto/领域模型之间的转换。constants和types:模块本地契约。
- 定义请求上下文和中间件:
- 在由 AsyncLocalStorage 支持的
RequestContext中携带tenantId、userId和traceId,并在每个入口点(RPC、HTTP、作业、CLI)初始化,通过getContext()读取。在线程间传递ctx参数会让每个函数签名不断膨胀,而以后新增字段也会影响每个调用点。实现见 references/api-design.md。 - 注册每个 RPC 方法时,必须明确指定认证策略;没有指定策略的方法应导致注册失败,而不是默认开放。
- 将认证、日志记录、错误处理和上下文放在共享中间件中,而非各个处理器的代码中。
- 在由 AsyncLocalStorage 支持的
- 定义前端边界(默认使用 Next.js App Router):
app/仅存放路由文件(page、layout、loading、error、route)。领域代码放在src/modules/<name>/中,并通过其根文件暴露;仅供单个路由使用的 UI 放在页面旁边的_components/文件夹中。页面逻辑变得复杂时,应将其移到模块中,而不是放到app/下的同级文件中。- 默认使用 Server Components;在交互叶节点上添加
"use client"。客户端包装器需要服务器渲染内容时,将内容作为children传入。 - 服务端状态使用 TanStack 或 Connect Query;客户端状态使用组件状态;只有适合跨领域客户端状态、且不适合前两者的情况才使用 MobX。每份数据都应有一个所有者:将服务端数据镜像到
useState,或通过useEffect同步两个存储,都是数据归属不明确的信号。 proxy.ts(Next 16 对middleware.ts的命名)负责重定向、重写和标头。授权在每个路由处理器和 Server Function 内部决定,因为被 matcher 排除的路径会跳过 proxy,而 Server Function 会向其页面的路由发起请求。
- 测试和发布:
- 单元测试不依赖 DB;集成测试/E2E 测试并行运行,并使用动态生成的 ID,确保各次运行不会在测试夹具上冲突。
- 以小型、完整、可逆的垂直切片发布,并为每项变更制定回滚计划。
- 只有在可靠性、错误路径、可观测性和面向用户的状态都得到覆盖后,一个切片才算完成;把这些推迟到润色阶段,往往就意味着它们永远不会交付。
- 简报中的每项契约都应注明能检测其违规情况的 lint 规则、类型检查或测试,然后继续进入 Harden 模式,将这些检查接入流程。
深化模式(现有代码库)
目标:基于领域知识深化,而非重写。加载 references/deepening-existing.md,了解分析方法、机会模式和输出模板。
- 梳理领域语言和决策。 如果存在
CONTEXT.md、docs/adr/或本地等效文件,请先阅读,然后阅读代码,了解团队如何命名实体、操作和上下文。记录偏差(一种概念,三种名称;或一个名称,三种概念)。格式和 ADR 规则见 references/domain-language.md。 - 根据变更落点限定扫描范围。 深化对持续变更的代码最有价值,因此先通过
git log --oneline查看足够长时间范围的历史记录,并重点关注反复出现的文件。未限定范围的扫描容易偏向推测性的清理。 - 寻找深化机会。 留意贫血概念、浅层模块、泄漏的边界、命名偏差、重复概念、原始类型痴迷、逻辑错置,以及被迫越过公共接口的测试。逐一记录具体的文件路径,不要只写模糊的问题描述。每个候选项都先检查能否删除:没有活跃调用方的概念、分支从不执行的标志,或只有一种实现的层。删除就是深化,而且是唯一不会让代码库更难阅读的做法。
- 按杠杆效应排序。 优先选择通过删除测试、能明确限定未来变更范围、变更频率低、满足当前需求且有可行测试接缝的机会。设计目标接口之前,先给候选项排序;舍弃推测性的清理。对排名最高的候选项,先设计两种接口,再择一采用。
- 先迁移一个纵向切片。 在推广通用化之前,先通过一个切片端到端验证杠杆效应最高的改动。
- 通过 lint、类型或测试检查强制执行新的边界,防止其退化,然后逐个模块推广。继续进入 Harden 模式,完成强制执行环节和 check-bites 测试。
强化模式(让规则落实)
分为两部分:防护措施阻止错误内容进入,寻路方式让正确内容容易找到。之所以两者都不可或缺,是因为 Agent 是通过 grep 找到内容,而不是阅读文档,所以警告必须出现在它们找到的位置,规则也必须通过退出码来落实,而不能只是一句可能被人记住的话。
优先采用最强的机制:结构,然后是 lint 和 CI,再然后是说明文档,最后才是人工审查;每条审查意见、事故或重复出现的错误,都意味着缺少一项检查(references/enforcement-ladder.md)。
步骤 1 至 3 始终执行。只有满足相应条件时才执行步骤 4 至 6;如果请求只要求添加一项检查,到步骤 3 即可。每次请求都执行全部六步会加载大部分内容包,这是此模式最容易出现的问题。
- 盘点现有内容。 检查包脚本、CI 步骤、钩子配置、lint 配置、说明文件和文档索引。找出三类内容:在本地运行但不会阻止合并的检查、在 CI 中运行但无法使流程失败的检查(
verification-tiers.md中的 “Commands that lie”),以及无人调用的休眠配置。接入第一类,修复第二类,删除第三类(references/contagion-markers.md)。 - 根据检查所防止的故障来选择检查项,切勿根据工具的人气来选。类别和工具见 references/guardrail-tooling.md。挑出此代码库实际出现的两三类故障;一次性安装全套工具,会迫使所有故障都采用最弱的执行等级。
- 安装每项检查: 针对现存违规选择一个执行等级(references/enforcement-ladder.md),以通过检查的状态交付,然后验证检查确实有效(运行检查,故意制造错误,确认它会失败并显示指出修复方法的消息,然后撤销更改)。将其接入提交前钩子和 CI。
- 寻路方式按 references/wayfinding.md 执行:命名与就近放置、添加新 X 的操作指南文件、标注可信度的文档索引,以及唯一的规范说明文件。
- 传播标记按 references/contagion-markers.md 执行:任何 Agent 不得复制或编辑的内容,都要在代码所在位置添加可通过 grep 搜索的标记,并注明应改用什么内容。
- 运行时体验: 按 references/verification-tiers.md 设置验证等级;按 references/agent-runtime.md 设置会话钩子、权限允许列表、无人值守修复任务和警报分流;在执行顺序的第 4 级进行审查门禁。
验证循环
运行与你所采用模式相匹配的项目,并在输出中记录结果。每项都需要证据;“看起来一致”不算通过。若因尚未安装任何内容或仓库不可写而暂时无法执行某项,请记录为 N/A 并说明原因。悄悄跳过检查,正是导致未经强制执行的约定带着“已验证”的外表发布的原因。
- 一致性(Design、Deepen):所有服务中的命名、模块接口和中间件规则表述一致。证据:矛盾扫描结果为零。
- 可强制执行性(全部):每项约定都注明对应的 lint 规则、类型检查或测试。证据:每项约定都有强制执行说明;对于实际已安装的检查,还需先通过,再通过故意引入违规使其失败,最后回退后再次通过。
- 可运维性(Design):针对每个可部署的表面,说明健康检查、回滚路径和可观测性,以便发现缓慢或卡住的情况(按路由统计 p95/p99 延迟;按任务类型统计队列深度和最早任务的等待时长)。缓慢、可重试或依赖第三方的工作应作为持久化任务运行,其状态存储在应用现有数据库的一行记录中,而不是新建队列或存储;直接调用模块,只有当调用方既不应等待消费者,也不应了解消费者时才使用事件(references/distributed-correctness.md)。每个自动化任务都必须有无需部署即可启用的紧急停止开关;每项功能都必须说明依赖项不可用时是故障开放还是故障关闭。证据:发布部分逐项列出这些内容;模块之间若使用事件或队列,必须说明为何不能直接调用。
- 质量门禁(代码有变更时):仓库的 lint、类型检查和定向测试(
pnpm lint、pnpm check-types、pnpm --filter <pkg> test或等效命令)。证据:引用通过检查的输出。 - CI 与本地一致(Harden):CI 步骤调用开发者运行的同一总控命令,或明确说明差异是有意为之。
- 没有悬空指针,冷启动时流程可行(Harden):通过 grep 证明文档索引和指令文件中列出的所有路径都存在;此外,由一个全新上下文中的 Agent 端到端遵循一份“添加新 X”的操作指南,且无需进一步指导。
- 整体简洁性(全部):结果应让读者需要记住的内容更少,而不是更多。证据:列出文件、表面和导出名称的净变化,说明并补偿每项增加所替代的内容;此外,对于新增的每个层、端口或间接层,都要指出使其落地的第二个调用方或实现。若架构审查只带来增加,即使其他各项都通过,也视为未通过此检查。
失败时:修正简述、约定或连线,然后重新运行验证循环。
输出模板
Design 模式生成此简述。Deepen 模式的机会排序模板见 references/deepening-existing.md。Harden 模式的输出是连线本身及验证循环的证据,而非文档。
# Architecture brief
## Context and constraints
## Repo shape
## Backend module interfaces
## Request context and middleware policy
## Frontend boundaries
## Testing strategy
## Quality bar and surface-area budget
## Rollout and rollback plan
## Open risks and follow-ups
简述的篇幅应与决策数量相匹配,而不是与模板相匹配。 项目不涉及的标题应直接删除,而不是勉强填写:没有前端的单租户内部服务,不必提供“前端边界”一节。每节应说明决策及其背后的约束,而不是复述参考资料中的约定。为了凑足九节而填充内容,会占用评审注意力,而这些注意力本应留给那两个存在争议的决策。
注意事项
Harden 模式的注意事项列于每个步骤加载的参考文档中(guardrail-tooling.md、enforcement-ladder.md、contagion-markers.md、verification-tiers.md、wayfinding.md)。
- 对于人数少于 5 人的团队,微服务会带来部署流水线、契约版本管理,以及每个服务各自的值班工作面。应从模块化单体仓库开始;等团队或规模压力证明边界确有必要时再拆分。
- 单体仓库根目录
package.json中的应用级依赖会被静默提升,因此应用可能在本地构建成功,却在单独部署时失败。每个应用都应管理自己的依赖。 - 如果没有导入边界规则,
handler/service/dao契约会在第一个截止日期到来时失效。编写契约的当天就添加规则(dao不得导入handler)。 - 在页面或布局级别使用
"use client"会将整个子树转换为客户端渲染,从而失去流式传输和直接访问服务端数据的能力。应将它推到叶节点。 - 在有 3 个或更多应用需要代码之前就将其提取到
packages/,只会徒然耦合发布周期。例外是两个界面已共享的契约(生成的类型、RPC 架构、品牌化 ID):它们是两者之间的接口,在有两个应用时就应放入一个包中。 - 在没有 outbox(或 CDC)的情况下,同时写入数据库和队列或 webhook,只要一边提交而另一边失败,就会丢失或凭空产生通知。参见
references/distributed-correctness.md。 - 通过构造强制执行、但可被外部强行改变的不变量(无符号类型、硬性 CHECK),会在外部强行改变状态时崩溃或截断值。应将其表示出来,在事后检测,并明确恢复。
- 若仅依赖
proxy.ts作为授权层:被 matcher 排除的路径会跳过它,而 Server Functions 会向其页面的路由发起 POST,因此 matcher 的变更会悄然移除覆盖范围。应在处理程序或 Server Function 本身检查授权。 - 不按
git log热点范围筛选,就对整个代码库进行深入扫描,会让清单充满无人触及的模块,而其中每一项从定义上说都是推测。
相关技能
agents-md:AGENTS.md / CLAUDE.md 文件本身。此技能负责该文件所指向的检查和文档树;可由 linter 强制执行的规则应在这里作为退出码实现,而不是写在那里作为说明文字。tidy:评审和限定于差异范围的清理工作,Harden 的防护规则可使其保持小规模。planning:针对单个功能的计划;Design 模式生成的架构概要会作为输入。scaffold-nextjs、scaffold-cli:创建随后由此技能进行结构设计的仓库。multi-tenant-architecture:租户识别、隔离和路由;此技能提供其下层的模块布局。dx-audit:包向外提供的开发者界面;此处的api-design.md仅涵盖契约形态。app-verification:构建和维护仓库自行运行的 verify CLI、doctor 命令和功能映射;此技能的 Harden 模式会接入 CI 检查,确保该工具的脚本和固件通过。请按名称调用。- 仅供维护:
evals/evals.json和evals/evaluation-scenarios.md保存此技能变更的回归场景;在用户任务期间,两者都不会加载。