iT邦幫忙

2026 iThome 鐵人賽

DAY 9
0
Claude AI

資深工程師的 Claude Code 工作筆記系列 第 9 篇

Day 9:規劃收斂完,最容易鬆掉的是動手那一刻

  • 分享至 

  • xImage
  •  

需求談清楚了,畫面跟架構也收斂完了,接下來真正要面對的是最現實的問題,動手寫的時候,會不會不知不覺就偏離了前面談好的版本。這件事聽起來不應該發生,前面都想清楚了,照著做就好,但實際做起來,寫的過程中冒出的枝節,常常會悄悄把方向帶偏,等到寫完回頭看,才發現跟當初講好的已經不是同一個東西。

這種偏移很少是一次性的大轉彎,通常是每次一點點,這裡順手多做一點看起來合理的延伸,那裡因為麻煩就先跳過一個邊界情況,單獨看每一個決定都不算離譜,累積起來卻可能讓最後的成品跟規劃階段講好的版本差了一大截。這也是為什麼這件事特別難防,因為沒有一個明顯的錯誤時刻可以指出來,是慢慢滑走的。

這種慢慢滑走的性質,讓實作階段的把關特別依賴一個明確的參照點,不能靠感覺去判斷有沒有偏離,感覺這種東西本身就是會漂移的,今天覺得這樣寫合理,是因為昨天已經接受了一個小小的妥協,妥協疊著妥協,回頭看才發現離最初的版本已經很遠。真正管用的參照點,是規劃階段留下來的那份文件,白紙黑字寫著當初講好的是什麼,不會跟著寫程式的過程一起漂移。

這也是為什麼前面一直強調判斷要寫成文件,這件事到了實作階段才真正顯出它的價值,一份寫死的規格,是唯一不會被寫程式過程中逐漸形成的慣性帶著走的東西。沒有這份文件,核對這件事根本無從做起,因為連要拿什麼當基準都不知道,只能拿自己當下的感覺當基準,而感覺正是最不可靠的東西,它會跟著寫程式的過程一起悄悄改變,卻不會留下任何痕跡讓人察覺。

完成不是感覺做完,是驗收條件逐條打勾

這條原則前幾天已經講過,套進實作階段這裡最容易被稀釋。寫程式的過程裡,很容易因為東西看起來能跑、畫面看起來對,就覺得應該差不多了。我自己現在收尾的時候,會把當初規劃階段定下來的每一條驗收條件拿出來,一條一條核對,這一條做到了沒有、用什麼方式驗證的、有沒有具體的證據,不是靠印象打勾,是真的去看程式碼或跑一次確認。

這個逐條核對的動作聽起來機械,實際做的時候常常會發現落差,某條驗收條件當初講得很清楚,寫的時候卻不知不覺漏掉了,因為寫的過程中注意力全放在讓主要流程跑起來,一些邊界情況反而被忽略。這種落差如果沒有逐條核對,很容易就被「感覺做完了」這種模糊的判斷蓋過去,直到真正用到那個邊界情況才會被發現,那時候要付的代價比現在核對一次高得多。

舉個假設的例子,一條驗收條件寫著某個列表要依照特定順序排列,主要流程測起來確實是照順序排的,但如果列表裡有兩筆資料排序依據完全相同,程式沒有講清楚這種情況要怎麼排,寫的時候如果沒有刻意想到這個邊界,很容易就順著程式語言預設的行為跑,看起來測試通過,其實這個邊界情況從來沒被驗證過,只是剛好沒被踩到而已。逐條核對的時候,這種「有沒有想過這個邊界」的問題才會被逼出來,不是靠主要流程能跑就代表整條驗收條件都真的被滿足了。

這種邊界情況的問題,還有一個更麻煩的地方,它們通常不會在一般測試的時候被發現,因為一般測試用的資料往往剛好避開了這種特殊情況,等到真實使用的時候,遇到剛好符合邊界條件的資料才會爆出來,而那時候已經是上線之後,修正的成本比開發階段高出許多,還可能已經造成使用者看到不該看到的結果。逐條核對驗收條件的時候,特別去想每一條有沒有藏著這種容易被忽略的邊界,是把這種問題提前攔下來的一個方式,雖然不是萬無一失,但比完全不想這件事好很多。

做不到的地方,留一個看得見的標記

實作過程裡,常常會遇到一開始規劃時沒想到的情況,某個驗收條件做起來比想像中複雜,或者牽涉到另一塊還沒處理的範圍。這種時候我不會默默把這條標成做完了糊弄過去,也不會為了做到這條,順手把手伸到規劃之外的範圍去改動不該碰的東西。正確的做法是留一個看得見的標記,寫清楚這裡目前做不到、原因是什麼、以後要交給哪個時機處理。

這個標記我自己會統一叫它待補項目,跟前面規劃階段講過的待確認、判斷卡是同一套邏輯,只是出現在實作階段。範圍會悄悄膨脹或悄悄縮水,通常就是因為沒有這種標記,做不到的事情要嘛被硬做出一個勉強能用的版本混過去,要嘛被完全跳過沒人知道,兩種都比誠實留下一個標記糟糕,前者留下一個看起來能用但根基不穩的東西,後者會在後面某個時間點才被發現漏了一塊,而且已經沒人記得原因。

範圍悄悄膨脹這件事特別容易被忽略,因為看起來像是多做、是件好事,寫的時候覺得反正都寫到這裡了,順手把旁邊一個相關的小功能也一起做掉,看起來很有效率。但這種順手做掉的東西,沒有經過規劃階段的逼問跟核對,很可能藏著沒被發現的問題,而且會讓後面核對驗收條件的人搞不清楚這塊到底是不是原本規劃裡就有的,多花時間去確認一件根本不在範圍內的事。範圍的邊界是規劃階段花力氣定下來的,實作階段不該自己悄悄往外推。

範圍悄悄縮水則是相反的心態,遇到一個比想像中麻煩的驗收條件,會有一種先跳過、之後再說的念頭,這個念頭如果沒有留下明確標記,很容易在後續的忙碌裡被徹底遺忘,直到有一天真的需要那個功能才會發現,當初規劃裡明明有講到,卻完全沒被實作。範圍膨脹跟縮水這兩種傾向剛好相反,但解法是同一個,任何跟原始規劃不一致的地方,不管是多做還是少做,一律留下看得見的標記,不靠事後回憶去補救。

測試要從驗收條件反推,不是寫完程式才補

我自己現在的習慣是,驗收條件定下來之後,會先想清楚每一條要用什麼方式驗證,再開始寫真正的邏輯。這樣做的好處是,驗證的方式在動手之前就已經想清楚,不會變成程式寫完之後才臨時想要怎麼證明這個東西是對的,那種情況下常常會就手邊寫出來的東西去湊一個看起來合理的驗證方式,而不是真正對照當初的驗收條件在驗。

反過來,先把每條驗收條件會怎麼被驗證想清楚,寫程式的時候等於是有一個明確的靶心在那裡,寫出來的東西要嘛通過這個驗證、要嘛通不過,不會有模糊地帶讓人自己說服自己已經做到了。這件事跟前面一路強調的道理相通,完成的判準要先定義清楚,而不是等做完了才回頭找一個理由說自己做完了。

實際操作起來的順序是,先把驗收條件轉成一個個可以自動重跑的檢查,這些檢查一開始當然是紅的,因為邏輯還沒寫,這個紅色狀態本身也有用處,它證明了這個檢查真的有在驗東西,不是一個不管寫什麼都會通過的空檢查,這件事聽起來理所當然,但我自己真的遇過寫出一個邏輯上永遠會通過的檢查,卻沒發現的情況,先確認檢查會紅,是唯一能排除這種假檢查的方式,也是最便宜的一種排除方式,不用額外花時間去讀懂檢查的邏輯,只要看它在邏輯還沒寫的時候有沒有正確地失敗。接著才開始寫邏輯,寫到檢查變綠為止,綠了之後回頭看還有沒有可以簡化、可以寫得更清楚的地方,這個順序讓每一步都有一個明確的訊號可以參考,不是憑感覺覺得寫得差不多了,紅跟綠這兩個狀態,比任何主觀的感覺都可靠。

檢查有沒有跑偏,要換一個沒寫過這段的角色

這個角色我會刻意跟寫程式的角色分開,交給它的東西不是程式碼本身,是規劃階段收斂完的那份文件,加上實際寫出來的東西,請它核對這兩者對不對得上,不是去看程式碼寫得好不好。這件事跟前面講過的程式碼審查是不同的關注點,審查程式碼看的是有沒有 bug、寫法好不好,這裡看的是做出來的東西跟原本談好的版本有沒有跑偏,兩件事都重要,但混在一起做很容易顧此失彼。

用一個沒有參與寫程式過程的角色來做這件事,是因為寫的人自己回頭看自己寫的東西,很容易帶著寫的時候形成的一套邏輯去看,覺得這樣寫理所當然,看不出跟原始規劃已經悄悄偏離的地方。換一個角色,直接拿規劃文件跟成果對照,才容易看出哪裡不小心加了規劃裡沒有的東西,或者少做了規劃裡明確要求的東西。

這個角色做核對的時候,我會要求它逐條列出規劃裡提到的每一個項目,標記這個項目在成果裡有沒有對應的東西、對應得完不完整,還有反過來,成果裡有沒有出現規劃裡完全沒提到的東西。這種雙向核對很重要,只檢查規劃有沒有被做到,會漏掉範圍悄悄膨脹的問題,只檢查成果裡有什麼,又會漏掉規劃要求但沒被做到的東西,兩個方向都要看,才算是真正核對過。

我也會要求這個角色講清楚每一條判斷的依據,是直接看程式碼確認的,還是看行為表現推測的,還是單純根據命名或註解猜的,這三種可信度差很多,全部混在一起講,會讓人誤以為每一條核對結果都一樣可靠。看程式碼確認過的可以直接採信,靠命名猜的,我會再自己確認一次,不會照單全收,這條規則跟前面講過的道理是一樣的,篤定的語氣不能取代真正查證過的證據。

程式碼裡的命名跟註解尤其容易造成誤導,一個函式的名字聽起來在做某件事,實際的邏輯卻可能因為後續修改而悄悄變了樣,名字沒有跟著更新,這種情況只憑名字判斷,很容易得出錯誤的核對結果。真正可靠的核對,一定要落到實際的邏輯或實際跑出來的行為,不能停在表面的命名,這件事說起來簡單,做起來卻是最容易被偷懶跳過的一步,畢竟讀名字比讀邏輯快得多,偷懶的誘惑一直都在。

不同性質的檢查,用不同等級的資源

驗收條件逐條核對這件事,拆開來看其實有兩層工作,一層是機械性的比對,這條驗收條件對應到哪個測試、測試有沒有通過,這種工作用中等等級的資源掃過去就好,不需要特別聰明,需要的是仔細跟不遺漏。另一層是判斷這個測試通過是不是真的代表驗收條件被滿足了,有沒有測試寫得通過但其實沒測到真正該測的東西這種情況,這種需要理解意圖的判斷,我會留給比較強的資源處理。

核對跑偏的那個角色也是一樣,逐項列出規劃跟成果的對應關係,這個列表本身可以交給中等資源去做,真正需要判斷力的是看出兩者之間有沒有細微但重要的落差,這種落差常常不是一眼就能看出來的,需要真的理解規劃背後的意圖,才知道成果是不是真的滿足了那個意圖,不是表面上看起來對應得上就算數。

這種按重要性分級的做法套用下來,整個核對流程的成本明顯控制得住,因為真正花力氣的地方,只有最後那一段需要理解意圖的判斷,前面大量的列表比對工作都可以用比較省的方式處理掉,不會因為要核對得仔細,就把每一步都用最貴的方式跑一遍,那樣核對這件事本身反而會變成拖慢整體進度的負擔,違背了核對原本是要幫忙把關、不是要拖累進度的用意,這條分寸抓不好,很容易讓一套原本該省時間的方法,變成另一種形式的拖延。

這件事不是每次都要搬出整套流程

跟前面幾天講的道理一樣,一個很小、影響範圍有限的改動,不需要每次都拉出驗收條件核對加換人檢查這一整套。真正值得走完整套流程的,是那種規劃階段花了不少力氣收斂、牽涉範圍比較大的工作,這種工作實作完如果沒有認真核對過,前面規劃階段付出的心力等於白費,因為規劃跟實作對不上,規劃再仔細也沒有意義。

判準還是同一個,這裡如果跑偏了,多久才會被發現,發現了要花多少代價修正。影響小、很快就能發現的東西,寫完自己看一眼順不順就好。影響大、可能要等很久才會被注意到落差的東西,才值得花時間走完整套核對流程。

我自己抓不準的時候,會偏向多花一點時間核對,這個習慣的代價是偶爾會核對一些其實沒問題的東西,浪費一點時間,但比起漏掉一個真正跑偏的地方、等到很久以後才被發現,這個代價我覺得划算。這條偏保守的判準跟前面講過的一樣,錯的方向如果只能選一邊,我會選代價比較低的那個方向錯,這不是因為謹慎是美德,是單純算過兩邊代價之後的選擇。

標記留下來之後,不能就這樣算了

待補項目寫下來之後,我自己會養成一個習慣,定期回頭看這份清單,不會讓它變成一份寫了就沒人再理的名單。有些待補項目後來發現其實不重要,可以直接關掉,有些會隨著專案進展變得必須處理,這種要盡快排進下一輪的工作裡。最怕的狀況是這份清單一直長、一直沒人清,變成一堆沒人記得為什麼存在的舊項目,那樣的話,留標記這個動作本身的意義就消失了,因為留了也沒人會回頭看。

這件事讓我想到,誠實標記待補跟確保標記真的被處理,是兩件分開的事,前面講的都是怎麼誠實標記,這裡想補一句,標記只是第一步,讓標記真的發揮作用,還需要有一個回頭清理的習慣,不然標記本身也會變成另一種形式的自我安慰,覺得反正有記下來就沒事了,實際上從來沒被真正處理過。

回頭清理的時候,我會特別留意那些放了很久都沒被排進工作的項目,這種項目通常代表兩種情況,一種是它其實沒有想像中重要,一直沒被排進去正好證明了這件事,可以放心關掉。另一種是它牽涉到的東西比想像中麻煩,每次考慮要不要排進去都會被跳過,這種項目反而要提高警覺,因為越拖,處理的難度可能越高,不是放著不動就會自己變簡單。

分辨這兩種情況的方式,我會問自己一個很直接的問題,如果永遠不處理這個項目,會不會真的出事。答案是不會,那就是第一種情況,可以放心關掉。答案是會,只是還沒發生,那就是第二種情況,反而要優先排進下一輪,不能因為它一直沒爆出來就誤以為它不重要,很多真正棘手的問題,都是在還沒爆出來之前看起來人畜無害,等到真的爆出來,才發現早就該處理了。

這件事解決不了什麼

老實講清楚這套做法的界限。逐條核對驗收條件跟換人檢查跑偏,能抓出來的是做出來的東西跟規劃對不對得上,抓不出來的是規劃本身有沒有問題。如果一開始的規劃就有漏洞,實作完全照著規劃做,核對起來完全吻合,看起來一切正常,但做出來的東西還是有問題,因為問題根本不在實作階段,早在規劃階段就已經埋下去了。這種狀況核對再仔細也發現不了,因為核對的基準本身就是錯的。

這件事讓我對核對這個動作的定位更清楚,核對驗證的是一致性,不是正確性,一致代表做出來的跟講好的一樣,不代表講好的東西本身是對的。這兩件事很容易被搞混,覺得核對通過就等於整件事沒問題,實際上核對通過只證明了實作沒有偏離規劃,規劃本身站不站得住腳,是前面幾個階段的責任,不是核對這一關能補救的。

這也是為什麼前面每一段講的逼問、盤點、組隊分析都不能省略,它們負責的是確保規劃本身是對的,實作階段的核對負責的是確保沒有偏離那個已經確認是對的規劃,兩種責任分屬不同階段,混在一起看,會誤以為只要最後核對通過就代表一切都沒問題,忽略了核對的前提其實是前面幾關已經先把好了關,這個前提一旦不成立,核對這道關卡能提供的保障就跟著失效。

還有一種比較棘手的情況,實作過程中發現規劃本身有問題,這種時候正確的做法不是硬著頭皮照規劃做完,也不是自己悄悄改掉規劃繼續做,是把這個發現明確標記出來,回頭去確認規劃要不要調整。核對這件事的前提是規劃值得信任,一旦發現規劃本身有問題,核對的意義就變成確認新的問題出現在哪裡,而不是單純比對兩者一不一致。

這種情況我自己這陣子也真的遇過,寫到一半才發現規劃裡某個假設在實際操作上根本行不通,繼續照著寫下去只會做出一個表面符合規劃、實際上沒辦法真的用的東西。這種時候硬做完最省事,看起來進度有推進,交出去的東西也顯得像模像樣,但做出來的東西實際上沒有價值,回頭重來的成本比當下停下來確認高得多。停下來把發現寫清楚,自己重新確認一次方向要不要調整,才是真正對整體進度負責的做法,不是表面上的進度好看就好。

從一個模糊的想法走到這裡

寫到這裡,回頭看走過的路,一開始只是一句聽起來還不錯的需求,被逼問出裡面藏著的分岔,寫成一份講清楚的規格,接著轉成畫面跟架構的收斂版本,中間可能還拉了一組角色分頭分析出一份站得住腳的技術方向,最後才落地成真正能跑的程式,還要回頭確認這個結果沒有偷偷長歪。這一整條路,沒有一段是可以跳過的,跳過任何一段,後面的東西都會建立在一個不夠穩的基礎上。

需求沒逼問清楚,畫面架構那段做的就是在收斂一個原本就模糊的目標,收斂得再仔細,目標本身還是模糊的,仔細只是把模糊包裝得比較好看而已。畫面架構沒收斂好,架構規劃那段分析的就是一個內部互相矛盾的東西,分析得再深入,矛盾還是在那裡,深入只是把矛盾看得更清楚,不代表矛盾消失了。架構沒想清楚,實作就是在一個不穩的地基上蓋房子,蓋得再仔細,地基垮了整棟都要跟著垮。每一段都在替下一段把關,任何一段做得不夠紮實,後面的關卡不管看起來多用心,都會提早失守,而且往往失守得無聲無息,等發現的時候已經走了很遠。

這種前後階段環環相扣的關係,也讓我對哪裡該多花時間、哪裡可以放心快一點,有了更清楚的判斷。越前面的階段出錯,波及的範圍越大,因為後面所有階段都建立在前面的結果上,這也是為什麼需求跟規劃這兩段,我自己願意花比想像中更多的時間去逼問跟核對,前面多花一分力氣,後面能省下的返工往往是好幾倍,這筆帳只要認真算過一次,就不會再想省略前面的步驟。

這條路走完最大的感覺是,每一段用到的具體做法都不一樣,逼問、盤點、組隊分析、逐條核對,但每一段都在做同一件事,先確保手上的資訊是乾淨可信的,再讓一個夠格的判斷立場做出取捨,最後留一道自己核對的手續,不會因為前面每一關都做得很仔細,就跳過最後這道把關。一個人要撐起原本一整條需要好幾個角色才走得完的路,靠的就是把這套動作在每一段都做一次,沒有捷徑,也沒有哪一段可以理所當然地被省略。

這條路也不是走一次就結束的。真正的產品開發是一輪一輪疊上去的,這次實作完成核對過的東西,會變成下一輪需求討論時的既有現況,下一次逼問的時候,要盤點的現有系統,就是這一輪產出的成果。每一輪走完留下的東西越紮實,下一輪要重新確認的東西就越少,這也是為什麼每一段都不能因為想快點進到下一段就隨便帶過,省下來的時間,會變成下一輪要花更多力氣去補的洞。

把這整套動作放進一段時間之後,我發現整條路線其實是一個持續累積的過程,不是每次都從零開始重新盤點、重新逼問。前一輪留下的規劃文件、判斷卡、待補清單,都會變成下一輪的起點,一輪一輪走下來,累積的不只是做出來的功能,還有這整套判斷方式本身沿路留下的紀錄,這些紀錄本身就是一份越來越值錢的資產,比單純做出來的功能更耐用,因為功能會過時,判斷的脈絡不會。

這件事也讓我對「一個人跑完整條線」這句話有了更具體、更貼近實際操作的理解,不是靠一次把所有事情都做對,是靠每一輪都留下夠紮實的東西,讓下一輪不用從頭來過。累積到某個程度之後,速度會自然加快,不是因為突然變厲害了,是因為要重新確認的東西越來越少,省下來的力氣可以放在真正還沒被想清楚的新問題上。這種加速感很容易被誤解成能力突飛猛進,實際上只是欠的功課還完了,之前每一輪認真核對、認真留紀錄的成本,到了後面開始回本,不是憑空冒出來的效率。


上一篇
Day 8:架構規劃這件事,我會組一個團隊來想,不是自己埋頭想
下一篇
Day 10:上線前最後一關,環境不一樣,結果就可能不一樣
系列文
資深工程師的 Claude Code 工作筆記 共 11 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言