# Pragmatic Programmer ### Chapter 7: While You are Coding [David Ye](https://dwye.dev/) @ Houzz --- ## Outline - [聆聽你的蜥蜴腦](#/2) - [不要靠巧合寫程式](#/3) - [演算法速度](#/4) - [重構](#/5) - [測試](#/6) - [安全性](#/7) - [命名](#/8) --- ## 聆聽你的蜥蜴腦 > [大腦中最原始的部分,和一隻蜥蜴沒有兩樣:肚餓時都會尋找食物,受到攻擊時都會戰鬥或逃跑](https://www.thenewslens.com/article/134413) <aside class="notes"> 直覺是我們大腦潛意識對模式的一種反應,不管是先天或後天學習,但總之是沒有意識的,最早是用於察覺危險,算是一種生物本能。很多時候大腦沒辦法馬上給你一個原因,但會先給你一個直覺 -> 聆聽直覺 </aside> <img src="https://i.imgur.com/smdSbZM.jpg" width="250"> ---- ### 恐懼空白頁 害怕空蕩蕩的螢幕,例如:開始一個新 module - 蜥蜴腦想跟你說話 → 聆聽他哪裡出了問題 - 只是擔心犯錯 <aside class="notes"> 擔心犯錯是一種合理的恐懼,可能會把 Code 中的錯誤看做是對我們能力上限的反映 -> 冒名頂替症候群(無法將自己的成功歸因於自己的能力,並總是擔心有朝一日會被他人識破自己其實是騙子這件事。) </aside> ---- ### 與蜥蜴腦交談 - 去做不需要動腦的事情:散步 / 吃午餐 / 洗澡 - 和別人聊聊你想做的事 - Brain hack: prototyping - 閱讀別人的 Code 不只是寫 Code 的時候,也可以在生活中練習 <aside class="notes"> 讓大腦在背後自我組織 / 盡情嘗試,會被丟棄的那種,失敗也沒關係,「弄懂你想做什麼」 / 找出別人的規律與思考模式,是什麼讓他們這樣寫 Code,配合他們的思考插入新邏輯,或學到新東西 </aside> --- ## 不要靠巧合寫程式 基於巧合的假設是危險的陷阱 > 證明你的假設 ---- 士兵在空地中前進,因為可能有地雷,於是士兵小心翼翼的邊走邊用刺到戳前方地面,邊戳邊走,走了一陣子發現都沒爆炸,於是他相信這塊地是安全的。 <img src="https://i.imgur.com/bYutWbU.png" width="350"> ---- ### 實作中的偶然 - 特定的邊界條件,例如 OS / 檔案順序 ```bash $ date -r 1473305798 # mac Thu Sep 8 11:36:38 CST 2016 $ date -d @1473305798 # ubuntu Thu Sep 8 11:36:38 CST 2016 ``` <aside class="notes"> mac: BSD / ubuntu: GNU tools<br> 只差一點點,用 +-1 修正 → 背後可能影藏著更大的 bug </aside> ---- ### 慎重地寫程式 - 能夠向一個比較初級的 Programmer 解釋嗎? - 確定你知道你的 code 為什麼 work - 如果你不知道他可不可靠,就是不可靠 - 先計畫,再 Coding - 記錄你的假設,測試你的假設 - 不要讓現有的 Code 支配未來的,隨時準備[重構](#/5) <aside class="notes"> Program Deliberately<br/> 沒有別人可以解釋的話,就跟小鴨說吧 / 知道你在寫什麼 / 所有的 Code 都是可以替換的<br/> 請確保這不是一個巧合 </aside> --- ## 演算法速度 知道你的 Code 的 Time Complexity / Space Complexity <aside class="notes"> 他講了很多 Big O 基本,請去看演算法課本 XD </aside> ---- ### 過早優化問題 Premature Optimization > [太早優化是萬惡之源](https://medium.com/@HyperConnezion/%E6%B5%81%E8%A8%80%E7%B5%82%E7%B5%90%E8%80%85-%E9%81%8E%E6%97%A9%E9%80%B2%E8%A1%8C%E5%84%AA%E5%8C%96%E6%98%AF%E8%90%AC%E6%83%A1%E4%B9%8B%E6%BA%90-98a550df6755) <aside class="notes"> 書中沒多著墨 </aside> ---- > Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: [premature optimization is the root of all evil](https://wiki.c2.com/?PrematureOptimization). Yet we should not pass up our opportunities in that critical 3%. 很多的優化是不必要的,但關鍵的優化(3 %)還是不能省略。 ---- 萬惡的優化: - 需求還不明確時 - 花一堆時間把一個 $O(n^2)$ 寫成 $O(n \lg n)$ ,結果根本不是效能瓶頸 > 最快的不一定最好 投入寶貴的時間來優化演算法前,請先確認那裡是 bottleneck - 善用 profiler <aside class="notes"> 找尋需要優化的證據 </aside> --- ## 重構 軟體發展不像蓋房子,蓋好就不會變 比較像是園藝,要隨時調整佈局並修剪 > 重組現有程式碼主體,改變其內部結構,而不改變其外部行為的嚴格技術 ---- ### 重構的時機 ~~Anytime~~ 任何讓你覺得錯了,可以更好的地方。 - Not DRY - Not Orthogonal - 過時的知識 - 需求的改變 - 效能問題 重構應該小規模,並經常做 ---- ### 解釋重構 壞的程式碼就像增生組織,不早點開刀切除,就會增長擴展,變得更難切除,總有一天會影響主體 <aside class="notes"> 問問大家怎麼做重構的?<br/> 我都會做 feature 中間偷時間做,之後找時間提交 </aside> ---- ### 如何重構 - 不要一邊做新 feature - Unit Test - 以簡短而慎重的步驟,一步一步進行 <aside class="notes"> 一邊做 feature 一個不小心就會爆炸 QQ<br/> unit test 避免行為改變了(嚴格)<br/> 一步一步來,確保行為相同,不要一次改太大 </aside> --- ## 測試 別讓 User 成為第一個使用你的 Code 的人 ### 思考測試的意義 - 開發者同時自己寫測試 - 為測試而設計 - 難以測試的地方,通常是耦合的地方 <aside class="notes"> e.g. 對於全域資料庫的依賴 </aside> ---- ### TDD 需要注意 > Q: 怎麼吃掉一個大象? > A: 一次吃一口。 需要知道你要去哪裡,否則會兜圈,或是走到錯的地方。 ---- ### About Testing - 把程式碼看作一個一個元件,對元件做測試(**軟體積體電路**) - 配合合約式程式設計,確保每個元件行為符合預期 - 把你的 REPL 臨時測試變成正式的單元測試 - **測試通道**:可以彈出診斷視窗,看內部狀態以及詳細 error message - 要確保身份驗證,不要被外面的人開啟 ---- ### 屬性測試 避免固定數據的先入為主,可以幫你找到隱藏的 edge cases ```python @given(some.lists(some.integers())) def test_list_size_is_invarient_accress_sorting(a_list): original_length = len(a_list) a_list.sort() assert len(a_list) === original_length ``` <aside class="notes"> 找出程式碼必須遵守的合約(Contracts) 不變量(Invariants) 加在自動化測試裡面 </aside> ---- `aabbc` -> `[('a', 2), ('b', 2), ('c', 1)]` ```python= def encode(input_string): count, prev, result = 1, "", [] for character in input_string: if character != prev: if prev: result.append((prev, count)) count = 1 prev = character else: count += 1 result.append((character, count)) return result ``` ```python= def decode(lst): q = "" for character, count in lst: q += character * count return q ``` ---- ```python= @given(text()) def test_decode_inverts_encode(s): assert decode(encode(s)) == s ``` ![](https://i.imgur.com/Jc7eKID.png) ---- ```python= def encode(input_string): if not input_string: return [] count, prev, result = 1, "", [] for character in input_string: if character != prev: if prev: result.append((prev, count)) count = 1 prev = character else: count += 1 result.append((character, count)) return result ``` https://github.com/dwy6626/Property-Based-Testing-Example --- ## 安全性 > 好籬笆造就好鄰居 很多的資安事件,不是因為攻擊者很厲害,只是因為員工太粗心 <aside class="notes"> 請假設所有使用者都是惡意的 </aside> ---- <img src="https://i.imgur.com/XezY0WS.png" width="1200"> <aside class="notes"> 更可怕的是,HackMD 筆記是預設公開的 </aside> ---- ### 待在安全的地方 - 最小化攻擊表面積(下一頁) - 最小權限: 不要什麼都用 root 跑 / 收回權限 - 提供安全預設值: 輸入密碼時,預設隱藏,但可以勾選顯示 - 對敏感資料加密 - 維護安全更新 <aside class="notes"> 永遠只提供最小的權限給使用者<br/> 更新很痛苦,但資料外洩或安全性降低更痛苦 </aside> ---- ### 最小化攻擊表面積 - 輸入資料 - 服務 - 不需驗證的公開平台:不要以為沒人找得到 - 某個管理帳戶的密碼沒有保管好 - 輸出 - 不要輸出像是「密碼已被使用」這種多餘訊息給未經驗證的 user - 除錯資訊 - 除錯通道要身份驗證 - 簡化程式碼 <aside class="notes"> 如果你的服務不需要驗證身份,那代表地球上的任何使用者都可以呼叫你的服務<br> 但許多網路設備或是使用者用的都是預設的或是最簡單的密碼,或缺乏保管<br> error stacktrace:不要讓別人知道你的 Code 怎麼運作 </aside> --- ## 命名 > 子曰:「必也正名乎」 <aside class="notes"> 名不正,則言不順;言不順,則事不成;事不成,則禮樂不興;禮樂不興,則刑罰不中;刑罰不中,則民無所措手足。 </aside> ---- ### [Stroop Effect](https://github.com/dwy6626/Stroop-Effect-Game) <img src="https://i.imgur.com/fWvrgQ5.png" width="1200"> ---- - 用更具體的名稱,例如 buyer 取代 user - 入境隨俗 - 維護一份專案術語列表 - 發現有問題的命名時,及時重新命名(即時重構) - 不要叫 getData 結果改了資料 <aside class="notes"> Q: 我們公司有這種東西嗎 XD </aside> --- # The End Thanks for listening! - [back to outline](#/1)
{"metaMigratedAt":"2023-06-16T14:57:48.383Z","metaMigratedFrom":"YAML","title":"Pragmatic Programmer Chp 7: While You are Coding","breaks":true,"slideOptions":"{\"height\":1000,\"width\":1500,\"theme\":\"white\"}","contributors":"[{\"id\":\"915f29e1-3f9c-4908-bbd4-a58795589e48\",\"add\":6927,\"del\":425}]"}
    473 views