# 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
```

----
```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}]"}