寫代碼之前應(yīng)該做的幾件事
作者:borisyang,騰訊 WXG 應(yīng)用開發(fā)工程師
作為程序員,剛剛開始學(xué)會寫代碼,常常是接過需求就開始擼代碼。有時(shí)候發(fā)現(xiàn),寫完代碼,需求變了。更多時(shí)候,覺得寫業(yè)務(wù)代碼枯燥無聊,沒有技術(shù)含量。另外一邊的事實(shí)卻是,項(xiàng)目里面研發(fā)人數(shù)變多了,項(xiàng)目的質(zhì)量缺卻變低了,多人開發(fā)也不過是一個(gè)個(gè)單打獨(dú)斗的組合而已。
1 研發(fā)環(huán)境日益成熟
經(jīng)歷過 PC 互聯(lián)網(wǎng)的不斷深入發(fā)展,移動互聯(lián)網(wǎng)的蓬勃生長,互聯(lián)網(wǎng)進(jìn)入了成熟繁榮期,研發(fā)環(huán)境也發(fā)生了巨大變化;從原來一個(gè)人,一把鍵盤,寫完代碼就上線,變成了更加規(guī)范的研發(fā)體系和更多人參與的共同協(xié)作。
研發(fā)流程不斷加速
為了盡可能提高需求交付速度,跟上市場的變化,我們通過不斷提搞軟件交付的速度,盡可能的,從需求,編碼實(shí)現(xiàn),測試,發(fā)布的流程中不斷優(yōu)化,利用 CICD,加速迭代。

多人協(xié)作無處不在
現(xiàn)在的軟件開發(fā)團(tuán)隊(duì),即使再小,也有 2-3 人一起研發(fā),更別提測試,運(yùn)維,運(yùn)營,產(chǎn)品人員。一方面是軟件產(chǎn)品的競爭日趨激烈,需求日益復(fù)雜,堆砌人力成了必然;另外一方面是專業(yè)性的要求,精密的行業(yè)自然要求精細(xì)化的職業(yè)劃分。
2 困局
研發(fā)流程的速度提上去了,團(tuán)隊(duì)的人也變多了,但是,需求變更依舊讓廣大研發(fā)同學(xué)感到痛苦,項(xiàng)目質(zhì)量還在日益變差。
需求變更之痛
需求變更的痛苦為難了廣大研發(fā)同學(xué),前腳剛為了優(yōu)化性能,采用了 kv 存儲,后腳需求就變成了要支持模糊查詢;這是一種典型的架構(gòu)設(shè)計(jì)不合理,導(dǎo)致業(yè)務(wù)需求的實(shí)現(xiàn)方式受限。
更令人痛苦的,還有產(chǎn)品需求變動多,今天簡單實(shí)現(xiàn)下,上線看看效果,明天用戶脾氣很大提了個(gè)訴求,再加一個(gè)功能上線,產(chǎn)品功能變成補(bǔ)丁加補(bǔ)丁。一方面是研發(fā)同學(xué)渴望一個(gè)完整又嚴(yán)謹(jǐn)?shù)男枨螅嵬晷枨筮M(jìn)入研發(fā)階段就不許改;另一方面是產(chǎn)品同學(xué)受到各方面的壓力,只希望先把主要問題解決下,細(xì)枝末節(jié)以后再說。
項(xiàng)目質(zhì)量變差
項(xiàng)目質(zhì)量變差,一部分歸功于補(bǔ)丁代碼的產(chǎn)生,迫于時(shí)間受限,先上一個(gè)補(bǔ)丁,卻打開了破窗的先鋒,下一次,下一個(gè)同學(xué)就更敢于加補(bǔ)丁代碼。一個(gè)個(gè)臨時(shí)的 if else 不斷堆砌,最終導(dǎo)致了整個(gè)項(xiàng)目的代碼腐爛。我曾經(jīng)維護(hù)過一個(gè)代碼片段,超過 20 個(gè) if else,中間還有些過時(shí)的錯誤注釋夾雜其中,維護(hù)起來令人苦不堪言。
項(xiàng)目代碼腐爛的另外一個(gè)原因是多人協(xié)作,團(tuán)隊(duì)的人越多,代碼反而變得越爛似乎成為了趨勢;為什么多人協(xié)作沒有提高代碼質(zhì)量呢?一方面,多人協(xié)作實(shí)際上只是分?jǐn)偟男枨髮?shí)現(xiàn)而已,大多數(shù)需求實(shí)現(xiàn)的分配中,反而盡可能將協(xié)作變少,避免實(shí)現(xiàn)受阻。另外一方面是,不同人的代碼模塊,設(shè)計(jì)意圖和代碼風(fēng)格也截然不同。維護(hù)前人代碼,如果沒有全局視角,了解設(shè)計(jì)意圖,也只能是往里面加補(bǔ)丁代碼了。
項(xiàng)目代碼腐爛容易導(dǎo)致程序員出現(xiàn)錯覺,一是業(yè)務(wù)代碼沒什么料在里面,不如搞基礎(chǔ)建設(shè);二是業(yè)務(wù)需求不可能完整又嚴(yán)謹(jǐn),最終也會變來變?nèi)サ?,最終質(zhì)量低下的鍋,一大半要給提需求的人。
3 怎么辦
在寫代碼之前要進(jìn)行設(shè)計(jì)和建模。相比歷史短暫的 IT 行業(yè),很多工業(yè),建筑行業(yè)的精密性,都離不開前期的設(shè)計(jì),在分析設(shè)計(jì)之后,按照圖紙規(guī)劃施工,寫代碼也應(yīng)當(dāng)如此。

設(shè)計(jì)建模的有效性源于,一,重新回到業(yè)務(wù)的跑道,跟業(yè)務(wù)一致;二,設(shè)計(jì)建模才能讓協(xié)作真實(shí)有效;
為什么研發(fā)實(shí)現(xiàn)需求跟業(yè)務(wù)一致很重要呢?研發(fā)和業(yè)務(wù)需求的摩擦,本質(zhì)是研發(fā)實(shí)現(xiàn)跟實(shí)際需求不一致,無論是研發(fā)走偏了,沒有理解需求,還是需求本身不能滿足涉眾的利益,都會使得最終上線的功能需要回爐重造,折磨項(xiàng)目組的成員。業(yè)務(wù)項(xiàng)目,需求很重要,是整個(gè)項(xiàng)目質(zhì)量的源頭,源頭的問題不處理好,會一直發(fā)散擴(kuò)大,問題傳遞到尾部,甚至到了產(chǎn)品上線,對整體造成的損耗越大。從設(shè)計(jì)的語言上看,設(shè)計(jì)的層次有所不同,不僅僅有代碼細(xì)節(jié)上的設(shè)計(jì),也有業(yè)務(wù)上高層次的設(shè)計(jì),高層次的設(shè)計(jì)是用業(yè)務(wù)的術(shù)語去表達(dá),最貼近業(yè)務(wù)實(shí)際情況,也能幫助研發(fā)同學(xué)發(fā)現(xiàn)業(yè)務(wù)中不合理的點(diǎn)。
設(shè)計(jì)建模為何能讓協(xié)作真實(shí)有效?我早先體驗(yàn)的協(xié)作流程,無非就是各自工作在自己的領(lǐng)域內(nèi)部,彼此盡量減少要協(xié)作的內(nèi)容,避免過多阻塞。研發(fā)側(cè)的協(xié)作,因?yàn)槿狈υO(shè)計(jì),不好分工,另外一方面,多人寫同一個(gè)模塊,也會引發(fā)沖突,所以更多將協(xié)作放在在 code review 上。但 code review 作用范圍也有限,一方面,review 成本較高,逐行閱讀代碼來厘清設(shè)計(jì)對代碼質(zhì)量要求很高,另一方面,review 時(shí)間節(jié)點(diǎn)往往發(fā)生較晚,臨近發(fā)布的時(shí)候,調(diào)整設(shè)計(jì)也不大可能。進(jìn)行設(shè)計(jì)建模能夠讓協(xié)作變得有效,一方面,設(shè)計(jì)建模前期是溝通和信息對齊,將協(xié)作的內(nèi)容提前,一方面,采用合適的圖形化工具,review 的成本是相對較低的。
4 怎么設(shè)計(jì)和建模
設(shè)計(jì)和建模分為好幾個(gè)部分
業(yè)務(wù)建模,關(guān)注業(yè)務(wù),不關(guān)注具體的實(shí)現(xiàn)
系統(tǒng)建模,關(guān)注所建設(shè)系統(tǒng)的邊界,找準(zhǔn)在業(yè)務(wù)中的系統(tǒng)的職責(zé)和約束
分析與設(shè)計(jì),定位核心領(lǐng)域,找到實(shí)際的類,厘清類的職責(zé)和類之間的關(guān)系,通過設(shè)計(jì)使得代碼抽象復(fù)用
業(yè)務(wù)建模
總體上來說,業(yè)務(wù)建模主要聚焦于分析涉眾利益,厘清業(yè)務(wù)流程。從工具上來說,主要是用例圖,流程圖;從內(nèi)容上來說,主要是找人(利益涉眾,系統(tǒng)執(zhí)行者),找業(yè)務(wù)實(shí)體(其余系統(tǒng),相關(guān)的重要對象)。
分析涉眾利益
分析涉眾利益之前,需要找到涉眾,一般要經(jīng)歷以下步驟:
找到軟件產(chǎn)品的愿景,愿景表達(dá)了軟件產(chǎn)品帶來的核心意義
找到利益相關(guān)的的涉眾和其利益訴求
表格是一個(gè)很好的表達(dá)方式,我負(fù)責(zé)的一個(gè)商戶從第三方商城采購刷臉設(shè)備,由倉配系統(tǒng)配送設(shè)備的業(yè)務(wù),可以表達(dá)如下:
愿景:將設(shè)備更多更快且準(zhǔn)確無誤成本低地賣給商戶。
涉眾 | 利益訴求 |
---|---|
物料組老板 | 在準(zhǔn)確無誤的情形下,更多更快成本低地將設(shè)備賣給商戶 |
設(shè)備渠道商 | 準(zhǔn)確無誤且快速地配送設(shè)備給商戶 |
商戶 | 更快地獲得自己購買的設(shè)備 |
物料運(yùn)營 | 更加準(zhǔn)確的配送設(shè)備給商戶 |
一般而言,涉眾的利益是否被滿足直接決定了軟件產(chǎn)品的成功與否。而分析涉眾利益需要進(jìn)行詳細(xì)的調(diào)研,研發(fā)同學(xué)可以根據(jù)產(chǎn)品的調(diào)研看到對應(yīng)的涉眾,及其利益。
業(yè)務(wù)用例圖
知道了涉眾的利益之后,就要分析業(yè)務(wù)流程,并對現(xiàn)有的流程進(jìn)行改進(jìn)。軟件產(chǎn)品沒誕生之前,業(yè)務(wù)是如何被處理的,找到原來業(yè)務(wù)的處理方式則可以梳理出業(yè)務(wù)用例。
筆者負(fù)責(zé)的一個(gè)業(yè)務(wù)是向購買設(shè)備的商戶配送設(shè)備,對于業(yè)務(wù)團(tuán)隊(duì)的實(shí)際業(yè)務(wù)來說,用戶購買設(shè)備有業(yè)務(wù)價(jià)值,業(yè)務(wù)用例如下:

物流公司和設(shè)備渠道商都是輔助購買設(shè)備的執(zhí)行者,因此放到右邊。值得注意的是,業(yè)務(wù)用例要體現(xiàn)價(jià)值,雖然在實(shí)際業(yè)務(wù)流程中,商戶同時(shí)做了很多事情,比如簽收設(shè)備,但簽收設(shè)備不能反映業(yè)務(wù)價(jià)值,故而只有一個(gè)購買設(shè)備的用例。
業(yè)務(wù)流程分析
了解了涉眾的利益并且畫出用例之后,需要分析業(yè)務(wù)流程,找到我們軟件系統(tǒng)能夠改進(jìn)的流程片段;完整的業(yè)務(wù)流程圖可能很龐大,需要關(guān)注的是其中最有可能影響涉眾利益的流程片段,如下為購買設(shè)備業(yè)務(wù)流程中配送設(shè)備的流程片段,該片段不大符合涉眾利益;

配送設(shè)備的業(yè)務(wù)流程
可以看到,在原來的業(yè)務(wù)流程中,配送設(shè)備的流程是在全部業(yè)務(wù)流程中較為繁重,人肉工作量大的流程片段,不符合涉眾利益;
- 收集商城訂單信息不及時(shí),導(dǎo)致配送不及時(shí),影響涉眾的利益
- 人作為節(jié)點(diǎn)參與處理,成本高,耗時(shí)長,也是不符合涉眾利益的
所以很明顯,我們的系統(tǒng)需要改進(jìn)流程,替代人的部分工作

新的流程有效地滿足了涉眾“將設(shè)備更多更快且準(zhǔn)確無誤成本低地賣給商戶”的利益訴求。
業(yè)務(wù)序列圖中,每一個(gè)箭頭代表的是職責(zé),在業(yè)務(wù)序列圖中,需要考慮的是職責(zé)的層次問題,過于小的職責(zé)放入流程圖中,會導(dǎo)致信息過載,忽略最有價(jià)值的職責(zé)。在上圖中,運(yùn)營核對設(shè)備的配送信息是一個(gè)很重要的職責(zé),在原來的需求中體現(xiàn)比較弱,研發(fā)同學(xué)可以借助業(yè)務(wù)流程的分析來分析需求中不合理的地方,完善需求,避免后期的改動,前期越是完善,后期的損失成本越低。
業(yè)務(wù)流程分析是一件很復(fù)雜的事情,研發(fā)同學(xué)可以利用需求中的信息,同時(shí)加上自己跟涉眾的日常溝通和調(diào)研,把握核心的涉眾利益,業(yè)務(wù)用例和業(yè)務(wù)流程,就可以解決大部分在需求上的理解偏差問題。
系統(tǒng)建模
系統(tǒng)建模關(guān)注的是系統(tǒng)與外部的邊界和系統(tǒng)自身的職責(zé)
系統(tǒng)建模需要做的事情
- 畫出系統(tǒng)用例
- 寫出用例規(guī)約
系統(tǒng)用例圖
系統(tǒng)用例圖是業(yè)務(wù)流程中,系統(tǒng)執(zhí)行者與系統(tǒng)發(fā)生的有價(jià)值的交互。系統(tǒng)執(zhí)行者可以是人,可以是外部系統(tǒng),甚至可以是時(shí)間。系統(tǒng)用例要體現(xiàn)系統(tǒng)的價(jià)值,系統(tǒng)會做很多事情來實(shí)現(xiàn)業(yè)務(wù)價(jià)值,我們應(yīng)當(dāng)關(guān)注業(yè)務(wù)價(jià)值。有些是低層次的職責(zé),沒有體系具體價(jià)值,如:“獲取商城訂單信息”是為了配送訂單中的設(shè)備而發(fā)生,應(yīng)當(dāng)關(guān)注“配送設(shè)備”。
如下是倉配系統(tǒng)的系統(tǒng)用例圖:

系統(tǒng)用例規(guī)約
有了業(yè)務(wù)流程圖和系統(tǒng)用例圖,需要根據(jù)進(jìn)一步細(xì)化系統(tǒng)邊界上的約束,保證系統(tǒng)的穩(wěn)定性。系統(tǒng)執(zhí)行者與系統(tǒng)的交互細(xì)化了詳細(xì)的約束,系統(tǒng)的穩(wěn)定性才能提高,如果沒有仔細(xì)列出約束,有可能會忽略一些邊界條件,導(dǎo)致系統(tǒng)的故障;如:倉配系統(tǒng)不考慮來自第三方商城訂單要配送的設(shè)備數(shù)量限制,則會因?yàn)榈谌缴坛浅霈F(xiàn)的錯誤,導(dǎo)致資產(chǎn)損失。
系統(tǒng)約束來源于系統(tǒng)用例,根據(jù)業(yè)務(wù)的規(guī)則,詳細(xì)地描述了業(yè)務(wù)流程中的基本路徑,擴(kuò)展路徑和約束。
配送設(shè)備:
- 系統(tǒng)每小時(shí)向第三方商城查詢待配送的訂單
- 系統(tǒng)驗(yàn)證訂單是否已經(jīng)配送
- 系統(tǒng)驗(yàn)證訂單要配送的 SKU 是否合法
- 驗(yàn)證訂單中配送的設(shè)備數(shù)量是否超過最大限制 1000 筆
- 系統(tǒng)向物流系統(tǒng)請求給訂單中的用戶配送訂單中的設(shè)備
因?yàn)椴襟E 2,3,4 還有其余可能的路徑,稱之為擴(kuò)展路徑。
- 訂單已經(jīng)配送
- 系統(tǒng)忽略該訂單
這里的約束不是告訴研發(fā)同學(xué)如何實(shí)現(xiàn)功能,這里的約束是業(yè)務(wù)規(guī)則,厘清系統(tǒng)執(zhí)行者與系統(tǒng)之間的邊界,以及邊界上的約束。設(shè)計(jì)評審的時(shí)候,可以關(guān)注關(guān)鍵路徑上的安全規(guī)則是否到位,這么做對提高系統(tǒng)的安全穩(wěn)定有極大的幫助。
類的分析與設(shè)計(jì)
這可能是大多數(shù)研發(fā)同學(xué)比較熟悉的領(lǐng)域,經(jīng)典的設(shè)計(jì)模式,類之間的關(guān)系,泛化,組合等。但是類從哪里來呢?是從需求之中憑空產(chǎn)生?又或者突然靈光一閃,有了類的雛形?對類的進(jìn)行設(shè)計(jì)與分析之前,需要做的是找到他
識別類
經(jīng)歷過業(yè)務(wù)流程,系統(tǒng)建模,我們終于來到了系統(tǒng)里面,來尋找類。我們所熟知的類有三種,邊界類,控制類和實(shí)體類。邊界類是外部系統(tǒng)在系統(tǒng)內(nèi)部的映射,借由邊界類,系統(tǒng)和外部系統(tǒng)交互。所以一些接口請求,輸入輸出都屬于邊界類的職責(zé)。在倉配系統(tǒng)中,商城就是一個(gè)邊界類,將外部系統(tǒng)轉(zhuǎn)移到內(nèi)部系統(tǒng)來,屏蔽了接口請求相關(guān)的細(xì)節(jié)??刂祁愅求w現(xiàn)用例流程,一般而言,一個(gè)用例就是一個(gè)控制類。實(shí)體類則是系統(tǒng)的核心,實(shí)體類良好設(shè)計(jì)能夠提高系統(tǒng)的復(fù)用程度,減低系統(tǒng)的復(fù)雜性。
找實(shí)體名詞
知道了有這三個(gè)類還是不足夠我們識別具體的類,識別具體的類需要去業(yè)務(wù)流程,系統(tǒng)流程,系統(tǒng)規(guī)約中經(jīng)常出現(xiàn)的名詞。在上面的流程圖中,訂單,商城,設(shè)備,物流,用戶是反復(fù)出現(xiàn)的名稱,說明這些類必然存在。

找到這些業(yè)務(wù)實(shí)體,就是找到類的第一步。
找到屬性
類的屬性也不是憑空產(chǎn)生的,需要對業(yè)務(wù)實(shí)現(xiàn)有價(jià)值,用戶不一定有姓名,在物流上下文中,用戶的屬性就只有 ID,收貨地址。找到那些對于系統(tǒng)實(shí)現(xiàn)必不可少的屬性,放到正確的類中。如倉配系統(tǒng)中的訂單,包含訂單號,商品,用戶。用戶則有收件地址。

在倉配系統(tǒng)中,用戶只有一個(gè)地址,在商城的系統(tǒng)中,用戶則有多個(gè)地址,充分說明了,不同的上下文中,類的屬性不是固定的。
找職責(zé)
從業(yè)務(wù)規(guī)則和約束中,可以找到一些實(shí)體應(yīng)當(dāng)有的職責(zé),如訂單,就有驗(yàn)證合法性的職責(zé)。

有時(shí)候,有些對象看起來信息很富裕,但是卻沒有什么職責(zé),說明他是一個(gè)值對象,像上圖中的用戶和收件地址,我們不關(guān)心他的 id,只關(guān)心收件地址,收件地址就代表著這個(gè)用戶。
狀態(tài)機(jī)
找到類和對應(yīng)的職責(zé),對于一些主要的實(shí)體類,還需要設(shè)計(jì)出他的狀態(tài)機(jī),清晰的狀態(tài)機(jī)能有效地厘清系統(tǒng)內(nèi)的一些事件和狀態(tài),增強(qiáng)系統(tǒng)整體的健壯性。
倉配中的訂單,從用戶購買的待發(fā)貨狀態(tài),到通知物流發(fā)貨,再到實(shí)際發(fā)貨,物流簽收有一些列狀態(tài)的演變。

5 總結(jié)
在多人協(xié)作的項(xiàng)目中,不斷提高項(xiàng)目質(zhì)量,除了依靠代碼之外的工程手段,還要依靠設(shè)計(jì)建模。
從具體實(shí)踐的角度來看,設(shè)計(jì)建模在不同的環(huán)境中,調(diào)整具體的流程和側(cè)重具體的節(jié)點(diǎn),也能夠?qū)崿F(xiàn)快速高效,不會導(dǎo)致繁瑣和低效能的現(xiàn)象。
在寫代碼之前應(yīng)該做的幾件事情:
找準(zhǔn)涉眾利益(越是新的項(xiàng)目,越是要分析好,如果是小功能的迭代,則可以從產(chǎn)品需求文檔中尋找);
畫出原來的業(yè)務(wù)流程圖,和改進(jìn)之后的業(yè)務(wù)流程圖(業(yè)務(wù)流程圖是對業(yè)務(wù)理解是有極大幫助,在絕大部分場景中都不應(yīng)該省略);
分析系統(tǒng)的職責(zé)邊界,畫出系統(tǒng)用例(往系統(tǒng)中添加較小的功能可以考慮不畫出來,心中有數(shù)即可);
寫出具體的用例路徑和約束(對系統(tǒng)安全和穩(wěn)定越是關(guān)注,越應(yīng)該去寫出具體路徑和約束);
識別主要的類,屬性和職責(zé),畫出重要實(shí)體的狀態(tài)機(jī)(簡單功能則直接用代碼來表達(dá)即可)。