我推Copilot推了半年,技术问题全是小意思,人的问题差点把我逼疯

30秒速览

  • 别以为推Copilot就是装个插件的事,你得一个一个人去聊,搞清楚他们到底在怕什么。代码质量反而容易变差,因为人会对AI的漂亮答案放松审查。最重要的是培养对AI说NO的判断力,不然团队的技术审美会被带歪。

“不就是个补全插件吗”——我当初也是这么想的,结果在会议室被怼了四十分钟

去年秋天我决定在团队里推GitHub Copilot的时候,心里的剧本很简单:发个通知,让大家装个VS Code插件,顶多再录个十分钟的演示视频,这事儿就算成了。我甚至提前在脑子里过了一遍技术风险清单——会不会泄露代码、生成质量稳不稳定、能不能适配我们那个用了三年还没重构的微服务项目。说实话,技术侧的问题我一个都没当回事,因为早在自己电脑上试了一个月,除了偶尔给我补出一段像模像样但完全跑不通的GraphQL resolver之外,大部分时候它的表现都比我带过的那个实习生靠谱得多。真正让我撞墙的是人。

第一次在全组20个人面前提这件事,我刚说完“我们试一下Copilot,能省不少写样板代码的时间”,架构组的王哥就举手了。他说话很客气,但问题很刁:“你确定它生成的代码不会违反我们的license吗?我们给客户签的合同里有第三方代码溯源条款,万一哪天出事了谁担?”我当时根本没想过license的问题,只能含糊地说“GitHub说训练数据来自公共仓库,应该是安全的”。王哥听完没再追问,但他那个表情我能记一辈子——就是一种“你小子果然没考虑周全”的无奈。散会后我去翻GitHub的FAQ、看了好几份律师解读,才发现这个问题远比我想的复杂。公共仓库里的代码有不少带着GPL、AGPL这种传染性协议,虽然Copilot的输出不完全等同于复制粘贴,但法律风险确实存在,谁也说不清楚。等我搞明白这些再去找王哥聊,他已经拿着自己整理的一沓资料去跟CTO汇报了,汇报的核心意思是“我们法务没点头之前,任何AI生成代码都不许进生产环境”。那一刻我突然意识到,推一个新工具根本不是技术评估就完了,你得先回答一堆你本以为无关紧要的问题,而且问这些问题的人往往比你更有话语权。

更大的阻力来自测试团队。我们公司的测试工程师特别强势,所有PR必须过他们那一关。测试负责人Lily直接在大群里说:“Copilot生成的代码我可不敢测,万一它偷偷加了个分支逻辑我没看出来,上线炸了算谁的?”这句话杀伤力很大,因为她说的是“我看不出来”,而不是“工具不行”。等于把风险抛回给开发者——你要用AI帮你写代码,就得自己确保每行都没问题,而且还得用某种方式向测试证明你已经检查过了。后来我试着在代码review流程里加了一个“AI生成代码标记”,凡是Copilot参与的部分都用注释标注出来,然后单独写一段说明这段代码是干嘛的、为什么接受它的建议。结果这个方法让review时间增加了将近30%,开发同事怨声载道。有人在standup meeting上直接说:“我本来用Copilot是为了省时间,现在倒好,用两分钟省下来的时间,又花十分钟写注释解释为什么要用这两分钟省下来的东西。”

到这里我才真正摸到门道:推Copilot这件事,最大的难点根本不在怎么配置代理、怎么调prompt、怎么处理补全冲突,而在你怎么让一群技术很强但性格各异的人,从“我不信任这个玩意儿”过渡到“我愿意试试,出了事我自己扛”。这个过渡过程需要你一个一个去谈,去理解每个人担心什么,然后给出有针对性的、他们能接受的方案,而不是发一封全组邮件期待大家自动配合。我花了差不多两个月才把核心阻力基本上化解掉,靠的不是技术文档,而是一对一请咖啡、帮人调配置、替人写那该死的注释说明。

代码质量反而变差了——不是因为Copilot太笨,而是因为它太“听话”

在大家都勉强接受Copilot之后,真正的技术坑才开始冒头,但即便这些技术坑,也全跟人的习惯绑在一起。我印象最深的是一个订单模块的Bug。我们有个函数负责计算运费,根据商品重量、送达区域、会员等级来打折。一个同事用Copilot帮忙重构了这段逻辑,最后生成的代码看起来干净得像教科书。大概是这样的:

function calculateShipping(weight, zone, memberLevel) {
  const baseRate = zone === 'international' ? 25 : 10;
  const weightCharge = weight > 20 ? (weight - 20) * 0.5 : 0;
  const membershipDiscount = memberLevel === 'premium' ? 0.8 : memberLevel === 'gold' ? 0.9 : 1;
  return (baseRate + weightCharge) * membershipDiscount;
}

乍一看没毛病,逻辑清晰,分支都覆盖了。结果上线当天晚上客服电话被打爆,因为所有国际订单的运费算下来都少收了将近一半。排查半天才发现,问题出在Copilot擅自“优化”了原本的逻辑。原来的代码里国际订单有个最低消费限制:运费不足50块的按50算,而且超重部分是在基础费率上叠加再打折。但Copilot生成的版本直接把这两个规则吃掉了,因为它从上下文里没感知到这些特殊业务规则,光看变量名叫zone就自作主张地给了个数字,然后同事review时觉得这段代码“看起来比老版本优雅多了”就合进去了。这事儿说到底不是Copilot的错,而是人——一个经验三年多的开发者,看到AI给出的代码结构更整齐、变量命名更短、链式调用更“现代”,就本能地认为这是一种优化,而忘了去核对原始的业务规约。

类似的事情之后反复发生。另一个同事在写一个定时任务时让Copilot生成了这么一段:

const cron = require('node-cron');
cron.schedule('0 2 * * *', async () => {
  const pendingOrders = await Order.find({ status: 'pending' });
  for (const order of pendingOrders) {
    await processOrder(order);
  }
});

代码能跑,语法没错,但生产环境里凌晨两点这个任务一旦启动,发现有一万条pending记录就会串行处理,数据库连接一直占着,直接把其他API的响应时间拖到十几秒。原来的代码里写的是用Bluebird的Promise.map配合concurrency参数做并发控制,可Copilot补全出来的只是最简单的那版,因为训练数据里绝大多数cron例子就是长这样的。同事心想“AI都这么写了,估计这就是最佳实践吧”,连想都没想就提交了。事后他说:“Copilot给的答案太确定了,那种语法高亮加自动补全的感觉,让我有一种它在替我思考的错觉。”这句话点醒了我——当一个人对工具的信任超过对自己判断力的信任时,代码质量就会不可逆转地滑坡。

我发现这其实是一个微妙的心理陷阱。Copilot在提供建议的时候姿态太低了,它永远不说“我建议这样做,因为…”,它只是安静地在你的光标后面塞一段灰色文字,像极了一个从不反驳你的实习生。人天然会对这种低姿态产生好感,进而放松审查。尤其是当你已经连续写了四个小时代码、大脑疲惫的时候,看到一个看起来完全能用的建议,你会不自觉地按下Tab。久而久之,你会从“审查AI的代码”变成“AI写了什么我就默认可以”。这个转变不是技术问题,是注意力管理和团队文化的问题。我们后来强制加了一个规则:任何Copilot生成的代码,必须至少有一个人为的微小改动——哪怕只是改个变量名,让提交记录里看得出“我确实看过这段代码”。听起来很蠢,但居然有效,因为那个动作逼你重新把注意力拉回到代码上。

团队里出现了两种人:一种把Copilot当拐杖,另一种偷偷摸摸用但嘴上说不碰

推了三四个月之后,我观察到一个很有意思的现象:团队里明显分裂成了两拨人。一拨是重度依赖派,他们不仅在写代码时用,连写个shell命令、配个Dockerfile都要让Copilot来,甚至还开始用Chat模式问“这段代码怎么优化”。效率确实高,但代码风格越来越趋同,很多PR看起来像同一个人写的——一样的变量命名习惯、一样的错误处理模式、一样的async/await套娃。另一拨是口嫌体正直派,他们开会时总说“我还是不信任AI”“手写代码更可控”,但我在GitHub统计里能看到他们IDE里Copilot插件一直都是活跃状态,代码补全接受率也不低。他们只是不愿意承认自己在用,好像用了就显得自己技术不行。

这两种人都让我头疼。重度依赖派慢慢丧失了处理复杂问题的肌肉记忆。有一次线上报了个内存泄漏,需要分析heapdump,一个平时用Copilot最猛的同事居然忘了v8的getHeapSnapshot怎么用,卡了十五分钟最后跑来问我。我说你之前不是写过两次dump分析脚本吗,他说“对啊,但那都是Copilot帮我写的,我根本没记住API”。那一刻我特别堵心——我们引入工具是为了提高效率,不是为了让工程师退化成一个只会用自然语言描述需求的人。后来我特意在每个sprint里塞了一个“不用Copilot日”,那天所有人必须关掉AI补全,纯手写。一开始大家哀嚎,但过了一个月,有人反馈说那个日子反而产出质量最高,因为写每一行之前都要认真想清楚,不会被AI带偏节奏。

口嫌体正直派的问题更隐蔽。他们表面上排斥AI,但暗地里依赖,却不愿意参与任何关于Copilot使用的讨论。导致我们想建立一套团队级别的AI辅助编码规范时,这些人完全不提供反馈,觉得“我又没用,跟我没关系”。结果等到某天他们的代码出问题,一查发现是Copilot生成的,就开始甩锅:“这东西又不是我写的,是AI搞的,我也不知道怎么会这样。”我后来在团队里定了一条死规矩:只要你IDE里装了这个插件,就当你是正式用户,出任何问题都得按我们约定的AI代码处理流程走,不许事后甩锅。这条规矩出来之后,反而让那拨人开始公开讨论自己的使用习惯,因为反正已经被“抓包”了。

这里面还藏着一个更深的问题:技术能力的差距被放大了。原来团队里初级和高级工程师的差别主要在设计思路和debug能力上,代码量产出其实差不太多。但Copilot一来,高级工程师用它如虎添翼,因为他们能快速判断一个补全建议是精华还是糟粕,甚至会故意写一行注释引导Copilot生成自己想要的结构。初级工程师则更容易全盘接受AI输出,结果就是写的代码量上去了,但代码质量方差反而变大。有一次我review一个新人用Copilot写的支付回调处理器,函数体有200行,注释工整,try-catch套了四层,看起来防御性十足,可实际上连最基本的状态机都没有,重复扣款的风险明晃晃摆在那儿。这个新人不是不努力,而是他觉得“AI给我这么多保护性代码,说明这些保护都是必要的”,他不知道哪些是Copilot因为训练数据里见过类似场景而机械补全出来的多余逻辑。

这件事逼着我把团队的技术辅导方式也改了。以前带新人重点是教怎么写,现在重点是教怎么审——怎么一眼看出Copilot生成的代码里缺少了哪些关键约束。比如看一个数据库查询的方法,我会让他们先问自己三个问题:有没有索引覆盖、有没有分页、有没有考虑软删除。因为AI几乎从来不会主动加这些业务相关的防御。这种训练比教语法难多了,因为它需要大量的业务上下文,而这些上下文恰恰是AI最缺的。

后来我换了一种推法:不推Copilot,而是推“对AI说NO的能力”

到了第五个月我基本放弃了以前那种“让每个人都用起来”的目标,转而把重心放在培养一种团队文化:用AI可以,但你必须有能力判断什么时候不用它。我开始在每周的技术分享会上专门讲那些Copilot翻车的真实案例,不是当成笑话讲,而是掰开揉碎分析当时那个人的决策过程——为什么他会接受这个建议?哪一步思考被跳过去了?然后我们总结出几个危险信号,贴在团队Wiki首页。比如“当AI给出的答案恰好符合你的预期时,这时候你最容易忽略它的错误”这条,几乎每两周就有人来跟我说他又中招了。

我们还做了一个实验:把同一个需求同时丢给两个不同的AI工具(Copilot和Cursor),再加上一个不用AI的开发者,对比产出的代码。结果发现,AI生成的代码在语法结构和命名规范上确实更统一,但在边界条件处理上明显薄弱。比如处理一个用户输入数组,手写代码通常会先检查数组是否为空、元素是否undefined,而AI经常直接上forEach。下面这段对比代码我到现在还留着当培训材料:

// AI生成的版本
function calcTotal(items) {
  return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}

// 手写版本
function calcTotal(items) {
  if (!Array.isArray(items) || items.length === 0) return 0;
  return items.reduce((sum, item) => {
    const price = item?.price ?? 0;
    const quantity = item?.quantity ?? 0;
    return sum + price * quantity;
  }, 0);
}

AI版本短小精悍,但一旦传入一个price字段缺失的对象,直接NaN污染整个计算结果,而且还不报错。我们用这个例子在全组做了一次盲测,让每个人判断哪个版本更可靠,结果超过一半的人选了AI版本,理由清一色是“更整洁”“更符合函数式风格”。这说明什么?说明我们团队对代码健壮性的审美已经被带偏了,开始把“看起来漂亮”当成“质量高”。这比偶尔出个Bug严重得多,它是在扭曲工程师的价值判断体系。

后来我反而感谢这次推Copilot的经历,因为它像一面照妖镜,把团队里原本隐藏的问题全照出来了:哪些人缺乏批判性思维、哪些人对业务理解浮于表面、哪些人只会照搬最佳实践但不理解其适用场景。这些短板在不用AI的时候还能靠经验蒙混过关;AI一来,短板被无限放大,因为AI给你一个看起来完美的答案时,你能不能硬气地说“不,这里得加个判空”才是真本事。

我现在不再跟别人说“你们应该用Copilot”,我会说“你们应该练就在AI面前说NO的能力”。这个能力包括:能在三秒内识别出一段代码里缺少了哪几项防御;能区分“这段代码跑得通”和“这段代码在生产环境跑得安全”;能在团队压力下坚持自己判断,哪怕所有人都说“AI写得不是挺好的吗”。推了这半年,技术上我学到的几乎为零,但关于人——关于信任、习惯、恐惧和责任感——我学到的东西够我消化好几年。如果你正打算在团队里推Copilot,听我一句:先把人的问题想透彻,技术那边,真的只是小意思。

发表评论