关注「索引目录」公众号,获取更多干货。
我使用像 Claude Code 这样的 AI 工具越多,就越清楚地认识到:工程技能才是让 AI 输出值得发布的关键。
人工智能让编写代码的速度更快。但交付高质量的软件仍然需要像以往一样的判断力。缺乏工程纪律的速度只会导致漏洞的更快地出现。
代码归你所有
人工智能只是你工具集中的一个工具,就像编译器、代码检查器或测试运行器一样。它并不拥有代码,代码的所有权在你手中。
生产环境中出现问题时,没人会问“这是哪个AI生成的?”,他们只会问是谁发布的。公关稿上署着你的名字。代码审核是你的责任。合并的决定也是你做出的。
人工智能具有倍增效应。如果你的工程技术能力薄弱,它也会放大这种薄弱环节。
人工智能不能为你做什么
- 考虑一下各种极端情况。
人工智能负责处理正常情况,你需要引导它去处理极端情况。 - 理解系统。
人工智能看到的是文件,你看到的是架构。 - 权衡取舍。
人工智能并不了解你团队的优先级、截止日期或技术债务容忍度。 - 了解团队背景。
你曾参加过团队决定弃用该服务的会议。你了解命名规则、架构决策以及“我们尝试过 X 但行不通”的历史记录。人工智能本身并不具备这些信息,除非你主动提供。
通过测试引导人工智能。
红绿重构
借助人工智能,测试驱动开发(TDD)变得更加强大。工程师定义测试内容,人工智能负责实现测试方法。
红色。编写能够覆盖预期行为和边界情况的失败测试:
describe('SearchFilter', () => {
it('renders input with placeholder', () => {
render(<SearchFilter onSearch={vi.fn()} />);
expect(screen.getByPlaceholderText('Search products...')).toBeInTheDocument();
});
it('calls onSearch after user stops typing', async () => {
const onSearch = vi.fn();
render(<SearchFilter onSearch={onSearch} debounceMs={300} />);
await userEvent.type(screen.getByRole('searchbox'), 'shoes');
expect(onSearch).not.toHaveBeenCalled();
await waitFor(() => expect(onSearch).toHaveBeenCalledWith('shoes'));
});
it('does not call onSearch for empty input', async () => {
const onSearch = vi.fn();
render(<SearchFilter onSearch={onSearch} debounceMs={300} />);
await userEvent.type(screen.getByRole('searchbox'), 'a');
await userEvent.clear(screen.getByRole('searchbox'));
await waitFor(() => expect(onSearch).not.toHaveBeenCalled());
});
it('shows loading spinner while searching', () => {
render(<SearchFilter onSearch={vi.fn()} isLoading />);
expect(screen.getByRole('status')).toBeInTheDocument();
});
it('trims whitespace before calling onSearch', async () => {
const onSearch = vi.fn();
render(<SearchFilter onSearch={onSearch} debounceMs={300} />);
await userEvent.type(screen.getByRole('searchbox'), ' shoes ');
await waitFor(() => expect(onSearch).toHaveBeenCalledWith('shoes'));
});
});
你没有编写任何实现代码。但你定义了组件的契约、边界情况和行为。这就是工程。
绿色。人工智能使用最少的代码即可通过所有测试。
重构。引导 AI 进行代码清理。提取辅助函数,遵循单一职责原则,清晰命名。目标:让下一个接触这段代码的工程师能够轻松上手。
如果没有测试规范,人工智能会生成未经测试但“看起来没问题”的代码。而通过测试驱动开发(TDD),人工智能可以在你定义的约束条件下运行。
使用端到端测试覆盖整个流程
单元测试验证各个部分的功能。端到端测试验证整个流程是否协同工作。
AI 可以搭建端到端测试框架,但你需要定义哪些流程至关重要,例如结账流程、身份验证序列或数据导出管道。这些决策需要对业务有深刻的理解,而不仅仅是代码。
test('user completes checkout flow', async ({ page }) => {
await page.goto('/products');
await page.click('[data-testid="add-to-cart"]');
await page.click('[data-testid="checkout"]');
await page.fill('#email', 'test@example.com');
await page.fill('#card-number', '4242424242424242');
await page.click('[data-testid="place-order"]');
await expect(page.locator('.confirmation')).toBeVisible();
});
您定义了关键路径。AI 可以填充细节、添加断言、处理设置/清理工作。但最终决定端到端测试哪些内容,则由您来决定。同样,对于边界情况也适用:支付失败时会发生什么?结账过程中会话过期时会发生什么?购物车为空时会发生什么?您定义这些场景。AI 会编写断言。
规范发布前必须执行标准
标准只有在得到执行时才有意义。三层结构:
代码检查规则。创建用于编码团队规范的规则。AI 会在配置后遵循这些规则,但您需要了解哪些规则对您的代码库至关重要。
Git钩子。推送前的钩子会运行代码检查和测试。未通过测试的代码不会发布。没有任何例外,即使是AI生成的代码也不例外。
AI 工具钩子。像 Claude Code 这样的工具支持钩子,可以拦截操作并自动执行规范。例如,每次提交前运行代码检查,每次推送前运行测试。AI 会在您定义的规则范围内运行。
工程师的工作是:设定规则。人工智能在这些规则范围内运行。
利用验证工具实现闭环
人工智能生成的每一项更改都需要验证。发现问题越快,人工智能就能越快修复问题。
验证反馈循环存在于各个层面:持续集成 (CI) 流水线运行视觉回归测试,浏览器自动化捕获屏幕截图,性能审计标记回归问题。其原理相同。每一次输出都成为下一次迭代的输入。
在我的工作流程中,我使用 Playwright MCP 和 Chrome DevTools MCP 直接在 AI 会话中完成这个循环:
-
截图显示布局错乱?AI修复了CSS。 -
控制台报错,提示缺少道具?AI 会添加该道具。 -
Lighthouse 审核发现可访问性问题?AI 会添加缺失的 aria 标签。 -
网络选项卡显示冗余的 API 调用?AI 重构了数据获取流程。
这使得人工智能的运行方式从“生成并寄希望于运气”转变为“生成、验证、迭代”。构建这个循环的工程师比仅仅发出指令就交付的工程师能获得更好的结果。
真正的技能不在于编写代码,而在于知道需要验证什么以及如何将验证结果反馈给系统。
像审阅初级员工的公关稿一样审阅人工智能的输出结果
AI生成的代码可以编译,通过了你编写的测试,看起来也合情合理。但这并不意味着它就是好的。
阅读差异。逐行阅读。查找:
- 不必要的复杂性。
人工智能喜欢抽象。这需要工厂模式吗?还是一个简单的函数就足够了? - 不易察觉的错误。
差一错误、缺少空值检查、竞态条件。人工智能生成的是看似合理的代码,而非经过验证的正确代码。 - 偏离既定模式。
你的代码库使用特定的错误处理模式。人工智能可能会发明不同的模式。 - 安全漏洞。
未经处理的输入、暴露的密钥、缺失的身份验证检查。人工智能默认情况下不会进行对抗性思考。
读懂代码的能力在阅读他人(或程序)编写的代码时尤为重要。你无法审查自己不理解的代码。因此,阅读代码的投入应该与编写代码的投入同等重要。
请解释原因,而不是方法。
人工智能往往会过度评论。它解释的都是显而易见的事情:
// Loop through the array and filter items
const filtered = items.filter(item => item.active);
// Set the state with filtered items
setItems(filtered);
这些评论只会增加干扰。代码本身已经说明了它的工作原理。
好的评论可以解释某事物存在的原因或其所代表的业务逻辑:
// Archived items are processed by a nightly batch job, not shown in the UI
const filtered = items.filter(item => item.active);
但在添加任何注释之前,请先问问自己:代码本身能解释清楚吗?一个命名良好的函数或变量通常可以完全消除注释的必要性。只有当代码本身无法完整表达其功能时,才应该添加注释。
这就是人工智能生成的噪音和工程判断之间的区别。
为下一位工程师准备的小模块
人工智能几秒钟内生成500行代码。你的任务:将其拆分成易于审阅和理解的部分。
提取功能。遵循单一职责原则。清晰命名。下一个工程师(或下一个人工智能团队)在接触这段代码时,会受益于清晰的代码结构。
这需要人为判断。人工智能会针对当前提示进行优化,而你需要针对项目的整个生命周期进行优化。一个只做好一件事的函数比一个“能用”的单体应用更容易测试、更容易复用、更容易替换。
小的 PR 比大的 PR 好,即使是 AI 编写的 PR 也一样。
要点总结
- 代码归你所有。
人工智能是一种工具,不是借口。公关稿上署的是你的名字。 - TDD指导AI运行。
先编写会失败的测试,让AI实现,然后再重构。 - 端到端测试可以弥补单元测试的不足。
明确关键流程和边界情况。 - 用细小的绒毛和钩子来强化标准。
在规范发布之前设置护栏。 - 形成闭环。
将屏幕截图、错误信息和审计结果反馈给人工智能,以便进行更好的迭代。 - 像审阅初级工程师的公关稿一样审阅。
逐行阅读。质疑每一个抽象概念。 - 请阐述原因,而非方法。重点
在于业务背景,而非实现细节。 - 小块处理比大块倾倒更高效。
将人工智能的输出拆分成便于后续工程师理解和处理的小块。
总结
人工智能并没有让工程技能变得可有可无,反而使它们成为制胜的关键。
人工智能是一种工具,一种强大的工具。但软件的交付并非工具所能完成,而是由工程师负责。学习基础知识,掌握测试技巧,理解你的架构,制定你的标准。然后,利用人工智能以你独自一人永远无法企及的速度执行任务。
代码编写是人工智能的工作,质量保证是你的责任。
关注「索引目录」公众号,获取更多干货。

