寫程式碼之前應該做的幾件事

騰訊技術工程發表於2020-07-14

作者:borisyang,騰訊 WXG 應用開發工程師

作為程式設計師,剛剛開始學會寫程式碼,常常是接過需求就開始擼程式碼。有時候發現,寫完程式碼,需求變了。更多時候,覺得寫業務程式碼枯燥無聊,沒有技術含量。另外一邊的事實卻是,專案裡面研發人數變多了,專案的質量缺卻變低了,多人開發也不過是一個個單打獨鬥的組合而已。

1 研發環境日益成熟

經歷過 PC 網際網路的不斷深入發展,移動網際網路的蓬勃生長,網際網路進入了成熟繁榮期,研發環境也發生了巨大變化;從原來一個人,一把鍵盤,寫完程式碼就上線,變成了更加規範的研發體系和更多人參與的共同協作。

研發流程不斷加速

為了儘可能提高需求交付速度,跟上市場的變化,我們透過不斷提搞軟體交付的速度,儘可能的,從需求,編碼實現,測試,釋出的流程中不斷最佳化,利用 CICD,加速迭代。

寫程式碼之前應該做的幾件事

多人協作無處不在

現在的軟體開發團隊,即使再小,也有 2-3 人一起研發,更別提測試,運維,運營,產品人員。一方面是軟體產品的競爭日趨激烈,需求日益複雜,堆砌人力成了必然;另外一方面是專業性的要求,精密的行業自然要求精細化的職業劃分。

2 困局

研發流程的速度提上去了,團隊的人也變多了,但是,需求變更依舊讓廣大研發同學感到痛苦,專案質量還在日益變差

需求變更之痛

需求變更的痛苦為難了廣大研發同學,前腳剛為了最佳化效能,採用了 kv 儲存,後腳需求就變成了要支援模糊查詢;這是一種典型的架構設計不合理,導致業務需求的實現方式受限。

更令人痛苦的,還有產品需求變動多,今天簡單實現下,上線看看效果,明天使用者脾氣很大提了個訴求,再加一個功能上線,產品功能變成補丁加補丁。一方面是研發同學渴望一個完整又嚴謹的需求,提完需求進入研發階段就不許改;另一方面是產品同學受到各方面的壓力,只希望先把主要問題解決下,細枝末節以後再說。

專案質量變差

專案質量變差,一部分歸功於補丁程式碼的產生,迫於時間受限,先上一個補丁,卻開啟了破窗的先鋒,下一次,下一個同學就更敢於加補丁程式碼。一個個臨時的 if else 不斷堆砌,最終導致了整個專案的程式碼腐爛。我曾經維護過一個程式碼片段,超過 20 個 if else,中間還有些過時的錯誤註釋夾雜其中,維護起來令人苦不堪言。

專案程式碼腐爛的另外一個原因是多人協作,團隊的人越多,程式碼反而變得越爛似乎成為了趨勢;為什麼多人協作沒有提高程式碼質量呢?一方面,多人協作實際上只是分攤的需求實現而已,大多數需求實現的分配中,反而儘可能將協作變少,避免實現受阻。另外一方面是,不同人的程式碼模組,設計意圖和程式碼風格也截然不同。維護前人程式碼,如果沒有全域性視角,瞭解設計意圖,也只能是往裡面加補丁程式碼了。

專案程式碼腐爛容易導致程式設計師出現錯覺,一是業務程式碼沒什麼料在裡面,不如搞基礎建設;二是業務需求不可能完整又嚴謹,最終也會變來變去的,最終質量低下的鍋,一大半要給提需求的人。

3 怎麼辦

在寫程式碼之前要進行設計和建模。相比歷史短暫的 IT 行業,很多工業,建築行業的精密性,都離不開前期的設計,在分析設計之後,按照圖紙規劃施工,寫程式碼也應當如此。

寫程式碼之前應該做的幾件事

設計建模的有效性源於,一,重新回到業務的跑道,跟業務一致;二,設計建模才能讓協作真實有效

為什麼研發實現需求跟業務一致很重要呢?研發和業務需求的摩擦,本質是研發實現跟實際需求不一致,無論是研發走偏了,沒有理解需求,還是需求本身不能滿足涉眾的利益,都會使得最終上線的功能需要回爐重造,折磨專案組的成員。業務專案,需求很重要,是整個專案質量的源頭,源頭的問題不處理好,會一直髮散擴大,問題傳遞到尾部,甚至到了產品上線,對整體造成的損耗越大。從設計的語言上看,設計的層次有所不同,不僅僅有程式碼細節上的設計,也有業務上高層次的設計,高層次的設計是用業務的術語去表達,最貼近業務實際情況,也能幫助研發同學發現業務中不合理的點。

設計建模為何能讓協作真實有效?我早先體驗的協作流程,無非就是各自工作在自己的領域內部,彼此儘量減少要協作的內容,避免過多阻塞。研發側的協作,因為缺乏設計,不好分工,另外一方面,多人寫同一個模組,也會引發衝突,所以更多將協作放在在 code review 上。但 code review 作用範圍也有限,一方面,review 成本較高,逐行閱讀程式碼來釐清設計對程式碼質量要求很高,另一方面,review 時間節點往往發生較晚,臨近釋出的時候,調整設計也不大可能。進行設計建模能夠讓協作變得有效,一方面,設計建模前期是溝通和資訊對齊,將協作的內容提前,一方面,採用合適的圖形化工具,review 的成本是相對較低的。

4 怎麼設計和建模

設計和建模分為好幾個部分

  1. 業務建模,關注業務,不關注具體的實現
  2. 系統建模,關注所建設系統的邊界,找準在業務中的系統的職責和約束
  3. 分析與設計,定位核心領域,找到實際的類,釐清類的職責和類之間的關係,透過設計使得程式碼抽象複用

業務建模

總體上來說,業務建模主要聚焦於分析涉眾利益,釐清業務流程。從工具上來說,主要是用例圖,流程圖;從內容上來說,主要是找人(利益涉眾,系統執行者),找業務實體(其餘系統,相關的重要物件)。

分析涉眾利益

分析涉眾利益之前,需要找到涉眾,一般要經歷以下步驟:

  1. 找到軟體產品的願景,願景表達了軟體產品帶來的核心意義
  2. 找到利益相關的的涉眾和其利益訴求

表格是一個很好的表達方式,我負責的一個商戶從第三方商城採購刷臉裝置,由倉配系統配送裝置的業務,可以表達如下:

願景:將裝置更多更快且準確無誤成本低地賣給商戶。

涉眾利益訴求
物料組老闆在準確無誤的情形下,更多更快成本低地將裝置賣給商戶
裝置渠道商準確無誤且快速地配送裝置給商戶
商戶更快地獲得自己購買的裝置
物料運營更加準確的配送裝置給商戶

一般而言,涉眾的利益是否被滿足直接決定了軟體產品的成功與否。而分析涉眾利益需要進行詳細的調研,研發同學可以根據產品的調研看到對應的涉眾,及其利益。

業務用例圖

知道了涉眾的利益之後,就要分析業務流程,並對現有的流程進行改進。軟體產品沒誕生之前,業務是如何被處理的,找到原來業務的處理方式則可以梳理出業務用例。

筆者負責的一個業務是向購買裝置的商戶配送裝置,對於業務團隊的實際業務來說,使用者購買裝置有業務價值,業務用例如下:

寫程式碼之前應該做的幾件事

物流公司和裝置渠道商都是輔助購買裝置的執行者,因此放到右邊。值得注意的是,業務用例要體現價值,雖然在實際業務流程中,商戶同時做了很多事情,比如簽收裝置,但簽收裝置不能反映業務價值,故而只有一個購買裝置的用例。

業務流程分析

瞭解了涉眾的利益並且畫出用例之後,需要分析業務流程,找到我們軟體系統能夠改進的流程片段;完整的業務流程圖可能很龐大,需要關注的是其中最有可能影響涉眾利益的流程片段,如下為購買裝置業務流程中配送裝置的流程片段,該片段不大符合涉眾利益;

寫程式碼之前應該做的幾件事

可以看到,在原來的業務流程中,配送裝置的流程是在全部業務流程中較為繁重,人肉工作量大的流程片段,不符合涉眾利益

  1. 收集商城訂單資訊不及時,導致配送不及時,影響涉眾的利益
  2. 人作為節點參與處理,成本高,耗時長,也是不符合涉眾利益的

所以很明顯,我們的系統需要改進流程,替代人的部分工作

寫程式碼之前應該做的幾件事

新的流程有效地滿足了涉眾“將裝置更多更快且準確無誤成本低地賣給商戶”的利益訴求。

業務序列圖中,每一個箭頭代表的是職責,在業務序列圖中,需要考慮的是職責的層次問題,過於小的職責放入流程圖中,會導致資訊過載,忽略最有價值的職責。在上圖中,運營核對裝置的配送資訊是一個很重要的職責,在原來的需求中體現比較弱,研發同學可以藉助業務流程的分析來分析需求中不合理的地方,完善需求,避免後期的改動,前期越是完善,後期的損失成本越低。

業務流程分析是一件很複雜的事情,研發同學可以利用需求中的資訊,同時加上自己跟涉眾的日常溝通和調研,把握核心的涉眾利益,業務用例和業務流程,就可以解決大部分在需求上的理解偏差問題。

系統建模

系統建模關注的是系統與外部的邊界和系統自身的職責

系統建模需要做的事情

  1. 畫出系統用例
  2. 寫出用例規約
系統用例圖

系統用例圖是業務流程中,系統執行者與系統發生的有價值的互動。系統執行者可以是人,可以是外部系統,甚至可以是時間。系統用例要體現系統的價值,系統會做很多事情來實現業務價值,我們應當關注業務價值。有些是低層次的職責,沒有體系具體價值,如:“獲取商城訂單資訊”是為了配送訂單中的裝置而發生,應當關注“配送裝置”。

如下是倉配系統的系統用例圖:

寫程式碼之前應該做的幾件事
系統用例規約

有了業務流程圖和系統用例圖,需要根據進一步細化系統邊界上的約束,保證系統的穩定性。系統執行者與系統的互動細化了詳細的約束,系統的穩定性才能提高,如果沒有仔細列出約束,有可能會忽略一些邊界條件,導致系統的故障;如:倉配系統不考慮來自第三方商城訂單要配送的裝置數量限制,則會因為第三方商城出現的錯誤,導致資產損失。

系統約束來源於系統用例,根據業務的規則,詳細地描述了業務流程中的基本路徑,擴充套件路徑和約束。

配送裝置:

  1. 系統每小時向第三方商城查詢待配送的訂單
  2. 系統驗證訂單是否已經配送
  3. 系統驗證訂單要配送的 SKU 是否合法
  4. 驗證訂單中配送的裝置數量是否超過最大限制 1000 筆
  5. 系統向物流系統請求給訂單中的使用者配送訂單中的裝置

因為步驟 2,3,4 還有其餘可能的路徑,稱之為擴充套件路徑。

  1. 訂單已經配送
  2. 系統忽略該訂單

這裡的約束不是告訴研發同學如何實現功能,這裡的約束是業務規則,釐清系統執行者與系統之間的邊界,以及邊界上的約束。設計評審的時候,可以關注關鍵路徑上的安全規則是否到位,這麼做對提高系統的安全穩定有極大的幫助。

類的分析與設計

這可能是大多數研發同學比較熟悉的領域,經典的設計模式,類之間的關係,泛化,組合等。但是類從哪裡來呢?是從需求之中憑空產生?又或者突然靈光一閃,有了類的雛形?對類的進行設計與分析之前,需要做的是找到他

識別類

經歷過業務流程,系統建模,我們終於來到了系統裡面,來尋找類。我們所熟知的類有三種,邊界類,控制類和實體類。邊界類是外部系統在系統內部的對映,藉由邊界類,系統和外部系統互動。所以一些介面請求,輸入輸出都屬於邊界類的職責。在倉配系統中,商城就是一個邊界類,將外部系統轉移到內部系統來,遮蔽了介面請求相關的細節。控制類往往是體現用例流程,一般而言,一個用例就是一個控制類。實體類則是系統的核心,實體類良好設計能夠提高系統的複用程度,減低系統的複雜性。

找實體名詞

知道了有這三個類還是不足夠我們識別具體的類,識別具體的類需要去業務流程,系統流程,系統規約中經常出現的名詞。在上面的流程圖中,訂單,商城,裝置,物流,使用者是反覆出現的名稱,說明這些類必然存在。

寫程式碼之前應該做的幾件事

找到這些業務實體,就是找到類的第一步。

找到屬性

類的屬性也不是憑空產生的,需要對業務實現有價值,使用者不一定有姓名,在物流上下文中,使用者的屬性就只有 ID,收貨地址。找到那些對於系統實現必不可少的屬性,放到正確的類中。如倉配系統中的訂單,包含訂單號,商品,使用者。使用者則有收件地址。

寫程式碼之前應該做的幾件事

在倉配系統中,使用者只有一個地址,在商城的系統中,使用者則有多個地址,充分說明了,不同的上下文中,類的屬性不是固定的。

找職責

從業務規則和約束中,可以找到一些實體應當有的職責,如訂單,就有驗證合法性的職責。

寫程式碼之前應該做的幾件事

有時候,有些物件看起來資訊很富裕,但是卻沒有什麼職責,說明他是一個值物件,像上圖中的使用者和收件地址,我們不關心他的 id,只關心收件地址,收件地址就代表著這個使用者。

狀態機

找到類和對應的職責,對於一些主要的實體類,還需要設計出他的狀態機,清晰的狀態機能有效地釐清系統內的一些事件和狀態,增強系統整體的健壯性。

倉配中的訂單,從使用者購買的待發貨狀態,到通知物流發貨,再到實際發貨,物流簽收有一些列狀態的演變。

寫程式碼之前應該做的幾件事

5 總結

在多人協作的專案中,不斷提高專案質量,除了依靠程式碼之外的工程手段,還要依靠設計建模。

從具體實踐的角度來看,設計建模在不同的環境中,調整具體的流程和側重具體的節點,也能夠實現快速高效,不會導致繁瑣和低效能的現象。

在寫程式碼之前應該做的幾件事情:

  1. 找準涉眾利益(越是新的專案,越是要分析好,如果是小功能的迭代,則可以從產品需求文件中尋找);
  2. 畫出原來的業務流程圖,和改進之後的業務流程圖(業務流程圖是對業務理解是有極大幫助,在絕大部分場景中都不應該省略);
  3. 分析系統的職責邊界,畫出系統用例(往系統中新增較小的功能可以考慮不畫出來,心中有數即可);
  4. 寫出具體的用例路徑和約束(對系統安全和穩定越是關注,越應該去寫出具體路徑和約束);
  5. 識別主要的類,屬性和職責,畫出重要實體的狀態機(簡單功能則直接用程式碼來表達即可)。

想和一群喜歡折騰與思考的同學一起提升嗎?來微信支付吧,這裡鼓勵思想的碰撞與應用,這裡有海量的資料,有令人激動的業務場景,有各有所長的優秀夥伴,快來一起玩耍吧。


參考文獻:

  1. 《軟體方法》
  2. 《軟體建模與設計》


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31559354/viewspace-2704267/,如需轉載,請註明出處,否則將追究法律責任。

相關文章