讀 Clean Agile ,思考敏捷的本質

Kevin W
9 min readAug 21, 2023

--

在軟體業工作的人,應該都聽過敏捷(Agile)
然而,我們認識敏捷的契機,可能都是來自 Scrum, Kanban 等等實務操作
而不曾細究過根本

敏捷究竟是什麼?敏捷又想解決什麼樣的問題?

我想在讀了 Clean Agile 這本書之後
談談我對「敏捷的本質」所做的一些思考。

另外,我也將我的讀書筆記,整理成了一張 Heptabase 白板
有興趣的朋友們也歡迎參考。

軟體開發的兩大難題

談敏捷之前,我想先談談敏捷的背景。

敏捷是在軟體業中發展出來的
而談到軟體業,就不得不談軟體業中經常碰到的兩個大問題:

「客戶要什麼」與「技術債」。

客戶要什麼

跟客戶溝通討論,問出他們的需求,這會有什麼問題呢?

問題在於:客戶也不曉得自己真正要什麼

客戶一開始會給我們一些場景和範例,作為開發軟體的參考
於是我們開始分析需求、設計軟體架構。

到目前為止沒什麼問題。

然而當我們將程式實作出來後,問題就來了:

「這邊可以改成這樣嗎?應該很容易吧!」
「這邊可以加個功能嗎?」
「這個功能想一想好像不需要,可不可以拿掉?」

我們費了好大的工夫寫出來的程式
客戶這邊要改、那邊也要改,沒完沒了

於是開發團隊只能疲於奔命
一邊應付客戶的要求,一邊追趕著時限
最後交出一個看似符合客戶需求,但是暗藏很多問題的半成品。

技術債

既然可以符合客戶需求,那會有什麼問題呢?

問題可大了。

因為開發過程中做了大幅度的修改,加了許多意料之外的功能
導致原本程式的架構設計不敷使用

於是新功能只能拼拼湊湊、疊床架屋
以類似違章建築的形式加在上面

就這樣程式碼漸漸變得越來越複雜
就像是一團義大利麵,剪不斷理還亂

而這團混亂的程式碼
我們就給他取了一個好聽的名字「技術債」。

更慘的是,這亂成一團的程式碼
和改需求的問題會開始產生一個惡性循環:

為了趕時間加需求呢,只好拼命蓋違建
而違建蓋上去之後呢,新需求又更難塞進去了

於是有一天,我們會發現
程式碼完全改不動了

我們只好祭出殺手鐧:砍掉重練!

於是,我們又開始了一個新的迴圈。

直覺害死了我們

究竟是什麼讓我們落到這步田地,我想我會這麽說:

是直覺害死了我們。

在我詳細說明之前,大家可以先回答看看這兩個問題:

  1. 當你想解決一個簡單的小問題時,你會怎麼開始?
  2. 當你面對一個複雜又麻煩的大問題時,你又會怎麼開始?

我想大多數人的回答應該都會是:

簡單的問題直接動手,臨機應變
複雜的問題則是先做分析和規劃,然後一步一步執行

對吧?

然而,我想說的是:正是這樣的直覺將我們推入深淵。

完整計畫的盲點

分析問題、設計解法、制定計畫、完美執行。

這樣的方法論確實可以幫助我們解決許多問題
但這個方法其實有一個很大的前提,就是:

這要是一個固定而不會改變的問題

例如我們準備考試,考試範圍是固定的
所以我們可以透過周全的準備來處理它

但如果問題本身改變,這套方法就失效了
(請想像一下清朝末年科舉廢除時,十年寒窗中傳來書生們的哀號)

這也就是「計畫趕不上變化」

而當問題的變動幅度與頻率越高,這個方法的失敗率也就跟著越高

變動正是軟體的本質

而軟體開發,恰恰好就屬於這種變動的問題
甚至我們可以說,變動本身正好就是軟體的使命

我們就是不希望每次修改功能都要「重新製造硬體」
所以才發明了「軟體」

「軟體」這個名字本身就代表著「容易修改」的意義
「容易修改」本來就是軟體的使命
「產出容易修改的軟體」也正是軟體工程師的價值所在

所以,到頭來,原來我們沒有資格抱怨客戶改需求!

晴天霹靂!

(請自行播放音效)

我們之所以陷在技術債和改需求的泥沼裡
罪魁禍首原來不是客戶和老闆

而是「做出完整計畫」的直覺害死了我們。

敏捷 — 解決動態問題的方法論

瀑布 vs. 敏捷

使用完整的設計與計畫來進行軟體開發
這種方式被稱為「瀑布式開發」

因為這種方式的流程是先分析、再設計、最後實作
一步接著一步,就像是水從河川的上游流到下游一樣
於是我們給它取了一個「瀑布式開發」這麼澎湃的名字。

但現在,我們已經可以看出這種方式的問題所在:

軟體的本質就是會變動
所以我們一開始做再多、再完整的分析與設計
其實就只是做白工而已。

那麼,敏捷是怎麼處理這樣的問題的呢?

我會用八個字來總結敏捷的精神:快速回饋,快速反應

因為問題本身不斷變動,所以我們必須不斷從問題身上取得回饋
得到了回饋,就有辦法修正我們的方向,緊緊追著問題跑

而為了能夠追上問題改變的速度,我們的計畫也不能太過龐大笨重
於是我們縮小計畫的規模,把做事的顆粒度變小
這樣就能在得到回饋時即時轉向

也就是說,敏捷放棄了一口氣解決大問題
而是透過把大問題切小,每次都只處理最重要的小問題

透過頻繁的追著重要的小問題跑
我們就可以跟上整個大問題改變的腳步。

敏捷的業務實踐:持續為客戶提供價值

以上的策略,實際應用到「客戶要什麼」的問題上
就成為了「敏捷的業務實踐」
也就是我們所熟知的 Scrum 或是 Kanban

以 Scrum 為例:

通常,我們會設定一個兩週到一個月的開發週期

每個週期開始前,團隊都會跟客戶討論當下最重要的需求
在這個開發週期內就只做這些事

而在週期結束時,團隊則會向客戶展示這些完成的功能
再開始下一個週期

於是,我們就有了一個需求與開發的迴圈
每個迴圈中團隊的所做,都是基於客戶當時的回饋

而在每個迴圈中,客戶只能決定需求的優先順序
能夠完成多少事則是由團隊決定

這樣一來,就能確保客戶永遠得到最重要的產出
而團隊也不需為了一些可能根本不需要的需求
趕工端出粗製濫造的產品

因為每個周期做的都是最重要的需求,我們也最小化了作白工的機率

而其他次要的需求,則因為團隊根本尚未開始設計與實作
客戶也可以輕易地改變心意,不會對開發的進度造成影響

於是,我們可以看到,敏捷以一種非常優雅的方式
一步一步地解決客戶當下最迫切的小問題

同時解決了「完成之後才發現不是客戶要的」這個大問題

敏捷的技術實踐:維持高品質的程式碼

現在我們知道了,敏捷是如何持續為客戶提供價值
接著,讓我們談談技術債的問題。

技術債的本質其實就是程式碼的品質問題
而我們可以將程式碼的品質分成兩個層面:功能面和設計面

在功能面,我們希望軟體能夠正確提供所需的功能
在設計面,我們則希望軟體可以擁有良好的架構,高效能、易修改

而當我們犧牲設計面來滿足功能面時,所得到的就是技術債
但設計面所累積的問題,到頭來還是會變成功能面的問題
因為功能面與設計面,其實是一體兩面。

面對技術債,直覺上我們仍然會這樣想像:

如果有一段時間可以把技術債一次解決
從此之後,就能過著幸福快樂的日子。

然而事與願違
還技術債通常還是免不了與新功能的開發同步進行

於是,我們就又碰到了相同的狀況:變動的問題

我們費盡心思梳理糾結的程式碼,想出更好的設計,改好了程式

結果程式碼想要合併時
卻發現同事最近新加的功能,又產生了一坨亂七八糟的程式碼

而這些程式碼和整理過後的設計並不相容

於是,負責處理技術債的工程師只好和負責新需求的工程師賽跑
拼命趕在新程式碼產出之前把舊的程式碼改好。

然而,就算成功趕上了,還有一個難關要過:

「你怎麼知道舊的功能沒有被改壞?」

於是為了避免改壞程式,只好做測試
但是做了測試,又被新加的程式碼超車了。

於是,重構的程式碼永遠無法真正上線使用

最後還是只能回到那團「可以動的程式碼」,將就著用。

那麼敏捷又是怎麼處理技術債問題的呢?

還是相同的精神:快速回饋,快速反應

首先,敏捷建議我們使用測試驅動開發(TDD)
也就是說,在實作一個功能之前,永遠都先實作這個功能的「測試」

所以當我們的實作有問題時,可以馬上得到回饋
而因為這時改動的程式碼還非常少,所以 debug 的速度也會快上許多

而且,當我們遵循 TDD 的紀律
程式中所有的功能都將會有對應的測試
這代表著:我們可以得到整個程式「功能面的完整回饋」

也就是說,只要所有的測試都亮綠燈
我們就可以很有信心的說:程式沒問題。

而有了這些測試作為基礎,我們就可以安心的進行結對編程 (Pair Programing)、程式碼審查等等敏捷實踐,讓團隊成員之間可以頻繁地互相給出「設計面」的回饋。

因為在完整的功能面測試的支持下
我們可以隨時隨地進行設計面的修改,而不再害怕把程式弄壞。

認真來說,技術債的問題其實比「變動的問題」更加複雜
因為它是「會持續累積的變動問題」

而 TDD 的解法
就像是在每個小問題可以發生的地方,都預先裝上了護欄

它不只解決了當下的小問題
在不斷累積之下,這些小護欄最後會形成一個很大的防護網

這個防護網確保了程式在功能面上的完整性
同時也給了我們對設計面動刀的勇氣
讓我們不再害怕會把功能面改壞。

TDD 表面上看起來,像是繞了個遠路去解決功能面上的小問題
而實際上,卻因為它堅實地守護住了功能面
而為程式的整體帶來長遠的複利效應。

能夠想出這個方法的人,我只能說佩服佩服!五體投地!

結論

軟體開發所面對的客戶需求與技術債都是頻繁變動的問題

客戶需求,是大問題當中的小問題不停變動
技術債,則是小問題不斷疊加變成大問題

而敏捷給我們的解方則是:

藉由快速得到小問題的回饋,來解決小問題

只要緊緊跟上問題的變動
就能一步一步將整個大問題解決。

然而,這樣的做法其實非常地違反直覺:

明明我們所面對的問題
是比大問題還要困難的「變動的大問題」

而敏捷卻不要我們「解決大問題」
甚至要我們從可以解決大問題的「完整計畫」向後退步
變成一種「有計劃地臨機應變

正是這樣的矛盾與不協調,形成一股隱形的力量,阻礙我們推行敏捷。

但只要當我們看清楚敏捷的本質在於「處理問題的變動性」時
我們就能夠真正接受敏捷、享受敏捷
並且開始欣賞敏捷的巧妙之處

下次,當我們碰到新的變動問題時
也許我們也可以思考看看敏捷的精神:快速回饋,快速反應
相信敏捷能夠再次派上用場!

--

--