在多年的IT生涯中,發(fā)現(xiàn)一個(gè)有趣的現(xiàn)象。項(xiàng)目或者產(chǎn)品出現(xiàn)問題的時(shí)候,大家痛定思痛總結(jié)教訓(xùn),第一個(gè)被拎出來說事的,總是需求——需求不明確,需求總變更,需求不全面。我們寫過大而全的需求文檔,也用過敏捷的故事卡片,卻總是無法將需求問題消弭于無形之中。近來有機(jī)會(huì)接觸到了《實(shí)例化需求》的方法論,才真正感覺看到了一絲曙光。所學(xué)所練所想,分享于此。
1 什么是實(shí)例化需求
實(shí)例化需求這個(gè)概念來自于一本書《實(shí)例化需求:團(tuán)隊(duì)如何交付正確的軟件》(Specification by Example: How Successful Teams Deliver the Right Software)。

書的作者叫Gojko Adzic,是一名英國的戰(zhàn)略軟件交付顧問。在近十幾年來,一直在各種行業(yè)領(lǐng)域(例如財(cái)務(wù)和能源交易平臺(tái)、移動(dòng)定位、電子商務(wù)、在線游戲、復(fù)雜配置管理系統(tǒng)等等)從事著程序員、架構(gòu)師、技術(shù)指導(dǎo)和顧問等工作。

實(shí)例化需求就是Gojko在這十多年的軟件生涯中總結(jié)出來的一套行之有效方法。對(duì)于它的介紹是這樣的:實(shí)例化需求是一組方法,它以一種對(duì)開發(fā)開發(fā)團(tuán)隊(duì)有所幫助的方式(理想情況下表現(xiàn)為可執(zhí)行的測(cè)試)描述計(jì)算機(jī)系統(tǒng)的功能和行為,讓不懂技術(shù)的利益相關(guān)者也可以理解,即使客戶的需求在不斷變化,它也具有很好的可維護(hù)性,可以保持需求的相關(guān)性。從而幫助團(tuán)隊(duì)交付正確的軟件產(chǎn)品。
在我看來,這就是這套方法最牛的地方——幫助團(tuán)隊(duì)交付正確的軟件產(chǎn)品。正確這個(gè)詞,說起來簡簡單單,但實(shí)現(xiàn)起來卻真的很難。
2 我們?yōu)槭裁词褂脤?shí)例化需求
我們都明白,需求是一切軟件開發(fā)的源頭。有了需求,開發(fā)才能開始。但,實(shí)際情況往往是,當(dāng)項(xiàng)目發(fā)生延期,或是項(xiàng)目測(cè)試投產(chǎn)出了問題的時(shí)候,第一個(gè)被揪出來當(dāng)替罪羊的也往往是需求——需求不明確!需求總變更!需求不全面!需求沒寫對(duì)!

你們開發(fā)也逃脫不了干系!不是應(yīng)該搞明白了需求才開始開發(fā)的嗎?怎么還會(huì)錯(cuò)?
造成這個(gè)問題的無非下面幾種情況。
1)真的沒好好搞清楚需求就開始了開發(fā)。
在需求的討論溝通中,很常見的一種情況就是,剛開始說的是要做什么,為什么要做,講著講著,大家的探討焦點(diǎn)就變成了怎么做,如何實(shí)現(xiàn),然后迫不及待地投入到軟件開發(fā)之中。很多團(tuán)隊(duì)都會(huì)有這樣的誤解:只有開始寫代碼才是真正的干活,澄清需求的時(shí)間并不多。 也許輸入的需求并不足以具體到支撐起一個(gè)系統(tǒng)的開發(fā),但卻沒人發(fā)現(xiàn),大家只管埋頭苦干,這為之后的問題埋下了很大的隱患。
2)項(xiàng)目干系人并沒有對(duì)需求有著一致的理解。
常規(guī)的需求通常是用自然語言編寫而成的文檔,自然語言本身就會(huì)存在著一千個(gè)人有一千個(gè)漢姆雷特這樣的理解偏差。如果團(tuán)隊(duì)使用的是需求-開發(fā)-測(cè)試這樣的瀑布流程,需求在產(chǎn)生階段并沒有整個(gè)團(tuán)隊(duì)的介入,僅由需求人員負(fù)責(zé),在開發(fā)之初才移交給開發(fā)團(tuán)隊(duì),即使需求人員和開發(fā)人員有理解偏差也很難在第一時(shí)間發(fā)現(xiàn)。
3)不是我不明白,是這世界變化快。
互聯(lián)網(wǎng)時(shí)代,軟件致勝只有一個(gè)法寶——快。很多人慨嘆,現(xiàn)在的時(shí)代過于浮躁,精雕細(xì)琢的東西不復(fù)存在。而事實(shí)是,整個(gè)IT界的業(yè)態(tài)變化得如此之快,如果你再按照從前的項(xiàng)目節(jié)奏走下去,當(dāng)你的產(chǎn)品交付之時(shí),也將是被淘汰之時(shí)。如今,大多相互的項(xiàng)目周期是按月來衡量的,項(xiàng)目階段則是以周甚至是天來計(jì)數(shù)的。 需求變更是常態(tài)。 如果團(tuán)隊(duì)期待的是一份大而全,且永不變更的需求文檔,并以此為依據(jù)進(jìn)行開發(fā),那項(xiàng)目延期或者出問題簡直是必然的情況。
4)過于依賴個(gè)人的認(rèn)知結(jié)構(gòu)。
需求從哪里來?
有的團(tuán)隊(duì)的做法是把用戶提出的需求當(dāng)做用戶故事的源頭,竭盡全力去實(shí)現(xiàn)。但是用戶對(duì)于系統(tǒng)和軟件開發(fā)并無概念,所以他所說的有可能超過了系統(tǒng)的范疇。再加上用戶也許并不真的像他以為的那么了解自己,往往把真實(shí)的需要隱藏在一個(gè)他自以為的解決方案中提出。如果真的照他說的做,那就慘了,只有在交付的時(shí)候才能聽到他說——這不是我想要的。
另一種做法是由產(chǎn)品人員把用戶的需求翻譯過來。但是在翻譯的過程中只有產(chǎn)品人員孤軍奮戰(zhàn)。而每個(gè)人總有每個(gè)人思維的局限性,一定會(huì)有想不到的地方。
除了上面的需求問題,還有一些問題也是讓軟件開發(fā)團(tuán)隊(duì)深惡痛絕的。
5)可怕的文檔。
傳統(tǒng)需求文檔的養(yǎng)成過程是這樣的:雖然極力想在開發(fā)之前全面描繪出軟件理想中的樣子,怎奈各種局限,只有寥寥數(shù)筆。此時(shí)的需求身材單薄,長相有些刻板但算整潔。
開發(fā)中不斷發(fā)現(xiàn)需求問題,甚至于到了不改需求沒法開發(fā)下去的地步,于是需求被改得日漸臃腫,從瘦弱少女變成身材走樣的中年大媽。
到了開發(fā)完成進(jìn)入測(cè)試,測(cè)出來bug但趕工期沒時(shí)間回歸,這時(shí)的法寶又是——改需求!誰讓需求和測(cè)試結(jié)果一致就不算bug呢?可憐的需求再次挨刀,一副整容手術(shù)失敗的沮喪模樣。
這樣的需求可讀性差,可維護(hù)性更差。讀的人一頭霧水,寫的人苦不堪言。但非常不幸的是,我們面對(duì)的需求文檔卻大多是這副丑陋模樣。
而在所有的需求文檔中,最可怕的莫過于遺留系統(tǒng)的需求。尤其是在你要改造或是重建遺留系統(tǒng)的時(shí)候,你徒勞地想從它浩如煙海又不說人話的需求文檔中揪出蛛絲馬跡,最后卻只能仰天長嘆。
6)返工危機(jī)
無論是傳統(tǒng)的瀑布模型,還是敏捷的Sprint中,趕在工期之前把代碼寫完,再瀟灑地丟給測(cè)試,都是能令程序員開心的事情。
什么會(huì)令程序員不開心呢?那就是測(cè)試工程師測(cè)出bug,再瀟灑地丟給程序員。
如此往復(fù),沒完沒了。
程序員向測(cè)試工程師大喊:你們就不能一次發(fā)現(xiàn)所有問題嗎?
測(cè)試工程師向程序員大喊:你們就不能早點(diǎn)發(fā)現(xiàn)問題改了再交給我嗎?
而項(xiàng)目經(jīng)理向所有人大喊:再改就要延期了?。?!
讓我們來看看實(shí)例化需求可以做什么吧。
實(shí)例化需求的核心是,讓項(xiàng)目的所有干系方進(jìn)行有效的協(xié)作和溝通,用實(shí)例的方式說明需求,用自動(dòng)化測(cè)試的方式頻繁地驗(yàn)證需求,從實(shí)例化的需求說明和自動(dòng)化測(cè)試用例中演進(jìn)出一套“活文檔系統(tǒng)”。這套“活文檔系統(tǒng)”既可以有效地對(duì)系統(tǒng)進(jìn)行說明,又可以當(dāng)做交付驗(yàn)收的標(biāo)準(zhǔn)
- 有效的交流溝通確保有足夠的時(shí)間澄清需求。
- 使用舉例的方法澄清需求能在第一時(shí)間識(shí)別出需求是否足以支撐開發(fā)。
- 所有的干系方參與需求討論,可以確保大家對(duì)于交付哪些東西有一致的理解。
- 具有不同領(lǐng)域背景的干系方一同參加需求討論,可以規(guī)避因個(gè)人認(rèn)知局限帶來的需求問題。
- ”活文檔系統(tǒng)”對(duì)于變更有著先天優(yōu)勢(shì),可以以最少的維護(hù)成本維持文檔的相關(guān)性和可靠性。又能避免過度說明需求而產(chǎn)生浪費(fèi),避免花時(shí)間在開發(fā)前有可能發(fā)生變化的細(xì)節(jié)上,對(duì)于變更天然友好。
- 采用自動(dòng)化測(cè)試的方法實(shí)現(xiàn)業(yè)務(wù)實(shí)例,代碼開發(fā)出來即可以驗(yàn)證,無須經(jīng)過冗長的手動(dòng)回歸測(cè)試,降低返工。
完美解決上述問題。
3 實(shí)例化需求怎么做
Gojko在書中提出了實(shí)例化需求的主要過程模式,包含以下幾個(gè)環(huán)節(jié):

1、從目標(biāo)中獲取范圍。
與客戶溝通協(xié)作,以用戶的業(yè)務(wù)目標(biāo)為起始,通過團(tuán)隊(duì)協(xié)作找出可以實(shí)現(xiàn)目標(biāo)的范圍。
Tip1:不要把用戶自以為的解決方案當(dāng)做系統(tǒng)需求。問他為什么,想解決什么問題。由你的團(tuán)隊(duì)討論解決方案,劃定范圍。
Tip2:需要牢記業(yè)務(wù)目標(biāo),為什么做這件事情,因?yàn)殚_發(fā)團(tuán)隊(duì)很容易把關(guān)注焦點(diǎn)轉(zhuǎn)移到怎么做上。
2、從協(xié)作中制訂需求說明。
協(xié)作是關(guān)鍵詞。目的是讓項(xiàng)目的干系人,包括產(chǎn)品、設(shè)計(jì)、開發(fā)以及測(cè)試都參與進(jìn)來,發(fā)揮整個(gè)團(tuán)隊(duì)的知識(shí)和經(jīng)驗(yàn),排除理解的不一致性,盡量減少個(gè)人認(rèn)知造成的局限。
Tip1:只有團(tuán)隊(duì)準(zhǔn)備好實(shí)現(xiàn)的時(shí)候才開始實(shí)例化需求,例如迭代開始時(shí)。如果提前開始實(shí)例化,有可能在真正開發(fā)的時(shí)候需求已經(jīng)發(fā)生了變更,又要重來。
Tip2:在協(xié)作的過程中需要大家共同建立起項(xiàng)目的領(lǐng)域模型,并在討論中嚴(yán)格遵循領(lǐng)域模型,這樣能確保大家對(duì)于術(shù)語和概念的認(rèn)知是一致的,討論是在共同的語境中進(jìn)行。
Tip3:在外部需求不明朗的情況下(例如遺留系統(tǒng)的遷移項(xiàng)目),可以從系統(tǒng)的工作流入手,梳理需求。在梳理系統(tǒng)工作流的時(shí)候,不僅關(guān)注系統(tǒng)間的調(diào)用關(guān)系,也要識(shí)別出系統(tǒng)間的數(shù)據(jù)傳遞,識(shí)別得越明確,對(duì)于舉例越有幫助。
Tip4:需求說明描述的是系統(tǒng)和用戶之間的交互,不應(yīng)該描述系統(tǒng)流程。也不應(yīng)該和代碼綁定緊密,陷入技術(shù)細(xì)節(jié)。也不應(yīng)該過度關(guān)注界面。
3、舉例說明。
舉例說明是項(xiàng)目需求交流過程中不可或缺的,團(tuán)隊(duì)中的人領(lǐng)域背景不同,對(duì)同一個(gè)事物的理解也可能不盡相同,通過舉例說明的方式可以讓目標(biāo)更一致。
在書中,作者提出,功能模塊的例子必須具有精確性(不是簡單的是或否的答案,使用具體的例子)、真實(shí)性(使用真實(shí)數(shù)據(jù),從客戶那兒獲取真實(shí)的例子)、完整性(使用不同的數(shù)據(jù)組合去試驗(yàn),利用其他方式去檢驗(yàn)和測(cè)試),并易于理解(不用試驗(yàn)所有組合,尋找隱含的概念)。
Tip1:例子應(yīng)該關(guān)注用戶和系統(tǒng)之間的交互,而非關(guān)注系統(tǒng)本身的處理流程。因此,例子應(yīng)該包含前置條件、輸入、輸出。前置條件指的是場景發(fā)生時(shí),未作為輸入傳遞到本系統(tǒng)中,但是已經(jīng)存在,且對(duì)業(yè)務(wù)產(chǎn)生影響的數(shù)據(jù)。
Tip2:當(dāng)大家用說的方式解釋不清的時(shí)候,舉例子是自然而然的選擇。事實(shí)上,即使你覺得能說清楚,也應(yīng)該舉例,以免大家的理解有誤差。例子應(yīng)該具體而精確,避免使用范圍。例如,不要用某一值“小于10”這一表述,而應(yīng)該用某一值等于“9”來舉例。
Tip3:在場景特別復(fù)雜的情況下,還可以使用流程圖來輔助舉例。使用什么樣的方式不重要,重要的是這種方式能夠達(dá)到在團(tuán)隊(duì)中澄清需求的目的。
Tip4:如果發(fā)現(xiàn)實(shí)例太復(fù)雜,就把它的復(fù)雜度降低,分解成若干個(gè)實(shí)例。例如,對(duì)于“如果數(shù)量大于10件,或者重量大于50kg,則收取50元運(yùn)費(fèi)”這個(gè)規(guī)則,可以拆分為“數(shù)量大于10件”和“重量大于50kg”兩個(gè)規(guī)則,再來舉出數(shù)量為20件和重量為60kg兩個(gè)實(shí)例。
Tip5:在舉例說明的過程中極有可能會(huì)發(fā)現(xiàn)之前未能識(shí)別出來的潛在概念。當(dāng)潛在概念出現(xiàn)時(shí),應(yīng)當(dāng)把它加入到領(lǐng)域模型之中。
4、提煉需求說明。
雖然協(xié)作過程中的需求討論可以建立大家對(duì)相關(guān)領(lǐng)域的共識(shí),但得到的實(shí)例往往包含很多不必要的細(xì)節(jié)。關(guān)鍵實(shí)例是從這些實(shí)例中提煉出來的,雖然精簡但足以說明業(yè)務(wù)的實(shí)例。并且這些提煉好的實(shí)例本身就可以當(dāng)作交付的驗(yàn)收條件。
Tip1:摒棄對(duì)業(yè)務(wù)走向沒有影響的實(shí)例。例如,當(dāng)輸入中的購買者字段是“中學(xué)生”“小學(xué)生”“大學(xué)生”時(shí),如果它們的區(qū)別僅在于名稱不同,系統(tǒng)對(duì)業(yè)務(wù)的處理完全一樣,此時(shí)應(yīng)該只保留其中一個(gè)實(shí)例作為代表。
** Tip2:**提煉實(shí)例可以由簡入繁??梢韵劝鸦镜某晒η闆r提煉出來,再逐步推及到各種異常和失敗。關(guān)注影響業(yè)務(wù)規(guī)則的實(shí)例,關(guān)注邊界條件的實(shí)例。
Tip3:實(shí)例應(yīng)當(dāng)有正反兩個(gè)方面。比如,有一條業(yè)務(wù)規(guī)則是:對(duì)于購買重量在10kg以上的訂單,才收取6元運(yùn)費(fèi)。所舉出的實(shí)例就應(yīng)該有正反兩個(gè):一個(gè)是重量是11kg,收取運(yùn)費(fèi)6元;一個(gè)是:重量為5kg,收取運(yùn)費(fèi)0元。
5、在不修改需求的前提下,用自動(dòng)化測(cè)試來驗(yàn)證需求。

Tip1:測(cè)試是為了驗(yàn)證需求,因此不可以只偏重于測(cè)試本身,而忽略了測(cè)試和需求之間的聯(lián)系。過度飽和的數(shù)據(jù)和測(cè)試用例的大爆炸是不可取的。
Tip2:還是那句,測(cè)試是為了驗(yàn)證需求,因此不要在測(cè)試代碼中引入業(yè)務(wù)流程或者業(yè)務(wù)邏輯,不要驗(yàn)證系統(tǒng)是怎么做的,而要驗(yàn)證系統(tǒng)做的事對(duì)不對(duì)。
Tip3:自動(dòng)化測(cè)試有很多種優(yōu)秀的工具,但不要執(zhí)著于于特定工具,認(rèn)為工具才是解決問題的王道。工具是為了測(cè)試服務(wù)的。
6、頻繁驗(yàn)證。
在傳統(tǒng)的流程中,龐大的需求說明往往跟不上實(shí)際開發(fā)中的需求變更,導(dǎo)致需求文檔在交付之時(shí)就已經(jīng)過時(shí)。代碼才是唯一能真正信任的。但是,如果對(duì)于需求說明進(jìn)行頻繁驗(yàn)證,我們就能及時(shí)發(fā)現(xiàn)需求說明和系統(tǒng)代碼之間的差異,及時(shí)解決它們,從而保證需求說明和代碼是一直同步的??梢韵裥湃未a一樣信任需求說明。
頻繁驗(yàn)證的方法有很多,自動(dòng)部署,持續(xù)集成,排除不確定性,對(duì)外部系統(tǒng)使用Mock,等等,作者在書中給出了很多建議。
7、利用工具,提取組織良好的、易于尋找的、前后一致的活文檔。
維護(hù)需求文檔,通常是件讓人頭疼的事情。過于繁瑣的需求文檔會(huì)讓人喪失維護(hù)的動(dòng)力??墒?,系統(tǒng)的重構(gòu)和更新又偏偏需要這樣一份文檔。怎么辦呢?所幸的是,實(shí)例化需求為我們提供了這樣一種省時(shí)省力的方式——從自動(dòng)化測(cè)試用例中提取文檔!
提煉出來的需求實(shí)例是對(duì)系統(tǒng)做了什么事最有力、最準(zhǔn)確、最新鮮的描述。而自動(dòng)化測(cè)試用例又使用了可執(zhí)行的方式實(shí)現(xiàn)了這些需求實(shí)例,我們?cè)偈褂靡恍┕ぞ甙炎詣?dòng)化測(cè)試用例提煉為HTML或是PDF的文本,那么,砰!一份簡單易懂的活文檔就產(chǎn)生了!水到渠成,簡單易懂,永不過時(shí)!

在作者的書中關(guān)于活文檔有句話我特別喜歡——如果沒有活文檔,任何重大的重構(gòu)都是自尋死路。
Tip1:活文檔和敏捷中的用戶故事卡有什么區(qū)別嗎?好像很類似的樣子。當(dāng)然有所不同。用戶故事是用故事的形式描述需求,活文檔則是運(yùn)用實(shí)例。另一個(gè)不同是,敏捷的故事卡在Sprint結(jié)束之后就沒有用了,通常不會(huì)長久保存,而活文檔則是易于保存,同步更新的。
寫到這里,實(shí)例化需求的主要環(huán)節(jié)已經(jīng)結(jié)束了。毫無疑問,實(shí)例化需求是一個(gè)好方法。但是,再好的方法也需要人的有力實(shí)施才能發(fā)揮效果。這其實(shí)也是本書作者的一種態(tài)度——實(shí)例化需求的方法更多的是確保團(tuán)隊(duì)成員之間的協(xié)作和溝通,而非推廣工具。希望看到本文的人能讓好方法發(fā)揮效用,令產(chǎn)品經(jīng)理、程序員、測(cè)試工程師們的世界變得更美好。