improve-codebase-architecture 精读:先决定扫哪里,把候选画成一张 HTML 报告,然后才开始问
improve-codebase-architecture 是一次勘察,不是一次救援:它用 YAGNI 决定扫哪里(先 git log 找热点),把加深机会画成一份写在系统临时目录里的自包含 HTML 报告(Tailwind + Mermaid,每张候选卡带 before/after 图和 Strong / Worth exploring / Speculative 三档徽章),然后在报告写完之后就停下,说一句 Do NOT propose interfaces yet,再调 grilling 走你挑中的那一个。本文走一遍它的三步(Explore → HTML 报告 → Grilling loop),讲清它为什么把词汇外包给 codebase-design 并禁止漂移、为什么报告拒绝写段落、以及它怎么对待一条被拒的候选。
本篇属于系列 读懂最优秀的开发技能是怎么写的(Matt Pocock skills 逐技能精读) · 第 22 篇
(本文读的是 mattpocock/skills 快照 5beb35b,2026-10-19。引用原文都能按文中路径回仓库核对。这是「逐个技能精读」系列的第二十二篇,聚焦 improve-codebase-architecture 一个技能。)
它怎么工作:先决定扫哪里,再产出一份报告,最后才进对话
skills/engineering/improve-codebase-architecture/SKILL.md 是 user-invoked(disable-model-invocation: true),全文 71 行,正好是三步:Explore → Present candidates as an HTML report → Grilling loop。它对自己要说的事写得很克制:
Surface architectural friction and propose deepening opportunities: refactors that turn shallow modules into deep ones. The aim is testability and AI-navigability.
注意动词:surface(浮出来)和 propose(提议)。全文没有一处让它改代码,产品文档说得更直白——It never changes the code. The whole run produces a conversation and one HTML file in your OS temp directory. 所以这是一次勘察,不是一次救援;文档原话是 This makes it a survey, not a refactoring tool, so you can run it on a codebase you are not ready to touch yet.
第一步 Explore,最值得学的是它先决定扫哪里再扫:
Scope before you scan: YAGNI. Deepening a module pays off by making future changes to it easier, so put extra weight on the parts of the codebase that have recently changed.
具体做法:用户点了方向就照方向走;没点,就先 git log --oneline 往回走一段,找出这个仓库的 hot spots,让那些反复出现的路径先把注意力拉过去;如果改动是散的、没有明显热点,再扩大范围。同时先读 GLOSSARY.md 和相关 ADR。然后才是派子代理去走查,而且明确禁止套公式:
Then spawn a sub-agent to walk the codebase. Don’t follow rigid heuristics; explore organically and note where you experience friction:
走查的判据从 codebase-design 借,而且词表写死在正文里:module、interface、depth、seam、adapter、leverage、locality,后面还跟一句 Use these terms exactly in every suggestion, and don't drift into "component," "service," "API," or "boundary."(SKILL.md:13)。最后一关是 deletion test:
Apply the deletion test to anything you suspect is shallow: would deleting it concentrate complexity, or just move it? A “yes, concentrates” is the signal you want.
第二步产报告。写到 $TMPDIR(回退 /tmp,Windows 是 %TEMP%)下的 architecture-review-<timestamp>.html,刻意 so nothing lands in the repo,再用 open / xdg-open / start 打开给人看。报告用 Tailwind 加 Mermaid 的 CDN,每张候选卡固定六项:Files、Problem、Solution、Benefits(要用 locality 和 leverage 说,并且说清测试怎么变简单)、before/after 并排图,以及一个推荐强度徽章(Strong / Worth exploring / Speculative)。最后收一段 Top recommendation。写完之后它做的事很少,就一句:
Do NOT propose interfaces yet. After the file is written, ask the user: “Which of these would you like to explore?”
第三步 Grilling loop。你点了一个候选之后才调 grilling 走决策树(约束、依赖、加深后的模块长什么样、seam 背后放什么、哪些测试活得下来)。副作用是内联发生的:新概念进 GLOSSARY.md(文件不存在就懒创建)、模糊的词当场锐化、你带着一个站得住的理由拒掉候选时,它提议把这个决定记成 ADR——Want me to record this as an ADR so future architecture reviews don't re-suggest it? 而且只在这个理由对未来探索者真的有用时才提,明确跳过「现在不值得」这类临时理由和显然的理由。
它和「一般技能」的差别:把发现和决策拆成两步,用一张图把中间那步变成可扫读的东西
一般的重构技能会给你一份建议清单,或者干脆直接动手。这里做了三件结构性的事。
一、把产出物从 diff 降级成候选。 一次运行下来,除了临时目录里那个 HTML 和 GLOSSARY.md、ADR 的零星更新,什么都没变。这一个决定把「能跑」和「该改」解耦了——你可以在一个暂时不打算动的仓库上跑它。文档里那句 testability and AI-navigability 解释了为什么值得做,但没有任何一处允许它直接实现。
二、报告是 HTML,不是 Markdown。 深浅、seam、泄漏这些词本来就是空间概念,散文写起来就是一长篇;报告因此被要求「不写前言、直接上候选」、每张图约 320px 高让 before/after 并排不滚动、模块标签用 text-xs uppercase tracking-wider 好让它读起来像示意图而不是界面,最后收一张稍大的卡:候选名、一句为什么、加一个锚点到它的卡片。规范里还有一条极狠的话:
No paragraphs of explanation. If the diagram needs a paragraph to be understood, redraw the diagram. (
HTML-REPORT.md:55)
配套的还有 No introduction paragraph. Straight into the candidates.、用徽章代替分数,以及「别让每张图长一个样」——Pick the pattern that fits the candidate. Mix them. Don't make every diagram look the same. Variety is part of the point.(HTML-REPORT.md:59)。
三、词汇是借来的,而且禁止漂移。 技能本身不定义 module/seam/depth,它 Call the Skill tool with "codebase-design" 去借,再把七个词和三个禁止替换项抄进正文。这跟 pr 那篇里「菜单旁写着『你几乎不会全用』」是同一路数:把定义权外置,技能只负责在本回合严格使用。
还有一个分工值得记:codebase-design 是一个参考,不是一个会话驱动器。文档里说得很清楚——如果你把 codebase-design 直接当任务交给一个新 agent,它会失败:它自己没有流程可走,agent 会自己发明一个、重新探索一遍代码、跑很久之后才开口问你。发现模块是 improve-codebase-architecture 的事,设计模块才是 codebase-design 的事。
它有意识摒弃了什么
一、摒弃了「顺手改掉」。 全篇没有一处允许修改代码;它把施工推回主流程(grill-with-docs 或 to-spec),自己只负责排队。
二、摒弃了全仓库平扫。 用 YAGNI 做范围判据,把注意力压到最近改过的路径上。理由写在正文里——没人动的代码里做一次加深,是一次永远收不回本的重构。
三、摒弃了固定启发式。 Don't follow rigid heuristics; explore organically。它给的是几个「哪里可能浅」的提问(理解一个概念要不要在多个小模块间弹跳?接口是不是宽得跟实现一样?纯函数是不是只为可测性抽出来的,真 bug 却藏在调用方式里?),而不是一张 checklist。
四、摒弃了「看完就直接给接口」。 Do NOT propose interfaces yet——接口设计是 grilling 里的事,不是勘察里的。
五、摒弃了把 ADR 当禁令清单。 只有当摩擦真的值得重开一条 ADR 时才写进卡片并标清楚;Don't list every theoretical refactor an ADR forbids.(SKILL.md:56)
六、摒弃了逐条解释的文字。 报告不写前言、不写段落,图讲不清就重画图。
七、摒弃了给候选打分。 用三档徽章代替一个 0–100 的分数,并且它容忍「这次没找到什么」——文档里说,如果一张报告里全是 Speculative,那这个技能就等于说了「没找到」。
八、摒弃了「一次会话处理完一整张报告」。 报告只是个临时文件,文档提醒你别把它带进后续对话:一次会话里连做几个候选,上下文窗口会同时塞进报告、grilling、领域模型改动和代码改动。挑一个、grill 它、把决定带进 /to-spec,其余转成工单以后再说。技能本身没有规定这套流程,文档却把这条说得很明确——carry the candidate forward, not the file。
原文证据
范围判据与词汇外置:
Scope before you scan: YAGNI. … walk back a good stretch of the commit history (
git log --oneline) to find the codebase’s hot spots
Call the Skill tool with “codebase-design” for the architecture vocabulary (module, interface, depth, seam, adapter, leverage, locality) … Use these terms exactly in every suggestion, and don’t drift into “component,” “service,” “API,” or “boundary.”
勘察的边界与收束方式:
Write a self-contained HTML file to the OS temp directory so nothing lands in the repo.
Do NOT propose interfaces yet. After the file is written, ask the user: “Which of these would you like to explore?”
被拒候选的处理时机:
User rejects the candidate with a load-bearing reason? Offer an ADR … Only offer when the reason would actually be needed by a future explorer to avoid re-suggesting the same thing; skip ephemeral reasons (“not worth it right now”) and self-evident ones.
可迁移结论
- 勘察和施工必须分属两次运行。 把「发现」的产出物定成候选清单而不是 diff,你才敢在还不想动的代码上跑它。
- 扫之前先定范围。 用
git log找热点、用 YAGNI 当判据:改动频繁的地方才值得加深。这一条比任何一条代码品味都更能决定报告有没有用。 - 把结论画出来。 空间性的判断(深浅、缝隙、泄漏)用 before/after 图讲,比用段落讲便宜得多;定一条「图讲不清就重画」的规矩,报告就不会退化成散文。
- 借一套词汇并明确禁止漂移。 把「用哪些词、不用哪些词」写进正文,比事后要求「术语一致」有效得多。
- 给建议加一档强度,而不是一个分数。 三档徽章既表达了把握,也允许「这次没找到什么」这种诚实的结果。
- 一次性产物写到仓库外。 临时目录加时间戳文件名,跑多少次都不脏仓库。
- 让「拒绝」变成资产。 一个站得住的拒绝理由值得写成 ADR,让下一次勘察别再提同一件事——但只在这个理由对未来的人真的有用时才写。
improve-codebase-architecture 是这套技能里唯一一个产物是一段可执行对话的技能:HTML 是给眼睛的,grilling 是给决定的,两者之间它什么都不做。它把「我知道该改哪」和「我们现在就改」之间的那道缝,写成了流程里的一步。
讨论
用 GitHub 账号留言;评论保存在公开仓库chengshu-blog-discussions的 Discussions 里。也可通过 RSS 订阅后续文章。