天天看點

程式員思維模式【主調試循環】

@[toc]

僅通過測試進行驗證基本上是在儀器上駕駛飛機,而不是能夠向外看擋風玻璃。視覺飛行和肌肉記憶飛行與儀器相結合,既高效又安全。你不太可能誤撞山。

當你已經編碼了十多年時,可能很難重新捕捉​​初學者的思想​​​,并向新手解釋如何像程式員一樣思考。我記得在大學裡,當我編碼的時間相對較短時,有一件事在我的腦海中結晶了編寫代碼背後的思維過程——你可以稱之為程式員哲學。我正在幫助一個朋友完成計算機科學101任務。他們對編碼完全陌生。

程式員思維模式【主調試循環】

他們從頭到尾在紙上寫了一個完整的解決方案——也許是100行代碼。然後,他們将其全部輸入到文本編輯器中,并運作它。你認為發生了什麼?他們得到了大約一千個文法錯誤。這時,他們來找我,感覺自己撞上了一堵磚牆。在同一堂課上,我一直坐在他們旁邊——但至關重要的是,我已經編碼了一段時間。我已經内化了編寫代碼的基本思維過程,而不必清楚地表達它。我們的老師未能傳授這種思維過程。

主調試循環

然後,我必須向我的朋友解釋的是我現在要稱之為“主調試循環”的思維過程。我相信這是一種自然的思維方式,在所有程式員中都會發展起來——假設他們成功地學會了編碼。它涉及将問題分解成非常小的部分。足夠小,以便您一次編寫1-3行代碼。每次編寫其中一個小塊時,您都會運作該程式。通常它不起作用,然後您重試。慢慢地,你積累了你已經說服自己有效的代碼。您可以疊代地建構整個解決方案。

關鍵是雙重的:你正在逐漸建構,并且你正在驗證你前進。随着經驗的積累,您将在越來越複雜的系統上工作。但這種心态可以擴充到任何複雜的問題——你隻需要把它們分解得更多。

這是主調試循環。編寫代碼,運作代碼。運作代碼就是驗證。

驗證在圖層中進行

究竟什麼是驗證?下面是我稱之為應用程式内驗證的示例。在Web開發中,您需要編寫幾行代碼,儲存檔案并重新整理浏覽器。然後,您手動與頁面/應用程式互動,以檢視剛剛更改的内容是否有效。對于速度,您可能隻是在測試快樂路徑,或者您目前正在實作的一個邊緣情況。對于任何其他類型的開發,過程是相似的,但具體細節看起來不同。

測試驗證不會在應用程式中發生。相反,您可以針對剛剛編寫的代碼運作一小組綜合斷言。這是一種稱為​​測試驅動開發的​​最佳實踐。TDD 與應用程式内驗證結合使用。在實踐中(與嚴格的TDD相反),我的觀察是開發人員在短循環中工作,驗證應用程式内,然後快速快速遵循單元測試覆寫率。

另一層驗證是自動化內建測試,使用Selenium等工具進行應用層驗證,或使用Postman進行API層驗證。您可能還需要進行詳盡的手動測試,可能由專門的 QA 工程師進行。最後,您可以使用功能門控在生産中進行驗證。所有這些驗證層都與音樂會一起工作,以確定品質。以疊代方式編寫代碼時,通常使用應用程式内驗證和/或測試驗證,因為它們要快得多。

優化循環時間

主調試循環可以是每小時執行數百次的内容。思考和輸入代碼是自然的瓶頸——畢竟,你比電腦慢得多。在理想情況下,運作代碼來驗證您剛剛編寫的内容是即時的。在我編碼生涯的絕大部分時間裡,運作一小塊代碼平均可能需要5秒。開銷是由于檔案系統中延遲注冊檔案已更新、運作時加載更改以及您自己的“人工時間”(與新更新的應用程式互動以運作更改并檢視結果)造成的。

計算機的循環時間部分根據語言,架構和應用程式本身而變化。腳本語言在運作之前不必進行編譯。某些類型的編碼自然涉及或多或少的人際互動。例如,與重新整理 Web 應用相比,運作控制台指令往往涉及更少的人為延遲。但是,由于主調試循環時間對開發人員工作流至關重要,是以語言和架構作者有很大的動機來優化它。開發人員同樣受到激勵,為它優化自己的應用程式。

為什麼快速循環更好

在我看來,慢循環時間讓人想起我職業生涯中一些最糟糕的調試噩夢,我稱之為“盲目飛行”。最壞的情況有兩個屬性:它們不能可靠地再現,并且每次嘗試複制都需要很長時間。對于調試,非再現性本身會導緻不可接受的慢循環時間。如果您要部署到生産環境以檢視它是否有效,或者您必須多次運作代碼以檢視它是否失敗一次,則這是最壞情況的慢循環時間方案。

很容易看出這種極端情況如何導緻開發緩慢和低品質輸出。

相比之下,我對“在心流中”的一些最積極的記憶喚起了與計算機進行持續,流暢對話的感覺。就像來回對話一樣,你們正在以協作方式得出結論。您正在驗證代碼的速度幾乎與您可以想到并鍵入它的速度一樣快。對我來說,即使能夠運作和驗證更改的10秒延遲也會破壞該流程。

盡管如此,斷言較短的循環時間更好是基于一個無法證明的假設 - 為給定的工作範圍運作更多的循環将導緻每個時間段的更高輸出和/或更高的品質。我相信這是真的,但我承認(無論個人多麼違反直覺),總體吞吐量和品質可能會随着循環時間的慢而達到或更高。

短循環時間是通用的嗎?

循環時間不能保證很短 - 事實上,技術熵将施加恒定的壓力以增加循環時間。需要花費大量的開發人員時間來確定測試套件繼續快速運作,應用程式快速重新加載代碼,并且UX本身(在面向使用者的應用程式上)為開發人員提供了快速重新加載和驗證的能力。

鑒于全新的項目往往從其母語言和架構中繼承了小型代碼庫上的短循環時間,是以在我迄今為止在小型初創公司的職業生涯中,我們往往循環時間較短也就不足為奇了。我們不得不花費精力來保持它們簡短,但我們通常能夠做到這一點。

但是,我已經看到,在較大的代碼庫中,對于較大的團隊,較短的循環時間并不是給定的。也許随着複雜性的擴大,保持較短的循環時間成本太高?無論原因是什麼,一旦循環時間達到中斷點,開發人員自然會尋求更短的循環,例如切換到測試驗證。在極端情況下,您可能根本不在應用程式中驗證,并相信您的測試正在驗證正确性。

對我來說,撤退到測試驗證似乎非常危險 - 但開發人員會做他們需要做的事情來保持循環時間短,即使他們沒有完全驗證。

一些綜合測試是必要的

如果在釋出代碼之前的某個時候,如果在完全內建的環境中運作代碼,則運送錯誤的風險會大大提高。這可能看起來像在編寫代碼時驗證應用程式内,詳盡的手動測試或生産中的門控驗證。僅在綜合方案中進行驗證根本無法提供與同時運作所有内容相同的信心。有多少次單獨開發的元件在最終內建後無法正常工作?即使接口完全比對,也會發生這種情況 - 組合行為通常仍然是錯誤的。

這個星球上沒有一個QA人員會容忍将某些東西運送到生産環境,而不以使用者體驗它的方式運作它 - 內建在一起。

僅通過測試進行驗證基本上是在儀器上駕駛飛機,而不是能夠向外看擋風玻璃。視覺飛行和肌肉記憶飛行與儀器相結合,既高效又安全。你不太可能誤撞山。

複雜性是否會導緻測試驗證循環?

當您擁有複雜的系統和大型代碼庫時,保持較短的循環時間是很困難的。熱重新加載本身可能需要超過10秒的時間,以便加載和重新編譯大型代碼庫。腳本語言在這方面有優勢,但有自己的非正交成本。如果架構需要轉譯,甚至腳本語言也可能具有不可接受的延遲。

面向服務的體系結構為主調試循環帶來了獨特的優勢和挑戰。一方面,您大部分時間都在處理單獨的,較小的代碼庫。熱重載時間更短。另一方面,運作由服務和外部資料存儲組成的應用程式既非常複雜,又需要大量的計算資源。不久之後,在本地運作它甚至是不可能的。

在實踐中,我注意到大型代碼庫、服務體系結構和将測試驗證作為主要調試循環的撤退之間存在相關性。

救援的暫存環境

過渡環境就像生産的微型版本。它應該設定所有相同的服務,以及相同的基本網絡架構。它隻是被大大縮小了。通常,它具有完全相同的資料存儲和架構,但資料集完全不同。暫存與生産完全隔離;您無法與任何服務的生産版本通信,也無法與生産資料存儲通信。

根據産品域的敏感度,您可以完整或清理地将生産資料同步到暫存。在許多領域中,從安全角度來看,這是不可能的,是以您需要建立虛假的測試資料,整個工程團隊使用相同的測試資料集和資料存儲。您開始擁有“最喜歡的”測試使用者和記錄,并且可以在應用程式中快速啟動它們。

過渡環境有很多用途 - 但它們如何幫助開發人員保持在應用程式内驗證流程中?智能業務路由可以幫助解決本地機器資源問題,并減輕維護本地資料集的負擔。缺點是它要求開發人員具有與分期的有效網際網路連接配接。

前提是将開發服務與暫存挂鈎,并通過正常的暫存服務圖路由各個應用程式内驗證請求— 目前正在開發的一個或兩個服務除外。暫存網絡拓撲會将服務調用圖的各個部分從雲中的暫存環境傳回到開發盒,可能通過 VPN。這聽起來真的很複雜,但這種動态路由是服務感覺路由網格架構(如​​linkerd)​​的标準功能。

結論

在主調試循環中從應用程式内驗證切換到測試驗證會導緻品質降低、速度變慢以及上下文切換。

繼續閱讀