用 BDD 驯服 vibe coding:Go + 大模型的最小可行实践

先把话说清:vibe coding 是什么

vibe coding 指的是一种强依赖大模型的编程方式:你用自然语言描述目标,模型生成代码,你更多依赖运行结果而不是逐行理解代码。这个说法来自 Andrej Karpathy 在 2025 年初的公开描述,随后被社区广泛引用。(Wikipedia: Vibe coding)

它的好处是速度快、试错成本低;问题是行为不稳定、回归成本高。你写得很快,但不知道系统到底“保证了什么”。

为什么 BDD 反而在这个时代更重要

BDD 的核心是“可执行的行为规格”。它把需求变成 Given / When / Then 这样的场景,不只是文档,而是能跑的验收标准。Dan North 在 2006 年把这一套从 TDD 里抽出来,强调“行为”比“测试”更有用,需求本身也应该是可执行的验收标准。(Introducing BDD)

Gherkin 规范把这套结构标准化:Given 是前置状态,When 是动作,Then 是可观察结果。(Gherkin Reference)

对 vibe coding 来说,这等于给“模型生成代码”配了一条安全带:

  • 先写清楚行为
  • 让模型生成实现
  • 用场景验收
  • 行为不对就回到场景,而不是乱改代码

实战:Go + Godog + LLM 的最小工作流

Go 里最主流的 BDD 实现是 Cucumber 官方维护的 Godog。(cucumber/godog)

1) 写行为规格(Gherkin)

features/discount.feature

Feature: discount

  Scenario: VIP gets 10% off
    Given a user is VIP
    And the cart total is 200
    When I calculate the price
    Then the final price should be 180

这不是测试代码,这是需求。任何人都能看懂。

2) 让 LLM 生成步骤定义(你可以直接用下面的提示)

你是 Go 工程师。基于以下 Gherkin 场景生成 godog 步骤定义:
- 每个 Step 用正则匹配
- 使用一个上下文结构体保存状态
- Then 阶段断言计算结果
输出 Go 代码:add_test.go

模型会生成类似下面的结构。你只要把业务逻辑补进去即可。

3) 步骤定义(Go + Godog)

add_test.go

package calc

import (
    "fmt"
    "testing"

    "github.com/cucumber/godog"
)

type ctx struct {
    isVIP bool
    total int
    final int
}

func (c *ctx) userIsVIP() error {
    c.isVIP = true
    return nil
}

func (c *ctx) cartTotalIs(total int) error {
    c.total = total
    return nil
}

func (c *ctx) calcPrice() error {
    discount := 0.0
    if c.isVIP {
        discount = 0.1
    }
    c.final = int(float64(c.total) * (1.0 - discount))
    return nil
}

func (c *ctx) finalShouldBe(expect int) error {
    if c.final != expect {
        return fmt.Errorf("expected %d, got %d", expect, c.final)
    }
    return nil
}

func TestFeatures(t *testing.T) {
    c := &ctx{}
    godog.TestSuite{
        ScenarioInitializer: func(s *godog.ScenarioContext) {
            s.Step(`^a user is VIP$`, c.userIsVIP)
            s.Step(`^the cart total is (d+)$`, c.cartTotalIs)
            s.Step(`^I calculate the price$`, c.calcPrice)
            s.Step(`^the final price should be (d+)$`, c.finalShouldBe)
        },
        Options: &godog.Options{
            Format:   "pretty",
            Paths:    []string{"features"},
            TestingT: t,
        },
    }.Run()
}

这套结构与官方 README 一致(godog.TestSuite + ScenarioInitializer)。(cucumber/godog)

4) 运行

go test -test.v -test.run ^TestFeatures$

关键点:用 BDD 驯服 LLM,而不是让它决定行为

在 vibe coding 里,最大的问题不是“代码能不能跑”,而是“行为是否稳定”。BDD 的实用价值就是把“可执行行为”前置,让模型围绕行为去产出。

一个可落地的流程是:

  • 业务先写 3 个场景
  • LLM 生成步骤定义
  • LLM 生成实现逻辑
  • 场景不通过就改场景/实现
  • 场景通过才允许合并

这能显著降低“生成代码很爽,但后期维护很崩”的风险。

结尾:vibe coding 不是答案,行为才是

vibe coding 会让你更快做出东西,但只有行为规格能让东西“可持续”。

如果你不想被模型牵着走,就用 BDD 先定义边界,再让模型去填细节。


参考资料:
Vibe coding(Wikipedia)
Introducing BDD(Dan North)
Gherkin Reference(Cucumber)
cucumber/godog(GitHub)

抢沙发

评论前必须登录!

立即登录   注册