1. 从“写一行删三行”到“AI是副驾”:我的真实演进路径
几年前第一次在编辑器里敲下// TODO: 这里需要一个防抖函数,然后盯着光标发呆十分钟——不是不想写,是根本记不清setTimeout的闭包陷阱怎么绕、clearTimeout该不该判空、节流和防抖的边界条件到底差在哪。那时候的我,把 AI 当成搜索引擎的升级版:输入“JavaScript 防抖实现”,复制粘贴,改两行变量名,再祈祷它别在生产环境凌晨三点崩掉。结果呢?有次上线后用户反馈“按钮点十次才响应一次”,排查两小时才发现 AI 给的代码里leading: false写成了leading: true,而我连这个参数是干啥的都没查。
这根本不是 AI 的问题,是我把自己当成了“代码搬运工”。真正转折点发生在一次紧急修复:后端接口突然返回结构变更,前端所有列表页全白屏。按老办法,得逐个页面找res.data.list改成res.data.items,再测兼容性。那天我试了新路子:把报错堆栈、旧接口响应示例、新接口文档片段一起喂给本地运行的代码模型,明确指令:“生成 TypeScript 类型定义 + 所有调用处的字段映射修改建议,不改逻辑,只做字段替换”。17 秒后,它输出了 4 个文件的精确修改行号、新旧字段对照表,甚至标注了“第 89 行需额外处理嵌套对象”。我照着改完,测试通过,喝完半杯咖啡。
这件事让我意识到:AI 写代码的本质,不是替代思考,而是把人从机械记忆和重复劳动中解放出来,去专注真正的设计决策。它不擅长判断“这个功能要不要加”,但极其擅长执行“如果要加,按现有架构该怎么插进去”。就像汽车的副驾驶——你决定去哪、走哪条路、什么时候刹车,它负责精准控制油门、转向角度、档位切换。这几年,我逐步把工作流拆解成“人类主控层”和“AI 执行层”,中间用一套可验证的协作协议衔接。下面说的每一步,都是踩过坑、测过数据、被线上事故教育过的真经验,不是理论推演。
提示:别一上来就问“帮我写个登录页”。先问自己:这个登录页要解决什么具体业务问题?现有系统里用户身份状态怎么流转?错误提示的文案规范是什么?把这些“人类专属信息”理清楚,AI 才能成为你的杠杆,而不是拐杖。
2. 三层过滤网:为什么我坚持让 AI 的输出必须经过三道人工关卡
很多人抱怨 AI 生成的代码“总要改半天”,其实问题不在 AI,而在缺少对输出内容的分层校验机制。我给自己立了死规矩:任何 AI 生成的代码,必须依次通过“意图层”“结构层”“执行层”三道关卡,缺一不可。这不是增加负担,而是用 3 分钟的前置检查,避免 3 小时的线上救火。
2.1 意图层校验:用“翻译法”揪出需求理解偏差
这是最容易被跳过的环节,却是错误率最高的源头。AI 对模糊指令的理解,往往和你脑中的画面天差地别。我的做法是:把 AI 输出的第一版代码,逐行翻译成自然语言需求描述。比如它生成了一段 React Hook:
const useDebounce = (value, delay) => { const [debouncedValue, setDebouncedValue] = useState(value); useEffect(() => { const timer = setTimeout(() => setDebouncedValue(value), delay); return () => clearTimeout(timer); }, [value, delay]); return debouncedValue; };我就立刻停手,把它“翻译”回去:
- “创建一个 Hook,接收当前值和延迟时间”
- “内部用 useState 存储防抖后的值”
- “每次 value 或 delay 变化时,启动新定时器,延迟后更新存储值”
- “组件卸载时清除定时器”
翻译完立刻发现问题:原始需求是“输入框内容变化后 300ms 触发搜索”,但这段代码在delay变化时也会重置定时器!如果用户动态调整搜索延迟,会导致意外行为。这就是典型的“AI 按字面理解,人按场景理解”的鸿沟。修正方案很简单:把delay移出依赖数组,或加注释说明“delay 应为常量”。
2.2 结构层校验:用“骨架比对”确认是否融入现有体系
第二关专治“代码孤儿症”——AI 生成的代码再漂亮,如果和项目现有架构格格不入,就是技术债。我的检查清单只有 3 项,但每项都直击要害:
- 命名一致性:它生成的
fetchUserData函数,项目里统一叫getUserProfile;它用的IUser接口,团队规范是UserProfileDto。不统一?立刻重构命名,哪怕只是加个as UserProfileDto类型断言。 - 错误处理范式:项目约定所有异步操作必须用
try/catch包裹,并调用统一错误上报函数reportError()。AI 生成的代码里只有console.error?直接补上,且确保catch块里包含reportError(e, 'fetchUserProfile')。 - 副作用隔离:项目要求所有 API 调用必须封装在
api/目录下的独立文件中。AI 直接在组件里写了axios.get(...)?立刻剪切到api/user.ts,并导出标准函数。
实测下来,这三项检查平均耗时 90 秒,但能拦截 83% 的后续集成问题。某次我跳过此项,AI 生成的表单验证逻辑直接用了new RegExp()构造正则,而项目规范要求所有正则必须预编译并缓存。上线后首屏性能下降 40%,就因为这个没被发现的细节。
2.3 执行层校验:用“最小闭环测试”验证真实行为
最后一关,也是最硬核的一关:不看代码,只看行为。我强制自己写一个极简测试用例,只验证核心路径。比如上面那个防抖 Hook,我不检查它用了useEffect还是useRef,而是写:
// 测试用例:输入连续变化,验证只触发最后一次 it('should only trigger with last value after delay', () => { jest.useFakeTimers(); const mockCallback = jest.fn(); // 模拟连续输入:a -> b -> c act(() => { hook.setValue('a'); }); act(() => { hook.setValue('b'); }); act(() => { hook.setValue('c'); }); // 快进到延迟结束 act(() => { jest.advanceTimersByTime(300); }); expect(mockCallback).toHaveBeenCalledTimes(1); expect(mockCallback).toHaveBeenCalledWith('c'); // 关键断言:必须是最后一次的值 });这个测试用例不到 15 行,但它逼着我去思考:这个功能在真实交互中,用户到底感知到什么?如果测试失败,说明 AI 理解错了“防抖”的本质,而不是代码写错了。这时候回溯到意图层重新沟通,比在 200 行代码里找 bug 高效十倍。
注意:这三道关卡不是线性流程,而是循环迭代。比如执行层测试失败,可能要回到意图层重新定义需求,也可能在结构层发现命名冲突导致逻辑误读。它们共同构成一个质量飞轮。
3. 工具链重构:为什么我淘汰了所有“AI 编程助手”插件,只留三个核心节点
市面上的 AI 编程插件,宣传语都像在卖永动机:“一键生成完整模块”“自动修复所有 Bug”。我曾经也信过,直到某次用某插件生成一个 WebSocket 心跳保活逻辑,它完美实现了setInterval发送 ping,却完全没处理网络断开时的重连退避策略。更糟的是,插件把重连逻辑硬塞进组件useEffect里,导致组件卸载后定时器还在跑,内存泄漏报警响彻整个监控平台。
那次事故让我彻底反思:工具的价值不在于“多”,而在于“准”;不在于“全自动”,而在于“可干预”。我把整个 AI 协作链路砍到只剩三个不可替代的节点,每个节点都承担明确职责,且全部可控:
3.1 输入端:用“结构化提示词模板”代替自由对话
我绝不直接在聊天框里打“写个登录接口”。取而代之的是一个固定模板,每次填空使用:
【角色】你是一个有 5 年经验的前端工程师,熟悉本项目技术栈(React 18 + TypeScript + Vite + TanStack Query) 【任务】生成一个符合以下要求的 React 组件 【输入约束】 - API 地址:/api/v2/auth/login - 请求体:{ username: string, password: string, captcha: string } - 成功响应:{ token: string, user: { id: number, name: string } } - 错误响应:{ code: number, message: string } 【输出约束】 - 使用 TanStack Query 的 useMutation 实现 - 错误 message 显示在表单下方(非 alert) - 登录成功后跳转到 /dashboard - 代码中禁止出现 console.log 【特别注意】 - 密码字段必须 type="password" - 提交按钮禁用状态需绑定 loading 状态这个模板看似繁琐,但它把“人类知识”(项目规范、技术栈)和“机器任务”(具体输入输出)做了物理隔离。实测表明,使用模板后,首次生成代码的可用率从 37% 提升到 89%。关键在于:它强迫我把模糊的“感觉”转化成可验证的条款。比如“错误 message 显示在表单下方”,就排除了所有用alert()或全局通知的方案。
3.2 处理端:用“本地轻量模型”替代云端大模型
我彻底停用了所有需要联网调用的 AI 插件。现在主力是本地运行的 CodeLlama-7b-Instruct 模型,通过 Ollama 在 M2 Mac 上运行。选择它的理由很实际:
- 响应速度:平均 1.2 秒返回,比云端 API 快 3 倍以上,写代码时思维不中断;
- 上下文可控:我能把整个项目
src/api/目录作为上下文喂给它,它生成的 API 调用必然符合现有风格; - 隐私安全:再也不用担心公司敏感接口地址、内部错误码被上传到第三方服务器。
当然,7b 模型也有局限——它搞不定超复杂算法推导。我的应对策略是:简单任务本地跑,复杂任务拆解后分步问。比如实现一个带权重的随机抽奖算法,我不让它“直接写完整函数”,而是分三步:
- “用 TypeScript 定义抽奖项接口,包含 name 和 weight 字段”
- “写一个纯函数,接收抽奖项数组,返回按权重计算的累积概率数组”
- “写主函数,接收累积概率数组和随机数,返回中奖项索引”
每步都小而确定,本地模型稳稳接住。这种“分而治之”的思路,比指望一个模型搞定所有事靠谱得多。
3.3 输出端:用“Git 提交前钩子”强制执行校验
最后一步,也是最狠的一步:我把前面说的三层过滤网,固化成 Git 提交前的自动化检查。只要代码里出现// AI-GENERATED注释标记,预提交钩子就会自动触发:
- 运行意图层检查:扫描注释附近是否有
TODO: 需确认需求等未解决标记; - 运行结构层检查:用 ESLint 规则校验命名、错误处理、API 封装是否合规;
- 运行执行层检查:自动执行该文件关联的
.test.tsx文件,失败则阻断提交。
这个钩子不是为了“防人”,而是为了把人的经验沉淀成机器规则。某次新同事没理解“错误必须上报”的规范,在 AI 生成的代码里只写了console.error。钩子直接报错:“检测到 console.error,需替换为 reportError()”。他点开报错信息,看到链接指向团队 Wiki 里的错误处理规范,立刻就明白了。这比口头提醒十次都管用。
提示:这三个节点不是孤立的,它们形成闭环。结构化提示词保证输入质量,本地模型保障处理可控,Git 钩子守住输出底线。少一个,整个链条就脆弱。
4. 真实战场复盘:一次支付模块重构,如何用 AI 缩短 70% 开发时间
去年重构支付模块时,我刻意记录了全程时间消耗和决策点,数据比任何理论都有说服力。目标很明确:把原来散落在 5 个组件里的支付逻辑,抽离成可复用的usePaymentHook,并支持微信、支付宝、银联三种渠道。按传统方式,预估需要 5 人日。
4.1 需求澄清阶段:用 AI 当“需求翻译器”,30 分钟完成 3 小时工作
最大的坑从来不在代码,而在需求本身。产品文档写着“支持多种支付方式”,但没说清楚:
- 用户切换渠道时,表单字段是否要动态变化?(微信要 openid,银联要 cardNo)
- 支付失败后,是否允许用户修改部分字段重试?(比如只改银行卡号,不重输姓名)
- 后端回调地址是统一的,还是各渠道不同?
我做的第一件事,是把产品文档、历史支付接口文档、客服反馈的 top3 投诉问题,一起喂给 AI,并指令:“列出所有隐含的、需要产品确认的歧义点,按风险等级排序”。它输出了 12 个问题,其中第 3 条“银联支付失败后,用户修改 cardNo 重试,是否需重新验证 cvv?”直击痛点——我们之前就是因为没确认这点,导致重试时 cvv 校验失败,用户反复提交。
我把这份清单发给产品经理,她当天就确认了全部问题。传统方式下,我要开 2 次需求评审会,拉通 4 个角色,耗时至少 3 小时。这次,30 分钟拿到清晰边界。
4.2 架构设计阶段:用 AI 当“架构陪练”,快速验证设计合理性
确定需求后,我面临关键抉择:Hook 的 API 设计。有两个方案:
- 方案 A:
usePayment({ channel: 'wechat' }),内部根据 channel 切换逻辑; - 方案 B:
useWechatPayment()、useAlipayPayment()三个独立 Hook。
我让 AI 分析两种方案的优劣,并给出具体依据:
- 方案 A 的维护成本低,但类型安全弱(channel 是字符串字面量,TS 难以推导);
- 方案 B 的类型安全强(每个 Hook 返回明确的字段类型),但新增渠道时要写三个文件;
- 关键洞察:项目未来半年只计划接入这三种渠道,且各渠道字段差异极大(微信要 openid,支付宝要 alipayUserId,银联要 cardBin),方案 B 的类型收益远大于维护成本。
这个分析让我果断选了方案 B。更重要的是,AI 在分析中提到了一个我忽略的点:“银联渠道的 cardBin 字段,前端需调用/api/card-bin接口实时查询,这个请求应该放在 Hook 内部,还是由调用方控制?”——这直接决定了 Hook 的颗粒度。我立刻补充到设计文档里,避免后期返工。
4.3 编码实现阶段:用 AI 当“超级代码补全”,聚焦核心逻辑
进入编码,我严格遵循三层过滤网。以useWechatPayment为例:
- 意图层:明确指令“生成 Hook,接收 openid、amount,调用 /api/wechat/pay,成功后返回 paymentId,失败时捕获特定错误码 4001(openid 无效)并抛出自定义错误”;
- 结构层:强制要求“所有 API 调用必须通过
api/payment.ts中的wechatPay函数”,我提前写好这个函数; - 执行层:写测试用例,只验证“传入有效 openid,应返回 paymentId;传入无效 openid,应抛出 WechatOpenIdInvalidError”。
AI 生成的代码,90% 是样板逻辑(useState、useEffect、调用 api 函数)。我真正花精力写的,只有三处:
- 如何解析后端返回的特定错误码(4001)并映射为前端错误类型;
- 微信 SDK 初始化的时机和销毁逻辑(避免内存泄漏);
- 支付成功后的状态同步(更新全局用户余额)。
这三处,才是真正的“人类价值区”。最终,5 个核心文件(3 个 Hook + 1 个 API 封装 + 1 个类型定义)的编码,我只花了 1.5 人日,比预估节省 70%。省下的时间,全用在了和 QA 同事一起设计边界测试用例上——比如模拟微信支付中途网络断开,验证重试机制是否健壮。
注意:这次重构没有“零 Bug”。上线后发现一个边缘问题:用户在微信支付页面停留超 5 分钟,返回时 Hook 状态未及时清理,导致再次点击支付按钮无响应。根因是
useEffect的清理函数没处理isMounted标志。这个 Bug 的修复,恰恰证明了三层过滤网的价值——它没拦住这个 Bug,但让定位过程从 4 小时缩短到 25 分钟:执行层测试直接复现了“长时间停留后点击无响应”,我立刻聚焦到状态清理逻辑。
5. 不是终点,而是起点:当 AI 成为“能力放大器”之后,我开始关注什么
现在回头看,“用 AI 写代码”这个说法本身就很误导人。AI 从不“写”代码,它只是把人类已有的知识、模式、规范,以极高速度重组并输出。真正的代码,永远诞生于人的大脑——那个决定“为什么需要这个功能”、权衡“哪种方案长期维护成本最低”、预判“用户在什么场景下会感到困惑”的决策中心。
所以,当我熟练运用三层过滤网、结构化提示词、本地模型后,我的关注点已经悄然转移。最近三个月,我把 60% 的学习时间,投入到了三个看似“不相关”的领域:
5.1 业务语义建模:让需求文档变成可执行的“代码原料”
我发现,AI 最容易出错的地方,是需求中那些“不言自明”的业务规则。比如产品说“VIP 用户享受优先客服”,AI 会生成一个isVip布尔值判断。但真实业务中,“VIP”可能涉及:
- 会员等级(钻石/VIP/普通)
- 有效期(是否在 30 天内续费)
- 黑名单状态(即使 VIP 也被禁用)
我现在做的,是和产品、后端一起,用一种极简的 DSL(领域特定语言)描述这些规则。例如:
rule "vip_priority_service" { when: user.membership.level in ["diamond", "vip"] && user.membership.expiry > now() && !user.status.is_blacklisted then: assign_queue = "vip_queue" }这个 DSL 文件,既是需求文档,也是 AI 的输入源。当我需要生成客服分配逻辑时,直接把这段 DSL 喂给 AI:“根据此规则,生成 TypeScript 函数getCustomerServiceQueue(user: User)”。它生成的代码,天然就包含了所有业务分支,不再需要我事后补漏。这本质上,是把人的业务理解,转化成了机器可消化的结构化知识。
5.2 错误模式库建设:把“踩过的坑”变成团队的“免疫系统”
过去,每个 Bug 都是孤例。现在,我把所有因 AI 生成引发的线上问题,抽象成“错误模式”,建立内部知识库。每个模式包含:
- 模式名称:如 “API 响应字段变更未同步 Hook 类型定义”
- 典型症状:编译通过,但运行时报
Cannot read property 'xxx' of undefined - 根因分析:AI 生成代码时,仅基于旧接口文档,未感知后端字段变更
- 防御方案:在 Git 钩子中加入“类型定义与 API 响应 Schema 自动比对”脚本
这个库不是用来指责谁,而是让新人第一天就能避开老手踩过的坑。上周,新同事在生成一个订单查询 Hook 时,钩子自动报错:“检测到响应字段orderStatus类型变更(string → OrderStatusEnum),请更新OrderDto接口定义”。他点开链接,看到对应的错误模式文档,5 分钟就解决了。这比让他自己 debug 两小时,高效太多。
5.3 人机协作协议设计:定义“什么该交给 AI,什么必须亲手写”
最后,也是最重要的,我开始和团队一起制定《人机协作协议》。这不是技术规范,而是认知契约。其中最关键的几条:
- 红线原则:所有涉及资金、权限、用户隐私的核心逻辑(如密码加密、Token 签发、权限校验),AI 只能辅助生成单元测试,主逻辑必须手写;
- 信任阈值:AI 生成的 UI 组件,可直接用于非核心页面(如帮助中心);用于交易流程的组件,必须通过视觉回归测试 + 交互流程测试;
- 责任归属:AI 生成的代码,其作者仍是提交者。协议明确:“你按下回车键的那一刻,你就对这段代码负全责”。
这条协议刚推行时,有同事质疑:“太严苛了”。直到某次,一位资深工程师在生成一个权限校验函数时,AI 给出了if (user.role === 'admin') return true;的简化版。他没细看,直接提交。幸好协议要求所有权限逻辑必须手写,Code Review 时被立刻打回。他后来在周会上说:“以前觉得协议是束缚,现在明白,它是给我兜底的安全网。”
我个人在实际操作中的体会是:AI 写代码的终极价值,不是让你写得更快,而是让你有余力去思考“为什么写”和“写得对不对”。当键盘敲击声变少,会议室里的讨论声变多;当 Stack Overflow 打开次数减少,和产品聊用户场景的时间增加——这才是技术真正进步的样子。