写点什么

软件设计原则失效了?怎么把代码写得更实在,最新神文让 AI 大神卡帕西和马斯克都点赞

  • 2024-12-30
    北京
  • 本文字数:2723 字

    阅读完需:约 9 分钟

软件设计原则失效了?怎么把代码写得更实在,最新神文让 AI 大神卡帕西和马斯克都点赞

最近,技术圈掀起了一波关于“怎么减少代码认知负荷”的热烈讨论。“导火索”是这样一篇文章:《Cognitive load is what matters》(《认知负荷才是关键》),阐述了为什么写代码最重要的事,不是用什么高级架构,不是追逐酷炫技术,而是别让人看了你的代码后“想骂人”。文章在 Hacker News 上迅速占据热榜,引发了技术社区的广泛共鸣。


作者 Artem Zakirullin 是一位经验丰富的软件架构师,拥有超过 13 年的软件开发和工程经验。他目前担任软件开发公司 Inktech 的 CTO 一职。


OpenAI 创始成员之一、前特斯拉 AI 主管 Andrej Karpathy 在 X 平台上转发了这篇文章,发出感慨评价:“这大概是最真实却最少被实践的观点。”



并且,埃隆·马斯克也在 Karpathy 的帖子下评论道:“确实。”


认知负荷为什么如此重要

文章提出了一个人人都能感受到但少有人正视的问题:写代码时的“认知负荷”(Cognitive Load)。


所谓认知负荷,是指开发人员为了完成一项任务所需要承担的思考量。也可以简单理解为:为了完成一项任务,需要动多少脑子、占用多少注意力。


特别是当我们阅读代码时,需要将变量值、控制流逻辑和调用序列等内容塞进自己的脑袋。普通人能够支配的记忆力大约可以容纳 4 个这样的构建块,而一旦认知负荷达到了这个阈值,对于事物的理解难度就会开始陡然上升。



想象一下,你需要接手一个完全陌生的项目并进行修复。当你打开代码,迎面而来的是前一位“天才开发者”留下的大量酷炫的架构设计、花哨的库以及时下流行的技术方案。这种情况下,你不仅要理解项目本身的逻辑,还得费力去消化这些复杂的设计决策——这就是典型的高认知负荷


Artem Zakirullin 进一步将认知负荷分为两种:


  • 内在负荷:源自任务固有的难度,这种没法避免,也是软件开发工作的核心所在。

  • 外在负荷:由信息的呈现方式导致,与任务本身无关,例如聪明作者们的个人习惯。这方面负荷是可以大大降低的。



因此, Zakirullin 在文章中的核心主张就是,尽量减少项目中的外在认知负荷

“减负秘笈”


那么,如何减少外在认知负荷?结合开发中常见的“认知雷区”例子,Zakirullin 给了一些接地气的建议:

1.复杂条件语句:拜托别玩脑筋急转弯

这类代码你可能见过:


if val > someConstant // 🧠+    && (condition2 || condition3) // 🧠+++,上一条件应当为真,c2或c3之一必须为真    && (condition4 && !condition5) { // 🤯,到这一步我们的脑袋就快爆炸了……    ...}
复制代码


解决办法?引入清晰的中间变量:


isValid = val > someConstantisAllowed = condition2 || condition3isSecure = condition4 && !condition5// 🧠,有了描述性变量,我们就不再需要记住条件if isValid && isAllowed && isSecure {    ...}
复制代码

2. 继承噩梦:少用继承,多用组合

“继承链”是认知负荷的天花板。你改 AdminController 的代码,发现它继承了 UserController,后者又继承了 GuestController,还不止这些——改着改着,突然冒出来个 SuperuserController,让你意识到一处改动可能引发蝴蝶效应。


解决办法?别玩继承了,多试试组合吧。

3. 小模块≠简单:浅模块问题

开发界流传着一种迷思,像“方法应该少于 15 行代码”或者“类不能太大”之类的常规习惯实际上是错的。


事实证明,结果一不小心,你的项目里塞满了多个小模块,那维护起来比修一张蜘蛛网还难。


  • 深模块——接口简单,但功能复杂;

  • 浅模块——相对于所提供的小功能,其接口相对复杂。



Zakirullin 认为更好的方法是设计深模块:隐藏内部复杂性,只暴露一个简单的接口。比如 UNIX 的 I/O 接口就非常简单,只有五个基本调用:

open(path, flags, permissions)read(fd, buffer, count)write(fd, buffer, count)lseek(fd, offset, referencePosition)close(fd)
复制代码


“信息隐藏非常重要,而浅模块中无法隐藏太多的复杂性。”Zakirullin 在文章中说道。

4. 语言功能越多越好吗

当遇到喜欢的编程语言发布新功能时,谁能不感到兴奋并迫不及待想用起来。然而,文章还提醒说,这种一时热情的背后藏着一个巨大的认知负荷陷阱。


Zakirullin 引用了 Rob Pike(Go 语言的主要设计者之一)的一句话:要通过限制选项的数量来降低认知负荷。


举个例子,当你用了一些新特性写了代码,可能当时觉得很“优雅”。但几个月后,当你回头看这些代码时,你不仅需要重新理解代码逻辑,还得想起“我当时为啥要用这个特性来解决这个问题?”


这无疑增加了外在认知负荷。简言之,丰富的语言功能肯定不是坏事,但前提这些功能要彼此关联。

5.别滥用分层架构

有人说,架构设计要“优雅”,于是各种抽象层、接口层、适配器层层叠叠。等到真要查 Bug 时,开发者得顺着 5 层调用链一路追溯问题来源,头发掉得比写论文还快。


分层架构的初衷是通过抽象隐藏复杂性。有些人可能以为这样的分层有助于快速替换数据库或者其他依赖项,但事实并非如此。存储方面的变更会导致很多问题。


Zakirullin 强调,开发过程中最不需要担心的就是数据访问层上的抽象机制。充其量,这些抽象能够节约 10%的迁移时间(这还是在比较乐观的情况下),而真正的麻烦永远来自数据模型不兼容、通信协议、分布式系统挑战以及隐式接口。


所以,既然后续根本没有回报,又何必要为这种分层架构付出高昂的认知负荷成本呢?


总之,分层架构只有在需要明确扩展点时才有意义。否则,这些层次带来的额外认知负荷远高于它能提供的好处。

6.领域驱动设计(DDD):用对了是神器,用错了是灾难

文章还提到另一个开发者热衷但经常误解的概念:领域驱动设计(DDD)。DDD 本质是关于问题空间的思考,而不是解决方案空间的代码设计。


问题是,许多团队在实践中“跑偏”了。本应专注于领域建模和边界划分的 DDD,变成了某种固定的文件夹结构、服务命名规则、或者对“Repository 模式”的机械化崇拜。


每个人对于 DDD 的理解和解释方式都可能独特且主观。如果基于这样的理解来编写代码,则必然会创造出大量非必要的认知负荷——相当于给后续接手的开发人员埋下一颗颗炸雷。

7.熟悉项目 VS 认知负荷

认知负荷高低,决定了一个项目对开发者有多“友好”。熟悉项目的开发者,已经把代码逻辑内化到脑子里,干活轻松省事;但对新人来说,如果项目充满谜语一样的命名、复杂的模块关系和稀缺的文档,他们很快就会懵圈,效率直线下降。



文章给了一个建议:每当有新人加入项目时,请尝试衡量他们的懵圈程度。如果他们花了 40 分钟还是一头雾水,那肯定意味着代码中存在需要改进的地方。


如果能将认知负荷始终保持在较低水平,那么新人在入职后的几个小时内就可以为代码库做出贡献。

写在最后

减少认知负荷并不是一个高深的哲学,而是开发者日常需要实践的基本功。或许,每次写代码时,我们都该问问自己:


“这段代码,是帮同事省脑子,还是让他们掉头发?”


参考链接:

https://minds.md/zakirullin/cognitive

2024-12-30 15:2216240

评论

发布
暂无评论

【七夕限定盲盒抽奖】一文带你搞懂盲盒抽奖的页面配置

hum建应用专家

阿里云 Serverless 函数计算

苦熬三个月整理的spring全家桶PDF版限时分享,整整400页

Java工程师

Java spring Spring全家桶

编译器工程师眼中的好代码:Loop Interchange

华为云开发者联盟

c c++ 开发 编译器

Github又火了!阿里重发系统设计核心原理全彩笔记,上线两天破百万阅读

Java工程师

Java 高并发

Jmix - 业务系统高效开发的少代码平台

世开 Coding

Java spring 快速开发平台 Jmix 少代码

在SaaS时代,帮助中心成为了许多企业的选择

Baklib

Eclipse Debug FFmpeg

贾献华

8月月更

【计算讲谈社】第七讲|AI 的价值探索:如何拓展商业边界?

大咖说

人工智能 AI 商业边界

观测云入驻青云云市场,提升云上系统统一可观测能力

观测云

学习大数据开发技术后好找工作吗?

小谷哥

混合云存储点燃万亿自动驾驶市场,加速产品落地

焱融科技

自动驾驶 AI 分布式文件存储 全闪

【云原生】Spring Cloud是什么?Spring Cloud版本介绍

java李杨勇

Java spring cloud stream 签约计划第三季

培训机构与自学的优缺点都有什么

小谷哥

Centos系统安装MySQL数据库

杨杰灵

MySQL 数据库

理解JavaScript中的“面向对象”

珲少

MySQL 概念

武师叔

8月月更

最具有中国特色的微服务组件!阿里新一代SpringCloud学习指南

JAVA活菩萨

Java 程序员面试 大厂技能 秋招 大厂面经

深入Linux内核IO技术栈

C++后台开发

Linux内核 内核开发 驱动开发 嵌入式开发 内核操作系统

Python图像处理丨如何调用OpenCV绘制直方图

华为云开发者联盟

Python 人工智能

认识一下MRS里的“中间人”Alluxio

华为云开发者联盟

大数据 MRS

北京零基础前端软件培训

小谷哥

AIRIOT答疑第7期|如何快速提升物联网项目交付速度?

AIRIOT

物联网

编译器工程师眼中的好代码(1):Loop Interchange

openEuler

开源 编译器 openEuler

优化客户服务的 7 个关键步骤

Geek_da0866

如何编写有效的FAQ常见问题页面

Baklib

语音聊天app开发——开发人员如何进行代码分析

开源直播系统源码

软件开发 直播源码 语音app开发

企业为什么要数字化转型?数字化转型成功的案例有哪些?

优秀

数字化转型

MASA Stack 第三期社区例会

MASA技术团队

Framework blazor

倪光南:openEuler已达国际同类社区水准

openEuler

开源 操作系统 openEuler

做个男人,做个成熟的男人

源字节1号

软件设计原则失效了?怎么把代码写得更实在,最新神文让 AI 大神卡帕西和马斯克都点赞_架构_罗燕珊_InfoQ精选文章