Feature Chain — 用一个 operationId 追踪全栈

修改一个功能需要动哪些文件?输入一个 operationId,答案即刻呈现。


问题

在全栈应用中,“一个功能"不会只存在于一个文件里。

假设你需要修改"接受提案(AcceptProposal)“这个功能。它存在于 API 规范中、服务逻辑中、数据库 schema 中、授权策略中、状态转移图中、外部函数调用中、测试场景中,以及前端中。

过去,要搞清楚这些完整范围,只有两种方法:

  1. 反复执行 Grep 数十次
  2. 手动追踪代码

两种方法都慢,都会遗漏。尤其是跨层引用——从 API 规范到服务层、从服务层到 DB schema、从服务层到授权策略——靠人工不遗漏地追踪,现实上几乎做不到。

但如果各层的源文件已经在符号层面相互引用,那么只需顺着这些引用走,完整范围就会自动浮现。


Feature Chain 是什么

Feature Chain 是从一个 API 功能(operationId)出发,提取所有关联源节点的工具。

以一个 operationId 为起点,沿源文件间的符号引用追踪,一次性输出所有相关文件及行号。数十次 Grep 被一条命令取代。

fullend chain AcceptProposal specs/
── Feature Chain: AcceptProposal ──

  OpenAPI    api/openapi.yaml:296                          POST /proposals/{id}/accept
  SSaC       service/proposal/accept_proposal.ssac:19      @get @empty @auth @state @put @call @post @response
  DDL        db/gigs.sql:1                                 CREATE TABLE gigs
  DDL        db/proposals.sql:1                            CREATE TABLE proposals
  DDL        db/transactions.sql:1                         CREATE TABLE transactions
  Rego       policy/authz.rego:3                           resource: gig
  StateDiag  states/gig.md:7                               diagram: gig → AcceptProposal
  StateDiag  states/proposal.md:6                          diagram: proposal → AcceptProposal
  FuncSpec   func/billing/hold_escrow.go:8                 @func billing.HoldEscrow
  Gherkin    scenario/gig_lifecycle.feature:4              Scenario: Happy Path - Full Gig Lifecycle
  Gherkin    scenario/gig_lifecycle.feature:42             Scenario: Unauthorized Access

一个功能的完整结构尽在一屏之内。未关联的层不会出现在输出中。


为何可行

这不是魔法。之所以可行,是因为源文件之间本来就存在相互引用。

在 fullend 框架中,各 SSOT(Single Source of Truth)层会以符号方式指向其他层:

  • SSaC@get Model.Method → DDL 表
  • SSaC@auth action resource → Rego 授权策略
  • SSaC@state diagramID → Mermaid 状态图
  • SSaC@call pkg.Func → Func Spec 实现体
  • OpenAPI 的 operationId → SSaC 文件名
  • Gherkin 的 action step → operationId
  • STML 的 endpoint → OpenAPI path

这些引用最初是为 crosscheck——验证各 SSOT 之间一致性的工具——而设计的。由于 crosscheck 已经在解析这些引用,复用同一套基础设施,仅需图遍历便可提取出 feature chain。


遍历路径

以 operationId 为起点,引用图展开如下:

operationId (起点)
├── OpenAPI → path + method
├── SSaC → 服务函数文件
│   ├── @get → DDL 表
│   ├── @auth → Rego 策略规则
│   ├── @state → Mermaid stateDiagram 转移
│   ├── @call → Func Spec 实现体
│   └── @publish → 队列订阅者
├── Gherkin → 引用该 operationId 的场景
└── STML → 引用该 endpoint 的前端文件

每个分支只跟随一层符号引用。这是广度优先而非深度优先的遍历。一个功能在哪些层留下了痕迹,一览无余。


它改变了什么

确定修改范围

“修改这个功能需要动哪里?”

为了回答这个问题,我们要读代码、跑 Grep、问同事。Feature Chain 用一条命令取代了这一切。只要符号引用存在,就不会遗漏任何文件。

AI 代码修改

让 AI 修改功能时,最难的事是"告知修改范围”。需要把所有相关文件都放进上下文窗口,AI 才能准确修改,而判断哪些文件相关,本身就要靠人。

有了 Feature Chain,情况就不同了。只需提供一个 operationId,完整的修改范围就会自动识别出来。交给 AI 的上下文,不再有遗漏。

代码审查

在 review PR 时,一旦怀疑"修改了这个功能,那个文件是不是也该改?",只需对照 chain 即可立刻确认。层间的不一致,由结构而非人来捕获。

新人上手

当新加入的开发者说"我想了解 AcceptProposal 是怎么运作的”,把一个 Feature Chain 给他看就够了。从 API 到 DB、从授权策略到测试场景——功能的全貌一目了然。


为何选择 operationId

起点的选择至关重要。Feature Chain 选择了 operationId。

原因很简单。在全栈应用中,功能的单位是 API endpoint。用户点击按钮,API 被调用,API 执行服务逻辑,读取 DB,校验授权,触发状态转移。这一切流程的起点,就是 operationId。

operationId 已经在 OpenAPI 规范中定义,是全团队共享的名称。说"需要修改 AcceptProposal",后端开发者、前端开发者、QA 想到的都是同一件事。用这个通用的名称贯穿整个技术栈,是 Feature Chain 的设计初衷。


前提条件

Feature Chain 能够运作,需要满足前提:

  1. 源文件之间必须以符号方式相互引用。 SSaC 的 @get@auth@state@call 指令必须显式指向其他 SSOT。如果引用是隐式的——例如 DB 表名只以字符串形式存在于代码中——则无法追踪。

  2. 必须通过 crosscheck。 Feature Chain 沿符号引用遍历,若引用无效则会产生错误结果。crosscheck 保证引用的一致性,Feature Chain 在此基础上进行遍历。

这就是 Feature Chain 不是通用工具的原因。它无法应用于任意项目。只有在源文件间引用经过设计的项目中——如 fullend 这种各 SSOT 显式相互指向的结构——才能运作。


未来:GEUL + SILK

目前,Feature Chain 通过逐一遍历各 SSOT 解析器进行探索,分别调用 SSaC 解析器、DDL 解析器、Rego 解析器、Mermaid 解析器,再汇总结果。

当所有 SSOT 都转换为 GEUL 图之后,Feature Chain 将变成单一索引查询。通过 SILK 的 SIDX 按位 AND 运算,一次查询即可提取与 operationId 关联的所有节点。

从逐解析器遍历到图查询。Feature Chain 的接口不变,但内部将发生根本性的变化。


小结

在全栈应用中,“功能"不在一个文件里。它跨越多个层,而这些层相互引用。Feature Chain 顺着这些引用,自动提取一个功能的完整范围。

只需输入一个 operationId,从 API 规范到 DB schema、从授权策略到测试场景——数十次 Grep 被一条命令取代。

代码:github.com/park-jun-woo/fullend