一次 OpenSpec 使用记录

一、故事背景 (虚拟背景)

某天我看到新同事在做 “扣费灰度” 功能,但他没有复用项目里沉淀好的 GrayReleaseUtil,而是直接让 AI 生成了一套全新的灰度逻辑。
我心里很清楚:
“这样下去,项目里会有N套灰度工具,最后没人敢改。”
正好我在研究AI相关的一些东西,于是我决定,用 SDD 来规范这件事。

二、SDD / OpenSpec 极简解析

概念 一句话解释
SDD Spec-Driven Development(规格驱动开发):把需求、规则和系统现状写成可追踪的 Spec,不让 AI 瞎写。
OpenSpec 管理这些文件的工具。
规则层 (skills/) 项目长期稳定的规范(比如:灰度必须用 GrayReleaseUtil)。
变更层 (changes/) 某一次具体需求的记录。
全局唯一真相 (openspec/specs/) 系统当前状态的唯一权威描述(Source of Truth)。

三、操作流程

第 1 步:建立规则层

看到同事乱写后,我意识到项目缺了“规矩”文档。
我先在项目根目录创建了 skills/ 目录,把项目缺乏的规矩写成文件。
操作:

mkdir -p skills/gray-release
touch skills/gray-release/SKILL.md

编辑 skills/gray-release/SKILL.md:

---
name: gray-release
description: 新功能需要灰度发布 / 流量分桶 / AB 测试
allowed-tools: [Read, Edit, Bash, Grep]
---
# Gray Release · iOS 项目规范

## 项目事实(必须遵守)
- 统一使用 `GrayReleaseUtil` 类
- 路径:`Sources/Utils/GrayReleaseUtil.swift`
- 已稳定运行 2 年,禁止自研

## 标准用法
```swift
let isInGray = GrayReleaseUtil.route(featureKey: "feature_key")
if isInGray {
    // 新逻辑
} else {
    // 老逻辑
}
```

✅ 做完这一步,AI 才知道“灰度”在我们项目里意味着什么。

第 2 步:启动变更(New)

有了规矩,我开始做需求。
我执行 /opsx:new 命令,告诉 OpenSpec 我要开始一个新的变更了。
操作:

/opsx:new add-deduction-grey-release 
或者是 
/opsx:propose add-deduction-grey-release
// 这个命令会自动帮你把 openspec/changes/add-deduction-grey-release/目录
// 以及里面的 proposal.md, design.md, tasks.md全部建好

结果:
OpenSpec 自动创建了变更目录结构(此时 openspec/specs/ 还是旧内容)。

第 3 步:编写 Proposal 和 Design

我打开 openspec/changes/add-deduction-grey-release/ 目录,开始写文档。
3.1 Proposal.md(定方向)

## Why
新扣费算法涉及资金安全,需灰度开关以降低全量风险。

## What Changes
- 在 `BillingService.swift` 的 `processDeduction()` 入口接入灰度判断。
- 新增配置中心开关 `deduction_v2_ios_20260115`。
- 新逻辑走 `DeductionEngineV2`,老逻辑保留。

## Capabilities
### Modified Capabilities
- `billing-calculation`: 扣费入口新增灰度分支。

## Impact
- **Reuses**: `skills/gray-release/SKILL.md`
- **依赖**: `GrayReleaseUtil`

✅ 关键点: 我在 Impact 里显式引用了刚才创建的 Skill。

3.2 Design.md(定方案)

## 技术选型
- 灰度工具:`GrayReleaseUtil`
- 配置中心:Firebase Remote Config

## 逻辑设计
- App 启动时拉取灰度配置并缓存。
- 扣费前判断灰度状态,分发至对应引擎。
- 新引擎 `DeductionEngineV2` 独立封装。

第 4 步:AI 自动加载与写代码(Apply)

文档写完后,我让 AI 开始干活。
因为我在 Proposal 里引用了 Skill,AI 会自动加载 skills/gray-release/SKILL.md。
操作:

/opsx:apply

发生了什么:

  1. AI 读取 proposal.md 和 design.md。
  2. AI 自动加载 skills/gray-release/SKILL.md。
  3. AI 知道必须用 GrayReleaseUtil,没有瞎写新逻辑。
  4. AI 按 tasks.md 清单修改 BillingService.swift。
  5. 生成 execute.log 记录过程。

第 5 步:归档与完善规则(Archive)

代码上线后,一切顺利。我执行归档,把这次变更合并到全局真相,并完善了规则层。
操作:

/opsx:archive

结果:

  1. 更新全局唯一真相:openspec/specs/billing-calculation/spec.md 变成了包含灰度逻辑的新版本。
  2. 归档变更目录:OpenSpec 会把本次 change 从 active changes 移动到 openspec/changes/archive/YYYY-MM-DD-add-deduction-grey-release/
  3. 完善规则层:基于这次的实现经验,我补充了 skills/gray-release/SKILL.md 的细节(v1.1),让规则更清晰。

补充说明:

  • /opsx:sync 负责把 change 里的增量 spec 同步到 openspec/specs/,但 change 仍然是 active 状态。
  • /opsx:archive 是收尾动作:如果主 spec 还没同步,它会提示或处理同步,然后把 change 移入 archive 历史目录。

完善后的 skills/gray-release/SKILL.md:

## 最佳实践
- ✅ 灰度状态在 ViewModel 初始化时判断一次,缓存结果。
- ✅ 支付按钮需处理重复点击,确保单次操作唯一。

✅ 规则层更完善了,下次新同事做灰度,AI 会自动用对。

四、目录变化(全流程)

1️⃣ 开发前(初始状态)

MyApp/
├── Sources/
│   └── Billing/
│       └── BillingService.swift     # 只有老扣费逻辑
│
├── skills/                           # ❌ 还没有灰度规范
├── openspec/
│   ├── specs/                        # 全局唯一真相(旧版)
│   │   └── billing-calculation/
│   │       └── spec.md
│   └── changes/                     # 空
├── AGENTS.md
└── Package.swift

2️⃣ 开发中(执行 /opsx:new 后)

MyApp/
├── Sources/
│   └── Billing/
│       └── BillingService.swift     # 正在改
│
├── skills/                           # ✅ 规则层(我第一步创建的)
│   └── gray-release/
│       └── SKILL.md                  # 规定:必须用 GrayReleaseUtil
│
├── openspec/
│   ├── specs/                        # 仍是旧真相
│   │   └── billing-calculation/
│   │       └── spec.md
│   │
│   └── changes/                      # ✅ 新增变更目录
│       └── add-deduction-grey-release/
│           ├── proposal.md           # 为什么做
│           ├── design.md             # 怎么做
│           ├── tasks.md              # 具体任务
│           ├── execute.log           # AI 执行记录
│           └── specs/                # 本次增量改动
│               └── billing-calculation/   ✅ Capability 同名
│                   └── spec.md       # 只写新增灰度规则
├── AGENTS.md
└── Package.swift

3️⃣ 开发后(归档完成)

MyApp/
├── Sources/
│   └── Billing/
│       └── BillingService.swift     # ✅ 已合入灰度
│
├── skills/
│   └── gray-release/
│       └── SKILL.md                 # ✅ 规则沉淀(v1.1,更完善)
│
├── openspec/
│   ├── specs/                        # ✅ 已更新为全局唯一真相
│   │   └── billing-calculation/
│   │       └── spec.md
│   │
│   └── changes/
│       └── archive/                  # ✅ 历史档案
│           └── 2026-05-13-add-deduction-grey-release/
│               ├── proposal.md
│               ├── design.md
│               ├── tasks.md
│               ├── execute.log
│               └── specs/
│                   └── billing-calculation/
│                       └── spec.md
├── AGENTS.md
└── Package.swift

五、SDD 的 8 步

  1. 先有规则层
    把项目规范(比如开关功能)写成 SKILL.md。
  2. 启动 change
    执行 /opsx:new add-deduction-grey-release。
  3. AI 自动加载 skill
    AI 读到 SKILL.md,知道不能乱写,因为 description (描述)匹配
  4. 写 proposal (提案)
    显式引用 skills/gray-release/SKILL.md。
  5. Capability 同名是关键
    billing-calculation 在 proposal 声明处、change specs 目录处、主 spec 目录处 这三处必须同名。
  6. AI 按 tasks 写代码
    执行 /opsx:apply,AI 严格复用 GrayReleaseUtil。
  7. Archive 回写 skill
    执行 /opsx:archive,规则层从 v1.0 升到 v1.1(更完善)。
  8. AI 自动用对
    新同事下次做灰度,AI 自动用对。

六、特别说明

这里的 Skill,说的是「规矩」不是「本事」
SDD / OpenSpec 里写在 SKILL.md 里的 Skill,指的是团队定死的规矩:这件事必须怎么做、哪些做法一律不许。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容