專案初期的最優技術實踐
所謂最優,就是從一堆不非最佳的方案中找到最好的一個,或者整合一個。
在我最近的三個專案裡,專案的週期都太短了,大抵都在 3~6 個月之間。短週期的專案裡,對於開發人員的能力增長並不是一件好事——我們所能沉澱的知識比較少。但因為專案週期太短,也帶來了一定的挑戰性,或許是關鍵的 “坑位” 帶來的挑戰。
幾乎所有的長期專案都是從短期專案開始的——多數情況下,只有我們驗證了 idea 是可行的,我們才會投入更多的時間、精力去演化這個專案。長期專案帶來的主要挑戰是:人員能力增長、架構演進以及程式碼實踐。而短期專案的主要挑戰是:技術實踐與業務進度的衝突。即我們在追趕業務進度的同時,如何實施好的技術實踐。
於是乎,在最近的日子裡,我開始在思考:如何進行最優的技術實踐。按我的理解,我將專案初期的實踐分為了三個時期:
- 技術準備期。進行一系列充分、不充分的技術工作,從搭建腳手架,到部署測試等等。
- 業務回補期。填補第一個時期造成的業務落後問題,技術實踐業務來證明技術的價值。
- 成長優化期。持續地對專案的技術和業務進行優化,以實現開發及業務人員的訴求。
每個時期都有自己要所要面對的挑戰。
技術準備期:探索技術
這是一個技術 First,業務 Second 的階段。
一旦開發一個新的專案,我們就得做大量的技術準備工作。哪怕是我們已經有了一個現成的腳手架,但隨著時間的推移,其中的一些依賴版本就需要更新,又或許是有一些依賴在這個專案又用不上,還需要進行一系列的定製化。
對於複雜的專案,我們還需要進行一系列的技術探索方面的工具。以為將來的開發,打下深厚的基礎(避免被罵)。
概念驗證:原型
在我的上一個專案裡,才算正式接觸 PoC ( 概念驗證)這個階段。但是,我發現並不是上一個專案獨有的,只是在那個專案裡,PoC 的階段比較長,達到幾周的長度。而一般的專案,這個階段可能是一週,也可能是幾天,幾個小時——因為之前已經有了相關的經驗。
概念驗證(英語:Proof of concept,簡稱POC)是對某些想法的一個較短而不完整的實現,以證明其可行性,示範其原理,其目的是為了驗證一些概念或理論。概念驗證通常被認為是一個有里程碑意義的實現的原理。[^wiki_poc]
在這個概念驗證階段裡,實踐的是對於一些技術理論的探索。比如我們看上了微技術、微前端等新技術,或者 GraphQL 等新框架,並且計劃在這個專案中使用,那麼我們就需要去驗證它是否能真正的被用上?
可當我們預先設想的技術和架構不能應用時,那麼我們到底是採用原有的系統架構,還是新設計一個合理的架構,還是採用 B、C 方案?這個問題就變成一種考驗,在這個時候到底什麼是第一優先順序,技術、業績還是業務?但是不可避免的,我們又花費了大量的時間在原型設計上。
迭代 0
完成概念驗證之後,I0 就要完成專案配套的技術準備工作,如建立腳手架、搭建持續整合、進行更細粒度的技術選型等。
迭代 0,又名為 I0,看這名稱就可以發現它與敏捷軟體開發的關係。開始一個專案開發的時候,進入的是迭代 1 的開發,那麼迭代 0 呢?我們可以將其視之為,所有迭代之前的準備工作。它在這種定義之下,在專案不復雜的情況下,PoC 階段會被列入迭代 0 的工作中。而專案複雜的時候,PoC 則會獨立於迭代 0。有意思的是,在方案和諮詢公司裡,如我司,是 PoC 都是在開始專案前進行一部分,而在專案進行時進行另外一部分。
在 PoC 編寫的是一些粒度較粗的程式碼,並且為了追求效率和驗證,可能有大量的 Code Smell(程式碼壞味道),諸如註釋的程式碼、未使用的程式碼、不經測試的程式碼等等。因此我們需要在迭代 0 ,對架構進行更細粒度的整理和優化。同時,我們還需要確認大部分的未來將使用到依賴軟體,這些依賴在可見的未來還會經過修改,但是進入開發階段時應該是可用的 。除此,我們還要進行部署的準備工作,嘗試進行第一次部署,包含內部部署 。
迭代 0 除了技術準備工作,還需要進行內部的技術培訓——只是簡單的技術培訓,用於介紹系統的架構,開發注意事項等等的內容。當然了,即使已經有相應的培訓,還需要準備基礎的架構方面的文件,以及必要的一些規範。
這個階段結束的標誌是,專案成員可以進行正常的專案開發,這個開發和未來開發沒有區別。
業務回補期:應對第一次 Deadline
在這個階段裡,業務開發已經進入了正軌,但是可能存在一定的進度落後。於是變成了業務 First,技術 Second 。
畢竟欠下的債(業務)總是要還的。 這是一步價值證明的階段,也是調配落後的進度與先進的生產力的時期。
除此,由於內部技術的問題已經解決了,我們還需要關注於與第三方整合,並開始著手準備應用的測試,即第一次上線的相關事宜。
追補業務
…作者太懶,沒什麼想補充的…
上線準備
在這個時期裡,我們所要面臨的第一個挑戰是:第一次 Deadline 及其上線。
如果我們的專案依賴於第三方服務、平臺,並且他/她們的上線週期,和我們是相近的。那麼,這個時候與他們聯合進行除錯,就是一種挑戰。特別是上線日期臨近時,如果遇上 Bug,那麼就需要反反覆覆確認。
當依賴 ready 時,我們就可以著手準備上線相關的事宜。小公司的購買相應的伺服器,大公司的申請相應的資源、審批流程等等。
因此,在 Deadline 提前,進行磨合也是不錯的策略。
測試
在一個前後端分離的專案裡,我們在平時的開發裡,已經進行了大量的聯調,基本可以應對上線。
- 我們可能還沒有進行測試,無法驗證業務的完整性。
- 我們可能沒有完整的測試規範,來保證整個流程正常。
- 我們可能還沒有準備好一個穩定的測試環境,以提供給測試人員進行測試。
即使是敏捷專案,在第一次時,測試人員進入專案的時間,也和瀑布型專案類似,略晚於開發人員進入專案測試。一來,前後端之間的聯調可能沒有完全 ready,二來,可測試的業務內容也相對比較少。但是敏捷專案在 QA 進入測試之後,便一直在專案中進行測試。測試除了帶來功能的穩定,還會帶給開發人員一堆的 bug,這些 bug 會進一步地影響專案的進度。
對於複雜的專案而言,自動化測試(單元測試、UI 測試等)在前兩個階段可能並不會出現——大量的時間被花費在相關的技術實現上。覆蓋率在這一階段要定一個基本的值,比如說 30%,先從 0 開始,然後再進入更高的數值。基於此此,我們才有機會進一步地提升程式碼的質量。
但是不管怎樣,在第一次上線之後,我們實現了從 0 到 1 的階段,接下來就是從 1 到 100 的時期了。
成長優化期:技術債與演進
這是一個技術證明業務、技術提升開發體驗的階段。同時,隨著業務程式碼的堆砌,我們開始面對它們帶來的挑戰。
值得注意的是:
- 如果一個專案的時間太短,那麼它就不會遇到這些挑戰。
- 如果領導們看來技術不重要,那麼這個時期實踐就少一些。
- 如果我們的能力不夠,那麼我們只能努力去提升。
但是不論怎樣,作為一個手工藝人,我們總得有點 “追求 ”。
彌補技術債
這個時期,我們面臨了一些遺留的技術問題,不得不去彌補這些債務,免得被利息拖垮了。
在技術準備期,我們花費了大量的時間在構建技術基礎;在業務回補期,花了大量的時間、精力在支援業務的開發上。在這兩個時期,我們都或多或少地採取了一些妥協方案,為的是能加快速開發流程。而在當前這個時期看來,這些問題將在未來成為我們的開發負擔。因此,我們將其稱為技術債。
這些債務,包含了很多方面,比如:
-
程式碼質量。常見的問題有:介面、函式重複實現 ,即 a 成員在自己的功能內,實現過這個功能,但是 b 成員又實現一份,沒有提取到公共的方法中。實現方式、模式不統一 ,這一點常見於我們採用了某個框架來解決問題,但是真實場景下,可能又會採取過去的實踐方式,諸如使用 Lambda、RxJava、Rx.js、Ramda 等框架來進行函數語言程式設計。少部分程式碼未按規範實踐 ,這個問題更加常見,不僅僅存在於沒有程式碼檢視(Code Review)的專案,還存在於擁有 Code Review 的專案,未 Code diff 過的程式碼,往往容易被遺漏。
-
測試覆蓋率,在面對技術不熟悉,業務又過急的情況下,測試往往是最先被拋棄的一環。值得商榷的是,國內的網際網路公司都不會有測試這種東西,所以它們就存在這種長期的債務了。
-
依賴問題。依賴對於短期專案來說,並不是問題。對於長期專案來說,依賴沒有及時更新是一個很嚴重的問題。諸如,我們使用的是 Redux 3.0 的版本,當 4.0 釋出的時候,按照語義化版本的規則,它可能修改大量的程式碼,而我們不得不追隨這個變化——除非,我們決定未來重寫現在這個應用,否則大量依賴過舊的問題,會導致我們難以對程式碼進行重構,因而不得不重寫應用。
除此,還有大量的前期我們設定的技術目標,我們也會在這個時期實施。
提升開發體驗
時間一長,業務的進一步實施,我們便開始在堆砌業務程式碼。堆砌業務程式碼,對於某些技術人員來說是一件難熬的事情——每天都在重複工作經驗,於是需要幫他們尋找一些挑戰。對於技術負責人來說,堆砌業務程式碼就會輕鬆一點,畢竟風險會小一點。除此,有時候會為了績效,也需要創造一些機會。基於多方面考慮,便需要去努力地提供開發體驗。
在這個過程中,我們可能寫了大量的類重複性程式碼。這些程式碼表面看上去並不是重複的,但是從抽象層來看是重複的。應對這種方式的方式之一,是可以用諸如《如何在業務程式碼中提升:建立領域特定語言 》中的方式來進一步抽象出內部 DSL 程式碼。那麼,我們就可以減少花費在業務程式碼上的時間,進一步地花費時間去抽象這一層抽象層。
除此,還會思考怎樣去自動化一些程式碼的編寫,比如在 UI 層採用的 Sketch2Code,又比如 Java 中的 XML 程式設計。
結論呢?
……這部分稿子被颱風吹走了……