“
不求甚解亦或是固步自封,都是從事IT行業(yè)所不可取的。如果只會(huì)寫一手好代碼,卻不會(huì)思考,那只能稱作碼農(nóng),而不是Coder?!峁爬埂ば
前言
我做過(guò)很多爛項(xiàng)目。爛項(xiàng)目有益?zhèn)€人成長(zhǎng),但早期它挺困擾我。因?yàn)樗屛覒岩勺约壕褪悄莻€(gè)敗事鬼:為啥我參與的項(xiàng)目都如此不堪?這種自責(zé)隨著職位的升高有所緩解。但原因絕不是可以訓(xùn)斥我的人在減少。這有點(diǎn)象透過(guò)一個(gè)鏡頭觀察世界。
多年來(lái)這個(gè)鏡頭逐漸拉遠(yuǎn),視野擴(kuò)大,我得以看到越來(lái)越多的東西。伴隨這個(gè)過(guò)程的,是思維的伸展:什么力量在驅(qū)動(dòng)軟件開(kāi)發(fā)進(jìn)程?軟件開(kāi)發(fā)困境(high failure rate)是如何形成的?是我們用的不成熟的框架?那位喜歡指責(zé)下屬的經(jīng)理?還是今年糟糕的天氣?
(high failure rate): IT項(xiàng)目、尤其是軟件研發(fā)項(xiàng)目的成功率長(zhǎng)期維持在慘淡的水平上。讀者可以參考這個(gè)來(lái)自IPLA的報(bào)告:(http://calleam.com/WTPF/?page_id=1445)
本文是我對(duì)此問(wèn)題的一點(diǎn)看法。雖沒(méi)帶來(lái)解決問(wèn)題的秘密武器,但也許能在思想上給同行們一點(diǎn)啟示。
軟件開(kāi)發(fā)最大挑戰(zhàn)是?
如果你問(wèn)周圍同事,軟件開(kāi)發(fā)的最大挑戰(zhàn)來(lái)自哪里?得到的答復(fù)可能五花八門:技術(shù)、管理、需求、甚至睡眠。如果你問(wèn)我同樣的問(wèn)題,我的今天回答將是“變化”。這里的“變化”尤指“未來(lái)的未知變化”,它基本與“不確定性”、“混沌”、“復(fù)雜”等單詞等價(jià)。因此本文會(huì)混用這些術(shù)語(yǔ)。
我們先看看變化是怎么發(fā)生的。
從歷史說(shuō)起
計(jì)算機(jī)發(fā)明之初,它還是一種巨大、昂貴、只有少數(shù)科學(xué)家能接觸到的設(shè)備。這種設(shè)備死氣沉沉、一無(wú)用處,除非你給它輸入一組指令讓它“活”起來(lái)。這些指令就是我們現(xiàn)在說(shuō)的“軟件”。雖然比現(xiàn)在的軟件簡(jiǎn)單很多,但當(dāng)時(shí)世上沒(méi)幾個(gè)人會(huì)寫它。要修改它,同樣要?jiǎng)跓┠切┳鹳F的科學(xué)家。事實(shí)上,除了專業(yè)的小圈子,沒(méi)人會(huì)對(duì)它提出修改要求。可以猜測(cè)那時(shí)的軟件開(kāi)發(fā)沒(méi)啥挑戰(zhàn),因?yàn)橛布膯?wèn)題遠(yuǎn)比軟件的多。
隨后幾十年的技術(shù)進(jìn)步推動(dòng)著計(jì)算機(jī)硬件快速演進(jìn):標(biāo)準(zhǔn)化、小型化、價(jià)格降低并進(jìn)入家庭。最后軟件開(kāi)發(fā)成為專門的行業(yè)從硬件中獨(dú)立出來(lái)。軟件所面對(duì)的用戶數(shù)量迅速擴(kuò)大。一旦意識(shí)到軟件是發(fā)揮計(jì)算機(jī)硬件能力的唯一方式(用CPU煎雞蛋似乎只有少數(shù)人會(huì)用到),人們對(duì)它的欲求便開(kāi)始迅速膨脹。而且他們發(fā)現(xiàn),相比房子、洗衣機(jī)、甚至一張桌子,軟件這種商品的修改要容易的多,還不用啥材料!比如,“把保存按鈕給我換成紅色能花你多長(zhǎng)時(shí)間?!”。軟件開(kāi)發(fā)人員的苦難歷程就是這么開(kāi)始的。
今天,手機(jī)應(yīng)用、互聯(lián)網(wǎng)服務(wù)等軟件形式能把企業(yè)的價(jià)值以最快的速度傳遞到最大規(guī)模的用戶群。這迅速拉近了企業(yè)與用戶的距離。事實(shí)上,距離如此之近,以至企業(yè)可以騰挪的空間大大縮小了。同時(shí)競(jìng)爭(zhēng)讓用戶的選擇變的更多。在這么短的距離上,用戶的選擇可以迅速?zèng)Q定企業(yè)的生死。
而軟件的創(chuàng)造者,即開(kāi)發(fā)人員的日子似乎正變得愈發(fā)艱難。他們不得不努力迎合用戶多變的需求,如果他們知道需求的話。對(duì)那些沒(méi)有確切掌握需求而又急于占領(lǐng)市場(chǎng)的開(kāi)發(fā)團(tuán)隊(duì),情況會(huì)更糟。他們唯一的機(jī)會(huì)可能來(lái)自“敏捷性”——適應(yīng)變化的能力:盡早發(fā)布產(chǎn)品、密集收集用戶反饋、盡快分析并調(diào)整產(chǎn)品、再發(fā)布。通過(guò)快速迭代,他們?cè)诰S持市場(chǎng)存在的同時(shí),不斷提升自己產(chǎn)品的競(jìng)爭(zhēng)力。
內(nèi)部的復(fù)雜性
其實(shí)情況還不算太糟,因?yàn)槲疫€沒(méi)談到軟件創(chuàng)造者自己內(nèi)部的問(wèn)題。
當(dāng)今軟件開(kāi)發(fā)的復(fù)雜性已經(jīng)顯著增大。比如僅從代碼行數(shù)看,幾十萬(wàn)行代碼構(gòu)成的軟件產(chǎn)品已經(jīng)很常見(jiàn),上千萬(wàn)行的產(chǎn)品也并不稀罕。在這么大的復(fù)雜度面前,壓根就沒(méi)有“銀彈”,“一招鮮”是絕對(duì)不夠的。專業(yè)分工、團(tuán)隊(duì)組織、工具優(yōu)化、甚至心理咨詢,我們要把所有能用到的武器投入戰(zhàn)場(chǎng)方能應(yīng)付。
可努力之后我們會(huì)發(fā)現(xiàn),除了外部世界帶給我們的苦,我們自身已經(jīng)成了痛苦的另一個(gè)來(lái)源。因?yàn)榉止?、協(xié)作、工具等等諸多變量已經(jīng)讓研發(fā)過(guò)程變成一個(gè)復(fù)雜的動(dòng)力系統(tǒng)。很多時(shí)候這一系統(tǒng)本身的問(wèn)題會(huì)成為我們的主要挑戰(zhàn)。在有些項(xiàng)目中,那些曾被我們痛恨的趾高氣揚(yáng)的客戶,現(xiàn)在被折磨成與我們一起苦苦掙扎、惺惺相惜的同命人,就是這個(gè)原因。
兩線作戰(zhàn)
從上文很容易總結(jié)出軟件開(kāi)發(fā)工作的進(jìn)展過(guò)程。它通常從未明確的需求開(kāi)始(這樣說(shuō)并不嚴(yán)格,因?yàn)槿魏雾?xiàng)目在開(kāi)始之初總或多或少掌握了一些需求。但未知或可能變化的需求總是我們最頭痛的。本文想闡明的是這部分內(nèi)容。)
這些未知需求構(gòu)成了一個(gè)黑暗的混沌世界。開(kāi)發(fā)團(tuán)隊(duì)靠大無(wú)畏的業(yè)務(wù)經(jīng)理找到突破口,然后向外摸索前進(jìn)。慢慢地,他們澄清了一些不確定性,并據(jù)此建立起一些秩序(設(shè)計(jì)、代碼實(shí)現(xiàn)等等)。隨著團(tuán)隊(duì)開(kāi)疆辟土,混沌初開(kāi)、曙光浮現(xiàn)。但內(nèi)部的混沌也隨之升起。它是未明確的職責(zé)、被誤解的需求、未掌握的技巧、待研究的算法等等內(nèi)容的混合物。
內(nèi)部的復(fù)雜性隨著工作規(guī)模的擴(kuò)大不斷增長(zhǎng)。直到有一天團(tuán)隊(duì)發(fā)現(xiàn)自己陷入兩線作戰(zhàn)的局面,一面是外部持續(xù)的壓力,一面是內(nèi)部增長(zhǎng)的混亂。他們竭盡所能、苦苦支撐,希望在任何一條戰(zhàn)線崩潰前把產(chǎn)品交付客戶了事。有些團(tuán)隊(duì)深陷其中,甚至?xí)?“交付”這個(gè)最初的目標(biāo)。
變化的影響
變化催生復(fù)雜結(jié)構(gòu)
構(gòu)成軟件的基本材料是程序代碼。變化對(duì)開(kāi)發(fā)進(jìn)程的影響最終會(huì)體現(xiàn)到代碼上。所以我想通過(guò)下面的 Python 程序來(lái)展示[^why_pydemo]變化的影響。這些代碼很簡(jiǎn)單,希望不懂Python語(yǔ)言的讀者也能明白。
假定我們要寫一個(gè)軟件,姑且叫它 hello。用戶說(shuō)只要這個(gè)軟件能打印出 “hello world” 就行了。那么我們能很快完成這個(gè)軟件 1.0 版:
print \”hello world\”
毫無(wú)疑問(wèn),你必須事先知道你要打印“hello world”字符串才能寫下上述代碼。可需求會(huì)變化。比如用戶還想打印出“Hi people”。好在我們不會(huì)把用戶的話奉為圭臬。我們能預(yù)見(jiàn)到這種變化。所以我們會(huì)象下面這樣實(shí)現(xiàn)它,姑且稱其為 hello-2.0 :
def print_message(msg):
print msg
通過(guò)定義一個(gè)函數(shù)`print_message`,我們現(xiàn)在可以應(yīng)付更多的可能性了。定義并實(shí)現(xiàn)這個(gè)函數(shù)的兩行代碼,就是我們(暫時(shí))凍結(jié)的內(nèi)容。我們靠遠(yuǎn)見(jiàn)成功地在不確定性中找到了可以固定的東西。
但遠(yuǎn)見(jiàn)覆蓋不住全部的可能性。比如,變化可能再次發(fā)生。這次用戶可能說(shuō),消息不僅要打印出來(lái),還要能輸出到文件。當(dāng)然,這個(gè)變化依然難不倒我們。我們會(huì)用新的方式實(shí)現(xiàn) 3.0 版的 hello,即象下面這樣把它分成兩個(gè)模塊`foo`和`bar`。
foo.py(v3.0):
def put_message(send, msg):
send(msg)
bar.py(v3.0):
def send_to_screen(msg):
print msg
def send_to_file(msg):
out = open(\’out.txt\’, \’w\’)
out.write(msg)
out.close
很多機(jī)智的開(kāi)發(fā)人員會(huì)在項(xiàng)目之初,即第一次聽(tīng)到用戶說(shuō)“只要這個(gè)軟件能打印出hello world就好啦”后就設(shè)計(jì)出 hello-3.0 的結(jié)構(gòu)來(lái)。這樣雖然覆蓋不住全部的可能性,但至少,我們固定住了一部分東西,即模塊`foo`,而把變化交給`bar`模塊來(lái)應(yīng)付。
這就是我們軟件開(kāi)發(fā)者的生存方式。由于程序員不可能對(duì)未確定的要求編寫代碼,團(tuán)隊(duì)的各種努力都是在幫助程序員在一定范圍內(nèi)消滅不確定性,即固化某些東西。我們一點(diǎn)點(diǎn)澄清和凍結(jié)不確定因素,直到范圍擴(kuò)大到可以交付的標(biāo)準(zhǔn)。在此過(guò)程中, _變化_ 迫使系統(tǒng)的復(fù)雜性增大并最終形成一種特定的結(jié)構(gòu)。
雖然上例展示的需求變化是來(lái)自組織外部,但同樣的道理也適用于內(nèi)部。這里我就不再舉例詳述了。另外,我是用編程來(lái)展示變化的影響,但我相信它所包含的方法論含義也可以應(yīng)用在其他方面,包括體系架構(gòu)、團(tuán)隊(duì)組織等。
比如對(duì)常見(jiàn)的多層軟件架構(gòu),如果未來(lái)不會(huì)出現(xiàn)變化,我們是不需要分層的:沒(méi)有系統(tǒng)壓力的變化,我們就不需為伸縮性(scalability)而把承擔(dān)壓力的主要業(yè)務(wù)邏輯部分單獨(dú)出來(lái)做一層;沒(méi)有用戶交互的變化,我們沒(méi)必要把界面處理從業(yè)務(wù)邏輯中抽出來(lái)作為獨(dú)立的GUI層;沒(méi)有數(shù)據(jù)存儲(chǔ)系統(tǒng)的變化,我們把數(shù)據(jù)訪問(wèn)層抽出來(lái)也是多余的… 讀者可以按這個(gè)角度聯(lián)想一下,就很容易意識(shí)到,是變化這一力量,促使軟件系統(tǒng)、甚至創(chuàng)作軟件的社會(huì)化組織——研發(fā)團(tuán)隊(duì),在微觀和宏觀層面產(chǎn)生出特定的結(jié)構(gòu)。這與生物學(xué)中的適應(yīng)性進(jìn)化多么相似啊。
變化帶來(lái)破壞
已經(jīng)固化的代碼(結(jié)構(gòu))并不是萬(wàn)無(wú)一失的。它會(huì)受到變化的侵襲。這種入侵會(huì)沿著代碼的依賴路徑向整個(gè)代碼庫(kù)蔓延,在復(fù)雜性上疊加混亂。我們還是以上面的 hello-3.0 為例。在 3.0 版中,為了分離出可以固化的東西即`foo`模塊,我們把易變的代碼放到了另一個(gè)模塊`bar`中。這創(chuàng)建了一個(gè)依賴關(guān)系,即`foo`對(duì)`bar`的依賴,即“依賴路徑”?,F(xiàn)在,假定一個(gè)未預(yù)料到的變化來(lái)臨,比如,如果用戶希望文件名可以用其他的怎么辦?好吧,你說(shuō),我們接受這個(gè)變化,因?yàn)槲覀儽緛?lái)也沒(méi)打算凍結(jié)`bar`模塊(把它分離出來(lái)就是為了應(yīng)對(duì)這一情況的)。然后你把它改成:
bar.py(v4.0):
def send_to_screen(msg, dest):
print msg
def send_to_file(msg, dest):
out = open(dest, \’w\’)
現(xiàn)在,`bar`模塊中的各個(gè)輸出函數(shù)都增加了一個(gè)參數(shù)`dest`以支持可變的輸出文件名。但現(xiàn)在你發(fā)現(xiàn),這一變化已經(jīng)不可阻擋地蔓延到`foo`模塊了。因?yàn)槟悴坏貌恍薷乃鼇?lái)適應(yīng)這個(gè)新的參數(shù):
foo.py(v4.0):
def put_message(send, msg, dest):
send(msg, dest)
當(dāng)然你可以用一些語(yǔ)言特色,比如變長(zhǎng)參數(shù),來(lái)保持`foo`模塊的穩(wěn)定。比如你可以這樣來(lái)實(shí)現(xiàn) 3.0 版的`foo`:
def put_message(send, *args):
send(*args)
有很多技巧(比如設(shè)計(jì)模式)幫我們處理類似情況。但這些技巧無(wú)法應(yīng)對(duì)全部的可能性。變化對(duì)結(jié)構(gòu)的破壞,或多或少,是無(wú)法避免的。
上例展示的變化蔓延在開(kāi)發(fā)期就可以被發(fā)現(xiàn)。但還有很多情況要在運(yùn)行期才可能暴露出來(lái)。那些不確定性將對(duì)我們固定的結(jié)構(gòu)產(chǎn)生更難預(yù)料的后果(與構(gòu)建期相比,嚴(yán)重的問(wèn)題好像總是在交付給客戶后才出現(xiàn),而它們的原因經(jīng)常又是詭異難尋)。
我們可以改進(jìn)系統(tǒng)設(shè)計(jì)和團(tuán)隊(duì)組織方式來(lái)緩解困境。但是,不僅我們無(wú)法杜絕變化,我們也無(wú)法完全阻止變化的蔓延。這就象病毒混在血液里向身體其他部分?jǐn)U散,我們不能掐斷自己的動(dòng)脈來(lái)阻絕它。
與其對(duì)抗,不如擁抱
傳統(tǒng)工程領(lǐng)域,比如機(jī)械、建筑等,在資源組織、設(shè)計(jì)、施工等一系例工作中也會(huì)對(duì)未來(lái)的不確定性做準(zhǔn)備。比如橋梁設(shè)計(jì)中要考慮負(fù)荷范圍,機(jī)械零件設(shè)計(jì)中要考慮公差等。但變化對(duì)這些領(lǐng)域的影響,似乎遠(yuǎn)沒(méi)有對(duì)軟件工程這么深遠(yuǎn)。它對(duì)我們的影響體現(xiàn)在方方面面,比如:
-
我們用對(duì)象、函數(shù)、包等程序結(jié)構(gòu)把變化的影響限制在一定范圍內(nèi);
我們?cè)O(shè)計(jì)出類型系統(tǒng)(type_sys)來(lái)跟蹤數(shù)據(jù)結(jié)構(gòu)的變化;
我們用編譯器(Compiler)在開(kāi)發(fā)期幫我們找出各種不一致問(wèn)題;
我們用測(cè)試來(lái)檢查系統(tǒng)運(yùn)行時(shí)各種可能性產(chǎn)生的影響;
我們不再迷戀“大設(shè)計(jì)”,因?yàn)樵诖罅康淖兓埃且环N浪費(fèi);
我們從步步為營(yíng)的[瀑布模型][waterfall]轉(zhuǎn)向敏捷模型,以期盡早獲得反饋消除不確定性;
我們用精英化的小團(tuán)隊(duì)而非人海戰(zhàn)術(shù),從而避免信息在由人構(gòu)成的傳播鏈路中失真(變化);
我們招聘時(shí)寧缺毋濫,因?yàn)槲覀兿嘈偶妓嚫叱娜四芸朔祟悓?duì)未知領(lǐng)域的本能恐懼;
… …
這個(gè)列表可以很長(zhǎng)。從中你大概能感受到我們對(duì)變化的抗?fàn)?。有一些人采用鴕鳥(niǎo)策略,固執(zhí)地對(duì)變化視而不見(jiàn)或認(rèn)為它們不會(huì)發(fā)生。這是否是造成軟件困境的原因呢?
我覺(jué)得也許我們要改變一下看問(wèn)題的視角了。我想說(shuō)的是,響應(yīng)變化不應(yīng)再是我們臨時(shí)的、不得以的工作。**它就是我們的軟件開(kāi)發(fā)工作本身**。這不是在否定那些缺乏未知因素的開(kāi)發(fā)工作。那一類開(kāi)發(fā)項(xiàng)目依然存在,只是它們代表不了未來(lái),可能也引不起推動(dòng)行業(yè)進(jìn)步的那些精英們的興趣。
這一視角也許能幫我們找到走出困境的新方法。事實(shí)上,現(xiàn)代軟件工程中的許多概念,比如敏捷開(kāi)發(fā)、DevOps,我認(rèn)為是與本文的視角一致的。所以如果以這樣的視角來(lái)理解上述概念,不僅能幫助我們正確地應(yīng)用它們,還可能發(fā)展出更符合自己實(shí)際情況的獨(dú)特做法。再不濟(jì),它也能緩解我們疲于應(yīng)付時(shí)的精神痛苦。
后記
如果不存在變化,軟件開(kāi)發(fā)就簡(jiǎn)單多了。但不論在外部還是內(nèi)部,變化都永遠(yuǎn)存在。進(jìn)入信息時(shí)代后,變化正在以更大的規(guī)模、更快的速度發(fā)生著。完全應(yīng)對(duì)這未知的洪流是不可能的。我們應(yīng)坦然面對(duì),并找到一個(gè)可以接受當(dāng)面沖擊的小根據(jù)地,然后逐漸擴(kuò)大它。這個(gè)思路可以應(yīng)用在日常的工作決策中,比如選擇合適的測(cè)試時(shí)機(jī)。
老司機(jī)介紹
王錦全,男,本科學(xué)歷,1996年畢業(yè)于中南大學(xué)機(jī)電工程學(xué)院。長(zhǎng)期從事軟的研發(fā)和管理工作,先后擔(dān)任過(guò)軟件工程師、系統(tǒng)架構(gòu)師、CTO等職務(wù)。曾服務(wù)于多家公司,包括世界100強(qiáng)、國(guó)內(nèi)大中型國(guó)有、私營(yíng)軟件企業(yè)等,也有過(guò)自主創(chuàng)業(yè)的經(jīng)歷。目前是杭州一家中型軟件企業(yè)的技術(shù)總監(jiān),主要負(fù)責(zé)公司的管理制度優(yōu)化、敏捷研發(fā)推廣以及大數(shù)據(jù)技術(shù)研發(fā)等工作。他是資深的 Java 和 Python 程序員,也是 Clojure(Lisp) 愛(ài)好者。讀書(shū)和思考是他的愛(ài)好。 您在其 linkedin主頁(yè)( http://cn.linkedin.com/in/johnwangwjq )可了解他的更詳細(xì)經(jīng)歷。
▽
【ArchSummit深圳2016】15大精彩專題,50位大咖講師,Cloudera、Hearsay Social、Uber、LinkedIn、Twitter等等,你將為哪家公司技術(shù)點(diǎn)贊?阿里巴巴、騰訊、百度、美團(tuán)、餓了么、滴滴、新浪微博等等,核心業(yè)務(wù)技術(shù)較量誰(shuí)又能觸動(dòng)你的神經(jīng)…最精彩的技術(shù)切磋從這開(kāi)始,ArchSummit九折門票倒計(jì)時(shí),詳情請(qǐng)戳閱讀原文
本文系InfoQ原創(chuàng)首發(fā),未經(jīng)授權(quán)謝絕轉(zhuǎn)載。
版權(quán)聲明:本文內(nèi)容由互聯(lián)網(wǎng)用戶自發(fā)貢獻(xiàn),該文觀點(diǎn)僅代表作者本人。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如發(fā)現(xiàn)本站有涉嫌抄襲侵權(quán)/違法違規(guī)的內(nèi)容, 請(qǐng)發(fā)送郵件至 舉報(bào),一經(jīng)查實(shí),本站將立刻刪除。