1. 当“造轮子”这件事被AI盯上
“Is library building obsoleted by AI coding?”——这个问题第一次砸到我面前时,我正在给一个内部工具写第三版日期处理模块。说实话,那一瞬间我有点恍惚:过去十年里,我花在封装通用库、设计API、写文档、维护版本兼容上的时间,加起来可能够我环游世界两圈。而现在,一个AI助手能在几秒内生成一个看起来像模像样的日期解析函数,甚至还能顺手补上单元测试。
但“看起来像模像样”和“能扛住生产环境”之间,隔着一条马里亚纳海沟。这篇文章不打算给你一个非黑即白的答案,而是想把我自己从“手写库狂热者”到“AI辅助库构建实践者”的转变过程拆开,聊聊哪些环节确实被AI加速了,哪些环节反而因为AI的介入变得更需要人的判断力,以及一个普通开发者现在到底该怎么看待“造库”这件事。
如果你正在纠结“还要不要自己写工具库”“AI生成的代码能不能直接进项目”“怎么用AI把库开发效率提上去”,那这篇内容就是写给你的。我会尽量少讲空话,多讲我实际踩过的坑和验证过的做法。
2. 先搞清楚:AI到底在“替代”库构建的哪个环节
2.1 库构建从来不只是“写代码”
很多人把“library building”等同于“敲代码实现功能”,这其实是个巨大的认知偏差。一个真正能被复用的库,它的生命周期至少包含这些阶段:
- 需求抽象:从多个业务场景里提炼出通用接口,决定哪些参数该暴露、哪些该隐藏。
- API设计:命名、参数顺序、返回值结构、错误处理策略,这些决定了库好不好用。
- 核心实现:算法、数据结构、边界条件处理。
- 测试覆盖:单元测试、集成测试、模糊测试,尤其是边界和异常路径。
- 文档与示例:README、API文档、快速上手指南、迁移指南。
- 版本管理与兼容:语义化版本、废弃策略、升级路径。
- 分发与构建:打包、产物格式、依赖管理、CI/CD。
- 长期维护:issue响应、安全补丁、社区运营。
AI目前最擅长的是核心实现和测试用例生成这两个环节,在文档初稿上也有不错的表现。但在需求抽象、API设计、版本兼容策略这些需要跨项目经验和长期判断的环节,AI基本还是个“实习生水平”——它能给你建议,但拍板的人必须是你。
2.2 一个反直觉的结论:AI让“小库”变多了,而不是变少了
我原本以为AI会让大家不再写库,直接让AI生成一次性代码就完事。但实际观察下来,情况恰恰相反:因为AI把“写一个库”的启动成本降到了极低,反而有更多人愿意把重复代码抽成小库了。
以前你写个日期格式化函数,可能觉得“就这几行,复制粘贴算了”。现在你会想:“反正AI能帮我生成测试和文档,不如封装成一个内部小库,以后统一改。”结果就是,项目里的“微库”数量在增加,每个库的职责更单一,维护成本反而因为AI辅助而可控。
这个现象背后的逻辑是:AI降低的是“从想法到可用代码”的摩擦,而不是“从代码到可靠库”的摩擦。前者是写代码,后者是工程化。工程化的那部分,AI能帮上忙,但替代不了。
2.3 哪些库最危险,哪些库反而更稳了
我把自己接触过的库粗略分了个类,用下面这张表来说明AI对不同类型库的冲击程度:
| 库的类型 | 典型例子 | AI替代风险 | 原因 |
|---|---|---|---|
| 纯算法工具 | 排序、字符串处理、日期计算 | 高 | AI生成质量已足够好,边界测试也能补 |
| 协议/格式解析 | JSON解析、CSV读写、URL处理 | 中高 | 标准明确,AI能覆盖大部分场景 |
| 框架适配层 | 某框架的ORM封装、HTTP客户端 | 中 | 依赖具体框架版本,AI知识可能滞后 |
| 业务领域模型 | 订单状态机、计费规则 | 低 | 强业务耦合,AI缺乏上下文 |
| 基础设施 | 连接池、缓存、并发原语 | 低 | 对正确性和性能要求极高,AI代码需大量验证 |
| 开发者体验工具 | CLI、代码生成器、调试辅助 | 中 | AI能生成骨架,但交互设计仍需人 |
这张表不是绝对的,但它能帮你快速判断:如果你正在维护的库属于“纯算法工具”那一栏,确实需要认真思考它的长期价值了。但如果你做的是“业务领域模型”或“基础设施”,AI更多是加速器,而不是替代者。
3. 我用AI辅助库开发的真实工作流
3.1 从“先写实现”改成“先写接口契约”
以前我写库的习惯是:打开编辑器,先写核心函数,跑通了再补测试和文档。现在我把顺序倒过来了:先用自然语言把接口契约描述清楚,让AI生成接口定义和测试用例,确认无误后再让AI填充实现。
举个例子,我最近要写一个“带过期时间的LRU缓存”小库。我没有直接让AI“写一个LRU缓存”,而是先给它一段描述:
我需要一个TypeScript泛型类ExpiringLRU,构造函数接受maxSize和defaultTTL两个参数。提供get、set、has、delete、clear五个方法。set方法可以覆盖默认TTL。过期条目在get和has时惰性删除,同时每次操作后检查容量,超出时按最近最少使用淘汰。请先只生成接口定义和对应的单元测试,不要生成实现。
AI给出的接口定义和测试用例,我逐条审查后调整了两处:一是把has方法的语义明确为“不触发过期检查”,二是增加了size属性。然后我再让AI基于这份契约生成实现。这样做的好处是,测试用例成了我和AI之间的“合同”,实现必须满足测试,而不是我事后去猜AI的实现有没有边界问题。
3.2 让AI做“边界条件枚举器”,而不是“代码生成器”
AI在生成正常路径代码时表现很好,但边界条件往往需要人提醒。我的做法是:实现生成后,专门开一个对话,让AI扮演“恶意测试者”,列出这个库可能出问题的所有边界场景。
比如对于上面的LRU缓存,AI列出的边界包括:maxSize为0、maxSize为负数、TTL为0、TTL为负数、同一key反复set、get不存在的key、并发set同一个key、系统时间回拨等。然后我从中挑出真正需要处理的,让AI补充对应的测试用例。
这一步的价值在于:AI能想到的边界比我多,但它不知道哪些边界在我的使用场景里是重要的。我来做优先级判断,它来做穷举。配合下来,测试覆盖率比我手写高出一大截。
3.3 文档生成:AI写初稿,人写“为什么”
AI生成的API文档,格式规范、参数齐全,但读起来像说明书。真正有价值的文档,需要回答“为什么这个参数要这样设计”“什么场景下不该用这个库”“和同类库比有什么取舍”。
我的做法是:让AI生成参数说明和示例代码,然后我自己补一段“设计决策”放在文档开头。这段内容AI写不出来,因为它没有经历过我当初的取舍过程。比如为什么TTL是惰性删除而不是定时器删除?因为定时器在Node.js里会阻止进程退出,而且大量缓存实例会创建大量定时器。这种“为什么”才是文档的灵魂。
3.4 版本兼容:AI最容易帮倒忙的地方
这里要特别提醒:不要让AI帮你决定版本号怎么升。我试过让AI根据代码变更判断是major、minor还是patch,结果它经常把破坏性变更标成minor。原因很简单:AI看不到你的用户是谁、他们怎么用你的库。
我的做法是:自己维护一个CHANGELOG,每次发版前人工判断变更类型。AI可以用来检查CHANGELOG格式是否规范,但不能用来做兼容性决策。这个环节一旦出错,下游用户的构建就会炸,信任一旦丢失很难挽回。
4. 那些AI生成代码直接进库的惨痛教训
4.1 一个“看起来完美”的深拷贝函数
我曾经让AI生成一个深拷贝工具函数,用于处理配置对象。AI给出的实现用了structuredClone,并附带了降级方案。代码看起来无懈可击,测试也通过了。但上线后第二天,有用户反馈:配置里的Date对象变成了字符串。
排查后发现,降级方案里AI用了JSON.parse(JSON.stringify()),而JSON序列化会把Date转成ISO字符串。AI在生成时“知道”这个限制,但它没有在代码里体现出来,也没有在测试里覆盖Date类型。这就是典型的AI知道但不说的问题。
教训:AI生成的代码,必须针对你的实际数据类型做验证。通用测试通过不代表你的场景通过。
4.2 并发安全:AI的盲区
另一个坑是并发。我让AI写一个简单的内存计数器库,AI用了let count = 0加自增操作。在单线程JavaScript里没问题,但这个库后来被用在了Worker线程共享的场景里,结果计数丢失。
AI生成并发代码时,默认假设是单线程或加锁环境,它不会主动考虑你实际运行时的并发模型。如果你的库涉及共享状态,必须自己审查并发安全性,或者明确在文档里声明“非线程安全”。
4.3 依赖地狱:AI喜欢引入新依赖
AI在实现功能时,倾向于引入它“熟悉”的第三方库。比如让它写一个日期处理,它可能直接import dayjs。但你的库可能定位是“零依赖”,或者你已经有自己的日期工具了。
我的做法是:在让AI生成实现前,明确告诉它“不允许引入任何第三方依赖”或“只允许使用标准库”。这个约束必须写在提示词里,否则AI会自作主张。
4.4 许可证污染
更隐蔽的坑是许可证。AI训练数据里包含各种开源代码,它生成的代码可能和某个GPL库高度相似。虽然目前法律上对AI生成代码的许可证问题还没有定论,但如果你要把库开源或商用,最好用代码相似度检测工具扫一遍。我自己的习惯是:核心算法部分尽量自己写,AI只用来生成样板代码和测试。
5. 现在到底该怎么定位“库构建”这件事
5.1 把库分成“消耗品”和“资产”两类
我现在会把要写的库先分个类:
- 消耗品库:一次性使用、生命周期短、只服务当前项目。这类库我直接让AI生成,不写文档、不发版本、不追求通用性,用完就扔。
- 资产库:会被多个项目引用、需要长期维护、有外部用户。这类库我仍然会投入大量精力做API设计和文档,AI只用来加速实现和测试。
这个分类帮我省下了大量时间。以前我会纠结“这个函数要不要抽成库”,现在我会先问自己:它是消耗品还是资产?消耗品就快速生成,资产就认真对待。
5.2 API设计能力反而更值钱了
当AI能写出大部分实现代码时,“设计一个好用的接口”这件事的稀缺性反而上升了。因为实现可以生成,但接口设计需要判断力:哪些参数该合并成对象?错误该抛异常还是返回Result?命名该用动词还是名词?
我最近在 review 一个AI生成的库时发现,它的功能完全正确,但API设计得很别扭:getData、fetchData、retrieveData三个方法做几乎一样的事,只是参数不同。这种库即使实现没问题,用起来也会让人抓狂。而修正这种设计问题,比修正一个bug要难得多,因为你要考虑向后兼容。
5.3 测试的重要性不降反升
AI生成的代码越多,测试就越重要。因为AI代码的“表面正确性”很高,你很难通过读代码发现隐藏问题。只有测试能给你信心。
我现在对AI生成的库代码,要求测试覆盖率不低于90%,而且必须包含边界测试和异常测试。这个标准比我手写代码时还高,因为手写代码时我对每一行都有直觉,而AI代码我需要靠测试来建立信任。
5.4 文档的“人味”成了差异化优势
当大家都能用AI生成API文档时,一份带有设计思考、使用场景、踩坑记录的文档就变得稀缺了。我现在写库文档时,会特意加一节“什么时候不该用这个库”,以及“和同类方案相比的取舍”。这些内容AI写不出来,但恰恰是用户最需要的。
6. 一个具体的小库:从AI辅助到人工打磨的完整过程
6.1 需求:一个带重试的异步任务执行器
为了把上面的思路串起来,我拿最近写的一个小库举例。需求很简单:封装一个retry函数,支持最大重试次数、退避策略、超时、以及自定义重试判断。
6.2 第一步:人工定义接口契约
我没有直接让AI写代码,而是先自己写了一段接口描述:
interface RetryOptions { maxAttempts: number; baseDelay: number; maxDelay: number; timeout?: number; shouldRetry?: (error: unknown, attempt: number) => boolean; onRetry?: (error: unknown, attempt: number, delay: number) => void; } function retry<T>(fn: () => Promise<T>, options: RetryOptions): Promise<T>;然后我把这段描述给AI,让它生成测试用例。AI生成了20多个测试,我筛选后保留了12个,覆盖了:正常成功、首次失败后成功、达到最大次数失败、超时、shouldRetry返回false、onRetry回调、退避延迟计算等。
6.3 第二步:让AI生成实现,但限制依赖
提示词里我明确写了:“只使用标准库,不引入任何第三方依赖。使用指数退避,加入抖动。超时用Promise.race实现,注意清理定时器。”
AI生成的实现基本可用,但我发现两个问题:一是超时后没有取消原始Promise(这在JavaScript里很难做到,但至少要在文档里说明);二是抖动算法用了Math.random(),导致测试不稳定。我把抖动改成可注入的随机函数,默认用Math.random,测试时传入固定值。
6.4 第三步:人工补充边界处理和文档
AI没有处理的一个边界是:maxAttempts为0或负数时应该直接抛错。我补上了这个检查。另外,我在文档里加了一段“已知限制”:超时不会取消底层操作,如果底层操作有副作用,需要调用方自己处理幂等性。
6.5 第四步:版本发布与后续维护
这个库我发了1.0.0,之后收到一个issue:希望支持“重试间隔固定”而不是指数退避。我评估后认为这是新增功能,不影响现有行为,于是发了1.1.0。整个过程AI帮我写了CHANGELOG初稿和迁移说明,但版本号是我自己定的。
这个库总共花了大约3小时,其中AI辅助编码占1小时,人工设计、审查、文档占2小时。如果全手写,我估计需要6到8小时。效率提升明显,但人的判断仍然是核心。
7. 如果你现在要开始一个库,我的实操建议
7.1 先问自己三个问题
在动手之前,我会问自己:
- 这个库是消耗品还是资产?消耗品就快速AI生成,资产就认真设计。
- 它的核心价值在实现还是在接口?如果核心价值在接口设计,AI只能打辅助。
- 我愿意为它维护多久?如果答案少于半年,建议不要做成库,直接复制代码。
7.2 提示词里必须写清楚的约束
根据我的经验,让AI辅助写库时,提示词里一定要包含这些约束:
- 允许使用的依赖范围(标准库/特定库/零依赖)
- 目标运行环境(Node版本、浏览器版本、是否支持Worker)
- 并发模型(单线程/多线程/需要加锁)
- 错误处理策略(抛异常/返回Result/回调)
- 测试框架和覆盖率要求
- 是否允许使用某些语言特性(如
structuredClone、Proxy)
这些约束不写清楚,AI就会按它的“默认习惯”来,而它的默认习惯往往和你的项目不匹配。
7.3 审查AI代码时重点看什么
我审查AI生成的库代码时,会重点看这几个地方:
- 边界条件:空值、零值、负数、超大值、重复调用。
- 资源清理:定时器、事件监听、文件句柄是否在异常路径下也能释放。
- 并发安全:共享状态是否有竞态条件。
- 依赖引入:是否引入了未声明的依赖。
- 错误信息:错误信息是否包含足够上下文,方便排查。
- 类型安全:TypeScript类型是否过于宽松(比如用了
any)。
7.4 文档里必须有的三样东西
不管库多小,我都会在文档里放这三样:
- 快速上手示例:复制粘贴就能跑。
- 设计决策说明:为什么这样设计,取舍是什么。
- 已知限制和不该用的场景:帮用户避免误用。
这三样里,AI能帮你写第一样,后两样必须自己来。
8. 回到那个问题:库构建被AI淘汰了吗
我的答案是:“写代码”这个环节被大幅加速了,“构建可靠库”这件事反而更需要人了。AI把实现的门槛拉低了,但把设计、验证、维护的门槛相对拉高了。以前你可以靠“实现能力强”来掩盖设计上的不足,现在实现能力被AI拉平了,设计能力和工程判断力就成了真正的分水岭。
我现在的状态是:小工具库基本让AI生成,我花时间审查和补文档;核心库仍然自己设计接口,AI辅助实现和测试;消耗品代码直接AI生成,用完就扔。这个分工让我既能享受AI的效率,又不至于让代码质量失控。
最后分享一个我最近养成的习惯:每次让AI生成库代码后,我会隔一天再 review。隔一天的好处是,我会忘记“这是我让AI写的”,从而用更挑剔的眼光去看它。很多当时觉得没问题的代码,隔天再看就能发现设计上的别扭之处。这个习惯帮我拦下了不少“能跑但不好用”的库。
如果你也在纠结要不要继续写库,我的建议是:别纠结“要不要”,先想清楚“为谁写、写多久”。想清楚了,AI自然知道该放在哪个位置。