Andrej Karpathy:軟件開(kāi)發(fā)中認(rèn)知負(fù)荷才是關(guān)鍵
Andrej Karpathy 推薦的關(guān)于軟件開(kāi)發(fā)文章"Cognitive load is what matters",非常值得一讀
“認(rèn)知負(fù)荷才是關(guān)鍵。這或許是最真實(shí)卻最少被實(shí)踐的觀點(diǎn)?!?-Andrej Karpathy
在軟件開(kāi)發(fā)的世界里,充斥著各種流行語(yǔ)和最佳實(shí)踐。然而,有一個(gè)問(wèn)題常常被忽視:認(rèn)知負(fù)擔(dān)。這并不是一個(gè)抽象的理論,而是開(kāi)發(fā)者每天都在面對(duì)的現(xiàn)實(shí)困境。代碼越難理解,開(kāi)發(fā)者的時(shí)間和精力就浪費(fèi)得越多,而這些成本最終都會(huì)轉(zhuǎn)化為團(tuán)隊(duì)的生產(chǎn)力損失
什么是認(rèn)知負(fù)擔(dān)?
簡(jiǎn)單來(lái)說(shuō),認(rèn)知負(fù)擔(dān)就是開(kāi)發(fā)者在完成任務(wù)時(shí)需要思考的量。當(dāng)你閱讀代碼時(shí),你的腦子里需要同時(shí)裝下變量的值、邏輯流程、函數(shù)調(diào)用順序等信息。而問(wèn)題在于,人類的工作記憶容量有限,通常只能同時(shí)處理 4 個(gè)左右的信息塊。一旦超過(guò)這個(gè)閾值,理解能力就會(huì)直線下降。
想象一下,你接手了一個(gè)完全陌生的項(xiàng)目,前開(kāi)發(fā)者使用了各種“高端”架構(gòu)、炫酷的庫(kù)和流行技術(shù)。結(jié)果呢?你還沒(méi)開(kāi)始寫(xiě)代碼,認(rèn)知負(fù)擔(dān)已經(jīng)爆表了
認(rèn)知負(fù)擔(dān)的兩種類型
1. 內(nèi)在認(rèn)知負(fù)擔(dān):由任務(wù)本身的復(fù)雜性引起,無(wú)法避免。例如解決算法問(wèn)題或?qū)崿F(xiàn)復(fù)雜業(yè)務(wù)邏輯
2. 外在認(rèn)知負(fù)擔(dān):由信息的呈現(xiàn)方式引起,與任務(wù)無(wú)直接關(guān)系,比如代碼風(fēng)格怪異、命名不清晰等。這種負(fù)擔(dān)是可以大幅減少的,也是我們應(yīng)該重點(diǎn)優(yōu)化的方向
實(shí)戰(zhàn)案例:如何降低外在認(rèn)知負(fù)擔(dān)?
1. 簡(jiǎn)化復(fù)雜條件語(yǔ)句
if (val > someConstant && (condition2 || condition3) && (condition4 && !condition5)) {
// 什么鬼?讀到這里已經(jīng)頭暈了
}
改進(jìn)后:
isValid = val > someConstant;
isAllowed = condition2 || condition3;
isSecure = condition4 && !condition5;
if (isValid && isAllowed && isSecure) {
// 條件清晰,變量名一目了然
}
通過(guò)引入中間變量,我們不需要再死記硬背每個(gè)條件的細(xì)節(jié),認(rèn)知負(fù)擔(dān)瞬間下降
2. 繼承噩夢(mèng)
AdminController -> UserController -> GuestController -> BaseController
修改某個(gè)功能需要從 BaseController 一路查到 AdminController,甚至還要考慮 SuperuserController 的影響。認(rèn)知負(fù)擔(dān)?爆表!
解決方案: 優(yōu)先使用組合而非繼承。組合模式雖然看起來(lái)不夠“優(yōu)雅”,但卻能極大降低理解代碼的成本。
3. 太多小方法,類,模塊
過(guò)多的小方法、小類或小模塊,會(huì)讓項(xiàng)目變得支離破碎。要理解一個(gè)淺模塊的作用,你往往需要先搞清楚與之相關(guān)的所有模塊,簡(jiǎn)直是噩夢(mèng)
解決方案:深模塊,簡(jiǎn)單接口,復(fù)雜實(shí)現(xiàn)
例如 UNIX 的 I/O 接口,雖然底層實(shí)現(xiàn)有幾十萬(wàn)行代碼,但接口只有 5 個(gè)簡(jiǎn)單的調(diào)用:
open, read, write, lseek, close
這樣的設(shè)計(jì)隱藏了復(fù)雜性,讓開(kāi)發(fā)者更容易上手
減少選擇,限制語(yǔ)言特性
編程語(yǔ)言的新特性總是令人興奮,但特性過(guò)多反而會(huì)增加認(rèn)知負(fù)擔(dān)。你不僅需要理解復(fù)雜的代碼,還要猜測(cè)作者為什么選擇某種特性
解決方案:限制選擇,保持特性獨(dú)立性
正如 Rob Pike 所言,語(yǔ)言特性可以有,但它們必須是正交的,互不干擾。
架構(gòu)設(shè)計(jì):簡(jiǎn)單才是硬道理
層次化架構(gòu)(Layered Architecture)本應(yīng)隱藏復(fù)雜性,但實(shí)際上卻增加了跳轉(zhuǎn)和追蹤的負(fù)擔(dān)。每一層的抽象都需要占據(jù)開(kāi)發(fā)者的工作記憶,這種設(shè)計(jì)往往得不償失。
解決方案:避免過(guò)度抽象,遵循實(shí)際需求
不要為了優(yōu)雅的架構(gòu)而添加無(wú)意義的層,抽象層應(yīng)該為功能擴(kuò)展服務(wù),而非制造額外負(fù)擔(dān)
結(jié)語(yǔ):認(rèn)知負(fù)擔(dān),開(kāi)發(fā)者的隱形成本
代碼的復(fù)雜性不應(yīng)該成為團(tuán)隊(duì)的負(fù)擔(dān)。無(wú)論是通過(guò)清晰的命名、簡(jiǎn)化的邏輯,還是合理的架構(gòu)設(shè)計(jì),我們都應(yīng)該盡量減少外在認(rèn)知負(fù)擔(dān)
記?。航档驼J(rèn)知負(fù)擔(dān),不僅是對(duì)自己負(fù)責(zé),也是對(duì)團(tuán)隊(duì)負(fù)責(zé)