一切皆程式碼:宣告式寫終點,程序式寫路徑
· tech
📑 目錄
前三章鋪完為什麼、原則、平台,從這章開始進入三個核心實踐,第一個就是招牌:把所有東西定義成程式碼——不只伺服器,網路、pipeline、監控、權限,全部。好處書裡列得很整齊(可重用、一致、透明、可測試、可 review),但這章真正的重頭戲是一個更根本的問題:「定義」用什麼語言寫? 宣告式和程序式的分工,決定了你的 IaC 三年後是資產還是災難。
宣告式:你寫終點,工具找路
宣告式(declarative)語言——Terraform 的 HCL、CloudFormation、Kubernetes 的 manifest——寫的不是「做什麼」,是「做完之後世界長什麼樣」。剩下的交給工具:
這個機制送你兩個大禮:
- 冪等(idempotency):重複執行是安全的——這正是恐懼螺旋出口(「更常跑」)的技術前提。不冪等的 script 跑兩次會建出兩倍的東西,誰敢排程每天跑?
- plan/diff 可以先看:變更執行前,工具能告訴你「將會改什麼」——基礎設施的變更第一次有了像 code review 的 diff 可讀。
程序式:你寫路徑,適合寫抽象
程序式(imperative / procedural)寫的是步驟與邏輯——一路從 shell script 到用真程式語言寫基礎設施的 Pulumi、CDK。它的強項是宣告式的弱項:條件、迴圈、組合、封裝。書的建議是把它用在「寫可重用的抽象」——把「我們公司一個標準服務要配的十樣東西」包成一個函式庫、一個模組,給團隊呼叫。
最重要的一節:別在宣告式語言裡寫程式
兩種語言各自都好,災難發生在混用的地帶:
宣告式語言是為「描述固定形狀的東西」設計的,但需求一複雜,大家就開始往裡面塞 count、for_each、三層巢狀的 {{ if }} template——用一個不是為寫程式設計的語言寫程式:沒有好的測試框架、沒有 debugger、錯誤訊息天書。反過來混也一樣糟:用程序式語言把一份本來三行宣告能講完的定義,包進三層自己發明的抽象,讀的人要先考古你的框架。
書的解法很清楚:兩邊各安其位——固定形狀的定義留在宣告式,需要邏輯的部分抽出來用程序式寫成模組;當一邊開始長出另一邊的長相,就是搬家的訊號,不是加更多 workaround 的訊號。
反思
YAML 裡的第一個 if,是最便宜的警報器
這章給了我一個可以直接用的 code review 準則:看到宣告式檔案裡出現第一個條件或迴圈,就停下來問「這段邏輯是不是進錯語言了」。 我在 Helm chart 的 template 裡看過最痛的版本——{{- if }} 套 {{- range }} 再套 {{- with }},一份 values 要對著 template 心算三層才知道會 render 出什麼,改一行要禱告。當時我以為那是 Helm 的原罪,讀完這章才看清:那是「在宣告式語言裡寫程式」這個 anti-pattern 的通例,Helm 只是其中一個現場。訊號出現時的正解是把變異收斂(回到最小化變異)或把邏輯搬去真的程式語言,而不是繼續堆 template 技巧。
「一切皆程式碼」最大的紅利是 diff,不是自動化
推 IaC 之前,基礎設施變更的溝通是「我等一下要去改 LB 設定」這種口頭廣播;推完之後,每個變更是一個 PR,有 diff、有 review、有紀錄,部署前還有 plan 告訴你會動到什麼。半年後回頭看,自動化省的時間反而是配角——真正改變團隊的是變更第一次變得「可讀」:新人用讀 PR 補基礎設施的來龍去脈,事故時用 git log 反查那天動了什麼。知識從「在誰腦袋裡」變成「在版本庫裡」,這才是把 雪花連根拔掉的那一下。
冪等是「敢常跑」的技術地基
第一篇說恐懼螺旋的出口是「所有變更走自動化、而且常跑」,這章補上了為什麼做得到:冪等讓重跑的成本趨近於零。一份不冪等的 script,每次執行前你都要先想「現在的狀態跑下去安全嗎」——這個心智成本就是沒人敢排程它的原因。宣告式 + 冪等把這件事反過來:比對之後沒差異就什麼都不做,於是「每小時收斂一次」可以無腦開著,飄移永遠活不過六十分鐘。我的檢驗法也跟著升級:一套 IaC 健不健康,看它的自動化「上一次執行」是多久以前——排程在天天跑的,才是活的;要人鼓起勇氣才跑的,已經在螺旋裡了。