目标不是取消 Code Review,而是建立一个真正的 SSOT:需求、业务规则和 BDD 场景聚合在同一份 Spec 中;技术实现和测试都直接围绕它工作,不再重复翻译和维护多份不同视角的副本。
在 Vibe Coding / Coding Agent 场景下,如果所有质量责任仍然压在“逐行人工读代码”上,Review 很快就会成为瓶颈。更深层的问题是:同一个需求往往被分别翻译成 PRD、研发理解、技术方案、测试用例和代码,信息在多次转述中不断漂移。
产品、研发、测试分别维护不同视角的副本。任何一次需求变化,都需要人工同步多份材料,越到后期越容易出现语义漂移。
Spec 本身同时承载需求、规则和 BDD 场景。实现代码直接实现 Spec,测试代码直接验证 Spec,不再额外维护一份测试视角的需求副本。
产品需求、业务规则、验收标准和 BDD 场景直接聚合在同一份 Specification 中。研发补充技术约束,测试补充边界与复杂场景,但不是各自复制一份需求,而是共同修改同一个 SSOT。
示例故意保持简单:连续输错密码 5 次后锁定账号 30 分钟。重点是看需求描述和 BDD 场景如何直接聚合在同一个 Spec 中,然后由实现代码和测试代码分别去实现、验证这一个 SSOT。
为了降低暴力破解风险,当用户连续输错密码达到阈值时,系统应临时锁定账号。
连续输错 5 次后锁定 30 分钟;锁定期间不能继续登录。
# SSOT Specification 点击左侧按钮,将原始需求直接沉淀到同一个 Spec: - 需求目标 - 业务规则 - BDD 场景 - 边界场景 - 需要人工确认的决策
左边不是额外维护的一份 BDD 文档,而是 SSOT Spec 中的场景片段;右边的测试代码直接对应这个片段。为了方便产品、研发、测试共同 Review,测试代码严格按照“假如 → 当 → 那么 → 并且”的顺序映射。
场景 AUTH-LOCK-001:第 5 次失败触发锁定 假如:用户已经连续失败 4 次 当:第 5 次密码校验失败 那么:账号立即锁定 30 分钟 并且:本次请求返回 locked=true
var _ = Describe("场景 AUTH-LOCK-001:第 5 次失败触发锁定", func() {
var (
时钟 *FakeClock
存储 *MemoryAttemptStore
策略 *LockoutPolicy
用户ID = "u-42"
)
Context("假如:用户已经连续失败 4 次", func() {
BeforeEach(func(ctx SpecContext) {
时钟 = NewFakeClock(
time.Date(2026, 8, 7, 10, 0, 0, 0, time.UTC),
)
存储 = NewMemoryAttemptStore()
策略 = NewLockoutPolicy(存储, 时钟, 5, 30*time.Minute)
for i := 0; i < 4; i++ {
已锁定, err := 策略.RecordFailure(ctx, 用户ID)
Expect(err).NotTo(HaveOccurred())
Expect(已锁定).To(BeFalse())
}
})
When("当:第 5 次密码校验失败", func() {
It("那么:账号立即锁定 30 分钟;并且:本次请求返回 locked=true",
func(ctx SpecContext) {
已锁定, err := 策略.RecordFailure(ctx, 用户ID)
Expect(err).NotTo(HaveOccurred())
// 那么:账号立即进入锁定状态
Expect(已锁定).To(BeTrue())
// 那么:锁定时间精确为 30 分钟
Expect(存储.LockedUntil(用户ID)).
To(Equal(时钟.Now().Add(30 * time.Minute)))
// 并且:本次请求返回 locked=true
Expect(已锁定).To(BeTrue())
},
)
})
})
})
Describe,方便测试报告直接显示业务场景。BeforeEach 中构造,且每一次都断言“尚未锁定”,保证前置状态本身也是可信的。When 中,它就是触发行为变化的动作。这里不要求业务同学理解每一行实现。真正需要对照的是:实现代码中的关键业务分支,是否直接对应同一份 Spec 中已经确认的规则,而不是对应研发自己重新解释的一份需求。
func (p *LockoutPolicy) RecordFailure(
ctx context.Context,
userID string,
) (bool, error) {
count, err := p.store.IncrementFailures(ctx, userID)
if err != nil {
return false, err
}
// 分支一:还没达到第 5 次失败,不锁定账号
if count < p.threshold {
return false, nil
}
// 分支二:达到第 5 次失败,计算 30 分钟锁定窗口
lockedUntil := p.clock.Now().Add(p.lockDuration)
// 分支三:把锁定状态写入共享存储
if err := p.store.Lock(ctx, userID, lockedUntil); err != nil {
return false, err
}
// 分支四:本次请求明确返回 locked=true
return true, nil
}
count <= 5,第 5 次失败仍返回 false。
“账号第 5 次失败应该锁定”已经定义在同一个 Spec 中。服务级 BDD、Integration 和 E2E 只是针对这份 SSOT 提供不同强度的验证证据,而不是分别维护三套需求描述。
这里关注的已经不是某个函数返回值,而是共享状态和副作用在真实并发下是否仍然正确。
var _ = Describe("场景 AUTH-LOCK-E2E-001:多实例并发失败仍只触发一次锁定事件", Ordered, func() {
var 测试环境 *TestEnv
BeforeAll(func(ctx SpecContext) {
// 假如:启动真实 Redis,并启动 3 个共享 Redis 的应用实例
测试环境 = StartTestEnv(
ctx,
WithRealRedisContainer(),
WithAppReplicas(3),
)
DeferCleanup(测试环境.Close)
})
Context("假如:3 个应用实例共享同一个 Redis", func() {
When("当:20 个请求同时提交错误密码", func() {
It("那么:账号最终被锁定;并且:安全事件只发送一次;并且:三个实例状态一致",
func(ctx SpecContext) {
const 并发数 = 20
用户ID := "u-concurrent"
var 等待组 sync.WaitGroup
for i := 0; i < 并发数; i++ {
等待组.Add(1)
go func() {
defer 等待组.Done()
_ = 测试环境.API.
RandomReplica().
Login(ctx, 用户ID, "错误密码")
}()
}
等待组.Wait()
// 那么:账号最终进入锁定状态
Eventually(func() bool {
状态, err := 测试环境.API.GetAccountState(ctx, 用户ID)
return err == nil && 状态.Locked
}).Should(BeTrue())
// 并且:安全锁定事件只发送一次
Eventually(func() int {
return 测试环境.RiskEvents.
Count("account_locked", 用户ID)
}).Should(Equal(1))
// 并且:三个实例读取到的锁定状态必须一致
Consistently(func() bool {
return 测试环境.AllReplicasAgreeLocked(ctx, 用户ID)
}).Should(BeTrue())
},
)
})
})
})
Spec 是唯一的需求与行为事实源。技术设计、实现代码、测试代码当然仍然存在,但它们不再保存一份自己的“需求副本”,而是通过场景 ID、规则 ID 或引用关系直接对齐到同一个 Spec。