# 【Day3】努力寫出更好的程式碼，有時你不是一個人在戰鬥

- 作者：林鼎淵（Dean Lin）
- 網址：https://deanlin.net/blog/202009/fb-ig-crawler-day-03/
- 發布：2020-09-18
- 主題：技術教學
- 系列：FB & IG 爬蟲專案從零開始
- 標籤：iT 邦幫忙鐵人賽、團隊合作、Clean Code、大家都能理解的程式
- 摘要：接下來幾天，我會把這個專案開發前所需具備的基礎知識分幾個主題向各位分享，希望這些文章能給初學者正確的觀念、也能讓老手思考自己開發上是否有忽略的細節

> 不是每個人都能成為明星，但是所有人都可以努力做到能力範圍的最好，並持續提高 - Henk Vermeyden

接下來幾天，我會把這個專案開發前所需具備的基礎知識分幾個主題向各位分享，希望這些文章能給初學者正確的觀念、也能讓老手思考自己開發上是否有忽略的細節

## 為什麼寫這篇文章？

我直到近一年才真正意識到好的程式碼在團隊合作中有多麼重要

在團隊裡面你不只是要完成自己負責的功能，你更要讓其他同事能在短時間理解你的程式

就算你是一個人獨立完成專案，好的程式碼能讓你在日後維護上節省更多的時間及精力

希望未來我們回頭看一年前寫的專案時，不求內心的小精靈呼喊:“X，寫得太精采了!"，只希望內心的小精靈不要滿嘴髒話就好

## 今日目標

對我來說高效能演算法與複雜的語法並不是我所追求的東西（其實是因為筆者資質駑鈍看不懂）

所以今天的目標就是如何寫出讓笨笨的筆者都能看懂的程式XD，這在程式界有個專有名詞叫做 `Clean Code(無暇程式碼)`

我將裡面幾個重要的點列在下方，並用我實際的經驗作解釋

1. 正確的宣告變數
2. 有意義的命名
3. 避免重複的程式碼
4. 大眾都能理解的程式
5. 好的註解
6. 錯誤處理 & 日誌(log)

## 1. 正確的宣告變數

用對的方式宣告變數才會讓人了解到他的意義

- **var** : 作用範圍在函式(function裡面)，這樣的宣告方式讓他可以在函式內的任何位置使用，但因為容許重複宣告，所以**會造成變數被覆寫的意外**，請盡量減少使用

```
function test() {
  // 宣告時給一個初始值
  var baby = "寶寶本尊"

  // 再次宣告
  var baby = "寶寶被污染了"
  console.log(baby) // 會印出'寶寶被污染了'
}

```

- **let** : 作用範圍在區塊(block裡面{})，可被重新指定值，let 與 const 皆不可重複宣告會報錯

```
function test() {
    // 宣告時給一個初始值
    let baby = "寶寶本尊"

    // 重新指定值
    baby = "寶寶變身"
    console.log(baby) // 會印出'寶寶變身'

    // 再次宣告
    let baby = "寶寶壞掉了"
    // 會跳出錯誤 SyntaxError: Identifier 'baby' has already been declared
}

```

- **const** : 作用範圍在區塊(block裡面{})，宣告後為不可被重新指定值的常數，使用他就是為了避免該常數被修改

- 定義不做改變的常數 : const PI = 3.14159

```
function test() {
    // 宣告時給一個初始值
    const baby = "寶寶不可改變"

    // 重新指定值
    baby = "寶寶變身失敗"
    // 會跳出錯誤 TypeError: Assignment to constant variable.
}

```

- 特別注意如果 **const 宣告陣列(array)或物件(object)時是可以被改變的**，因為他們都是 reference type

```
function test () {
    // 宣告為物件
    const baby = { name: "寶寶本尊" }

    // 重新指定值
    baby.name = "寶寶變身"
    console.log(baby) // 會印出'{ name: '寶寶變身' }'
}

```

## 2. 有意義的命名

這裡要看你工作環境的默契去做調整，但還是有幾個基礎要求

- **函式(function)的命名**

- 要一眼就能明白他要做的事情

```
// 不好的命名，你無法從名字理解他要做的事情
function abc(a, b) {
  return a + b
}

// 好的命名，淺顯易懂
function sum(a, b) {
  return a + b
}

```

- 命名不要複雜化，做一件事情就好

```
// 不好的命名，一個函式做太多事情了
function goBackpageWriteContent() {...}
// 好的命名，一個函式昨一件事，上面的function可以做拆分
function goBackpage() {...}
function writeContent() {...}

```

- **變數(variable)的命名能直接知道他代表甚麼**

```
// 不好的命名，你看到會覺得疑惑
let haha = '猜猜看這個變數代表甚麼'

// 好的命名，非常直觀無須猜想
let name = '好想兔'

```

## 3. 避免重複的程式碼

在快速開發時期複製貼上是最爽的事情，但你在未來會付出沉重的代價如下

- **專案需求變更** : 你會發現維護是如此的困難，你可能已經忘記之前有多少程式是複製貼上，導致不斷產生新的Bug
- **程式體積龐大理解不易** : 兩段長得很像的程式你會需要用大家來找碴的方式去思考

**解決方案**

- 如果有功能一模一樣且多次被不同檔案使用的函式建議你寫一個**專案共用函式庫**(ex: public.js)，這樣功能修改時只要改一個地方就好
- 如果遇到功能很接近的需求請把他們**結合到一個函式**中吧，你可以**搭配參數**來處理當中的差異性

## 4. 大眾都能理解的程式

- 我周圍曾經遇過程式能力極強，但是**幾乎無法與人合作的工程師**，他們所寫的程式有幾個特點

- **程式邏輯非常難理解**：他們可能使用效能最好的處理方案，但是邏輯非常跳躍晦澀難懂
- **語法非常新潮**：使用時代最尖端的語法，但同事在閱讀理解上會很痛苦

- 也遇過只求完成功能**得過且過的工程師**，他們也有幾個特點

- **他們不知道自己寫了甚麼**：都是從網路上複製貼上，能跑就好，甚至參數命名都不改，於是交接的時候就是地獄，因為連他都不知道這段程式做了什麼
- **功能都是複製貼上**：就算是一模一樣的功能，由於過去都是從網路複製貼上完成，所以有時他們甚至忘記自己寫過他，再次從網路複製另一段程式不一樣但功能一樣的code貼上…，認真覺得這種解決事情的方法超級母湯

遇到前者是你的幸運也是你的不幸，雖然你可以與神同行，但是**要不斷的提升自己**才能夠勉強跟上他們的思路；遇到後者就當成鍛鍊，你要嘗試將這些不明所以的程式碼**重構**，讓他足以被大眾理解

而我寫程式的目標是希望在交接的時候被人輕易理解，所以在撰寫的過程中會不斷反思這些程式碼寫在這裡的意義是甚麼，如果自己都無法解釋的程式，就不用期待他人能夠理解了

## 5. 好的註解

雖然有神人說過：最好的註解就是程式碼本身

但我想絕大多數的人都難以到達這個境界，所以要乖乖地寫註解

- **註解的好處**

- 交代前因後果：為什麼這樣寫?遇到了什麼困難?解決了什麼問題?
- 作為文件說明：有些技術是需要被學習後才能夠看得懂的(ex:引入的模組、套件)，你的註解能讓他快速理解這些工具怎麼使用

- **請不要有註解如下**

- 毫無意義的牢騷(ex:我好崩潰啊，這段程式我寫了一個禮拜…)
- 把註解當成版本控制(ex:2020/08/23完成此函式)
- 說明這段程式是誰寫的(簽名檔)
- 把舊的程式碼註解起來怕以後會用到(但通常再也不會用到，而且這個問題用版控就能解決)

## 6. 錯誤處理 & 日誌(log)

寫的再好的程式都會有漏洞，而完善的錯誤處理 & 明確的日誌紀錄可以協助你更快速的除錯(debug)

- **錯誤處理**：通常使用 try-catch 的方式來處理，這個部分在後面的章節會更詳細介紹
- **日誌(log)**：記錄程式執行時重要的節點

- 程式執行到哪個步驟
- 資料是否取得
- 如果發生錯誤時式在哪個環節

以上是我對 Clean Code 的個人感想，如果大家對於文章的內容有任何想法歡迎討論～

### 參考資源

1. [Clean Code 無瑕的程式碼](https://medium.com/%E6%89%8B%E5%AF%AB%E7%AD%86%E8%A8%98/clean-code-index-51e209cc47db)
2. [變數、常數與命名](https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part3/var_const_naming.html)
3. [var, let, const 差異](https://medium.com/@totoroLiu/javascript-var-let-const-%E5%B7%AE%E7%95%B0-e3d930521230)
4. [var 與 let 的主要差別](https://medium.com/@realdennis/%E9%96%92%E8%81%8A-var%E8%88%87let%E7%9A%84%E6%87%B6%E4%BA%BA%E5%8C%85-javascript-b5a3f40ee28d)
5. [JavaScript ES6 中使用 const 宣告常數](https://pjchender.blogspot.com/2017/01/const.html)

> **在許多人的幫助下，本系列文章已出版成書，並添加了新的篇章與細節補充：**
> 加入更多實務經驗，用完整的開發流程讓讀者了解專案每個階段要注意的事項
> 將爬蟲的步驟與技巧做更詳細的說明，讓讀者可以輕鬆入門
> 調整專案架構
> 優化爬蟲程式，以更廣的視角來擷取網頁資訊
> 增加資料驗證、錯誤通知等功能，讓爬蟲執行遇到問題時可以第一時間通知使用者
> 排程部分改用 node-schedule & pm2 的組合，讓讀者可以輕鬆管理專案程序並獲得更精確的 log 資訊
> **有興趣的朋友可以到天瓏書局選購，感謝大家的支持。**
> **購書連結**：[https://www.tenlong.com.tw/products/9789864348008](https://www.tenlong.com.tw/products/9789864348008)

---

關於作者：林鼎淵（Dean Lin），台灣的生成式 AI 企業內訓講師、外商資安公司軟體專家，也是生成式 AI 創新學院發起人。出版 7 本書（包含全台第一本 ChatGPT 應用專書），兩度獲得 iThome 鐵人賽佳作，累積上百場授課與顧問經驗。（https://deanlin.net/about/）
引用時請附上原文網址：https://deanlin.net/blog/202009/fb-ig-crawler-day-03/
