前言

今年三月初折腾完 ChatGPT API 之后(见我之前那篇文章),我就开通了 GitHub Copilot,到现在小半年了。这小半年里我几乎每天用它写业务代码、写单测、啃不熟悉的库,从最初”哇这也太神了”,到中段”幻觉 API 差点坑我上线”,到现在”它已经是我工作流的固定成员”——心态走了完整的一个 U 型曲线。

这篇文章是我的半年期复盘:它的工作原理、我高频使用的四个场景(附真实前后对比)、效率的真实数据、它擅长什么不擅长什么,以及使用习惯上发生的变化。写给还在观望要不要付费的你。

1. 半年使用情况概览

先交代背景:我的主要工作是 React + TypeScript 中后台项目,偶尔写 Node 服务端脚本。半年里 Copilot 的使用画像大概是这样的:

  • 日常写业务代码:几乎全程开着,补全接受的代码大约占我产出代码的 30%~40%;
  • 写单元测试:重度依赖,是最提效的场景,没有之一;
  • 探索不熟悉的库:中度依赖,配合官方文档食用;
  • 写注释、文档、commit message:顺手就让它写。

特别声明:下面的效率数据都是我自己的主观估算,不是严格的 A/B 实验,看看趋势就好,别当论文引用。

2. 它是怎么工作的:补全 vs 对话

用半年再回头看原理,反而更清楚了。Copilot 有两种交互形态,对应两种不同的产品逻辑:

补全模式(Copilot):编辑器实时工作。它把当前文件内容、光标前后代码、打开的相关文件(比如你 import 的模块)打包成上下文,请求模型,返回”接下来最可能写的代码”。特点是快、无感、每几秒一次——本质是代码版的输入法联想,只是联想的是整段逻辑。

对话模式(Copilot Chat):今年陆续开放,像 ChatGPT 一样一问一答,且能感知你的编辑器上下文(当前文件、选中代码、报错信息)。适合”不明确知道要写什么”的场景,和补全的”明确知道方向、懒得敲细节”正好互补。

一个容易忽略的点:补全的质量极度依赖上下文喂得够不够。同样一个组件,如果同目录下已经有别的类似组件,Copilot 会模仿现有代码的风格和命名,输出质量远高于在空文件里凭空写。所以养成的第一个好习惯是:先写好类型定义、接口约定、命名规范,再让 AI 补肉——上下文就是 Prompt。

3. 四个高频使用场景(附前后对比)

3.1 样板代码:从”复制上一个项目”到”直接说”

中后台项目的 CRUD 页面、表单弹窗、列表页都是高度模板化的。以前我的做法是翻老项目复制改名,现在直接写注释让它生成。以一个防抖搜索框为例:

1
2
// 我只写了这一行注释:
// 防抖搜索输入框:300ms 防抖,输入为空时立即清空结果,组件卸载时取消定时器

Copilot 直接给出完整实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { useState, useEffect, useRef } from 'react';

export function useDebouncedSearch(fetchResults, delay = 300) {
const [keyword, setKeyword] = useState('');
const [results, setResults] = useState([]);
const timerRef = useRef(null);

useEffect(() => {
if (!keyword) {
setResults([]);
return;
}
timerRef.current = setTimeout(async () => {
const data = await fetchResults(keyword);
setResults(data);
}, delay);

return () => clearTimeout(timerRef.current);
}, [keyword, delay, fetchResults]);

return { keyword, setKeyword, results };
}

防抖、空输入处理、卸载清理——注释里要求的三个细节一个不落。这类”逻辑简单但细节繁琐”的代码,以前要写五分钟还要担心细节,现在十几秒校对一遍完事。这是接受率最高的场景,我估算整体节省 60% 以上时间。

3.2 单测生成:最提效的没有之一

以前写一个工具函数的单测,覆盖正常路径加边界值要半小时起步,写完经常自己都嫌烦。现在的流程是:函数写完 → 光标落在空的单测文件里 → 写一行 // tests for formatFileSize → 接受 Copilot 给的第一批用例 → 手动补它没想到的边界。

前(手写):

1
2
3
4
// 只测了最普通的路径,边界全靠自觉
it('1024 bytes -> "1.00 KB"', () => {
expect(formatFileSize(1024)).toBe('1.00 KB');
});

后(Copilot 首轮输出,我只需挑选和微调):

1
2
3
4
5
6
7
8
describe('formatFileSize', () => {
it('0 bytes -> "0.00 B"', () => expect(formatFileSize(0)).toBe('0.00 B'));
it('小于 1KB 保留 B', () => expect(formatFileSize(512)).toBe('512.00 B'));
it('1024 bytes -> "1.00 KB"', () => expect(formatFileSize(1024)).toBe('1.00 KB'));
it('MB 级别', () => expect(formatFileSize(1024 * 1024)).toBe('1.00 MB'));
it('负数抛出错误', () => expect(() => formatFileSize(-1)).toThrow());
it('非数字输入抛出错误', () => expect(() => formatFileSize('abc')).toThrow());
});

边界值覆盖明显比我手写时勤快(AI 没有”懒得写第二个用例”的情绪)。单测整体时间我估算了下省了 50%~70%。唯一必须坚持的原则:跑测试、看断言是否真的符合预期,它偶尔会写出一个断言”想象中行为”的用例。

3.3 未知库 API 探索:从查文档到”问 + 验证”

用没碰过的库(比如那阵子接的 dayjs 插件、resize-observer-polyfill),以前要在文档和源码之间反复横跳。现在直接在 Chat 里问:

1
2
3
4
5
6
问:我想用 dayjs 计算两个日期之间相差多少个自然月,怎么写?
答:dayjs(a).diff(dayjs(b), 'month') 得到的是带小数的月数,直接取整当"自然月数"用会差一点。
如果要"自然月"语义,建议比较 y + m:……

问:这样写对闰年 1/31 到 2/28 会怎么算?
答:……

它的价值不只是给答案,还会解释”为什么你直觉的写法不对”——这类语义坑(diff 的 month 到底怎么算)文档里藏在犄角旮旯,问它十秒钟得到。但铁律是答案必须验证:我会立刻写个 REPL 或单测跑一下它给的 API 签名和返回值。

3.4 注释转代码:把设计先写成注释

这是半年里我用法上最大的进化:现在写复杂函数前,我习惯先用 3~5 行注释写”伪代码级”的实现思路,然后让 Copilot 展开。比如:

1
2
3
4
5
6
7
/**
* 实现思路:
* 1. 用 IntersectionObserver 监听列表项进入视口
* 2. 首次进入时才加载真实图片(data-src -> src)
* 3. 进入视口后立即 unobserve,避免重复触发
* 4. 暴露 destroy 方法供组件卸载时清理
*/

这一步其实是在用 Copilot 强迫自己”先想清楚再写”——注释写得越清楚,生成的代码越准;写不清楚的注释,往往就是设计没想清楚的地方。它变成了我检查自己思路的一面镜子。

Copilot 结对编程工作流:注释驱动开发与上下文补全

4. 效率实测:一份主观但不客气的数据

综合半年的感受(再次强调是主观估算):

  • 样板代码 / CRUD:提效 **60%+**,接受率高;
  • 单元测试:提效 **50%~70%**,是它最擅长的;
  • 胶水脚本、正则、数据处理:提效 **50%+**;
  • 核心业务逻辑、复杂状态管理:提效 **10%~20%**——写起来快了一点,但设计和 debug 时间没少;
  • 整体编码时间:大约省 **25%~35%**。

注意最后一行和第一行的巨大落差。AI 大幅加速了”写”这个动作,但”想”的部分——需求理解、方案设计、架构取舍——它帮不了太多。而资深工程师的时间恰恰越来越多花在”想”上,所以资历越深,体感提效越明显;对新手来说省下的时间容易被”看不懂 AI 写的代码”反向吃掉。

5. 它擅长什么,不擅长什么

半年观察下来的能力清单,这一节可能是全文最值钱的。

擅长:

  • 模式明确的代码:CRUD、hooks、工具函数、配置文件、类型定义;
  • 一文件内自洽的逻辑:上下文给足,几乎不用返工;
  • 单测、注释、文档这类”苦力活”;
  • 常见库的常见用法(React、lodash、dayjs 这类大库接受率极高)。

不擅长(都是我亲历的坑):

  • 幻觉 API:这是最大的坑。它会一本正经地调用不存在的函数——我遇到过它编造 dayjs().isBetween() 的自定义签名、编造一个不存在于任何版本的 React hook。它生成”看起来极其合理”的代码,恰恰因为合理才危险。防线只有一个:对它写的每个不确定的 API,跳转到定义或查文档确认。
  • 长上下文丢失:项目一大,它只能看到当前文件和少数相关文件。跨文件的隐式约定(比如全局的埋点规范、统一的错误处理封装)它不知道,会按”通用最佳实践”给你另起炉灶。我的对策是把约定写进注释和类型,让上下文自己”携带规范”。
  • 架构级决策:状态管理选型、模块边界划分、目录结构,它给的永远是”教科书答案”,不会考虑你们团队的迁移成本和历史包袱。这类事问了也是白问。
  • 安全合规顾虑:公司敏感代码千万别贴进 Copilot Chat,很多企业对此有明确政策——先搞清楚你司的规定再开通,这不是小事。

6. 最大的转变:从”写代码”到”审代码”

这是我半年体会最深的一点,比任何效率数字都重要。

以前我的工作流是”想清楚 → 敲代码 → 自测”。现在变成了”想清楚 → 用注释/签名描述意图 → 审阅 AI 生成的候选 → 挑选、修改、组装”。产出代码的来源变了,但责任没有变:AI 写的每一行进入仓库,都默认署着我的名字。于是我的核心技能从”快速写出正确代码”变成了”快速判断一段代码的正确性、边界和安全”。

这个转变对代码 review 能力是实打实的锻炼——天天审别人(AI)写的代码,我对边界条件、竞态、内存泄漏的敏感度反而提高了。但也要警惕反面:接受补全太快、不仔细看,会写出自己都不理解、出了 bug 不会修的代码。我的纪律是:接受的代码必须逐行看过;看不懂的代码宁可不用,或者问 Chat 让它解释,解释完还觉得别扭就自己重写。

7. 给准备入手的你几条建议

  1. 先在私人项目试两周,把幻觉 API 的坑踩明白再进团队项目,尤其确认公司合规政策;
  2. 上下文优先于提示:把类型定义、接口注释、命名规范写好,补全质量立涨;
  3. **养成”验证反射”**:不认识的 API 一律跳定义/查文档,AI 说的库行为一律跑一遍;
  4. 单测和样板代码交给它,架构和核心逻辑留给自己——前者省时间,后者攒能力;
  5. 写注释的投入加倍:它既是给 AI 的指令,也是给三个月后自己的文档,一举两得。

写在最后

半年下来,我对 Copilot 的定位很清晰:它是一个不知疲倦、博闻强识、偶尔胡说八道的初级结对搭子。它把编码中机械重复的部分加速了 60%,把需要判断力的部分原样留给了我。有人担心”AI 会不会取代程序员”,用半年之后我的感受反而更笃定:取代的不是程序员,是”只会翻译需求成代码”的那部分工作——而理解问题、定义方案、守住质量的判断力,恰恰因为 AI 变得更值钱了。

年底之前我计划把这些经验沉淀到团队工作流里(AI review 进 CI、Prompt 模板库扩容)。另外我最近在用 React 做一个 AI 聊天应用,流式渲染那套东西挺有意思,写完再来交作业。