###### tags: `jubo` `tutorials` # General Guiding Principles [toc] ## 『A Philosophy of Software Design』 :::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 - 最近幾年也很紅的共識演算法:[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/#/)) ## Revisit SOLID :::info :information_source: [YouTube - Fred 聊聊 SOLID 設計原則](https://youtu.be/e0UOuQ_lCUY) - :timer_clock: 約 2 小時 講者資訊 Shih-Peng (Fred) Lin ([linkedin](https://www.linkedin.com/in/shihpeng-lin-4553331a6)) - 現任街口支付 CTO - 此 talk 是 Fred 為了街口內部的內訓教材所錄製的影片,包含了 Fred 針對 Uncle bob 自 198x 起至今的文獻重新整理以及對 SOLID 更深一層的體悟 - 充分利用實務情境說明何謂 SOLID,不會有搔到癢處的感覺 ::: :::info [Fong - 你真的懂 SOLID 原則嗎?](https://www.youtube.com/watch?v=Dmv6tMnaCQA) ::: Robert Martin (aka. Uncle Bob) 提出了 SOLID 五項原則,若程式設計能夠應用這五項原則,將使程式變得更加容易進行維護及擴充。 你可能找過許多文章試著了解這五大原則,然後心中出現以下疑問: - **單一職責原則**中,何謂「單一職責」?**職責的範疇**到底在哪? - **開放閉合原則**中,我可以接受一個設計良好的系統應該能夠在不改變原始碼的前提下輕易擴展,但到底**怎麼做**? - **依賴反轉原則**中,何謂「應該依賴介面,而非依賴具體實作」? - 找到的文章舉的例子都**好抽象**,覺得搔到癢處。到底在我實際 programming 中何處適合使用? ![](https://i.imgur.com/uco0631.png) ![](https://i.imgur.com/h6p99Gp.png) ### 程式碼異味 (code smell/design smell) 的表徵 :::info :spiral_note_pad: [代碼異味](https://zh.wikipedia.org/wiki/%E4%BB%A3%E7%A0%81%E5%BC%82%E5%91%B3) - Wiki ::: Uncle bob 自從 35 歲起不斷到處跟人筆戰、倡議一個論點:他認為**軟體工程最大的痛點就是「可維護性」**。 並深究出**影響可維護性的本質**:即是程式碼的 **「耦合 (coupling)」** ,aka. **「相依性 (interdependence)」**。 :::info :arrow_heading_up: RECAP 與前述 John 的觀點「complexity comes from dependencies」相呼應 ::: 又所謂的「程式碼異味」、「設計壞味道」,指的就是那些有**較差維護性**、**較高耦合**、**較高相依性**的軟體有的表徵: ![](https://i.imgur.com/FkiRiCY.png) ![](https://i.imgur.com/a0aYXZW.png) **SLOID 的本質是在試圖解決系統組件之間過強相依性的問題** - :point_right: 降低相依性產生的問題,一定程度上就是降低了系統複雜度 ### 重點摘要:單一職責原則 以下簡單摘要此 talk 中對單一職責原則的討論: ![](https://i.imgur.com/jQoUfT8.png) - :thinking_face: 增刪改查到底算幾個職責? ![](https://i.imgur.com/GMwXq5K.png) - 其實關注點應該是 **「人」**,**你的業務需求方是誰,他們的需求就是你的 app 的職責** ![](https://i.imgur.com/nNJrkGl.png) - 這項原則是一個「概念」,它同樣可以用來指導如何設計 **function/class/module/package** 、 **database table schema** 、 甚至到 **microservice** ![](https://i.imgur.com/Rz7Aw58.jpg) - 翻閱一下 Uncle Bob 自己的 blog,的確有提到 SRP 要關注的對象是 people ![](https://i.imgur.com/qfLms9a.jpg) ### Summary SRP 是此 talk 最先討論的主題,承先啟後地再帶出與其他四個原則的關係。原內容有舉一個實務的例子來輔助說明 SRP,請再自己閱讀原始影片囉,可以保證會獲得非常多啟發。 ## The Twelve-Factor App - A methodology to build a better SaaS app | | | | -------- | -------- | | ![](https://i.imgur.com/r4LhMxF.png) | ![](https://i.imgur.com/GEIFEYp.png) | > 2022/10/31 updated 更棒的整理:[[Architecture] The 12 factor App 筆記](https://marcus116.blogspot.com/2020/09/architecture-12-factor-app.html) > ![](https://i.imgur.com/FJtAGmt.png) 如今,軟體通常會作為一種服務來交付,它們被稱為網路應用程式或軟體即服務(SaaS)應用。12-Factor 為想要實現以下目標的 SaaS 應用提供了方法論: - 使用***聲明式***的自動化配置,從而使新的開發者花費最少的學習成本加入這個項目 - 和作業系統之間盡可能的劃清界限,在各個系統中提供最大的可移植性 - 適合部署在現代的***雲端平台***,從而在伺服器和系統管理方面節省資源 - 將***開發環境***和***產品環境***的差異降至最低,使得持續交付的方法得以實施以最大化敏捷開發 - 可以在工具、架構和開發流程不發生明顯變化的前提下***實踐擴展*** :::info - 原文: https://12factor.net/ - 簡中: https://12factor.net/zh_cn/ ::: 當我們在替 application 維運時,很容易碰到了一些問題。我發現常見的問題與其中 6 個原則很有關係,故摘要它們出來: - I. Codebase - III. Config - IV. Backing services - VI. Processes - VIII. Concurrency - X. Dev/prod parity 這些原則與觀念將有助於我們在設計開發階段想得更清楚,讓你的 app 更適應雲平台的部署與維運方式。 ### [I. Codebase](https://12factor.net/codebase) ![](https://i.imgur.com/bxI1Cao.png) - 程式碼應該要在不同的 deploys 之間保持一致 (local development environment 也是一種 deploy) - 故如果程式碼內部出現以下這種邏輯,應就得視你的 app 為不一致的: ``` if env==dev: read ENV_URL_FOR_DEV; else if env==prod: read ENV_URL_FOR_PROD; ... if env==dev: use ENV_URL_FOR_DEV do somwthing else if env==prod: use ENV_URL_FOR_PROD do somwthing ``` - 上述的邏輯將導致每增添一種 deploy,就得顯示地去改動 source code 以滿足特定環境所需 - 應同時考慮 ***III. Config*** 談論的原則,**讓環境 (configurations) 來決定 app 的行為、而不是讓 app 感知環境後做出意料之外的行為** ### [III. Config](https://12factor.net/config) :::success :bulb: 每一種環境的部署,對於周邊環境的需求總是不確定的。你無法保證未來新業務所需的環境能夠套用過去的經驗。 ::: - 應利用「環境變數」儲存 config,因為「環境變數」可以很方便地在不同環境中做出改變、且不用改動任何一行 code。 :::success :bulb: 使用環境變數作為引入配置的手段為第一優先。都使本地開發與各環境部署更加簡潔。 只有極少數的需求會強迫透過載入檔案的方式來取得配置。就算有這種例外,「檔案的路徑」也需要設計成能夠透過環境變數給予。 一樣,你無法掌握部署環境的 file system 的全貌,故將職責交給部署的人去明確指定該檔案的位置即可。 ::: - 你可能會想要**將環境變數做出組別** (`development`, `test`, `production`),但你很快就會發現這方法不 sacle,你會**疲於管理**多種部署環境的排列組合 (i.e. `staging`, `ga`, `joes-staging`)。所以**不要這樣做**。 - 其實**只要環境變數切得夠細、不彼此依賴**,讓負責部署的人根據該環境需求,管理要如何給定參數即可。 :::info :information_source: 那我們目前「部署的人」是如何管理環境變數的差異? :point_right: [sa/manifests](https://gitlab.smart-aging.tech/sa/manifests) ::: ### [IV. Backing services](https://12factor.net/backing-services) ![](https://i.imgur.com/Z4ktgxI.png) - 所謂的 *backing service* 是指那些需要透過 network 溝通取得資源後,才能讓你的 app 有 normal operation 表現的 services。並將這些 services 視為一種資源 (resources) - 這些 service 理論上都是透過給定 URL 來連接、存取,並使用 ***III. Config*** 提到的方式來儲存 - e.g. 利用 config 來儲存,你可以很輕易地讓 app 連向 local 環境的 resource 或其他第三方的 resource ### [VI. Processes](https://12factor.net/processes) - 在有了 ***IV. Backing services*** 這樣的原則為前提,一個 12-factor app 會進一步實踐 ***stateless*** and ***[share-nothing](https://en.wikipedia.org/wiki/Shared-nothing_architecture)*** 的架構 - 另外,app 可能依賴於 [sticky sessions](https://en.wikipedia.org/wiki/Load_balancing_%28computing%29#Persistence) 的設計,我們要知道它其實是違反 12-factor 精神的 (文中建議是使用 Redis/Memcached 等方式來嘗試解耦) ### [VIII. Concurrency](https://12factor.net/concurrency) ![](https://i.imgur.com/69VzCv6.png) - 若服務吞吐量不足,優先考慮 **Scale out via the process model** :::success - 對我們來說,就是 app 得做好 stateless 的準備,然後使用 K8S 的 [Horizontal Pod Autoscaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) 自動做 scale out - 當然,也會迫使我們在設計服務時,就先想清楚對 DB 做出併發請求是否會產生「業務邏輯上的 race conditions」 (e.g. [read phenomena](https://en.wikipedia.org/wiki/Isolation_(database_systems)#Read_phenomena)) ::: ### [X. Dev/prod parity](https://12factor.net/dev-prod-parity) > 開發/產品等價 - 大部分的人,在有越多的開發經驗後皆會感同身受: 1. app 在 development, staging, production **環境之間有差異的時間越久**,在 production 的環境上越容易出問題 2. **開發與維運的人員差異**,容易造成部署後出問題 3. 本地開發環境與線上環境使用的 **tech stacks 不同** (e.g. SQLite vs. MySQL),造成問題 - 而 12-factor 的精神會希望一個 app: 1. 採用 [Continuous Deployment](https://en.wikipedia.org/wiki/Continuous_deployment),儘可能縮小 development 與 production 的差異 2. 盡可能**開發與維運合一** (i.e. **我開發、我維運的主人翁精神**) 3. 盡可能本地開發與線上環境使用**相同的 tech stacks** ![](https://i.imgur.com/8EezLs2.png) :::success :bulb: 實務上,針對這部分,我都使用: 1. [Docker](https://www.docker.com/) 直接運行與線上環境相同的 teck stack 2. 運用 Makefile 模擬 app 運行的環境。事先定義好環境變數讓 app 讀取並運行 (*[Makefile Tutorial By Example](https://makefiletutorial.com/)*) ::: ## Design Review & Code Review > References: > - [Google’s Code Review Guidelines](https://google.github.io/eng-practices/review/) > - [如何進行 Code Review?](https://enginebai.medium.com/code-review-guidelines-b76a859c377c) > - [How to Review the Code Like a Pro ](https://medium.com/@yar.dobroskok/how-to-review-the-code-like-a-pro-6b656101eb89) > - [從 Code Review 的小事看到大事](https://william-yeh.net/post/2023/09/on-code-review/) > - [Best practices for writing code comments from StackOver flow](https://blog.taiwolskit.com/best-practices-for-writing-code-comments) ### Pros - 知識共享 - :point_right: 滿足小夥伴們的成長需求 - 凝聚共識、降低理解成本 - :point_right: 提高系統的可讀性、可維護性 - 揭露各種 hidden knowledge、掌握風險 - :point_right: 減少意料之外的 bugs/issues 產生 ### Cons - 在設計開發階段花費較多時間 :::danger :fire: 但通常在軟體開發領域,事前先花這些時間 code review、提早找出問題的時間成本,遠比軟體上線後發生再修正、再維護所花費還要低 ::: ### 誰是你最佳的 Reviewer(s)? - 可能是程式碼的 owners - 但也可以針對特定程式碼段落,尋找最適合的人來跟你討論 ### Review 的形式不拘 - in-person (sync mode, pair programming, pre-committed review) - online (async mode, inline comments, IM notifications) ### Design review 的關注點 我的話,會關注以下面向: - [x] 我了解每個 public API 的職責嗎? - [x] 我了解每個依賴的外部系統的職責嗎? - [x] 是否使用適合的、易理解、好維護的架構/技術棧? - [x] 我了解目前的架構「妥協」的項目嗎?這些被妥協的項目何時會變得又重要又緊急? - [x] 我了解目前的規劃接受了哪些「風險」嗎? :::info :information_source: A design review RFC example (ref. 《微服務實踐》P.309) |Aspects|Purposes| |-|-| |問題與背景|這個功能想要解決的問題? 為什麼我們要這麼做?| |解決方案|打算如何解決此問題| |依賴項目與整合|如何與現有的或預計要開發的服務、功能及組件進行互動| |API 接口|服務會暴露哪些操作| |擴展性與效能|該功能如何進行擴展?維運成本試算?| |系統可靠度|希望可靠度到達什麼程度?| |冗余性(redundancy)|備份、復原、部署與回滾| |監控儀表板|如何了解服務的行為?| |故障情境|如何因應或消除潛在的故障影響| |安全性|威脅樣態、資料保護等| |部署|如何使該功能上線| |風險與開放問題|目前已識別了哪些風險?有哪些是開發者不知道的風險?| ::: ### Code review 的關注點 以下內容摘要自 [Code Review Developer Guide at Google](https://github.com/google/eng-practices/blob/master/review/index.md),並分成 reviewer 以及 author 兩者立場應關注的面向。 內容都不長,再自己讀一下,偷學 Googler 怎麼做事。 #### How To Do A Code Review? *(as a reviewer)* > *A practical checklist from [Code Review Developer Guide: What Do Code Reviewers Look For? - at Google](https://github.com/google/eng-practices/blob/master/review/index.md#what-do-code-reviewers-look-for-lookfor)* |Aspects|Purposes| |-|- |Design| 程式碼是否精心設計且適配你的系統? <br> *Is the code well-designed and appropriate for your system?* |Functionality| 程式碼是否符合作者的意圖?是否顧全 user 的利益? <br> *Does the code behave as the author likely intended? Is the way the code behaves good for its users?* |Complexity| 程式碼可以更簡潔嗎?其他人是否可以輕易理解並應用? <br> *Could the code be made simpler? Would another developer be able to easily understand and use this code when they come across it in the future?* |Tests| 程式碼是否有正確且設計良好的自動化測試? <br> *Does the code have correct and well-designed automated tests?* |Naming| 變數、類別、方法命名是否清晰? <br> *Did the developer choose clear names for variables, classes, methods, etc.?* |Comments| 註解是否清晰且有用? <br> *Are the comments clear and useful?* |Style| 程式碼是否遵循我們的慣例? <br> *Does the code follow our style guides?* |Documentation| 相關文件是否有更新? <br> *Did the developer also update relevant documentation?* #### The CL Author's Guide *(as an author)* > CL: change list. interchangeable terms: code changes, MR (merge request), and PR (pull request) ***Writing good CL descriptions*** > see [original page](https://github.com/google/eng-practices/blob/master/review/developer/cl-descriptions.md) for some good and bad examples - 應在 title 對你的改動有簡短的總結,並提供足夠清楚的訊息於 body - 而小改動 (i.e. only 1 line),需要闡述清楚的是 context ***Small CLs*** 每次改動範圍小,好處多多: - reviewer 被打斷的時間更短,review 更快、更詳盡、周全 - 容易設計的更好,再寫出 bug 的機率較低 - 若被 reject,工作量也不會太大 - 容易 merge、容易 rollback (revert) ***How to handle reviewer comments*** :point_right: ***別往心裡去 (Don't Take it Personally)*** - 記得,大家都是為了如何讓程式碼品質上升、維護好 code base 及產品的品質而努力。絕非針對你個人的能力或人格作出攻擊或評價 - 當然, reviewer 也有責任提出有建設性的建議,而不要作出不禮貌或違反職場禮儀的評論 - 若你覺得 reviwer 有任何冒犯之處,請嘗試像他表達你的感受。如情況未改善,請找更高層的 manager 反應此情事 :point_right: ***修正程式碼 (Fix the Code)*** - 如果 reviewer 提出了一段 code 他看不懂,表示很有可能其他 developers 也會看不懂 - 如果你同意 reviewer 的意見,此時你應該是修改程式碼撰寫風格使其更簡潔清晰,或在程式碼中加入適當的註解以解釋為何存在。而不是寫在 code review tool (e.g. Gitlab),因為其他 developers 在未來看到的是 code 而不是當年的 MR。 - 當然,如果你不同意,或 reviewer 的意見真的無關緊要,直接回應在 code review tool 上就可以了 :point_right: ***自我覺察 (Think for Yourself)*** - 你花了很多力氣寫 CL description,你當然希望 reviewer 少廢話、給我過。但請收到意見回饋時退一步思考該 reviewer 的建議有沒有價值。此時可放在心上的第一個疑問是:「reviewer 的意見到底正不正確?」 - 如果你無法判斷 reviewer 的建議是否正確,**請向 reviewer 要求再進一步的澄清、說明** - 如果你已經考慮過,並且仍然認為自己的判斷較合適,**請隨時準備好解釋為什麼你的做事方法對 code base、users、products 與/或 company 更好** - reviewer 的確很可能缺少足夠的上下文來做出最正確的評論,你也確實知道一些 reviewer 不知道的細節。**試著給 reviewer 更多的背景,以讓雙方在技術事實上達成共識**