写点什么

软件设计原则失效了?怎么把代码写得更实在,最新神文让 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:2215769

评论

发布
暂无评论

更新啦!第 59 期《HelloGitHub》开源月刊

HelloGitHub

GitHub 开源

Elasticsearch Fetch Phase

escray

elastic 七日更 28天写作 死磕Elasticsearch 60天通过Elastic认证考试

Linux入门篇 —— Linux 磁盘管理之磁盘理论篇

若尘

Linux linux编程 磁盘

工作两三年了,整不明白架构图都画啥?

小傅哥

Java 后端 小傅哥 架构设计 画架构图

程序员之禅(一)

每天读本书

读书笔记

Wiki.js 配置 LDAP 认证

东风微鸣

wiki

华为云举办AI经典论文复现活动,打造领先AI开发者学习社区

华为云开发者联盟

AI 华为云 modelarts 论文 AI Gallery

程序员专属“灯谜”大挑战,答对六题算你赢!

京东科技开发者

编程语言 集群

海豚调度dolphinscheduler SQL脚本初始化流程

cloudcoder

海豚调度 调度引擎 分布式任务调度

技术案例 | 云原生微服务落地难?百度自用CRM这样做

百度开发者中心

微服务 CRM #百度智能云#

国产芯片WiFi物联网智能插座—电耗采集功能设计

不脱发的程序猿

28天写作 国产芯片 电耗检测 电压电流 华大MCU

EEPROM CAT24CXX实现分页读、写数据

不脱发的程序猿

28天写作 CAT24C08 EEPROM 嵌入式软件 单片机

GaussDB(DWS):非侵入式备份及其在NBU上的应用

华为云开发者联盟

架构 GaussDB 集群 备份 NBU

刷屏洗脑的“吗咿呀嘿”,到底是个啥?

架构精进之路

商业模式 28天写作 3月日更

MySQL字段默认值设置详解

Simon

MySQL 数据库

构建一套适合微服务的高可用架构

环信

QA视角看数据匿名化

BY林子

数据安全 测试右移 用户数据 数据脱敏

如何写好一份解决方案

数列科技杨德华

28天写作

android布局优化!Android屏幕适配很难嘛?其实也就那么回事,内含福利

欢喜学安卓

android 程序员 面试 移动开发

android程序开发!2021Android精选面试实战总结整理,大厂直通车!

欢喜学安卓

android 程序员 面试 移动开发

元宵节元宵钱,不买元宵买云资源! | 2核4G低至0.79元/天

京东科技开发者

云主机 云服务器 云存储 云硬盘

看完你就明白什么是图神经网络

华为云开发者联盟

神经网络 深度学习 节点 图神经网络 图结构

树莓派上的家庭监控中心

冯骐

运维 树莓派 监控系统 Open-Falcon 物联网,

使用 pyVmomi 采集 vSphere 监控指标

冯骐

Python 运维 监控 Open-Falcon vpshere

LeetCode题解:123. 买卖股票的最佳时机 III,动态规划,JavaScript,详细注释

Lee Chen

算法 大前端 LeetCode

树莓派上的温湿度环境监控

冯骐

运维 树莓派 物联网 监控告警

MongoDB 在评论中台的实践

vivo互联网技术

数据库 mongodb 分布式 集群

华为云原生数据仓库GaussDB(DWS)深度技术解读:融、快、大、稳、易

华为云开发者联盟

数据库 云原生 华为云 GaussDB 数仓

搭建一个 802.1x 的 web 测试服务

冯骐

网络 监控系统 Open-Falcon radius eduroam

山东青岛推进平安小区建设!源中瑞智慧社区平台解决方案

源中瑞-龙先生

解决方案 山东 源中瑞 青岛 智慧社区

OS命令--shell中数组的操作

cloudcoder

数组 Shell 循环引用

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