如果你曾经想要为代码库添加接口,但却遭到了反对,那么你肯定听说过这样的论点:“为了实现同样的功能,所需的代码量会增加一倍。”

说实话,这个观点确实有一定道理。你需要编写接口、抽象类或协议,然后再进行实现。本来只需要一个文件就能完成的工作,现在却需要两个文件——这样会导致代码量增加,间接引用增多,维护成本也会提高。

Ruby和Rails社区围绕这一理念形成了一套开发哲学:优先遵循约定而非手动配置,减少繁琐步骤,降低编码工作量。如果框架能够理解你的意图,为什么还要把它们明确写出来呢?

然后人工智能出现了。

最近我和一位CEO讨论了当代软件工程师们常犯的错误,他总结得非常到位:

“几个月前,抽象接口确实会让人觉得麻烦,因为编写这样的代码需要双倍的功夫。但有了人工智能,编码成本已经大大降低了。我们仍然需要这些抽象结构,是因为在某些情况下,人类还是需要亲自查看代码。而接口可以帮助减少人们的认知负担。”

这个观点让我印象深刻。编写代码的成本下降了,而阅读代码的成本却几乎没有变化。这种不对称性彻底改变了我们对抽象设计的思考方式。

下面我来详细解释一下我的意思。

目录

你的大脑才是真正的瓶颈

这并不是一个纯粹基于主观感受的观点。实际上,神经科学为接口的作用提供了有力的支持。

1988年,教育心理学家约翰·斯威勒提出了“认知负荷理论”。2022年ACM的综述文章详细介绍了这一理论在计算机教育领域的应用情况。

简单来说,在处理信息时,你的大脑会面临三种类型的负荷:内在负荷指的是问题本身带来的难度;外在负荷是指那些杂乱无章的信息、不必要的细节或糟糕的命名方式;而相关负荷则是指构建有用思维模型所需要付出的努力。

关键在于:你的工作记忆一次只能容纳少量的信息——认知科学家通常认为这个数量在2到6个之间。这里说的不是2到6个文件或类,而是2到6个信息单元而已。

费莉安·赫尔曼斯在《程序员的大脑》(2021年出版)一书中探讨了这一现象,她认为设计模式起到了帮助人们将复杂信息进行整合的作用。当你识别出某种策略模式时,你的大脑会将整个类层次结构压缩成一个认知单元——“策略”这个词就取代了原本需要理解的五个类及其相互关系。这并不是在空谈代码的简洁性,而是人类记忆的实际运作方式。

我们甚至可以通过脑部扫描来验证这一现象。2021年,诺曼·佩特克和珍妮特·西格蒙德领导的团队发表了一项关于程序理解能力的fMRI研究,该研究在ICSE会议上获得了ACM SIGSOFT杰出论文奖。他们让开发者进入脑部扫描设备,观察他们在阅读代码时的大脑活动情况。研究结果发现:在语义层面上理解代码的含义,所需的神经激活量远远少于从语法层面分析代码的运作方式。

一个良好的接口能够帮助人们在语义层面上理解代码。《UserRepository.findById(id)`这样的接口能够让你无需了解其具体实现细节,就能掌握所需的所有信息。你的大脑不需要同时记住SQL查询语句、连接池的逻辑、错误处理机制以及结果映射规则,因为这个接口将所有这些内容整合成了一个简洁的整体。

这并不是什么优雅的设计手法,而是神经科学领域的发现。

**伟大的先驱们早已认识到了这一点**。计算机科学的基础是由那些在我们大多数人出生之前就已经开始研究这一领域的人们奠定的。迪杰斯特拉曾准确地指出:“抽象的意义不在于使表述变得模糊不清,而在于创造一个新的语义层次,在这个层面上我们可以做到绝对精确。”

抽象并不是为了隐藏那些无法理解复杂事物的人所不了解的内容,而是为了创建一个让我们能够清晰地进行推理的思考框架。大卫·帕纳斯在1972年发表的ACM论文中正式阐述了信息隐藏的概念:“每个模块都体现了其对某种设计决策的理解,而这种理解会被隐藏起来,不让其他模块知道。”他证明了,按照设计决策来分解系统,而不是按照处理步骤来划分,所得到的模块既更加灵活,也更容易被理解。可理解性并不是附加的收获,而是设计的核心标准。

托尼·霍尔认为,抽象是人类智慧最强大的工具之一,它让我们能够通过关注关键要素、忽略无关细节来有效管理复杂性。马丁·福勒则用更通俗的语言表达了这一观点:“任何傻瓜都能写出计算机能理解的代码,但优秀的程序员写的却是人类能够理解的代码。”

约翰·奥斯特豪特的著作《软件设计哲学》(2018年出版)更是明确指出了抽象与认知负担之间的关系。他的核心观点是:如果某种代码格式能够减少人们的认知负担,那么这些代码实际上可能会变得更加简单易懂。

他提出的“深度模块”概念——即用简单的接口来隐藏复杂的实现机制——本质上是在说明:接口在代码中确实具有重要的价值。Unix文件系统API(`open`、`close`、`read`、`write`、`lseek`)这五个函数,实际上隐藏了极其复杂的逻辑结构,这就是典型的“深度模块”,也正是我们应该追求的目标。

“四人组”在他们的书中将这一理念放在首位,这是有原因的。书的第一页就写着:“应该根据接口来编写程序,而不是依据具体的实现方式。”

这些观点本身并没有争议,但当你的AI工具能在三秒钟内生成200行功能完备的代码时,这些原则就很容易被人们忘记了。

经济形势已经发生了变化

在这里,CEO们的洞察力就转化成了具有经济学意义的论据。

过去,反对使用接口的主要理由总是与“编写代码的成本”有关。使用接口意味着需要编写更多的代码、创建更多的文件,还需要维护大量的样板代码。Python、Ruby、JavaScript这类动态类型语言的出现,在一定程度上正是对Java等语言所要求的大量繁琐编码规范的一种反应——人们更倾向于遵循约定而非重复劳动,认为“少即是多”。

但试问自己:如今,编写这些样板代码的成本到底还有多少呢?

GitHub在2022年进行的一项研究发现,使用Copilot工具的开发者完成任务的速度快了55%。那些过去被认为会导致必须使用接口的样板代码——比如额外的文件、类型定义、方法签名等——现在只需几秒钟就能生成。因此,编写接口所花费的成本实际上已经降为零。

不过需要注意的是,阅读这些代码所需的成本却并没有因此而减少。

罗伯特·C·马丁在《清洁代码》一书中指出,开发者花在阅读代码上的时间远远多于编写代码的时间——他给出的比例是10比1。

这个数字可能有些夸张,但各种研究得出的结论都是一致的。一项针对78名专业开发者的大规模调查发现,他们在3,148个工作小时中,约有58%的时间用于理解代码的含义。新入职的开发者通常需要花费六周的时间来熟悉现有的系统,而这段时间主要用于理解现有代码,而非编写新的代码。

Addy Osmani非常准确地指出了这种不对称性。他在2026年3月发表的文章中提到了“理解债务”这一概念:

“当团队中的开发者编写代码时,人工审核过程历来都是一个瓶颈——但这个过程同时也是富有成效且有助于学习的。阅读他们提交的代码确实能帮助人们更好地理解这些代码。然而,AI生成的代码打破了这种反馈机制,因为生成代码的速度太快了,导致人们没有足够的时间去理解这些代码。”

AI生成的代码看起来很规范,也能通过代码检查工具的检测,而且符合各种编码规范——这些特征在过去确实能让开发者更有信心将这样的代码合并到项目中。但“理解债务”与“技术债务”不同,因为它是在人们毫无察觉的情况下逐渐积累起来的。你的开发效率指标、DORA评分以及提交的代码数量看起来都一切正常,但实际上,团队对整个代码库的理解程度却在不知不觉中逐渐下降。

那么,具体的数据是这样的:人工智能将编写抽象代码的成本降到了接近于零的水平。然而,不使用抽象代码所带来的一系列问题——比如增加人类的阅读难度、降低新员工的上手效率以及影响代码的可理解性——这些成本却丝毫没有改变。因此,“这个界面是否值得使用?”这个问题的临界点已经发生了巨大的变化,现在显然更倾向于“是值得的”这一答案。

数据为这一观点提供了有力支持

这并不是理论上的推论。我们拥有实际数据来证明当人工智能在没有合理抽象设计的情况下生成代码时会发生什么。

GitClear在2020年至2024年期间分析了2.11亿行被修改过的代码。他们的研究结果显示:与人工智能出现之前相比,代码的更改频率——即那些在两周内就被重新修改或更新的代码行数——增加了一倍;复制粘贴的代码块所占的比例也从8.3%上升到了12.3%;而与代码重构相关的修改则从25%下降到了不足10%。

用他们的话来说,人工智能生成的代码“就像是一个流动性很强的贡献者,很容易破坏所访问代码库中的设计规则”。

METR在2025年进行的一项研究得出了更加惊人的结果。经验丰富的开源开发者们原本预测,使用人工智能会让他们的工作效率提高24%;而他们在实际使用后认为效率提高了20%——但实际上效率反而降低了19%。这种认知上的差距很能说明问题:当你编写那些日后会增加工作量的代码时,你可能会觉得自己非常高效。

还有一项来自Anthropic公司的研究(没错,就是那个开发了Claude的公司)。他们观察了52名软件工程师学习新库的过程。其中,使用人工智能辅助学习的小组完成任务的速度与对照组相同,但在后续的理解测试中他们的得分却低了17%——具体来说,他们的正确率仅为67%,而对照组的正确率为83%。其中,调试能力下降得最为明显。你可以发布那些你并不完全理解的代码,但你无法调试那些你不了解的代码。

Kent Beck直截了当地说:“我90%的技能现在都变得毫无价值了;而剩下的10%技能的价值却提高了1000倍。”他并没有明确说明那剩下的10%技能具体指的是什么,但这一点很容易让人联想到系统设计的重要性。

反对抽象设计的人的观点,以及为什么这些观点实际上也与事实相符

如果我不讨论那些反对使用抽象设计的人的观点,那就太不诚实了。其中有些人确实非常聪明。

Casey Muratori在《Clean Code, Horrible Performance》一文中指出,多态性和虚拟调用机制有时会使得代码的运行速度比直接使用过程式编程方法慢10到15倍。

他的测试结果是真实的。如果你正在开发游戏引擎或高频交易系统,那么在那些关键路径上使用抽象接口确实会带来负面影响。

Dan Abramov在看到过早使用抽象设计使得自己的代码库变得更难维护之后,写下了《Goodbye, Clean Code》这篇文章。

“我的代码以牺牲可修改性的代价来减少代码重复,但这种交换并不划算。”

Sandi Metz更直截了当地指出:“代码重复带来的成本远低于使用错误的抽象层次结构所带来的损失。”

而Rich Hickey在他的演讲《Simple Made Easy》中也明确指出了这一关键区别:简单并不意味着容易理解。错误的抽象设计反而会使代码变得复杂——它们会将各种问题纠缠在一起,而不是将它们分开处理。

需要强调的是,这些观点并不是在反对抽象设计本身,而是在反对糟糕的抽象设计方法。

Muratori提出的性能考量主要适用于那些对性能要求极高的系统中的关键路径,而不适用于你的REST API服务层。Abramov和Metz则反对过早进行抽象设计——在尚未充分理解业务需求之前就套用某些抽象模式。而Hickey的整个演讲都在倡导使用正确的抽象设计方法,那些真正能够简化代码结构而非使其变得复杂的抽象方式。

具有讽刺意味的是,在人工智能辅助开发的背景下,这些观点反而更容易被落实。你可以先生成没有使用抽象设计的原始代码版本,让它稳定运行一段时间,观察其中出现的规律模式,然后再提取出相应的抽象概念——让人工智能来处理后续的代码重构工作。这样一来,“先重复编写代码,再进行抽象设计”这种做法的成本几乎降为零。

这对你意味着什么

如果你正在使用人工智能工具来编写代码——事实上,现在我们大多数人都正在这样做——那么你可能会想让人工智能自动生成代码即可,因为这样代码能够通过测试,也可以直接投入使用。

但“代码能运行”只是一个基本要求。更重要的问题是:下一个阅读这段代码的人能否在五分钟内理解它?而你自己六个月后还能理解它吗?

接口设计的意义并不在于让代码看起来更美观,或者满足某些抽象的设计原则。它们其实是帮助人类大脑进行理解的工具,能让人们从语义层面而不是语法层面来理解代码。既然人工智能已经消除了创建接口时唯一的真正成本——那些重复性的编写工作,那么再不使用接口设计就没有任何经济上的理由了。

规则并没有改变,只是用来逃避这些规则的借口已经不再成立了。

参考文献

学术论文

演讲与博客文章

Comments are closed.