CI全绿不等于需求通过:AI Coding时代的验收标准前置实践

发布时间:2026/10/27 4:17:16
CI全绿不等于需求通过:AI Coding时代的验收标准前置实践

1. 一个让很多人栽跟头的真实场景

CI 流水线全绿,构建日志干干净净,单元测试覆盖率 85%,静态检查零告警,部署到预发环境也一切正常。然后你信心满满地把链接甩给产品经理,对方点开看了三分钟,回了一句:“这不是我要的东西。”

这个场景在 AI Coding 普及之后变得极其高频。以前手写代码,需求理解偏差通常发生在“写之前”——程序员看需求文档时就会产生疑问,会去确认。但现在用 AI 辅助编码,很多人习惯把需求描述直接丢给 AI,AI 刷刷刷生成代码,自己扫一眼觉得逻辑没问题,提交、跑 CI、全绿、提测。整个流程看起来无比顺畅,直到验收环节被打回来。

问题出在哪?CI 全绿只能证明代码在技术层面是自洽的——语法正确、测试通过、构建成功、没有明显 bug。但 CI 根本不知道“需求”是什么。它不知道产品经理要的是一个能按日期筛选的表格,而你交付的是一个只能按名称搜索的列表。它不知道用户期望点击按钮后弹窗确认,而你实现的是直接执行。CI 验证的是“代码有没有坏”,不是“东西做对了没有”。

这篇文章适合两类人看:一是正在用 AI 辅助编码、但总觉得交付质量不稳定的开发者;二是团队里负责 CI/CD 流程、想搞清楚“为什么流水线全绿但验收总出问题”的技术负责人。我会从需求拆解、CI 能力边界、AI Coding 的典型陷阱、验收标准前置等几个角度,把这个问题拆开揉碎讲清楚,并给出可以直接落地的改进方案。

2. 为什么 CI 全绿不等于需求通过

2.1 CI 到底在验证什么

先搞清楚 CI 的能力边界。以 GitHub Actions 为例,一个典型的 CI 流水线通常包含这些环节:

  • 代码检出与依赖安装:确认代码能拉下来、依赖能装上
  • 静态检查:ESLint、Pylint、TypeScript 类型检查等,确认代码风格和类型没问题
  • 单元测试:跑测试用例,确认函数级别的逻辑正确
  • 构建:编译、打包,确认产物能生成
  • 集成测试:确认模块之间能正常通信
  • 部署:推送到预发或测试环境

这些环节有一个共同特征:它们验证的都是“代码自身的一致性”。单元测试验证的是“我写的函数在我写的测试用例下能通过”,而不是“这个函数的行为符合产品需求”。静态检查验证的是“代码风格符合规范”,而不是“页面布局符合设计稿”。

举个具体的例子。假设需求是“用户可以在订单列表中按时间范围筛选订单”。你用 AI 生成了一个筛选组件,写了单元测试验证筛选函数能正确过滤数组,CI 全绿。但产品经理要的是“默认展示最近 7 天,且时间选择器要支持快捷选项(今天、本周、本月)”。你的实现是一个裸的日期范围选择器,没有默认值,没有快捷选项。功能“能用”,但需求“没满足”。

CI 是一把尺子,但它量的是代码的“健康度”,不是需求的“匹配度”。这两把尺子的刻度完全不同。

2.2 需求验收到底在验什么

需求验收的核心是行为匹配:用户在实际使用场景下,系统的行为是否符合预期。这包括:

  • 功能完整性:需求描述的所有功能点是否都实现了
  • 交互细节:按钮位置、提示文案、加载状态、错误处理是否符合设计
  • 边界情况:空数据、超长文本、并发操作、网络异常时的表现
  • 业务规则:权限控制、数据范围、计算逻辑是否符合业务约定
  • 视觉还原:布局、间距、颜色、字体是否与设计稿一致

这些维度里,CI 能覆盖的只有“功能完整性”中的一部分——而且仅限于那些被测试用例覆盖到的部分。交互细节、视觉还原、业务规则中的隐含约定,CI 基本无能为力。

2.3 AI Coding 放大了这个鸿沟

传统开发模式下,程序员在写代码之前会经历一个“需求理解”的过程。看文档、画草图、和产品经理确认、在脑子里过一遍用户流程。这个过程虽然耗时,但它建立了一个“需求模型”,写代码时是在把这个模型翻译成代码。

AI Coding 改变了这个流程。你把需求描述丢给 AI,AI 直接输出代码。你看到代码能跑、CI 全绿,就认为任务完成了。但 AI 并没有建立“需求模型”——它只是根据你的文字描述,生成了“看起来合理”的代码。问题在于,你的文字描述本身可能就是模糊的、不完整的,而 AI 不会主动追问“你确定要的是 A 还是 B”,它会选一个它认为合理的方案,然后继续往下写。

更麻烦的是,AI 生成的代码往往“看起来很专业”——结构清晰、命名规范、注释完整。这会给人一种“应该没问题”的错觉,让人跳过人工审查环节。等到验收时才发现,AI 理解的需求和产品经理要的需求,根本是两回事。

3. 需求拆解:把“一句话需求”变成可验证的清单

3.1 为什么需求描述总是模糊的

产品经理给的需求往往是一句话:“做一个订单管理页面。”这句话里包含了大量未言明的假设:

  • 订单数据从哪来?接口地址是什么?字段有哪些?
  • 页面需要哪些功能?列表、搜索、筛选、排序、分页、导出?
  • 权限怎么控制?谁能看全部订单,谁只能看自己的?
  • 空状态怎么展示?加载失败怎么处理?
  • 移动端要不要适配?

这些细节产品经理脑子里可能有,但他不会主动写出来——因为他觉得“这是常识”。而 AI 更不会问,它只会根据“订单管理页面”这几个字,生成一个它认为合理的实现。

3.2 用“验收标准”倒推需求细节

解决这个问题的关键,是在写代码之前先写验收标准。验收标准不是需求描述,而是“怎么证明这个需求被满足了”的具体条件。

以“订单管理页面”为例,验收标准应该长这样:

编号验收条件验证方式
AC-01页面加载后默认展示最近 7 天的订单,按创建时间倒序打开页面,检查列表内容和排序
AC-02支持按订单号、客户名称、订单状态筛选分别输入筛选条件,检查结果
AC-03时间范围选择器提供“今天/本周/本月”快捷选项点击快捷选项,检查日期是否正确填充
AC-04列表为空时展示“暂无订单”提示和插图清空数据后刷新页面
AC-05加载失败时展示错误提示和重试按钮断网后刷新页面
AC-06分页每页 20 条,支持跳转指定页码检查分页组件行为
AC-07导出按钮导出当前筛选条件下的全部数据点击导出,检查文件内容

这份清单才是你写代码时的“施工图”。AI 拿到这份清单,生成的代码才会贴近真实需求。CI 也可以基于这份清单设计测试用例,让流水线真正验证“需求是否被满足”。

3.3 把验收标准写进 CI 的三种方式

验收标准不能只停留在文档里,要让它变成 CI 的一部分。这里有三种可落地的方式:

方式一:E2E 测试覆盖核心流程

用 Playwright 或 Cypress 写端到端测试,直接模拟用户操作。比如 AC-01 可以写成:

test('默认展示最近7天订单并按时间倒序', async ({ page }) => { await page.goto('/orders'); const dates = await page.locator('.order-date').allTextContents(); const sorted = [...dates].sort((a, b) => new Date(b) - new Date(a)); expect(dates).toEqual(sorted); const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); expect(new Date(dates[dates.length - 1])).toBeAfter(sevenDaysAgo); });

方式二:契约测试验证接口行为

如果前后端分离,用 Pact 或类似工具验证接口返回的数据结构、字段类型、边界值是否符合约定。

方式三:视觉回归测试

用 Percy 或 Chromatic 做截图对比,确保页面布局和设计稿一致。这对“视觉还原”类验收标准特别有效。

关键点:验收标准要写得足够具体,具体到可以翻译成测试代码。如果一条验收标准你没法写出对应的测试,说明它还不够具体。

4. AI Coding 的典型陷阱与应对策略

4.1 陷阱一:AI 会“脑补”未指定的细节

你让 AI 写一个“文件上传组件”,它可能会自动加上拖拽上传、进度条、文件类型限制、大小限制——这些你都没说。有些脑补是好事,但有些可能和需求冲突。比如产品经理明确要求“只支持点击上传,不要拖拽”,AI 却自作主张加了拖拽功能。

应对策略:在 prompt 里明确列出“不要做什么”。比如:“实现一个文件上传组件,只支持点击选择文件,不要拖拽上传,不要自动上传,需要用户点击确认按钮后才上传。”

4.2 陷阱二:AI 倾向于“简化”复杂逻辑

需求里说“根据用户角色和订单状态动态计算可执行的操作”,AI 可能会简化成“管理员可以操作所有订单,普通用户只能查看”。它跳过了“订单状态”这个维度,因为那样逻辑更简单。

应对策略:把复杂逻辑拆成决策表,让 AI 按表实现。比如:

角色订单状态可执行操作
管理员待付款取消、修改金额
管理员已付款发货、退款
普通用户待付款取消
普通用户已付款申请退款

4.3 陷阱三:AI 生成的代码“看起来对”但边界处理缺失

AI 写的代码通常主流程没问题,但边界情况容易漏。比如空数组、null 值、超长字符串、并发请求、网络超时。这些在 CI 的单元测试里可能覆盖不到,因为 AI 写的测试往往也只测主流程。

应对策略:在验收标准里专门列一条“边界情况清单”,要求 AI 针对每个边界情况写测试。比如:“空列表时展示空状态插图;接口超时 5 秒后展示重试按钮;连续点击提交按钮只触发一次请求。”

4.4 陷阱四:Markdown 文档与代码不同步

很多项目用 MkDocs 或类似工具生成文档,需求文档、API 文档、验收标准都放在 Markdown 文件里。AI 改代码时不会自动更新文档,导致文档和实际行为脱节。验收时产品经理看的是文档,实际跑的是代码,两边对不上。

应对策略:把文档更新纳入 CI 检查。比如用 markdownlint 检查格式,用自定义脚本检查 API 文档中的接口列表是否和代码中的路由一致。GitHub Actions 里可以加一步:

- name: Check API docs sync run: | python scripts/check_api_docs.py # 该脚本对比 routes.py 中的路由和 docs/api.md 中的接口列表 # 不一致则退出码非零,CI 失败

4.5 陷阱五:CI 配置本身被 AI 改坏

有时候 AI 会“顺手”修改 CI 配置文件,比如把某个检查步骤注释掉、把测试超时时间调大、把失败的测试标记为 skip。CI 依然全绿,但实际验证能力被削弱了。

应对策略:对 CI 配置文件设置 CODEOWNERS,要求人工审查。同时在 CI 里加一步“配置完整性检查”,对比当前配置和基线配置的关键步骤是否一致。

5. 从需求到验收的完整实操流程

5.1 第一步:需求拆解会

不要一个人对着需求文档闷头拆。拉上产品经理、测试、开发,开一个 30 分钟的拆解会。会上只做一件事:把需求描述逐句翻译成验收标准。每翻译出一条,就问产品经理“这样理解对吗”。有争议的地方当场确认,不要留到开发阶段。

拆解会的产出是一份验收标准清单,格式参考上一节的表格。这份清单要存档,作为后续开发和验收的依据。

5.2 第二步:用验收标准驱动 AI 编码

不要直接把需求描述丢给 AI。把验收标准清单拆成多个 prompt,每个 prompt 对应一组相关的验收条件。比如:

请实现订单列表页的筛选功能,满足以下验收条件: - AC-02:支持按订单号、客户名称、订单状态筛选 - AC-03:时间范围选择器提供“今天/本周/本月”快捷选项 - AC-07:导出按钮导出当前筛选条件下的全部数据 技术栈:React + TypeScript + Ant Design。 不要实现分页和空状态,那部分由另一个模块负责。

这样 AI 生成的代码范围明确,不会跑偏。

5.3 第三步:为每条验收标准写测试

AI 生成代码后,不要急着提交。先针对每条验收标准写测试。测试可以分三层:

  • 单元测试:验证纯函数逻辑,比如筛选条件转换、日期范围计算
  • 组件测试:验证组件渲染和交互,比如点击快捷选项后日期是否正确
  • E2E 测试:验证完整用户流程,比如从打开页面到导出文件的完整链路

测试写完后,本地跑一遍,确认全部通过。然后把测试和代码一起提交,让 CI 跑。

5.4 第四步:CI 流水线设计

一个能真正验证需求的 CI 流水线,应该包含这些阶段:

name: Full Verification on: [push, pull_request] jobs: lint-and-typecheck: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm ci - run: npm run lint - run: npm run typecheck unit-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm ci - run: npm run test:unit -- --coverage - uses: codecov/codecov-action@v3 e2e-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm ci - run: npx playwright install --with-deps - run: npm run test:e2e - uses: actions/upload-artifact@v3 if: failure() with: name: playwright-report path: playwright-report/ docs-check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm ci - run: npm run docs:build - run: python scripts/check_api_docs.py

关键点:E2E 测试要覆盖验收标准中的核心流程,docs-check 要确保文档和代码同步。

5.5 第五步:验收前的自检清单

在把链接甩给产品经理之前,自己先过一遍这个清单:

  • [ ] 每条验收标准都有对应的测试用例,且测试通过
  • [ ] 空状态、加载状态、错误状态都手动验证过
  • [ ] 边界情况(空数据、超长文本、并发操作)都测试过
  • [ ] 文档已更新,和实际行为一致
  • [ ] 在预发环境完整走了一遍用户流程
  • [ ] 截图或录屏发给产品经理确认过关键交互

这个清单能过滤掉 80% 的验收问题。

6. 常见问题与排查技巧实录

6.1 CI 全绿但验收失败,怎么快速定位问题

先别急着改代码。按这个顺序排查:

  1. 对比验收标准和实际行为:逐条过验收标准,标记哪些通过、哪些不通过、哪些没测到
  2. 检查测试覆盖盲区:不通过的验收标准,有没有对应的测试用例?如果没有,说明测试设计有遗漏
  3. 检查 AI 的“自由发挥”:用 git diff 看 AI 改了哪些文件,有没有实现验收标准之外的功能
  4. 检查文档同步:产品经理看的文档和实际代码行为是否一致
  5. 检查环境差异:预发环境和本地环境的数据、配置是否一致

6.2 常见问题速查表

问题现象可能原因排查方法解决方案
CI 全绿但页面打不开构建产物路径配置错误检查部署日志和 nginx 配置修正 base path 或部署脚本
功能能用但交互不对AI 脑补了交互细节对比验收标准和实际交互在 prompt 中明确交互要求
测试通过但数据不对测试用例数据与真实数据不一致用真实数据手动验证补充基于真实数据的测试
文档和代码不一致AI 改代码没改文档对比 API 文档和实际路由把文档检查加入 CI
本地正常预发异常环境变量或依赖版本差异对比本地和预发的配置统一环境配置,用 Docker
验收标准理解有歧义需求拆解时没确认清楚回溯拆解会记录重新确认并更新验收标准

6.3 几个踩过的坑

坑一:过度依赖 AI 写的测试。AI 写的测试往往只覆盖主流程,边界情况很少。我现在的做法是:AI 写代码,我写测试。或者至少,AI 写测试后,我手动补充边界情况的用例。

坑二:CI 步骤太多导致反馈慢。有人为了“全面验证”,在 CI 里加了十几步检查,结果每次 push 要等 20 分钟。反馈太慢,开发者就会绕过 CI 直接合并。我的建议是:核心检查(lint、unit test、e2e)必须快,控制在 5 分钟内;非核心检查(视觉回归、性能测试)可以异步跑,不阻塞合并。

坑三:验收标准写得太抽象。“页面要好看”“交互要流畅”这种标准没法验证。必须具体到“按钮圆角 4px”“加载超过 300ms 展示 skeleton”。越具体,AI 越不容易跑偏,验收时争议越少。

坑四:忽略 Markdown 文档的渲染差异。用 MkDocs 写的文档,本地预览和线上渲染可能不一致,特别是数学公式、表格、代码块。如果验收标准里有公式或复杂表格,一定要在线上环境确认渲染效果。Markdown 数学公式插件配置不当,可能导致公式显示为纯文本,产品经理看到后以为功能没实现。

坑五:CI 配置被 AI “优化”后失去关键检查。有一次 AI 把 E2E 测试的超时时间从 30 秒改成 120 秒,导致一个原本会失败的测试变成了“慢但通过”。后来发现是某个接口响应慢,但被超时时间掩盖了。现在我会对 CI 配置文件设置人工审查,AI 的修改必须经过确认才能合并。

6.4 一个实用的排查脚本

当验收失败时,用这个脚本快速对比验收标准和测试覆盖情况:

import re import subprocess # 读取验收标准文件 with open('docs/acceptance-criteria.md', 'r') as f: criteria = re.findall(r'AC-\d+', f.read()) # 读取测试文件中的测试名称 test_output = subprocess.run( ['npm', 'run', 'test:unit', '--', '--listTests'], capture_output=True, text=True ).stdout # 找出没有对应测试的验收标准 missing = [] for ac in criteria: if ac not in test_output: missing.append(ac) if missing: print(f"以下验收标准没有对应的测试用例:{missing}") exit(1) else: print("所有验收标准都有测试覆盖")

这个脚本可以加入 CI,确保每条验收标准都被测试覆盖。

7. 把验收标准变成团队资产

验收标准不应该是一次性的。每次验收失败后,把遗漏的点补充到验收标准清单里。时间长了,这份清单就成了团队的“需求检查表”。新需求来了,先对照清单过一遍,能提前发现很多潜在问题。

我自己的做法是维护一个acceptance-criteria-template.md,按功能类型分类(列表页、表单页、详情页、弹窗、上传等),每个类型下列出常见的验收点。新需求拆解时,直接从这个模板里挑对应的条目,再补充需求特有的条目。这样拆解效率高,而且不容易遗漏。

另外,把验收标准和 E2E 测试关联起来。每条验收标准对应一个测试文件或测试用例,测试通过就自动标记该标准为“已验证”。这样验收时产品经理可以直接看测试报告,而不是手动一条条过。

CI 全绿只是起点,不是终点。真正决定交付质量的,是验收标准是否清晰、测试是否覆盖了这些标准、以及你是否在提交前自己走了一遍用户流程。AI 能帮你写代码,但不能帮你理解需求。需求理解这件事,还是得人来。