mcpField
D
doc-and-modernize

doc-and-modernize

github.com/github开发40.5k111 次安装今天更新
代码仓库

关于 doc-and-modernize

用于记录本地检出的仓库架构或规划其现代化改造的技能;制定计划前可先生成架构基线。它强调依据本地文件并附上引用,且按系统的可测试性和安全约束分阶段规划变更。

主要功能

  1. 01根据本地代码生成带引用的架构文档
  2. 02梳理仓库结构与运行时配置
  3. 03检查依赖项和验证命令
  4. 04制定分阶段的现代化计划
  5. 05按系统可测试性匹配安全策略

适用场景

  1. 01帮助工程师熟悉陌生代码库
  2. 02记录本地检出项目的架构
  3. 03规划旧系统升级或迁移
  4. 04识别现代化风险与验证步骤

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/github/awesome-copilot --skill doc-and-modernize

安装后在 Claude Code 中输入/doc-and-modernize确认已加载。

附带文件references/copilot-instructions.template.mdreferences/migration-hazards.md

详细介绍

文档与现代化

针对用户已在本地检出的仓库,提供两个互补的工作流,打包为一个技能:

  • 文档模式 — 基于磁盘上的代码产出一份权威、带引用的架构文档。非常适合入职引导、系统设计图谱,或作为现代化工作的证据基础。
  • 现代化模式 — 将该架构转化为分阶段、带安全阶梯的升级、迁移或重写遗留系统的计划。

模式选择

  • 如果用户想要理解、记录、绘制、研究或入职了解某个代码库,运行文档模式。
  • 如果用户想要现代化、迁移、升级或重写某个系统,运行现代化模式。现代化模式是自给自足的:如果尚无架构文档,它会先(在同一次执行中)运行文档模式工作流,然后直接继续到计划。

如有疑问,先产出架构文档 — 它是两种模式共同依赖的、经过审查的证据基础。

文档模式

针对用户已在本地检出的仓库,生成一份权威、带引用的架构文档。目标是写出一份可以交给新工程师作为入职参考的文档 — 广度足以覆盖整个系统,深度足以在难点上有用,并且可信,因为每一条论断都能追溯到磁盘上的某个文件。

为何以本地优先

从本地检出(而非 GitHub API 或网络)读取是有意为之的默认做法。它更快、免费、避开速率限制,而且——最重要的是——它描述的是你面前的这份确切代码,而不是远端 main 恰好呈现的样子。唯一的代价是仅存在于远端的事实(star 数、完整的 CI 运行历史、同级仓库)不可见。这没关系:将这些声明为超出范围,或标记为 [UNVERIFIED],而不是猜测。

本地优先并非本地永不远端:网络/API 查询是有意为之的最后手段兜底,仅用于某种确实无法从磁盘确定、且对文档有实质影响的事实。当你确实要用它时,要清楚标注结果(例如 [UNVERIFIED] / 来源于远端),让读者知道它不是来自检出,也绝不要让它变成取代阅读磁盘代码的捷径。

工作流
  1. 先确立身份。 运行 git remote -v、git branch --show-current 和 git log -1,使文档锚定到特定的远端、分支和提交。读者必须能分辨它描述的是哪个快照。远端 URL 可能包含嵌入的凭据(例如 https://<token>@github.com/...)——在将 URL 记入文档之前,抹去任何凭据/token。
  2. 检测,而非假设。 阅读真实的清单文件(go.mod、package.json、Cargo.toml、pyproject.toml、pom.xml 等)、Makefile/任务运行器、CI 配置,以及任何仓库特定的 Agent 或贡献者文档(AGENTS.md、CONTRIBUTING、README、docs/)。它们是技术栈和命令的事实来源——优先于你对框架的既有知识。
  3. 先铺广度,再钻深度。 首先构建整个仓库的图谱(下面的三个视角),然后挑出 2-3 个最难的子系统深入下去。
  4. 边做边验证。 打开你引用的文件。如果你引用了某个行号,你应当确实读过那一行。此处不支持的论断比省略更糟——这份文档的全部价值就在于它值得信任。
输出结构

产出一份单个 Markdown 文件,包含以下章节,按此顺序。根据实际项目调整标题(命令行工具没有“前端”视角——将该位置并入对该仓库重要的内容),但保留三视角的结构和验证纪律。

第 1 部分 — 全仓库技术深度剖析
  • 该仓库是什么(一段话,引用 README)。
  • 技术栈检测表:层级 | 技术 | 证据(文件+行号)。
  • 入口点(后端、前端、命令行 — 凡适用者)。
  • 命令与验证清单 — 一张规范项目命令表 (command | purpose | evidence),需对照任务运行器 / 清单文件 / CI 配置进行核实,而非猜测。涵盖构建、运行/服务、测试(以及如何 运行单个测试)、lint、格式化,以及 — 若存在 — 类型检查、 端到端/冒烟、契约,以及任何其他门禁命令,外加运行它们的 CI 工作流及其触发条件。还要记录 CI 是否被 强制实施 — 即是否有任何工作流是必需状态检查 / 分支保护规则,真正阻止合并,而非仅仅运行 — 因为这一区别是 人工配置的设置,Modernization 模式必须将其呈现出来,而不能假设。 强制实施通常无法仅从本地检出确定:应询问用户,或标记为 [UNVERIFIED],除非从权威来源确认(根据上述本地优先规则,任何 远程查询都是被标记为最后手段的做法)。此清单是 下游规划(Modernization 模式)引用的唯一事实来源,以使其退出 标准可运行,而非空想。按各生态系统检测这些内容(npm/yarn/ pnpm、make、just、cargo、go、poetry/tox/nox、gradle/maven 等)— 不要假设技术栈。将任何无法核实的命令 标记为 [UNVERIFIED]。
  • 每个主要区域的目录布局,每个目录配一行用途说明。
  • 部署与运行时表面 — 一张表,列出语言/运行时 及支撑服务版本被固定用于运行系统的所有位置(而不仅仅是 构建):容器基础镜像(Dockerfile/Containerfile、 docker-compose* 构建上下文)、CI runner 镜像 / setup-* 版本、 engines/.nvmrc/.tool-versions/runtime.txt、无服务器/lambda 运行时, 以及有状态数据存储镜像标签(DB/缓存/代理/搜索)。每项均以 文件+行号引用。这一表面是后续平台/运行时升级必须 同步变动的内容 — 在此处标记构建运行时与运行运行时之间的任何漂移, 以便在编写现代化计划之前就可见。
  • EOL / 死依赖扫描 — 指出已停止维护、已终止生命周期、 或在可能的目标主版本中已被移除的框架、运行时、基础镜像 和库(例如某个框架的下一个主版本重命名命名空间或 移除某个组件系列)。酌情将每项标记为 [INFERRED]/[UNVERIFIED]。这是 Modernization 模式的可行性探针与风险红队所依据的原材料。
  • 数据/存储层、API、插件/扩展、后台任务、CI/CD、测试。
第 2 部分 — 上下文与生态系统
  • 本地检出身份表(远程、分支、HEAD 提交、版本、许可证)。
  • 存在的仓库特定 Agent/贡献者文档,以及它们编码了哪些规则。
  • 开发者易踩的坑(测试监视模式默认值、缓慢构建、代码生成必须提交、 pre-commit 钩子)— 每项均引用来源。
  • 该项目如何与其更广泛的生态系统或同类服务相关联,以 磁盘上可见者为准(构建标签、可选链接仓库、可独立部署的 组件)。不要引入远程生态系统的琐碎信息。
第 3 部分 — 架构蓝图
  • 技术栈摘要(可引用第 1 部分的表格)。
  • 以 Mermaid 绘制的 C4 风格图:第 1 级系统上下文、第 2 级容器、 第 3 级一个具有代表性的请求/组件生命周期。
  • 分层与依赖规则(什么可以依赖什么,以及由什么来强制执行)。
  • 横切关注点表格:认证、配置、日志、指标/追踪、密钥、 错误处理、功能开关 — 每项均附其位置和证据。
  • 推断出的架构决策记录(根据代码 + 文档重建)。
  • 治理与执行机制(CI 门禁、代码生成验证、 CODEOWNERS、评审门禁、兼容性规则)。
  • “如何添加一项功能”指南以及常见陷阱。
子系统深入剖析

找出 2-3 个最复杂或最具架构重要性的子系统 — 也就是 新工程师最感吃力的部分,例如评估/调度 引擎、插件加载流水线、状态机,或渲染/迁移 框架。针对每个子系统,增加一个专门的子章节,涵盖其内部结构、 生命周期或状态机、关键类型和数据流,并附上本地文件+行号 引用,以及在有助于说明流程时附上一张小型 Mermaid 图。这正是 一份有用的入职文档与一份目录清单之间的区别 — 请在此投入真正的精力。

置信度评估

一张主要论断领域的表格,评级为 High / Inferred / Unverified,以便 读者确切知道哪些部分可以直接信任、哪些需要复核。

脚注 — 本地文件引用

本文档所依赖的关键本地文件列表,每项都附一句说明, 指出它确立了哪些内容。

让文档值得信赖的惯例

这些习惯正是本技能的产出区别于泛泛概述之处。它们很 重要,因为本文档的全部价值就在于读者可以信赖它, 而无需重新推导一切。

  • 为每个非显而易见的论断提供引用,指向本地路径,并在其 确定某个具体内容时附上行号(pkg/server/server.go#L39-L41)。相对于 仓库根目录的相对路径可保持链接可点击。
  • 诚实地标注不确定性。 对于你推理得出但未见到明确陈述的内容,使用 [INFERRED];对于你重复引用但未加确认的内容 (例如来自某份文档、而你未重新测量的构建耗时论断),使用 [UNVERIFIED]。 诚实的空缺比虚假的自信更有用。
  • 解决矛盾,而不是重述矛盾。 如果两个来源不一致(比如代码中的 版本字面量与清单文件中的不一致),去读代码,确定真正的 答案,并将其标注为 [Resolved contradiction] 并附上解释。让 读者自己去纠结一处冲突,是一种失败模式。
  • 注明仓库强制执行的兼容性与部署节奏规则 — 前后端 PR 分离、存储双向兼容、protobuf 仅做增量变更 — 因为这些是新来者最容易打破的规则。
  • 用精确数字而非模糊表述。 “73 个服务包”、“89 个工作流 文件”(来自目录列表)读起来像是经过核实的;“许多服务”读起来像是 猜测。
范围控制

让文档紧扣检出的代码库。人很容易向外漂移到 项目更广泛的生态(相关产品、README 营销内容、周边仓库) — 除非它就在磁盘上可见,否则要抵制这种冲动,并清楚地标注任何来自 本地代码树之外的内容。读者要的是这个代码库,忠实记录。

现代化模式

为遗留代码库生成一份完整、可执行的现代化计划。本技能聚焦于前瞻性工作——要现代化什么、为什么现代化、以什么顺序现代化以及如何现代化——但它自给自足:它会先确保架构文档存在,在需要时通过文档模式生成一份。

默认假设已死。 人们之所以诉诸现代化,恰恰是因为旧技术栈难以升级或根本无法升级——已 EOL 的运行时、无法编译的原生模块、已停用的软件包镜像、被废弃的框架。因此本技能不假设你能够复活遗留工具链,并在动任何东西之前建立起一个全绿的 CI 门禁。那种“先冻结再提升”的做法属于幸运情况,而非默认情况。相反,本技能会运行一次限时可行性探针,然后根据系统实际的存活程度,选定一种迁移策略和一种安全策略。对于一个真正已死的应用,构建一个绿色的遗留门禁本身就是一个现代化项目——这正是本技能旨在避免的循环陷阱。

有三个理念驱动整个计划,下面逐一介绍:可测试性里程碑(按组件而言,系统何时能够真正重新构建、运行并通过测试)、安全阶梯(你实际能够达到的最高回归安全级别,且降级被视为一种受认可的正当结果,而非失败),以及 CI 里程碑(哪个阶段首先建立 CI——同时提醒一点:将 CI 强制执行为必需检查是需要人工完成的手动步骤,而非 Agent 能做到的事情)。

前提条件

本技能在制定计划之前,需要先理解系统的架构。按如下方式解决这一点:

  1. 如果架构文档已经存在——由上述文档模式生成,或用户指向的 README / ARCHITECTURE.md,或有足够的先前对话上下文——则使用它,并跳至下面的工作流。
  2. 如果不存在,先运行上述文档模式工作流,生成一份带引用的架构文档,然后在同一轮中直接继续到下面的第 1 阶段。不要停下来让用户单独运行它,也不要在两份文档之间暂停等待评审。

最终产出两份成果物:架构文档(经过审计的证据基础)和这份现代化计划(前瞻性的行动集合)。

在制定计划之前,确认存在一份《命令与验证清单》。 退出标准只有在可运行时才有价值,因此计划必须能够引用项目规范的 build / run / test / lint / typecheck / e2e / contract 命令以及 CI 门禁。文档模式会在第 1 部分生成这份清单;如果你依据的是缺少它的 README 或先前上下文,请在编写退出标准之前自行检测并记录这些命令(按生态系统区分——npm/yarn/pnpm、make、just、cargo、go、poetry/tox/nox、gradle/mvn 等)。绝不要编造一个你尚未对照任务运行器 / 清单 / CI 配置验证过的命令。

工作流
第 1 阶段:评估当前状态(基于现有文档)

阅读架构文档并提取:

  1. 技术栈清单——语言、框架、运行时、依赖项(在可见的情况下附带版本)
  2. 功能 / 领域地图——系统的各主要功能区域
  3. 已识别的痛点——现有文档中记录的技术债、隐患、已废弃依赖、架构违规
  4. 部署与基础设施——它如今是如何运行的

不要重新阅读每一个源文件。信任架构文档。只有在某个现代化问题需要核实细节时(例如,“这个依赖实际被使用了,还是仅仅被声明了?”),才打开具体文件。

阶段 2:可行性探针、策略分叉与安全阶梯

这是整个计划的转折点。在推荐目标架构或编写任何阶段之前,先完成这一步。 它的任务是快速而诚实地回答:这个系统还有多少活气,哪种迁移形态合适,以及我们实际能获得多少回归安全性?

2a. 运行一个有时间盒限制的可行性探针

给它设定一个硬性时间盒(例如一天)。这个探针的问题不是“我们能不能让 CI 变绿?” —— 而是**“我们能不能让它哪怕只运行一次,以捕获其行为?”** 针对每个组件 / 可部署单元进行探查:

  • 它今天能否安装其依赖(理想情况下从已提交的 lockfile 安装,无需手工修补)?
  • 原生 / 构建步骤能否在当前受支持的工具链上成功?
  • 它能否启动 / 运行?
  • 测试运行器能否至少执行,并且至少 1 个有意义的测试通过?

记录你实际观察到的内容 —— 包括部分成功(在 monorepo 中很常见:一个服务能安装且测试全绿,而另一个无法编译)。不要投入两个冲刺去复活一具尸体,才发现它无法复活。当时间盒到期时,用你已有的证据做决定。

2b. 定位可测试性里程碑(按组件)

“可测试”不是你在一个死应用上可以满足的前提条件 —— 对于死应用,它是现代化的产出,而不是输入。 因此,每个计划都必须在一开始就命名其可测试性里程碑:系统(或某个给定组件)首次同时满足全部四个条件的具体阶段:

  1. 运行在受支持(非 EOL)的运行时上。
  2. 依赖从 lockfile 安装,无需手工修补。
  3. 原生 / 构建步骤在当前工具链上成功。
  4. 测试运行器执行,并且至少 1 个有意义的测试在 CI 中通过。

按组件评估这一点 —— 正如参考的 Nylas 运行所显示的,一个接近现代的云服务可以立即跨过这条线,而一个遗留 Electron 客户端要到几个阶段之后才会跨过它。这个里程碑是计划中唯一最重要的标记。 要明确说出每个组件的里程碑落在哪里。

2c. 将每个阶段标记为可测试性之前或之后

可测试性里程碑将工作分成两个具有不同安全规则的区间:

  • 可测试性之前(“黑暗”)区间: 该组件不能被信任可以运行。安全性来自非测试来源(见下文的安全阶梯)。在这里要求 CI 测试门禁是范畴错误 —— 你在要求一个尚未活过来的东西提供绿色测试。
  • 可测试性之后(“点亮”)区间: 它在受支持的活跃技术栈上构建并运行。现在,真正的 CI、特征化和 e2e 是可以实现且值得投资的。经典的绿色门禁属于这里。

规则:绝不要在一个组件跨过它自己的可测试性线之前,要求该组件有自动化测试门禁。

2d. 选择迁移策略——A/B 分叉

技术预研决定两种形态中哪一种更合适(在 monorepo 中这是按组件逐个判断的):

  • (A)先冻结,再提升。 先把旧应用原样纳入保护网,然后在保护网下进行升级。这要求旧应用能以可接受的成本被复活——也就是说,它几乎能运行。这是传统的“先写特征测试,再做任何改动”路径。属于可选方案,而非默认方案。
  • (B)先建立滩头阵地,再逐步扩展(行走骨架)。 不要给尸体罩保护网。先把尽可能薄的一条端到端切片驱动到现代技术栈上,直到它能构建、能启动、能测试;然后逐步把其余部分绞杀进来,并在过程中在新栈上编写测试。这是已死应用的默认方案。 在策略 B 中,第一阶段是*“用最小的切片达到可测试性里程碑”,而不是“冻结整个遗留工具链”*。

如果某个组件完全无法构建或运行,那么在旧技术栈上做特征测试从定义上就是不可能的——此时应反转思路:不要再试图给尸体罩保护网,而是在目标技术栈上逐步构建保护网,并将旧代码/输出作为参照基准。

2e. 在安全阶梯上选择可达到的最高一级

回归安全是一个阶梯,而不是二元的。针对每个组件,选择你实际能够达到的最高一级;降级到更低一级是一种被认可的一等结果——而不是失败——前提是残余风险被明确命名。

  • L4 —— 全自动门禁: CI 中 lint + 单元 + 特征化 + e2e 全绿。
  • L3 —— 部分门禁: 部分套件通过 + 锁文件 + 对可达成子集的 CI;其余部分隔离并附一份具名清单。
  • L2 —— 仅特征化 / 黄金主控: 无法在 CI 中运行,但可捕获 I/O 快照 / 行为差异(可接受手动方式)。
  • L1 —— 基于可逆性: 无可运行的测试;安全性来自小而可逆的步骤、绞杀者 / 并行运行、冒烟检查清单以及评审。
  • L0 —— 无网可依: 视为规范驱动的重写(旧代码 = 规范)或归档。

安全不只是测试。 至少存在五种来源,而在已死的应用上,2–4 往往比 1 更重要:

  1. 测试(特征化 / 单元 / e2e)—— 最强,但在已死的应用上往往无法达成。
  2. 可逆性—— 极小的提交、每次变更一个分支、可轻松回滚。
  3. 隔离—— 功能开关、绞杀者模式、旧系统与新系统并行运行并对输出做差异比对。
  4. 以运行中的旧系统作为基准—— 录制 / 重放真实 I/O,在没有测试框架的情况下特征化行为。
  5. 人工评审 + 领域知识。

将安全锚定在行为的接缝上,而不是单元上。 任何应用持久且与栈无关的契约都是其对外可观察的行为:HTTP 端点、数据库 schema / 查询、文件 / 传输格式、命令行输出、协议。将这些固定为黄金主控 / 审批快照——它们能在重写中存活;而针对已死的遗留代码的单元测试在你替换该模块的那一刻就会被删除。 获取一个基准,按可用性排序:

  • 最佳——它在某个地方运行(生产环境、虚拟机、旧容器,甚至只读),并且你被允许使用它:录制真实 I/O 并将其重放到新代码上。一次成功的捕获胜过永久保持绿色的遗留 CI。(不要假设这一级总是可用——一个忠实的第三方重新实现或复活的遗留二进制文件可能因政策、许可或一个明确的决定——即"仅用我们手头的东西"来构建——而不被允许使用。若是如此,跳过到自冻结那一级。)
  • 退而求其次——它完全无法运行: 旧的源代码就是规范;辅以生产日志、数据库转储、文档、工单,并接受这样一点:特征化测试是针对新实现编写的,以便在验证其正确后将其锁定。
  • 自冻结黄金主控——不存在或不允许外部参照: 当没有任何可信之物可运行且你不得借用外部基准时,现代化组件在它首次运行的那一刻记录其自身的黄金主控(在其可测试性里程碑处),此后每个阶段都与该冻结快照做差异比对。这是一级真实且可用的级别——但要大声说出其残余风险:它保证的是后续重构的自洽性,而非首次启动的正确性(快照的正确性仅与其生成它的那次运行相同)。将其与人工评审 / 规范交叉核对搭配使用,以认可首次运行。
2f. 将经济 / 基准分诊纳入选择

在选择级别时权衡回归成本:该应用是否在生产环境中?它是否有真实用户?旧系统是否仍可作为参照运行?一个零用户、被遗弃的应用的回归成本几乎为零——在那里设置昂贵的门禁是过度工程。一个有用户的生产系统则值得更高的级别和一个真正的基准。

2g. 定位 CI 里程碑(指明哪个阶段搭建 CI)

正如可测试性里程碑指明某个组件何时能够首次运行并接受测试,CI 里程碑指明持续集成何时首次搭建——而且它必须在路线图中明确写出,而不是埋在某个阶段的正文里。 现代化计划的一个常见失败,是让读者无法判断哪个阶段引入了 CI;要明确点名。

规则:CI 在第一个点亮阶段搭建——在组件跨过其可测试性里程碑之时或紧随其后——绝不提前。 CI 不可能在一个尚无法构建/运行的组件上变绿,因此在黑暗期配置 CI 门禁,与在那里要求测试门禁是同一类错误。(琐碎的脚手架——仅 lint 或仅 build 的工作流——可以更早出现,但权威的测试门禁落在 CI 里程碑处。)

CI 是两个不同的步骤,而第二步只能由人完成——两者都要点明:

  1. 编写 CI 工作流文件(例如 .github/workflows/*.yml、.gitlab-ci.yml、Azure Pipelines),用于运行门禁。Agent 可以完成这一步。
  2. 把该工作流变成强制执行的门禁——即必需状态检查 / 分支保护规则 / 合并请求批准规则。这是一个手动操作平台 UI / 管理员步骤(GitHub → Settings → Branches;GitLab 受保护分支 + MR 设置等),Agent 无法执行。 在有人配置之前,CI 会在 PR 上运行,但不会阻止合并。

由于第 2 步超出 Agent 的能力范围,每一个搭建 CI 的计划都必须把它作为明确的用户行动项列出(写在该阶段的退出标准以及 §9 开放问题 / 需要利益相关者决策的事项中),这样读者才知道:在他们开启之前,这道门禁不会自动生效。

阶段 2 的输出: 对每个组件——spike 结果、所选策略(A 或 B)、定位到的可测试性里程碑、pre/post 标签,以及目标安全阶梯层级和任何残余风险——再加上计划整体的 CI 里程碑(哪个阶段搭建 CI),以及提醒:强制执行它是手动的人工步骤。下游的一切都依赖于这些。

阶段 2.5:对照危险目录对每个阶段进行红队审查(强制)

上述战略脚手架(可测试性、安全阶梯、CI 里程碑)是必要的,但并不充分。现代化计划也会在战术性、生态可预测的危险上失败——隔离集不完整、遗漏了命名空间 codemod、运行时升级却把基础镜像落在后面、把有状态数据存储的大版本更新当作镜像升级、边缘重写丢掉了匿名路由类。这些危险如此反复出现,以至于有了自己的目录:references/migration-hazards.md(H1–H8)。

规则:在任何阶段实施之前,先对照 references/migration-hazards.md 中的每一个危险对其计划进行红队审查。 对每个危险,问“这个阶段会触发它吗?”,对真实仓库运行该危险的检测探针,并在写代码之前把其计划行动纳入该阶段的任务/退出标准。在规划中捕获的危险是一个任务;在评审中捕获是返工;在生产中捕获是一场事故。记录你检查过并已排除的危险,而不仅仅是那些触发的——静默跳过与遗漏无法区分。

这是独立评审 pass 的制度化形式。你自己走一遍目录来做,并且在有独立第二意见可用时(例如 rubber-duck / 评审 Agent)——用它:在参考的 PiggyMetrics 运行中,这样一次 pass 在每一个阶段中都捕获了一个阻塞性计划缺口,而每个缺口都是 H1–H8 某一类的实例。把结果固化进每个阶段的“已做决策”和退出标准中,这样执行 Agent 继承的是已排除的检查清单,而不是重新去发现危险。

阶段 3:推荐目标架构

决策框架——始终按此顺序评估: 对于每个组件/依赖项,请按从破坏性最小到破坏性最大的顺序依次评估以下层级。在第一个能解决问题的层级停止:

  1. 原地升级 — 能否提升主版本号并修复破坏性问题? (例如 Node 14→22、Python 2→3、React 16→19、Rails 5→7)。这是默认 答案。只有在升级路径官方不受支持、破坏性变更范围过大以致超过重写成本,或该框架 本身已被弃用/已停止维护(EOL)时,才拒绝这一方案。

  2. 替换依赖项 — 如果特定库已停止维护,但模式本身没问题,能否替换为 接口形态相同的、仍在维护的替代方案? (例如 Moment.js → date-fns、Request → got/fetch、CoffeeScript → TypeScript 配合 decaffeinate)。在存在可直接替换或机械化迁移工具时,优先使用它们。

  3. 包装/适配(绞杀者模式,Strangler Fig) — 如果组件的接口是合理的,但 内部实现已无法维护,能否在其前面放置一个干净的接口,并在其后逐步替换内部实现? 当耦合程度可控、且系统能够在旧实现与新实现共存的情况下运行时,这种方法可行。

  4. 重写 — 仅在以下情况下才考虑:框架已被弃用且没有升级路径,架构 从根本上无法支持所需的新能力(例如仅支持同步的设计必须变为实时),或 诚实的评估表明升级成本超过重写成本。每一条重写建议都需要明确的理由 — “它太旧了”并不充分。

  5. 移除 — 死代码、零使用的功能、已被其他系统取代的弃用能力。 在建议移除之前,先核实使用情况的说法。

倾向于保守。 最便宜的迁移就是你不用做的迁移。 每一层级的破坏性都会增加风险、时间线和成本。一个运行在 较旧框架上的可用系统,胜过一个运行在新框架上的半成品重写。

机械化迁移配套事项(只要建议涉及升级、替换或移除,就适用)。 这些是目标架构决策中初稿计划通常会遗漏的部分;每一项都对应 references/migration-hazards.md 中的一个危害类别:

  • 传递依赖隔离的完整性(H1)。 当移除/切换整个 依赖族时,在所有清单文件中 grep 已废弃的构件,并列出 必须迁移或被隔离的完整依赖项集合 — 而不是你最初想到的子集。明确说明 切换后的构建目标。
  • 框架主版本的 codemod(H2)。 主版本升级会带来与你的逻辑无关的 机械化破坏性变更 — 命名空间重命名、移除的弃用 API、 变更的测试引擎、变更的配置键。将每一项列为独立任务, 并在大型代码树上优先使用生态系统的自动化配方/codemod 工具(OpenRewrite、 jscodeshift/react-codemod、2to3/pyupgrade 等),而不是手工编辑。不要 忘记测试框架的迁移 — 这是最容易被遗忘的一项。
  • 运行时与部署的同步(H3)。 如果某项建议提升了语言/运行时的主版本, 那么所有为运行应用而固定该运行时的地方 — 基础镜像、docker-compose 构建上下文、 CI runner 镜像、engines/.nvmrc/.tool-versions、无服务器运行时 — 都必须在 同一阶段迁移。构建运行时与运行运行时绝不能跨阶段边界 出现偏差,否则应用会构建成功但运行失败。

生成一个目标架构章节,其中包含:

  • 推荐技术栈,并为每项选择给出理由(框架、语言、运行时、数据库、基础设施)
  • 架构模式建议(单体 → 模块化单体 → 服务、事件驱动、无服务器等)并附论证
  • 保留什么 vs. 移除什么——使用上述决策框架,明确对每个主要组件进行分类:
    • ✅ 保持原样(仍然现代/合适)
    • ⬆️ 原地升级(升级版本、修复破损)
    • 🔀 替换依赖(用仍在维护的等效库替换已死库)
    • 🔄 包装/适配(添加接口层,逐步替换内部实现)
    • 🔁 重写(根本性不兼容——需要理由)
    • 🗑️ 移除(死代码、已弃用功能、已被取代)

对于每项建议,编写一条内联 ADR:

#### ADR: [Decision Title]
- **Context:** [Why this decision is needed]
- **Decision:** [What we chose and which decision framework level it falls under]
- **Alternatives considered:** [What else was evaluated and why it lost]
- **Consequences:** [Tradeoffs accepted]
阶段 4:按功能迁移分析

对于架构文档中识别出的每个主要功能/领域,产出一节内容,涵盖:

  1. 当前实现——一段总结,附文件引用
  2. 迁移策略——该组件在阶段 2 中的 A/B 选择(先冻结再迁移 vs. 滩头阵地/行走骨架),加上具体战术:绞杀者模式、大爆炸重写、平移迁移、增量重构或保持原样
  3. 可测试性状态——该组件的可测试性里程碑落在哪里,以及其安全阶梯层级(L0–L4),并点名残余风险
  4. 依赖与耦合——还有什么会随之破坏或必须随之迁移
  5. 工作量估算——T 恤尺码(XS/S/M/L/XL)并附理由
  6. 风险评估——可能出什么问题、数据迁移顾虑、用户影响
  7. 验收标准——你如何知道迁移成功(针对所选 oracle/seam 契约来表达,而不是针对一个尚未存在的测试套件)
阶段 5:分阶段实施计划

产出一份有序的分阶段计划。每个阶段都应可独立部署(不能存在无法在生产环境运行的半迁移状态)。

阶段门控是制度感知的(适用于每个阶段)。 在分阶段计划顶部说明这条规则,并在每个阶段的退出标准中遵守它。一个阶段在其验证与退出标准通过之前不算完成。这些标准必须 (a) 可客观验证;(b) 在下一阶段开始前实际执行并记录;以及 (c) 受门控——在阶段 N 的标准被证明满足之前,不要推进到阶段 N+1。但哪些标准有效取决于组件的制度(阶段 2c):

  • **可测试后("点亮")阶段:**退出标准是可运行的命令/绿色 CI——该阶段分支/PR 上的绿色 CI 是权威信号。
  • 可测试前("黑暗")阶段:组件尚未存活,因此绿色 CI 测试门控是一个范畴错误。退出标准改为来自可达到的安全阶梯层级——捕获的 seam/oracle 快照、经证明存在的可逆性脚手架、通过的冒烟检查清单、有记录的评审。不要用尚无法运行的测试套件来阻塞一个黑暗制度阶段。

向用户报告任何通过/失败未知的阶段,而不是假设它已通过。

第一个阶段建立最大可达到的安全性——而不是一个固定的"绿色遗留门。" 不要硬编码"复活一切并让 CI 变绿。"根据阶段 2 的输出,按组件塑造第一个阶段:

  • 策略 A(先冻结再提升),组件可复活: 第一阶段就是经典的安全网与基线 —— 纯增量,不改变行为:
    • 在接缝处的特征测试/黄金测试/契约测试,固定当前可观察的行为(协议轨迹、API 快照、输出黄金样本)。
    • CI + lint + formatter 接入并全绿(为后续点亮阶段的权威门禁)。
    • 固定依赖/提交 lockfile,并记录已知良好的基线(版本、工具链、当前如何构建和运行)。
    • “安全网被证明会失败。” 故意变异代码(例如改变一个协议码或返回值),确认新测试变红,然后还原。一个从不失败的安全网什么也证明不了 —— 证明它真正有效。
  • 策略 B(滩头阵地/行走骨架),组件已死: 第一阶段是**“用最小切片达到可测试性里程碑”** —— 驱动最薄的端到端路径到现代技术栈上,直到它能构建、启动并让 ≥1 个真实测试变绿,同时从旧系统捕获接缝/判定契约用于验证。不要试图给尸体布网。 它的退出标准是可达到的安全梯级,并明确列出残余风险 —— 一次受认可的降级不是失败。

只有在第一阶段的验收标准达成后(无论处于哪一级),后续的基础设施和功能阶段才开始。

将每个阶段结构化为:

### Phase N: [Name] (T-shirt size: M)

**Goal:** [One sentence]
**Regime:** [pre-testability ("dark") | post-testability ("lit")] — per component
**Safety rung:** [L0–L4, with residual risk named if below L4]
**Prerequisites:** [Which phases must complete first]
**Duration estimate:** [Relative, not calendar — e.g., "2-4 sprints"]

#### Tasks
| ID | Task | Component | Blocked by |
|----|------|-----------|------------|
| N.1 | ... | ... | — |
| N.2 | ... | ... | N.1 |

#### Risks & Mitigations
- **Risk:** ... → **Mitigation:** ...

#### Decisions made
- [Every sub-decision this phase depends on, resolved and documented here so the
  phase can be implemented without further user input. State "dropped" vs
  "deferred" explicitly for anything cut.]

#### Verification & Exit Criteria (Definition of Done)
- [ ] [Regime-appropriate criterion. For LIT phases: a runnable command / green
      CI check, citing the actual command from the Commands & Verification
      Inventory. For DARK phases: the achievable safety-ladder rung's evidence —
      captured seam/oracle snapshots, reversibility proven, smoke checklist
      passed — NOT a green test suite the component can't yet run.]
- [ ] [Include a parity/characterization check against the chosen oracle whenever
      the change is meant to preserve behavior.]
- [ ] [If purely additive, assert it: "no behavior/dependency/logic changed."]
- [ ] [If exiting below L4, state the residual risk and which later phase closes
      it (e.g., "client test gate deferred to the client-migration phase").]

排序原则:

  • 先定位可测试性里程碑,按组件 —— 把达到它的最小切片尽早排期,而不是把“可测试”推迟到最后,却在开始时要求门禁。
  • 在组件跨过自己的可测试性界线之前,绝不要要求它的自动化测试门禁。
  • 在路线图中命名 CI 里程碑。 明确说明哪个阶段搭建 CI(第一个点亮阶段,在可测试性里程碑处或之后),这样读者就不必猜测 CI 何时到来 —— 并标明强制执行它(必需状态检查/分支保护)是人工手动步骤,不是 Agent 任务。
  • 先建立第一阶段的安全,再改变行为 —— 在修改行为之前,建立可达到的最高梯级(不一定是完整的绿色门禁)。
  • 接下来是基础设施和横切关注点(认证、配置、日志、CI/CD)
  • 接下来是最高风险或最高价值的功能(尽早验证模式)
  • 最后是低风险、低耦合的功能(易于并行化的快速收益)
  • 数据迁移有独立的阶段,并附带回滚计划
阶段 6:执行治理

现代化计划失败在执行,而非纸面。内置治理,使每个阶段保持诚实和可回滚(跨任何生态通用):

  • 每个阶段一个分支。 切勿将阶段工作提交到默认分支。每个阶段开一个 PR;在合并前,让 PR 上的 CI 成为权威的绿色信号, 仅适用于 lit-regime 阶段。对于 dark-regime 阶段,PR 仍需携带该 阶梯等级的证据(捕获的契约、冒烟结果、可逆性脚手架)。
  • 在下一阶段之前合并到 trunk;切勿堆叠(H7)。 每个阶段分支都 从 trunk 切出,其 PR 在下一阶段开始之前合并到 trunk。切勿将阶段分支或其 PR 基于同级阶段分支——那正是 PR 相互合并、trunk 悄然落后 好几个阶段、后续阶段被迫陷入必须打破 禁止堆叠规则这一窘境的原因。在开始一个阶段之前,确认上一阶段已 合并到 trunk,且在创建分支时 git log origin/<trunk>..HEAD 为空。 如果受控堆叠确实不可避免,则仅允许在配有 必需的调和 PR 的情况下进行,该 PR 将堆叠落到 trunk 并附有明确的残余风险说明 ——切勿将其作为静默默认做法。
  • 确认 trunk 名称;停用遗留默认值。 锁定仓库的真实 trunk (例如 main),并且如果存在第二个类似默认的分支(master),将其标记为 "仅历史——不要作为目标",以免阶段工作落到错误的分支上。
  • 门禁与机制相匹配。 Lit 阶段凭绿色 CI 推进;dark 阶段 凭可实现的安全阶梯等级推进,并指明残余风险——而不是凭 "看起来完成了"的判断,也不是凭尚无法运行的测试套件。
  • 在 CI 里程碑建立 CI,并将强制执行移交给人工。 指明引入 CI 的阶段(第一个 lit 阶段)。区分两个 步骤:编写工作流文件是 Agent 可完成的;将其变为强制执行的 必需状态检查 / 分支保护(或 MR 批准)规则是需要手动进行的 平台 UI 步骤,Agent 无法执行。 在人工配置之前,CI 会运行但不会阻止合并——因此将"启用分支保护 / 必需检查"记录为明确的用户操作项(在 CI 阶段的退出 标准以及 §9 利益相关者决策中),切勿记录为已完成的 Agent 任务。
  • 保持接口不变且可独立部署。 每个阶段都让 系统保持可运行,因此回滚 = 重新部署上一版本。优先采用 保持接口不变的改写(将内部实现包装在稳定的接口之后),并 用接缝/预言机契约(在存在的地方使用黄金/特征化 测试)证明等价性。
  • 活计划。 在每个阶段的 退出标准满足时更新 MODERNIZATION_PLAN.md 状态标记——✅ 完成 / ⏭️ 缩减范围 / 🗑️ 放弃——记录 实现过程中做出的决策,并随着各组件跨过其可测试性 界限,更新安全阶梯等级 / 残余风险登记册。
  • 也让可执行文档保持鲜活(H8)。 活计划纪律不仅 是计划文件。任何改变拓扑的阶段——模块/反应器 成员关系、移除的服务、重命名的分支、变更的命令、新增/移除的 端点——都必须在同一个 PR 中更新 .github/copilot-instructions.md、README 以及任何 模块/拓扑列表。将这些文档更新视为该 阶段完成定义的一部分,而不是后续事项——一份过时的"隔离模块"列表 或一个硬编码的旧分支链接会主动误导下一个 Agent/人工。
  • 在实现每个阶段之前对其进行红队演练(阶段 2.5)。 对照 references/migration-hazards.md(H1–H8)走查阶段计划,并在编写代码之前将 修复纳入任务和退出标准。将已清除的检查清单记录在 该阶段的"已做出的决策"中。
  • 生成可编辑的指令。 根据 references/copilot-instructions.template.md 处的模板生成 .github/copilot-instructions.md (如需要则创建 .github/ 目录)——一个命令表加上 机制感知的阶段门禁和分支/PR 规则——以便执行 Agent/人工在 GitHub Copilot 自动加载的位置拥有规范命令和门禁。用户 将其编辑为自己的门禁。如果 .github/copilot-instructions.md 已 存在,不要覆盖它——将命令/门禁/分支部分合并进去,或 将生成的文件另存为 .github/copilot-instructions.modernization.md 并告知用户进行合并。
阶段 7:迁移安全网

记录所需的运维护栏:

  • 功能标志 — 哪些内容会被标记,以及旧/新如何共存
  • 数据迁移策略 — schema 变更、回填、双写时期。 对于带持久化卷的有状态数据存储重大升级(H5), 升级引擎镜像/标签不是迁移:选择并记录一条路径 —— 带步骤 + 回滚的顺序/功能兼容性版本升级,或 当数据是临时/演示性质时,明确的破坏性重置(删除卷、重新播种)—— 声明为一项有意为之的决策,并保留先前大版本的镜像 用于回滚。(例如,在既有 /data/db 卷上将 Mongo 3→7 升级,演示场景需要 down -v + 重新播种,真实数据则需要分阶段的 FCV 升级。)
  • 回滚计划 — 每个阶段,如果出现问题如何回退
  • 过渡期不安全状态登记册(H6) — 为保持系统在迁移中途可运行而 引入的每一个刻意弱化的状态(允许所有的 shim、禁用的 CSRF、开放的端点、 占位密钥)逐一列出:为何需要它、 关闭它的阶段、残余风险,以及面向评审者的“在阶段 N 之前属设计使然” 说明。尽可能收窄每一项弱化的范围(例如, 允许所有的链仍应显式禁用 form-login/HTTP-Basic)。 预先声明这些内容可将反复出现的扫描器/评审者反复纠缠转化为一项受跟踪的 决策。
  • Oracle 与接缝契约 — 哪个运行实例 / 记录的 I/O / 黄金 快照(或在无外部参照可用或不被允许时,采用自冻结黄金母版) 作为行为参照,以及新工作如何与其进行 diff
  • 测试策略 — 需要哪些新的测试覆盖(构建在目标 技术栈之上,随着组件跨越其可测试性线),以及哪些保持隔离
  • 可观测性 — 证明新系统与旧系统一致的指标/告警
输出结构

生成一个名为 MODERNIZATION_PLAN.md 的主 Markdown 文件,包含:

  1. 执行摘要(一段话:做什么、为什么、大致范围)
  2. 当前状态评估(来自架构文档)
  3. 可行性 spike 结果与策略 — 按组件:spike 发现、 A/B 迁移策略、定位到的可测试性里程碑、目标 安全阶梯层级,以及针对任何低于 L4 的内容的残余风险登记册; 再加上计划范围的 CI 里程碑(哪个阶段建立 CI),并附说明: 强制执行它是一项手动人工步骤
  4. 目标架构及 ADR
  5. 逐功能迁移分析(带可测试性状态 + 安全层级)
  6. 分阶段实施计划 — 第一阶段建立最高的可达成 安全层级(而非固定的绿色遗留门禁);每个阶段都标注为 可测试性之前/之后,并带有符合相应制度、受门禁控制的退出标准; CI 里程碑阶段被明确命名,并将“启用分支保护 / 必需状态检查”列为手动用户操作
  7. 执行治理(每阶段一分支、制度感知门禁、活计划状态)
  8. 迁移安全网(Oracle 与接缝契约、标志、回滚、可观测性)
  9. 待决问题 / 需要利益相关者做出的决策 —— 包括 Agent 无法 执行的任何手动平台配置交接(例如启用 必需状态检查 / 分支保护,以让 CI 成为受强制执行的门禁)

同时生成一个配套的 .github/copilot-instructions.md(GitHub Copilot 自动加载的路径;若 .github/ 不存在则创建它),源自 references/copilot-instructions.template.md,填入项目的 规范命令以及阶段门禁 / 分支规则,供执行该计划的人编辑和采用。 切勿覆盖已有的 .github/copilot-instructions.md —— 合并进去,或写一个同名并列的 .github/copilot-instructions.modernization.md 并向用户标明。

约定
  • 引用架构文档来指代当前状态,而不是重新复述全部内容。保持本文档面向未来。
  • 要有明确立场。 给出具体建议,而不是“你可以做 A 或 B”。说明你会选什么以及为什么。用户可以否决。
  • 务实地界定范围。 一个试图一次性改变所有内容的现代化计划只是空想。要明确哪些属于第一阶段,哪些属于“未来考虑项”。
  • 在规划期间解决所有阶段内决策。 阶段计划必须包含其实现所依赖的所有子决策,并且已经决定并记录(“已做出的决策”区块),这样该阶段才能在不回头找用户的情况下执行。对于被砍掉的内容,要明确说明是 “丢弃”还是“推迟”——二者并不相同。[DECISION NEEDED] 只保留给真正会阻塞某个阶段的利益相关方/业务选择(预算、团队规模、产品方向、时间线压力);在规划期间与用户一起推动这些决策落地,而不是把它们留在一个可实现的阶段中悬而未决。
  • 不要镀金。 如果某个组件在旧技术栈上运行良好且没有维护负担,“保持不动”就是一个有效的建议。
  • 默认死亡;让安全性自适应。 不要一开始就要求一个全绿的遗留 CI 门禁。运行可行性探针,选择可达到的最高安全阶梯层级,并将降级视为一种受认可的结果,同时点名剩余风险——绝不是失败。
  • 在时间中定位可测试性。 每个计划都必须按组件指明其可测试性里程碑,并且绝不要求某个组件在其自身跨过可测试性界线之前就具备自动化测试门禁。
  • 指明 CI 里程碑,将强制执行交给人工。 说明哪个阶段建立 CI(第一个点亮的阶段),并明确将 CI 变成强制必需检查/分支保护规则是人工平台步骤,Agent 无法完成——要把它作为用户行动项提出,而不是已完成任务。
  • 关注接缝,而非尸体。 将安全性锚定在外部可观察的契约(协议、schema、线缆/文件格式、端点)上,并使用排序的判定基准(运行实例 → 录制的 I/O → 代码即规范 → 当不存在或不允许外部参照时使用自冻结黄金主件)。不要浪费多个冲刺去复活一条已死的工具链,只是为了运行一个会在重写时被删除的测试。
  • 保持通用。 用项目自身的包管理器/测试运行器/CI 来表达命令和门禁——绝不要假定特定技术栈。
  • 在实现每个阶段前进行红队审查(H1–H8)。 将每个阶段计划对照 references/migration-hazards.md 检查,并先把修复纳入任务/退出标准——在规划中发现的隐患是一个任务;在评审中发现是返工;在生产中是事故。这些战术性隐患(不完整隔离、框架 codemod、运行时/基础镜像联动、路由类枚举、数据存储升级路径、过渡性不安全状态噪音、堆叠 PR/主干漂移、活文档漂移)正是仅靠战略性脚手架一直会遗漏的内容。

评论

支持 Markdown