[toc] ## 『A Philosophy of Software Design』 {%youtube bmSAYlu0NcY %} :::info :information_source: [A Philosophy of Software Design | John Ousterhout | Talks at Google](https://www.youtube.com/watch?v=bmSAYlu0NcY) - :timer_clock: 約 1 小時 講者資訊 ([wiki](https://en.wikipedia.org/wiki/John_Ousterhout)) - Professor of computer science at Stanford University - Created the Tcl scripting language and the Tk platform-independent widget toolkit - 最近幾年因為 K8S + etcd,也很紅的共識演算法:[Raft algorithm](https://raft.github.io/),John 也是共同作者之一 (指導教授) ::: ### 什麼是 Good Software Design? :::info :memo: 為什麼想要有比較好的 design? - 因為你希望系統能夠適應持續疊加的需求、保持良好的開發效率與程式效能 -> ***scalability*** :up: - 因為你想要盡可能最小化系統的複雜度 -> ***maintainability*** :up: - 減少處理沒有營養的技術債的機會,可更專注在學習能提升自我價值的技術上 -> ***personal ability*** :up: ::: John 提到,過去幾十年來都沒有任何一門課程清楚地論述並教導如何做出好的 software design。 John 在反思及尋找答案的過程中,也設計了 CS 190 這門課,試圖去教授一些他覺得如何設計出好軟體的方法。 他參考英文寫作教學的方式,**學生寫、老師改、提供回饋、學生再寫**,這樣的 iterative approach 來教 software design: ![](https://i.imgur.com/HcoPn6O.png) 他也透過這些年來的教學,整理出了不少概念,可以作為軟體設計撰寫的指導原則。 #### 所以,一個好的 software design 應該有什麼樣子? ![](https://i.imgur.com/OELQdAU.png) 紅字是此 talk 挑出來分享的: - Code 能動還不夠,需要不斷**降低複雜度** - 策略型編程 vs. 戰略型編程 - Class 應該要做到**深層抽象** - 妥善規劃錯誤處理,就像是它不存在一樣 其餘的部分,在書中可找到更多討論。 這並不是什麼很明確的 step-by-step recipe,皆是概念原則。期待的是大家有了這些原則後,再對具體程式碼開啟討論。 課程中,會透過 ***code review*** 的過程,不斷地指出程式碼如何違反上述的原則、造成什麼後果,並且再嘗試提出改善方案。 最後希望讓大家在心中埋下的是對於 ***:flag-al: red flags*** 的警覺心,而不只是一個照著做的 recipe。 ### 重點摘要:Classes Should be Deep 這是一個思考如何做到 ***information hiding*** 的思路。 :::info :information_source: information hiding 是 object-oriented design 在談 encapsulation 時提到的原則。 ::: ![](https://i.imgur.com/iKcJPWe.png) 想像你寫的任何一個 class 是一個矩形,他的面積是你這個 class 能提供給 user (i.e. caller) 的 ***benefits***。 :::info :information_source: class 不是語法上的 `class`。可以小至一個 function, method, module,大至 functionality, sub-system 都是可類比的概念。 ::: 而它的上邊可以類比成是它的 ***interface*** ,是 user 要***使用這個 class 所必須知道的知識*** (not just function signature)。 這個 interface 對 user 來說,當決掉要使用這個 class 時,它就是一種***對系統額外增加的 complexity cost***。而我們身為軟體工程師/軟體設計師一定是希望這個 cost 能越低越好。 所以,我們應傾向撰寫 deep class,而非 shallow class。 #### Example: 最典型的 shallow method ![](https://i.imgur.com/wqBEubK.png) :::danger :negative_squared_cross_mark: ***完全沒有做到 inforamtion hiding*** :negative_squared_cross_mark: 也沒有任何實作 (implementation) 在裡頭 :negative_squared_cross_mark: 反而 call 這個 function 還敲擊了更多的字元 結果,增加的系統複雜度多過於它能提供的 benefit (almost zero) :cry: ::: :::success :memo: 大家多多少少都有過 trace 程式碼的經驗。查看一段主業務邏輯,之中用到一個 function,只看 naming 還是不確定作者實作了什麼,故進去裡面看,但只寫了幾行。好,懂了,然後再 back to previous function。 這個動作,其實很消耗你的專注力。因為每切換一個 function/file directory,就是大腦做一次 context switch。而這是一種隱性的 complexity。 最討厭的情況是,程式碼裡充斥著一堆小小的 function,讓你不斷地 來回巡覽、學習大量冷知識。美其名是提高了 reusability,但事後會發現大部分是 early optimization,扼殺了彼此的工作效率。 而上述情境指的「作者」與「協作者」,最常扮演的人分別就是「過去的你」與「現在的你」。 ::: ![](https://i.imgur.com/auETseD.png) 我們為何會常常寫出 shallow clasee?因為過去被教導,function should be small。甚至會被給出一個具體的數字,例如 function 行數不該超過 10, 15, 20 行? 但最後的結果是造成許多的 ***classitis***。 :::danger *Noun. The practice of authoring stylesheets with **redundant and semantically unhelpful classes**.* - [wiktionary](https://en.wiktionary.org/wiki/classitis) ::: 所以,所謂的 small class/method/function,***應專注的不是程式碼行數***,而是如何做到 ***deep abstraction***。 故就算某段 function 含有百行程式碼,但它仍是 relatively clean 的,它就是一個很好的 deep module。 #### Example: good abstraction on Unix file I/O ![](https://i.imgur.com/C5WfhWq.png) John 舉一個覺得很美妙的例子,是 Unix file I/O。就只提供 caller **5 個 functions** 來對 file 做操作。 :::info John also mentioned: 對當代的我們來說很平常 (因為我們在用的系統大多都基於 Unix)。但在當年 Unix 的競品就抽象化做得不夠好,甚至會要求 caller 開檔案的時候要決定是 random access or sequential access。 ::: ### 重點摘要:Tactical vs. Strategic Programming 這是有關編程思維的選擇。 ![](https://i.imgur.com/sWtoExa.png) 大部分的我們都礙於「時間壓力」下,不斷地使用 ***tactical programming*** 的策略來完成 feature/bugfix。 這種編程思維描述的是:我們太專注在該 approach 是否 working,就算是個「shortcut」我們也接受。 不斷寫出 working code 的結果將導致系統過於複雜難以維護,製造出 ***tactical tornado***。 :::info :information_source: tactical tornado 是 John 用來形容這種情況並聚焦討論的詞彙,指透過 tactical programming 產生的程式碼通常有 80% 的品質都堪憂,但卻全力在系統中運轉,長期來說是在「破壞」我們的系統。 ::: 故如果我們有個目標是想要程式有更好的架構 (i.e. 更可讀、更好維護、更有效率),需要知道 **working code isn't enough**。 ![](https://i.imgur.com/hN3LkE3.png) 若目標是一個好的軟體設計,就需要調整思維。 從「戰術層面 (tactical)」提昇到 **「戰略層面 (strategic)」** ,變成要求**產出 great design**。 具有戰略的思維,將驅動我們不斷地想辦法找出目前系統的**複雜度**在哪,並且找方法最小化它。 這當然會使得我們在初期要投入的成本的確高一些,**甚至是那些微小的事物 (you have to sweat the small stuff)**,但你會在非常近的未來就獲得效益。 :::info > *「 So the issue is you have to invest. If you're not willing to take a little bit of extra time today, **at least a little bit**, you can't do good design. 」* - :older_adult: John Ousterhout ::: ### Summary: revisit complexity and how to spot them 總括一句,其實我們寫程式除了要完成需求,同時我們也是在面對**複雜度 (complexity)**,並且總是設法降低它。 在 ***A Philosophy of Software Design*** 書中一開始先定義了何謂複雜度: - 如果**某段程式沒辦法輕易讓其他人讀懂,那就是太複雜了** - 如果要**修改某段程式碼,需要同時修改到許多其他地方**,那也是太複雜了 - 或是要**修改某段程式碼,卻在改的過程引出另一個 bug**,這也代表原本的寫法太複雜了 上述的道理都很直觀,應該也沒什麼爭議。 但有沒有實務方法讓我們有效揭露或評估系統是否過於複雜呢?其實就是透過 ***architecture design review*** 與 ***peer code review*** 來達成了。 ### Reading materials - Quick learning: [A Philosophy of Software Design | John Ousterhout | Talks at Google](https://www.youtube.com/watch?v=bmSAYlu0NcY) - Deep learning via Book: [2018-John-A Philosophy of Software Design.pdf](https://github.com/rocky-191/Awesome-CS-Books/blob/master/SoftwareEngineering/Architecture/2018-John-A%20Philosophy%20of%20Software%20Design.pdf) (:point_right:[簡中翻譯](https://go7hic.github.io/A-Philosophy-of-Software-Design/#/))