1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        萬(wàn)字長(zhǎng)文|什么樣的技術(shù)文章會(huì)被馬化騰點(diǎn)贊?

        共 15291字,需瀏覽 31分鐘

         ·

        2024-04-11 16:00

        9125199aaa8aa0526e84105ff48bdce5.webp

        28442ce894ae98d6e71b3515aed1f711.webp


        ??騰小云導(dǎo)讀

        業(yè)務(wù)系統(tǒng)復(fù)雜性一直是令開(kāi)發(fā)者頭痛的問(wèn)題。復(fù)雜的不是增加一個(gè)需求需要耗費(fèi)多少時(shí)間,而是在增加一個(gè)需求后帶來(lái)的蝴蝶效應(yīng):其它功能會(huì)不會(huì)受到影響、要如何去找到這些影響,最終如何實(shí)現(xiàn)系統(tǒng)正常運(yùn)行......功能之間隱秘增加的耦合、不可避免的代碼腐化在導(dǎo)致業(yè)務(wù)復(fù)雜性增加。大家都在說(shuō)的軟件開(kāi)發(fā)提效到底在提什么?程序員日常工作中應(yīng)該如何提升開(kāi)發(fā)效率?敏捷開(kāi)發(fā)、瀑布流式開(kāi)發(fā)孰是孰非?歡迎閱讀。

        ?? 看目 錄, 點(diǎn)收藏

        1 業(yè)務(wù)背景與目標(biāo)

        2 軟件開(kāi)發(fā)提效

        3 業(yè)務(wù)系統(tǒng)復(fù)雜的根本原因

            3.1 功能之間隱蔽增加的耦合

            3.2 不可避免的代碼腐化

        4 總結(jié)






        01

         業(yè)務(wù)背景與目標(biāo)

        我依然非常清晰地記得去年的某個(gè)時(shí)候,我所在團(tuán)隊(duì) Leader 曾講過(guò)一段話:


        我擔(dān)憂的是,我們團(tuán)隊(duì)規(guī)模的擴(kuò)張并不是因?yàn)橛脩粢?guī)?;驙I(yíng)收規(guī)模的增長(zhǎng),僅僅是因?yàn)槲覀冇性絹?lái)越多的事情要做導(dǎo)致人手緊缺?!?/span>


        這個(gè)擔(dān)憂相信很多開(kāi)發(fā)者都能感同身受,也許你所在的團(tuán)隊(duì)也正面臨同樣的問(wèn)題:為什么用戶規(guī)?;蛘郀I(yíng)收規(guī)模不增加,事情反而越來(lái)越多呢?出現(xiàn)這個(gè)現(xiàn)象的原因其實(shí)也不難想到:由于業(yè)務(wù)規(guī)模停滯或者下滑,產(chǎn)品側(cè)不得不做更多的事情來(lái)止住頹勢(shì)甚至想要以此力挽狂瀾。要么是不斷地拓展產(chǎn)品的邊界,在一個(gè)應(yīng)用里加入更多的功能,也就是所謂的交付更多的用戶價(jià)值,從而吸引更多潛在用戶;要么是不斷地優(yōu)化現(xiàn)有功能,例如通過(guò)排版來(lái)從心理學(xué)角度提高用戶停留時(shí)長(zhǎng)和點(diǎn)擊率,亦或是進(jìn)一步優(yōu)化產(chǎn)品的交互流程,也就是所謂的提升用戶體驗(yàn),從而提升口碑,穩(wěn)固用戶基本盤。


        這些要做的事情對(duì)應(yīng)到開(kāi)發(fā)側(cè),那自然就是有更多的需求。 一個(gè)需求不能提升指標(biāo),那就要兩個(gè);一個(gè)策略效果不及預(yù)期,那下次就得 A、B 兩個(gè)策略同時(shí)做實(shí)驗(yàn)??雌饋?lái),這勢(shì)必會(huì)加劇團(tuán)隊(duì)開(kāi)發(fā)人員的壓力。


        e7551c6b1bcbdd22eef39c8fa939ef96.webp大版本上線前同時(shí)趕多個(gè)需求.gif

        所以 Leader 希望開(kāi)發(fā)團(tuán)隊(duì)能夠提升效率,提升需求的吞吐率。但問(wèn)題是,需求的增多,就一定要伴隨著開(kāi)發(fā)團(tuán)隊(duì)人力的增多嗎?


        對(duì)其他行業(yè)來(lái)說(shuō),這個(gè)問(wèn)題的答案是顯而易見(jiàn)的。你要修更多的房子,勢(shì)必要更多的工人;你要送更多的物資,勢(shì)必需要更多的車;你要打贏一波團(tuán)戰(zhàn),勢(shì)必需要更多的隊(duì)友…然而這個(gè)問(wèn)題到了軟件行業(yè),答案卻變得模糊了。


        因?yàn)檐浖袠I(yè)有個(gè)特有的性質(zhì)——幾乎 0 成本的可復(fù)用性。某項(xiàng)功能別人實(shí)現(xiàn)了,那其他人就能立刻拿過(guò)來(lái)用。這對(duì)于建筑等行業(yè),那真的就是降維打擊。即使工地要修 10 棟一模一樣的宿舍樓,它也得一棟一棟地修,每一棟的人力和物力都是一樣的。而對(duì)于軟件行業(yè),你只需要修一棟,剩下的 9 棟就是 0 成本的復(fù)制粘貼。


        那對(duì)于一個(gè)在軟件工程上有追求的團(tuán)隊(duì)來(lái)說(shuō),大家不是一直在追求可復(fù)用嗎?前期開(kāi)發(fā)了那么多需求和功能,想必也沉淀了非常多的能力吧?如果有那么多能力可以 0 成本復(fù)用,那后續(xù)是不是開(kāi)發(fā)效率會(huì)大大提高?


        因此對(duì)很多人來(lái)說(shuō),理想中的軟件開(kāi)發(fā)團(tuán)隊(duì),隨著功能的不斷增多,開(kāi)發(fā)成本應(yīng)該至少保持一個(gè)線性的關(guān)系。而且如果引入了一些重大的新技術(shù),開(kāi)發(fā)成本的線性增長(zhǎng)率還會(huì)更低。


        309334f0e657462c90264bc146aa3351.webp


        這種 functionalities-cost 的線性關(guān)系對(duì)很多團(tuán)隊(duì)來(lái)說(shuō)是習(xí)以為常的。在很多團(tuán)隊(duì)的項(xiàng)目管理中,每次迭代的需求數(shù)量都應(yīng)不少于上次迭代,否則就需要復(fù)盤。而對(duì)于開(kāi)發(fā)效率的提升,則可以用本次迭代的需求數(shù)量較之前有多少比例的提升來(lái)判斷。如果團(tuán)隊(duì)有一個(gè)好的技術(shù)架構(gòu)和合理的模塊劃分方案,考慮到可復(fù)用性可以減少成本,functionalities-cost 的關(guān)系會(huì)更平緩。類似這樣:


        1e41c7c02b88e4e2ad2051edf789f530.webp

        然而,現(xiàn)實(shí)可能會(huì)給你沉重一擊。如果你去問(wèn)問(wèn)一線的開(kāi)發(fā)人員,他們可能會(huì)告訴你真實(shí)的感受是這樣的:


        59b0edcc890d7d7647fdbb30ef7e5ecf.webp


        隨著向系統(tǒng)中添加越來(lái)越多的功能,實(shí)現(xiàn)每個(gè)功能會(huì)變得越來(lái)越困難而不是越來(lái)越簡(jiǎn)單。復(fù)用?基本不存在的。像上圖中的綠色線性關(guān)系都不太可能實(shí)現(xiàn),更不必說(shuō)優(yōu)秀架構(gòu)理想中類似于對(duì)數(shù)函數(shù)的曲線了。


        過(guò)去 20 年中,軟件開(kāi)發(fā)行業(yè)中部分人推崇敏捷開(kāi)發(fā),其終極目標(biāo)也不過(guò)是為了追求上圖中的黑色線條這種線性關(guān)系——也就是現(xiàn)在很多團(tuán)隊(duì)習(xí)慣的,每個(gè)迭代完成固定數(shù)量的需求。在敏捷團(tuán)隊(duì)看來(lái),你必須要付出很多額外的努力,不斷地提取知識(shí)和重構(gòu),才有可能維持住這種恒定的需求吞吐率。然而現(xiàn)實(shí)是很多團(tuán)隊(duì)什么都沒(méi)做,卻對(duì)這種穩(wěn)定的吞吐率習(xí)以為常了。到底是敏捷開(kāi)發(fā)那一套是畫(huà)蛇添足,還是 996 太好用了?這是一個(gè)值得深思的問(wèn)題。


        那到底為什么現(xiàn)實(shí)世界中的 functionalities-cost 曲線是一個(gè)指數(shù)型,而不是理想中的線性或者對(duì)數(shù)型呢?這其實(shí)涉及到軟件模型的根本復(fù)雜性問(wèn)題。在展開(kāi)講這個(gè)概念之前,我們先講一些軟件開(kāi)發(fā)的現(xiàn)狀。





        02

         軟件開(kāi)發(fā)提效

        過(guò)去一年,許多的公司都在大力推動(dòng)降本增效。一般降本增效理解為降本增效兩件事情,但過(guò)去一年大部分公司大部分還是降本。也許作為開(kāi)發(fā)者的你,過(guò)去的一年被拉進(jìn)了無(wú)數(shù)的“ 成本歸屬群 ”,收到了來(lái)自中臺(tái)的各種賬單。對(duì)中臺(tái)來(lái)說(shuō),確實(shí)是降本了,不過(guò)這些降掉的本又加到了業(yè)務(wù)方頭上。


        所以項(xiàng)目組內(nèi)部肯定也要做各種降本,比如更精細(xì)化地使用服務(wù)器和存儲(chǔ)資源,投入更多精力去關(guān)注云上賬單,該省的省。 在需求的技術(shù)評(píng)審環(huán)節(jié)加上成本預(yù)估,讓那些提極低的 ROI 需求的產(chǎn)品經(jīng)理知難而退。 上面這些手段都是降本。


        更進(jìn)一步地講,就是減少不必要的浪費(fèi)。這種減少浪費(fèi)的降本手段在短期很有用,但很快就會(huì)達(dá)到收益天花板。更大的收益還是要提升效率,但增效相關(guān)的工作,也許你感知到的是寥寥無(wú)幾。


        其實(shí)說(shuō)到軟件開(kāi)發(fā)的增效,大多數(shù)人首先想到的就是工程效能 (EP) ,也就是開(kāi)發(fā)工具。利用各種好用的工具,來(lái)提升寫(xiě)代碼、構(gòu)建服務(wù)以及協(xié)同開(kāi)發(fā)的效率。例如擁有可以極速處理幾百 G 大倉(cāng)的代碼托管平臺(tái),擁有高度可配置的流水線來(lái)自動(dòng)化一些日常繁瑣的構(gòu)建任務(wù),有良好設(shè)計(jì)的 RPC 開(kāi)發(fā)框架,還有先進(jìn)的可觀測(cè)平臺(tái),還有無(wú)數(shù)其他的效能平臺(tái)……


        這一切的一切,最終目標(biāo)或許大家也經(jīng)常聽(tīng)到——讓程序員可以專注于業(yè)務(wù)的開(kāi)發(fā)。但問(wèn)題是,在整個(gè)軟件的開(kāi)發(fā)中,到底是業(yè)務(wù)開(kāi)發(fā)工作量的占比高,還是非業(yè)務(wù)開(kāi)發(fā)工作量占比高?


        IBM 大型機(jī)之父、圖靈獎(jiǎng)得主、軟件行業(yè)圣經(jīng)《人月神話》的作者 Fred P. Brooks ,在他的一篇著名的文章 《沒(méi)有銀彈:軟件工程的本質(zhì)性與附屬性工作》 中提到:

        把一個(gè)復(fù)雜的軟件系統(tǒng)分成兩個(gè)部分:。

        • Essential Complexity 是說(shuō)軟件要實(shí)現(xiàn)某種功能,而這種功能本身內(nèi)在就具有復(fù)雜性。

        • Accidental Complexity 則代表了程序員在用代碼實(shí)現(xiàn)功能時(shí),由于各種軟硬件等的限制以及人和人的溝通不暢而額外引入的工程上的復(fù)雜性。


        如果你對(duì)上面提到的《沒(méi)有銀彈》感興趣,可以在公眾號(hào)后臺(tái)回復(fù)「銀彈」,領(lǐng)取離線PDF閱讀材料。


        開(kāi)發(fā)軟件的目的,就是要交付某項(xiàng)功能 。那么這項(xiàng)功能的 Essential Complexity 就是不可避免的。即使你消除了所有的Accidental Complexity,Essence Complexity 依然存在。


        所以沒(méi)有銀彈,No Silver Bullet。


        回到上面的問(wèn)題,我們可以發(fā)現(xiàn):EP 和各種中臺(tái)為開(kāi)發(fā)者提供工具和服務(wù),其實(shí)就是在盡量減少 Accidental Complexity。然后讓大家可以專注于業(yè)務(wù)本身的開(kāi)發(fā),也就是 Essence Complexity。如果二八法則適用于大部分的現(xiàn)實(shí)場(chǎng)景,那么在軟件開(kāi)發(fā)中,到底 Accident Complexity 是八還是 Essence Complexity是八?如果 Essence Complexity 是八。那我們一直只在 EP 上做文章,是否有點(diǎn)“隔靴搔癢”?


        對(duì)于這個(gè)問(wèn)題,我個(gè)人很喜歡《鳳凰項(xiàng)目》這本書(shū)中的一種思考方法:當(dāng)你遇到比較復(fù)雜想不清楚的問(wèn)題時(shí),你可以假設(shè)你有一個(gè)無(wú)所不能的魔法棒,它能實(shí)現(xiàn)你的任何愿望。所以你不用糾結(jié)具體問(wèn)題怎么解決,你可以直接想象“你期望的狀態(tài)是什么”。所以,假如開(kāi)發(fā)者也有這么一個(gè)魔法棒,魔法棒揮一下,公司的各種基礎(chǔ)設(shè)施和工具立刻達(dá)到了太陽(yáng)系的頂級(jí)水準(zhǔn),幾萬(wàn) PB 的大倉(cāng)毫秒級(jí)克隆,用羅永浩的 TNT 口述就是能得到業(yè)界最牛的 CI Pipeline、業(yè)界最頂級(jí)的觀測(cè)系統(tǒng)、 最頂級(jí)的發(fā)布系統(tǒng),全是頂級(jí)。不過(guò)然后呢?即使IT 和 EP 系統(tǒng)已經(jīng)到了完美的程度,還是不得不面對(duì)這樣一個(gè)現(xiàn)實(shí):終于可以專注于開(kāi)發(fā)業(yè)務(wù)了。 但是業(yè)務(wù)到底怎么開(kāi)發(fā)呢?


        無(wú)數(shù)現(xiàn)實(shí)中的例子, 由于業(yè)務(wù)建模的不合理、由于需求的倉(cāng)促上線、由于接口設(shè)計(jì)的不合理、由于各種無(wú)謂的耦合……建立在最牛的基礎(chǔ)設(shè)施之上的業(yè)務(wù)系統(tǒng),一段時(shí)間之后又將變成一座廢山。代碼看得令人眩暈,改功能不知道去哪里改,不知道會(huì)影響哪些功能,不知道需要改動(dòng)的點(diǎn)是否都被覆蓋到了,改個(gè)小功能要改無(wú)數(shù)的地方 ……


        8ee97f6ab66d61852421581294943fbf.webp

        接手別人的項(xiàng)目.gif

        然后,functionality-cost 曲線又變成了這樣。


        20248736bc3e25d15ff0afccb5084206.webp


        不管基礎(chǔ)設(shè)施多么優(yōu)秀,業(yè)務(wù)代碼依然是廢山。所以只靠工具提效是遠(yuǎn)遠(yuǎn)不夠的,還需要關(guān)注業(yè)務(wù)本身,Essential Complexity。


        這種想法其實(shí)大家早就該有,畢竟真正交付用戶價(jià)值,幫助項(xiàng)目組賺錢給大家發(fā)工資、發(fā)獎(jiǎng)金的,就是這段業(yè)務(wù)代碼。只是業(yè)務(wù)類型千千萬(wàn),又必須時(shí)刻根據(jù)市場(chǎng)反饋去變化,看起來(lái)永遠(yuǎn)是個(gè)開(kāi)放命題。所以對(duì)于大部分人來(lái)說(shuō),在業(yè)務(wù)中去歸納一些 pattern,遠(yuǎn)比去做一些 scope 較為固定的工具要困難和難以落地,這可能也是很多開(kāi)發(fā)者喜歡做工具或者 infra 、而不喜歡做業(yè)務(wù)的重要原因。因?yàn)闃I(yè)務(wù)看起來(lái)真的太縹緲了,似乎沒(méi)什么可總結(jié)和沉淀的。并且,很多人會(huì)錯(cuò)誤的估計(jì)業(yè)務(wù)系統(tǒng)的復(fù)雜性,總覺(jué)得做業(yè)務(wù)開(kāi)發(fā)就是單純的增刪查改,沒(méi)什么技術(shù)含量。


        對(duì)業(yè)務(wù)復(fù)雜性的錯(cuò)誤認(rèn)知和低估,會(huì)進(jìn)一步加劇廢山的形成,從而讓 functionality-cost 曲線變得更加陡峭。接下來(lái)我們聊聊業(yè)務(wù)系統(tǒng)復(fù)雜的根本原因。





        03

         業(yè)務(wù)系統(tǒng)復(fù)雜的根本原因
        結(jié)合過(guò)去參與過(guò)的很多業(yè)務(wù)系統(tǒng)開(kāi)發(fā)以及近期閱讀書(shū)籍的一些思考,拋開(kāi)人的原因 (假設(shè)人都是理智的有追求的) ,個(gè)人認(rèn)為導(dǎo)致業(yè)務(wù)系統(tǒng)復(fù)雜的根本原因有兩個(gè): 功能之間隱秘增加的耦合 、 不可避免的代碼腐化。

            3.1 功能之間隱蔽增加的耦合


        相信絕大部分開(kāi)發(fā)者在項(xiàng)目一開(kāi)始的時(shí)候,都有一顆“整潔架構(gòu)”的心,都希望把代碼寫(xiě)好。尤其是項(xiàng)目一開(kāi)始,需求做的飛快,每天幾千行代碼也不在話下。大家會(huì)關(guān)注函數(shù)的顆粒度,會(huì)關(guān)注模塊的劃分和職責(zé)是否單一,也會(huì)關(guān)注單元測(cè)試情況和代碼的可測(cè)性。即使這樣,隨著時(shí)間推移,大家還是會(huì)發(fā)現(xiàn)代碼改起來(lái)越來(lái)越痛苦——總會(huì)牽一發(fā)而動(dòng)全身,或者明明是修改功能 A,卻不得不關(guān)注功能B是否受影響。這是為什么呢?
        答案就是——耦合。 很多人一說(shuō)到耦合,就會(huì)心生厭惡。確實(shí),很多時(shí)候不合理的耦合是萬(wàn)惡之源。但是耦合又是不可避免的。因?yàn)?Essential Complexity的存在。如果某個(gè)功能本來(lái)就需要多個(gè)模塊共同參與,不論你怎么分解這些模塊,只有把它們“集成”到一起,才能實(shí)現(xiàn)有意義的功能。把它們集成到一起,A 依賴于 B、B 又依賴 C、C 又會(huì)反饋給 A,這不就是耦合嗎?
        軟件工程中有句話每個(gè)人都爛熟于心:高內(nèi)聚,低耦合 但很多人只記住了后面三個(gè)字: 低耦合 ,卻忘記了前面的三個(gè)字: 高內(nèi)聚

        在高內(nèi)聚的邊界內(nèi),各個(gè)模塊是不是就是強(qiáng)耦合的呢?即使你認(rèn)真的進(jìn)行架構(gòu)設(shè)計(jì)去拆分模塊,這種耦合也是難以避免的。 下面我們舉兩個(gè)例子。


        某團(tuán)隊(duì)開(kāi)發(fā)了一個(gè)社區(qū)類應(yīng)用。社區(qū)應(yīng)用大家應(yīng)該也用過(guò)不少,大體架構(gòu)是差不多的,一般都會(huì)包含如下業(yè)務(wù)模塊:

        • 資訊:可以簡(jiǎn)單理解為 feeds 流,主要以左文右圖的方式來(lái)展示。
        • 社區(qū):用戶開(kāi)放交流的地方,可以類比于新浪微博或者 twitter,用戶可以發(fā)帶圖片的內(nèi)容。
        • 評(píng)論區(qū):資訊、動(dòng)態(tài)都可以發(fā)評(píng)論。
        • ……


        上述這幾個(gè)模塊都是比較獨(dú)立的業(yè)務(wù),產(chǎn)品形態(tài)也有差異,因此一般都會(huì)由不同的小 Team 來(lái)負(fù)責(zé)。


        有一天,產(chǎn)品經(jīng)理希望做一個(gè)新功能,叫作“名片系統(tǒng)”。簡(jiǎn)單來(lái)說(shuō)就是,它允許用戶自定義自己頭像后展示哪些名片或者標(biāo)簽 (可以有多個(gè)) ,以突顯身份特征,例如:

                        

        552da2b3974d99405d2e5902264da127.webp

        這個(gè)需求其實(shí)初看起來(lái)沒(méi)有多復(fù)雜,閉上眼睛琢磨大概就能想到。首先需要做一個(gè)配置頁(yè)面讓用戶選擇要展示的標(biāo)簽并保存起來(lái),同時(shí)需要在 App 中各種需要展示頭像的位置去讀取用戶的相關(guān)配置,來(lái)讓客戶端進(jìn)行展示。看起來(lái)不難對(duì)吧?


        但是再深入想一想,你就會(huì)發(fā)現(xiàn)其實(shí)并沒(méi)有想象的那么簡(jiǎn)單。如果沒(méi)有意識(shí)到由 Essential Complexity 引入的耦合,開(kāi)發(fā)者很可能在排期的時(shí)候少估算了天數(shù),最后不得不需要用各種“責(zé)任感”、“Ownership”這種精神力量通過(guò)加班來(lái)盡量保證不 delay。下面我們來(lái)看看為什么。


        首先配置頁(yè)面需要從不同的系統(tǒng)去獲取用戶擁有的標(biāo)簽。例如用戶勛章,需要從成就系統(tǒng)去獲取“連續(xù)簽到 30 天”,“連續(xù)創(chuàng)作一周”之類的成就,會(huì)員信息需要從會(huì)員系統(tǒng)獲取,主要就是用戶會(huì)員的 VIP 等級(jí)。個(gè)人/企業(yè)認(rèn)證,類似于微博的黃 V 和藍(lán) V,需要從認(rèn)證系統(tǒng)去獲取。


        這里的重點(diǎn)不是要去不同的系統(tǒng)查數(shù)據(jù)麻煩,重點(diǎn)是這里引入了新的耦合。 這些原本設(shè)計(jì)之初毫不相關(guān)的概念,被這個(gè)需求關(guān)聯(lián)在一起了。這種后來(lái)的再建立關(guān)聯(lián)關(guān)系,任誰(shuí)在系統(tǒng)設(shè)計(jì)之初也不可能在架構(gòu)層面去設(shè)計(jì)。并且,考慮到產(chǎn)品功能的完整性,這會(huì)帶來(lái)一個(gè)問(wèn)題,就是這個(gè)需求會(huì)變得很長(zhǎng)尾。


        后續(xù)如果一個(gè)負(fù)責(zé)資訊板塊的產(chǎn)品經(jīng)理想要增強(qiáng)平臺(tái)優(yōu)質(zhì)內(nèi)容的豐富度,要做一個(gè)簽約作者的體系。這時(shí)開(kāi)發(fā)者除了要讓推薦系統(tǒng)對(duì)該簽約作者發(fā)的內(nèi)容在推薦流量上做些傾斜調(diào)整,還不能忘了要到和那個(gè)需求「基本沒(méi)啥關(guān)系」的名片系統(tǒng)上來(lái)做些修改,從而讓這部分用戶能對(duì)外展示自己是“簽約作者”的標(biāo)簽。


        后續(xù)只要是和身份相關(guān)的內(nèi)容,都會(huì)和這個(gè)功能耦合。當(dāng)然,也許有人會(huì)說(shuō):“其實(shí)也可以不全支持,又不是不能用。”


        b7549b77aef7a7bb4e1e9853c8157a5a.webp

        周五傍晚產(chǎn)品經(jīng)理找你改需求.gif


        但是這就會(huì)帶來(lái)負(fù)向的用戶體驗(yàn),進(jìn)一步引發(fā)的負(fù)面輿論處理不及時(shí),很可能這個(gè)需求的收益反而不如它帶來(lái)的負(fù)面影響。


        除了上述配置端,展示端其實(shí)也被耦合了。原本資訊的 feed 流、社區(qū)的動(dòng)態(tài)列表、評(píng)論區(qū)以及資訊詳情頁(yè)的作者頭像展示部分的樣式都是不一樣的。有的只展示一個(gè)名字,有的展示名字加頭像,有的還要展示個(gè)人簡(jiǎn)介等等。


        但現(xiàn)在它們都要額外考慮名片的展示,問(wèn)題是有的場(chǎng)景位置不夠,放不下那么多標(biāo)簽怎么辦?哪個(gè)標(biāo)簽更重要?有沒(méi)有權(quán)重?到底是名片系統(tǒng)統(tǒng)一來(lái)處理每個(gè)場(chǎng)景展示什么標(biāo)簽,還是各自場(chǎng)景自行決定?這也是個(gè)兩難的選擇。如果分發(fā)邏輯做在名片系統(tǒng),那每增加一個(gè)露出場(chǎng)景,名片系統(tǒng)也要跟著改。如果是不同場(chǎng)景各自負(fù)責(zé),那它們除了實(shí)現(xiàn)自己的邏輯,還不能忘了名片系統(tǒng)。但不論怎么選,這里也引入了強(qiáng)耦合。


        并且即使這次梳理完了所有的現(xiàn)有場(chǎng)景,開(kāi)發(fā)者把所有的位置都改一遍,這就完了嗎?顯然沒(méi)有,它是一個(gè)長(zhǎng)尾的需求。后續(xù)只要是某個(gè)需求涉及到展示用戶頭像,是不是就需要考慮名片?萬(wàn)一這個(gè)需求開(kāi)發(fā)者之前沒(méi)參與過(guò)名片需求的開(kāi)發(fā)怎么辦,他怎么知道要考慮名片?免不了上線后又是一通緊急 bug fix…很多開(kāi)發(fā)者怕的不是這個(gè)功能本身有多復(fù)雜,怕的就是不知道改了這里會(huì)影響其他什么地方,或者別的地方也需要一起改。



        05510aac86cc016ca362d1cb278145e9.webp

        bug.gif


        我們可以看到,當(dāng)系統(tǒng)變得復(fù)雜,功能之間會(huì)逐漸產(chǎn)生耦合,它們的關(guān)聯(lián)關(guān)系也會(huì)變得復(fù)雜。這些無(wú)意間引入的耦合,會(huì)給后續(xù)所有的需求開(kāi)發(fā)增加一些額外的負(fù)擔(dān)。


        所以,當(dāng)你在做新需求時(shí),還必須考慮它和一些舊的特性怎么結(jié)合當(dāng)系統(tǒng)的功能不斷膨脹,這些額外負(fù)擔(dān)會(huì)不斷增加,想讓每個(gè)迭代的需求吞吐率還能保持恒定簡(jiǎn)直是癡人說(shuō)夢(mèng),更別說(shuō)想象中的需求交付速度越來(lái)越快了。


        再舉個(gè)例子。 做App肯定都希望看到用戶的裂變?cè)鲩L(zhǎng),引流就是一件非常重要的事情,尤其是從微信這個(gè)巨大的流量池引流。我們想把App上部分優(yōu)質(zhì)的內(nèi)容分享到微信,這樣就能在微信中裂變傳播,吸引更多的用戶來(lái)下載和安裝。這個(gè)非常合理的需求,其實(shí)也引入了業(yè)務(wù)上的強(qiáng)耦合。


        大部分手機(jī)App都是用原生的方式開(kāi)發(fā),例如 IOS 用 Swift/OC、Android用 Java/KT。但微信中只能分享 H5 的 Web 頁(yè)面。這就意味著同樣一個(gè)需求,除了要用原生做一遍,還需要用 H5 再做一遍。不僅如此,由于分享到微信的H5 頁(yè)面,用戶打開(kāi)后肯定都是沒(méi)有登錄態(tài),因此還需要讓 H5 依賴的后臺(tái)接口支持無(wú)登錄態(tài)調(diào)用。


        實(shí)際上,這沒(méi)那么好支持。有些接口邏輯強(qiáng)依賴于用戶登錄態(tài)怎么辦?例如查看資訊詳情的接口,接口內(nèi)部除了要返回資訊內(nèi)容,還要記錄用戶的瀏覽記錄,還需要給資訊的瀏覽量+1。如果你沒(méi)有關(guān)注資訊的作者,可能頭像旁邊要展示一 個(gè)關(guān)注按鈕……這些都需要依賴于用戶的登錄態(tài)才能完成。因此在沒(méi)有登錄態(tài)的情況下,就必須閹割一部分現(xiàn)有功能。


        那要怎么閹割呢? 在原接口中各種 if else?太 bad taste 了,不僅代碼亂成一鍋粥,統(tǒng)一的鑒權(quán)網(wǎng)關(guān)也很難處理。最好就是新開(kāi)接口專門處理來(lái)自 H5 的調(diào)用,把它當(dāng)成另一個(gè)獨(dú)立的需求,而不是強(qiáng)行和之前的接口邏輯寫(xiě)在一起。但這還不夠,還有很多問(wèn)題,例如像文章瀏覽量這種數(shù)據(jù)怎么處理?沒(méi)有登錄態(tài),就沒(méi)法對(duì)瀏覽量進(jìn)行去重。如果每次請(qǐng)求都累加,就會(huì)被灰產(chǎn)利用來(lái)刷數(shù)據(jù)。如果不累加,似乎對(duì)作者又不太公平?這可能會(huì)導(dǎo)致產(chǎn)品側(cè)需要同時(shí)記錄有效瀏覽量和無(wú)登錄態(tài)瀏覽量,這又是一個(gè)新需求了。
        dda6dfc68754860ef6c31baf11c5b224.webp 嘗試調(diào)整一些邏輯.gif
        此后,如果一個(gè)功能頁(yè)要支持分享到微信,客戶端要做一版完整的雙端接口,H5 要做一版簡(jiǎn)化的。后臺(tái)要給客戶端提供一套接口,還要給 H5 提供一套無(wú)登錄態(tài)的定制版接口。就這樣一個(gè)分享到微信的功能,它又變成了長(zhǎng)尾的需求,還讓后續(xù)所有的開(kāi)發(fā)者工作量乘以 2 。
        這些是技術(shù)架構(gòu)不合理或者代碼寫(xiě)得不好導(dǎo)致的嗎?顯然不是,這就是隨著產(chǎn)品功能不斷疊加,各種 Essential Complexity 帶來(lái)的天然耦合導(dǎo)致的。 到項(xiàng)目后期,每新增一個(gè)變更,除了修改這個(gè)變更本身,可能還要修改和它耦合的n+1 個(gè)位置。而且沒(méi)有辦法通過(guò)軟件上的優(yōu)化來(lái)消除這種復(fù)雜性,因?yàn)閺?fù)雜性是不滅的。工程上的任何架構(gòu)或者設(shè)計(jì)模式的引入,只會(huì)把復(fù)雜性從一個(gè)位置轉(zhuǎn)移到另一個(gè)位置,但永遠(yuǎn)不會(huì)消失,No Silver Bullet

            3.2 不可避免的代碼腐化


        除了業(yè)務(wù)本身的耦合帶來(lái)的復(fù)雜性,代碼腐化也是另一個(gè)讓業(yè)務(wù)系統(tǒng)變得復(fù)雜的重要原因。
        相信大部分開(kāi)發(fā)者都經(jīng)歷過(guò)這樣的心路路程:
        項(xiàng)目剛開(kāi)始時(shí)雄心勃勃 :維護(hù)前人的廢山是無(wú)奈,從零開(kāi)始,要讓大家看看什么才是整潔架構(gòu)的時(shí)代!

        開(kāi)發(fā)過(guò)程中:時(shí)間都是倒排,CR別人的代碼就是 5 秒后在企微回復(fù)一個(gè) d。需求改來(lái)改去,業(yè)務(wù)邏輯扭扭曲曲,辛苦寫(xiě)好的單測(cè)又失效了,算了不裝了,為什么跟自己過(guò)不去呢?

        后期:吶,搞出廢山大家都不想的咯,這次不算,下次一定,我煮碗面給你吃


        a066067717421319803f764cfac475e6.webp

        嘗試CR別人的代碼.gif


        作者自己就是一個(gè)很典型的例子。大學(xué)畢業(yè)剛工作時(shí),我負(fù)責(zé)維護(hù)了一個(gè)非??鋸埖捻?xiàng)目,沒(méi)有任何文檔,一個(gè) PHP 文件幾萬(wàn)行、一個(gè)函數(shù)上千行、一個(gè)接口能返回幾種完全不同的 JSON 作為 response。每天還有幾十號(hào)人在瘋狂 push代碼。倉(cāng)庫(kù)不斷膨脹,每次修改個(gè)功能,心中都是一萬(wàn)個(gè)不情愿,一不小心就會(huì)出現(xiàn)線上事故,至今我對(duì) PHP 等沒(méi)有類型的語(yǔ)言開(kāi)發(fā)項(xiàng)目還心有余悸。


        后來(lái)有個(gè)機(jī)會(huì)從零開(kāi)始負(fù)責(zé)一個(gè)公司重量級(jí)的運(yùn)營(yíng)系統(tǒng)的開(kāi)發(fā),內(nèi)心非常的激動(dòng)。終于可以按照自己工作之余看書(shū)學(xué)到的最佳實(shí)踐方法來(lái)構(gòu)建項(xiàng)目了,這下要讓所有人刮目相看。開(kāi)發(fā)過(guò)程中,也是恪盡職守,每天晚飯后都花至少1個(gè)小時(shí)拉著團(tuán)隊(duì)另外幾個(gè)開(kāi)發(fā)人員做 Code Review,經(jīng)常還爭(zhēng)執(zhí)得面紅耳赤,對(duì) Bad Taste 堅(jiān)決抵制。


        項(xiàng)目整體推進(jìn)得很順利,上線后取得了很大的成功,只是后來(lái)由于組織架構(gòu)變動(dòng),去了另一個(gè)團(tuán)隊(duì),不再負(fù)責(zé)那個(gè)項(xiàng)目了。不過(guò)本人一直覺(jué)得,自己給接盤方打下了一個(gè)非常好的基礎(chǔ),對(duì)方一定會(huì)感謝自己……直到一年后的某天,和一個(gè)同事無(wú)意間聊起來(lái),他們就負(fù)責(zé)了我之前的那個(gè)項(xiàng)目 (他不知道我之前負(fù)責(zé)那個(gè)項(xiàng)目) 。本以為能從他那得到些正向的評(píng)價(jià),結(jié)果全是吐槽,諸如代碼看不懂、風(fēng)格奇葩、擴(kuò)展困難等等。最后補(bǔ)了一句,后來(lái)實(shí)在受不了,他們重寫(xiě)了。


        這就是發(fā)生在作者身上的真實(shí)故事,一個(gè)滿腔熱血,熟讀《整潔架構(gòu)》《重構(gòu)》《設(shè)計(jì)模式》《領(lǐng)域驅(qū)動(dòng)》 《演進(jìn)式架構(gòu)》的人,從零開(kāi)始開(kāi)發(fā)系統(tǒng),卻依然避免不了舊代碼走向腐化,成了后人口中的廢山始作俑者。

        70f2393585d425fb8eb99a3adae3da51.webp

        偶然間看見(jiàn)自己多年前寫(xiě)的代碼.gif


        到底代碼是如何腐化的? 這是一個(gè)非常大的話題,這里就不展開(kāi)了,因?yàn)樯鲜鎏岬降臅?shū)中幾乎都是講這些 Bad Taste 和相應(yīng)的應(yīng)對(duì)之道的,作者也沒(méi)有能力和自信能比它們講得更清楚。因此,本文只講為什么我覺(jué)得這種腐化是不可避免的。


        核心原因:架構(gòu)設(shè)計(jì)和模塊抽象只能面向當(dāng)下,它天然是短視的或者說(shuō)是有局限性的。這種局限性即使是最優(yōu)秀的架構(gòu)師也是無(wú)法逾越的。


        說(shuō)到這個(gè)問(wèn)題,先講兩個(gè)常見(jiàn)的開(kāi)發(fā)模式。 大家可能聽(tīng)過(guò),現(xiàn)在更提倡敏捷開(kāi)發(fā)而不是瀑布流式的開(kāi)發(fā)。 但到底什么是敏捷,什么是瀑布流呢?


        • 瀑布流式開(kāi)發(fā)


        瀑布流就是上個(gè)世紀(jì)比較傳統(tǒng)的開(kāi)發(fā)模式。甲方提需求,我要做一個(gè)什么樣的軟件,它要包含哪些功能 。軟件公司作為乙方,來(lái)承接甲方的需求。它首先需要有人去調(diào)研甲方的需求,具象化每個(gè)功能點(diǎn),然后形成最終的需求文檔和性能要求文檔。當(dāng)甲方對(duì)需求認(rèn)可并簽字后,就進(jìn)入了架構(gòu)師的設(shè)計(jì)階段。這個(gè)階段架構(gòu)師能夠看到所有的需求,他擁有全局的視角,然后進(jìn)行架構(gòu)設(shè)計(jì)、方案設(shè)計(jì)和模塊的拆分。最后根據(jù)架構(gòu)師的設(shè)計(jì),開(kāi)發(fā)部門就分模塊進(jìn)行開(kāi)發(fā)。開(kāi)發(fā)完成之后進(jìn)入測(cè)試階段,測(cè)試完成后再交給甲方去驗(yàn)收,驗(yàn)收通過(guò)就正式交付。


        這就是瀑布流式的開(kāi)發(fā)。必須前一步做完再交給下一步,就像瀑布一樣順流而下。這種開(kāi)發(fā)方式現(xiàn)在看來(lái)是不好的,因?yàn)檫@種開(kāi)發(fā)方式周期很長(zhǎng),動(dòng)輒就是以6 個(gè)月甚至 1 年起步,很多大項(xiàng)目甚至要 3 年以上。但商場(chǎng)如戰(zhàn)場(chǎng),形勢(shì)瞬息萬(wàn)變,等你做出來(lái),黃花菜都涼了,再好的軟件又有什么用呢?


        很多軟件工程的書(shū)上都講過(guò)“項(xiàng)目失敗”的案例,大多就是這種用瀑布流開(kāi)發(fā)方式開(kāi)發(fā)的項(xiàng)目,由于開(kāi)發(fā)周期太長(zhǎng),預(yù)算嚴(yán)重超支,或者還沒(méi)做完就發(fā)現(xiàn)市場(chǎng)已經(jīng)不需要了,或者還沒(méi)做完發(fā)現(xiàn)技術(shù)方案已經(jīng)過(guò)時(shí)了等等。并且,瀑布流式開(kāi)發(fā)實(shí)際上在后期有非常多的問(wèn)題。大家現(xiàn)在開(kāi)發(fā)完一個(gè)小需求之后多方一起聯(lián)調(diào)都覺(jué)得痛苦,你能想象某個(gè)大型項(xiàng)目,等所有的功能開(kāi)發(fā)完再去測(cè)試會(huì)有多少問(wèn)題嗎!即使好不容易處理完項(xiàng)目本身的問(wèn)題,甲方驗(yàn)收時(shí)還有更大的麻煩:“當(dāng)初說(shuō)的做 XXX,但是你們做出來(lái)是 YYY,根本不是我要的,不滿足需求”。這又會(huì)涉及大量的返工,進(jìn)一步讓項(xiàng)目延期。


        因?yàn)槠俨剂鬟@種開(kāi)發(fā)方式太過(guò)于笨重,無(wú)法適應(yīng)現(xiàn)代軟件交付速度的預(yù)期,中間有大量的人力空轉(zhuǎn)和內(nèi)耗,所以后來(lái)一群大佬在一起做了一個(gè)“敏捷宣言”,提倡敏捷開(kāi)發(fā)流程。敏捷開(kāi)發(fā)其實(shí)就是對(duì)瀑布流式開(kāi)發(fā)做出了修改,之前是收集好所有需求,再來(lái)做整體設(shè)計(jì),再來(lái)開(kāi)發(fā),最后測(cè)試。任何一個(gè)環(huán)節(jié)出問(wèn)題,都會(huì)導(dǎo)致后續(xù)環(huán)節(jié)出問(wèn)題。比如需求沒(méi)整理對(duì),那后續(xù)所有工作都白搭。架構(gòu)沒(méi)設(shè)計(jì)好,開(kāi)發(fā)就會(huì)很痛苦。開(kāi)發(fā)的代碼難以測(cè)試,那測(cè)試進(jìn)展就非常緩慢。


        • 敏捷開(kāi)發(fā)


        敏捷開(kāi)發(fā)的解決方法就是小步快跑。先做最重要的部分。如果要造汽車,我先做發(fā)動(dòng)機(jī)和4個(gè)輪子,只在駕駛員那綁個(gè)凳子,讓它能夠先跑起來(lái)。等跑起來(lái)了,再去逐步完善其它地方。我先做個(gè)后視鏡,如果沒(méi)人關(guān)心那就這樣了,不繼續(xù)投入了。我再試下給車加個(gè)擋風(fēng)玻璃。如果市場(chǎng)反應(yīng)非常好,那就加大投入繼續(xù)優(yōu)化,除了前擋,四周上下都給圍上。我再試下多加幾個(gè)凳子,市場(chǎng)反應(yīng)炸裂,那就加大投入,把凳子換成沙發(fā)......


        這就是敏捷開(kāi)發(fā)。小步快跑,在迭代中識(shí)別出更重要的需求,這樣才能快速響應(yīng)市場(chǎng)的變化。


        但這里需要糾正很多人對(duì)敏捷開(kāi)發(fā)的一個(gè)誤區(qū)。聽(tīng)到敏捷開(kāi)發(fā),大家總以為這種方式能提高開(kāi)發(fā)效率和開(kāi)發(fā)速度。其實(shí)不對(duì)。從上面的例子你應(yīng)該可以看明白,敏捷交付的是半成品,它的解決方案就是不要一口吃個(gè)大胖子。小步快跑,做一點(diǎn)交付一點(diǎn)。


        如果從完成品的角度來(lái)講,敏捷并不會(huì)提高交付速度,甚至它會(huì)更慢。你可以很直接地看到,這種開(kāi)發(fā)方式缺失了對(duì)整體目標(biāo)的把控,設(shè)計(jì)上天然有欠考慮的地方,后期要改就得花更多的成本。


        但是敏捷的優(yōu)勢(shì)在于,它能夠快速捕捉市場(chǎng)機(jī)會(huì),讓自己活下來(lái),活下來(lái)才有機(jī)會(huì)談成本,再找到性價(jià)比高的地方去優(yōu)化。


        很多人曾經(jīng)都在想,自己團(tuán)隊(duì)能否嘗試一下敏捷開(kāi)發(fā)? 其實(shí),現(xiàn)在不就已經(jīng)是了嗎?雖然在流程上和國(guó)外提倡的敏捷開(kāi)發(fā)存在較大差異,可以稱之為“中華田園敏捷開(kāi)發(fā)”,但確實(shí)也是敏捷開(kāi)發(fā)?,F(xiàn)在互聯(lián)網(wǎng)公司基本上都是快節(jié)奏的發(fā)布,做App 都是先發(fā) MVP 版本,然后再持續(xù)優(yōu)化。每個(gè)迭代,產(chǎn)品經(jīng)理都是只提幾個(gè)有限的需求,開(kāi)發(fā)也只開(kāi)發(fā)這幾個(gè)需求就上線。然后就進(jìn)入不斷堆功能的小步快跑階段,縫縫補(bǔ)補(bǔ)又一年。產(chǎn)品經(jīng)理也會(huì)用各種方式嘗試去識(shí)別功能的收益,埋點(diǎn)、報(bào)表、同比環(huán)比等等。


        聊了這么多關(guān)于瀑布流式開(kāi)發(fā)和敏捷開(kāi)發(fā),這和代碼不可避免的腐化有什么關(guān)系呢?


        其實(shí)當(dāng)大家知道現(xiàn)在這種“中華田園式敏捷開(kāi)發(fā)”后,馬上就能意識(shí)到,每次大家在做技術(shù)方案設(shè)計(jì)時(shí),能拿到的信息僅僅是宏大視圖中的小小一角,根本沒(méi)有全貌,并不能像瀑布流開(kāi)發(fā)那樣拿到產(chǎn)品的整體視圖。僅僅憑借這一點(diǎn)點(diǎn)信息,再牛的架構(gòu)師設(shè)計(jì)出來(lái)的方案也是有局限性的,這也是為什么前面說(shuō)架構(gòu)設(shè)計(jì)和模塊抽象只能面向當(dāng)下,它天然是短視的。這不是人的問(wèn)題,這是開(kāi)發(fā)方式的問(wèn)題。當(dāng)然,現(xiàn)實(shí)情況是,這種局部的需求,很多人也沒(méi)有去做設(shè)計(jì)。拿到需求,直接從 controller 開(kāi)始寫(xiě)代碼解析入?yún)?,然?service 組合一下RPC 和 DB 調(diào)用,DAO 再實(shí)現(xiàn)幾個(gè)數(shù)據(jù)庫(kù)查詢就可以了。根據(jù)作者的經(jīng)驗(yàn),這種情況甚至能占到 80%以上。在一個(gè)項(xiàng)目中只有少量的局部架構(gòu)設(shè)計(jì)+這些架構(gòu)設(shè)計(jì)還不一定合理+80%以上任何設(shè)計(jì)都沒(méi)有+有上千種讓代碼難以閱讀的編碼方式,如果說(shuō)代碼不腐化,你信嗎?


        這里再舉個(gè)例子。


        有個(gè)后臺(tái)管理系統(tǒng)需要做權(quán)限管理功能,所以開(kāi)發(fā)者基于業(yè)界常見(jiàn)的 RBAC 模型開(kāi)發(fā)了一個(gè)權(quán)限管理模塊。在做方案設(shè)計(jì)時(shí),大家一直比較關(guān)注可復(fù)用性,因?yàn)楹罄m(xù)可能有別的系統(tǒng)也需要權(quán)限管理。


        其實(shí)辦法也很簡(jiǎn)單,在模型中加入了租戶的概念 ( appid ) ,所有的 Role 表和 Access 表都帶上 appid 字段。這樣,不同業(yè)務(wù)就可以自定義自己的 Role 和 Access 而不干擾其它的業(yè)務(wù)。這個(gè)設(shè)計(jì)按理說(shuō)也還可以,只要是基于 RBAC 模型的權(quán)限管理,后續(xù)分配幾個(gè) appid就可以用了。


        然而,兩周后的一個(gè)需求直接就來(lái)打臉了。這個(gè)需求也要做權(quán)限管理,它表面上看也是基于 RBAC 模型的,但是有細(xì)微的區(qū)別。簡(jiǎn)單說(shuō),這個(gè)需求類似于游戲里的幫派管理,幫主有所有權(quán)限。他還能夠設(shè)置任意多個(gè)管理組,比如副幫主、長(zhǎng)老、堂主等;管理組成員可以管理幫派成員。管理組之間也有權(quán)重,權(quán)重高的管理組可以管理權(quán)重低的管理組,比如副幫主可以管理所有長(zhǎng)老和堂主,長(zhǎng)老可以管理所有堂主。


        看起來(lái)依然是基于 RBAC 模型,不同管理組就是不同 Role 。但是這里最大的區(qū)別就是,原始的 RBAC、Role 之間是互相無(wú)感知的,不同 Role 不需要知道別的 Role 的存在,它只需要知道它有哪些 Access。但是對(duì)于這個(gè)需求,Role 之間需要建立關(guān)系,有優(yōu)先級(jí),高級(jí)的 Role 可以管理低級(jí)的 Role。


        這種 Role 之間的關(guān)聯(lián)關(guān)系,在一開(kāi)始設(shè)計(jì) RBAC 模塊時(shí)是沒(méi)想到的,所以大家當(dāng)時(shí)的設(shè)計(jì)只能應(yīng)對(duì)當(dāng)時(shí)的需求,擴(kuò)展性也只是多租戶,而對(duì)于新的需要修改模型的功能就無(wú)能為力了。這也是為什么說(shuō)在“中華田園敏捷開(kāi)發(fā)”中,架構(gòu)設(shè)計(jì)總是短視的。


        6cb30ff88e5d943b5f018557b32d190e.webp

        功能先上了再說(shuō).gif


        后來(lái)大家進(jìn)行了一個(gè)小復(fù)盤,為什么設(shè)計(jì)的通用權(quán)限管理第一個(gè)需求,就沒(méi)法復(fù)用?大家為后臺(tái)管理設(shè)計(jì)的模型,誰(shuí)能想到產(chǎn)品要做幫派管理。雖然擴(kuò)展性設(shè)計(jì)只考慮多租戶也確實(shí)過(guò)于簡(jiǎn)單了,但是如果考慮更多擴(kuò)展性,工時(shí)是不是也會(huì)增加呢,會(huì)不會(huì)又有過(guò)度設(shè)計(jì)的嫌疑呢?……后來(lái),那個(gè)業(yè)務(wù)又只能重新開(kāi)發(fā)一套了,當(dāng)然里面還包含很多其它性能優(yōu)化。因?yàn)樗鼈兊恼?qǐng)求量比較大,各種數(shù)據(jù)要緩存到 Redis。而一開(kāi)始的面向后臺(tái)管理系統(tǒng)的 RBAC,一天也沒(méi)幾個(gè)人用,每次都直接讀 mysql。


        這樣的例子其實(shí)還有很多,就不一一列舉了。大家也可以想想自己項(xiàng)目中的通用 XXX 系統(tǒng),看看到底通不通用。很多時(shí)候看似類似的需求,其 Essential Complexity 是很不一樣的,對(duì)應(yīng)的軟件建模也是有區(qū)別的。盲目地追求復(fù)用,在函數(shù)后不斷地加參數(shù),可能適得其反。


        說(shuō)到復(fù)用,開(kāi)源界在國(guó)外有兩個(gè)比較形象的說(shuō)法: Free as Beer、Free as Puppy。


        有些“可復(fù)用的能力”是像啤酒一樣免費(fèi)的 Free as Beer,拿來(lái)就喝不給錢。


        還有些“可復(fù)用的能力”是像小狗一樣免費(fèi)。雖然你免費(fèi)獲得了一只可愛(ài)的小狗,在收獲短暫的快樂(lè)后,你需要各種鏟屎、各種照護(hù)。到底快樂(lè)多還是負(fù)擔(dān)多,就看你是不是愛(ài)狗人士了。


        那些在設(shè)計(jì)之初就沒(méi)有經(jīng)過(guò)精心考慮的“通用系統(tǒng)”,對(duì)于用戶來(lái)說(shuō)就是 Free as Puppy。要用只得捏著鼻子用,后續(xù)要改動(dòng)加功能還很困難。其實(shí)也不復(fù)雜,不如…...造個(gè)輪子吧——Yet Another Shit Comes!


        當(dāng)然也不是所有的系統(tǒng)都是短視的。業(yè)界也有很多 Free as Beer 的系統(tǒng)。這些系統(tǒng)大多都是面向特定的場(chǎng)景,例如 ERP、CRM,以及云上各種 Saas Paas。要注意的是,它們都是面向特定場(chǎng)景的產(chǎn)品,有明確的邊界。只有這樣它們才能在內(nèi)部進(jìn)行充分的建模,從而構(gòu)建出符合特定場(chǎng)景的通用產(chǎn)品。


        因此建議各位開(kāi)發(fā)者在想著做“通用”的時(shí)候,先想想自己的“通用”指什么、邊界在哪里。 一般來(lái)說(shuō),你要做的東西業(yè)界或者公司都有同類產(chǎn)品,你為什么不用?那些你不愿意用的產(chǎn)品,它其實(shí)也是想做通用的,但是你有沒(méi)有想過(guò)為什么它沒(méi)有達(dá)到目的呢?你去做的話,你有自信可以讓你的東西 Free as Beer 嗎?請(qǐng)想清楚了再動(dòng)手。


        通過(guò)上面的例子,我們可以看到,腐化除了來(lái)自開(kāi)發(fā)者低質(zhì)量的代碼,更核心的是來(lái)自于系統(tǒng)架構(gòu)的腐化。而在“中華田園敏捷開(kāi)發(fā)”的這種開(kāi)發(fā)方式下,需求本身就是零散的,目標(biāo)也是模糊的。在沒(méi)有全局視圖的情況下,架構(gòu)自然就是有局限的,只能適應(yīng)當(dāng)下。而隨著項(xiàng)目的發(fā)展,只能適應(yīng)當(dāng)下的架構(gòu)就會(huì)失效。


        如果意識(shí)不到這個(gè)問(wèn)題,后續(xù)在這種失效的架構(gòu)上進(jìn)行任何修修補(bǔ)補(bǔ)和魔改可能都會(huì)進(jìn)一步加劇它的腐化,導(dǎo)致代碼更難以看懂





        04

         總結(jié)

        由于 Essential Complexity 的存在,No Silver Bullet。為了快速響應(yīng)市場(chǎng)的“中國(guó)田園敏捷開(kāi)發(fā)”的開(kāi)發(fā)方式,帶來(lái)不可避免的代碼腐化。難道這就是程序員的黑暗森林嗎?


        其實(shí)程序員并不害怕 Essential Complexity。只要狀態(tài)好,日敲千行代碼不在話下。程序員最害怕的還是代碼腐化。很多設(shè)計(jì)上的決策和代碼為什么要這么寫(xiě),是內(nèi)隱的 ( Tacit Knowledge ) ,它只存在于最開(kāi)始的開(kāi)發(fā)者腦中,隨著那個(gè)人的遺忘或者離開(kāi),這些內(nèi)隱知識(shí)將永久丟失。所以通過(guò)文檔沉淀內(nèi)隱知識(shí)對(duì)于項(xiàng)目是非常重要的。道理各位開(kāi)發(fā)者都懂,但誰(shuí)又愿意費(fèi)勁寫(xiě)文檔呢?


        因此,代碼腐化+文檔缺失,會(huì)極大地增加后續(xù)的開(kāi)發(fā)者認(rèn)知負(fù)擔(dān),使得某些功能的流程難以辨認(rèn),不知道從何下手。應(yīng)對(duì)方法也很直接,要做的就是代碼防腐以及知識(shí)沉淀,但這些恰好又是很多人嫌麻煩又不愿做的地方畢竟人都是自私的,誰(shuí)愿意干前人栽樹(shù)后人乘涼的事呢,多堆點(diǎn)需求幫業(yè)務(wù)掙錢拿個(gè)五星去晉升不香嗎,我為啥要防腐為啥要寫(xiě)文檔?


        并且,做代碼防腐通過(guò)事前做一點(diǎn) EPC 是遠(yuǎn)遠(yuǎn)不夠的,它只能提升代碼質(zhì)量的一點(diǎn)點(diǎn)下限。結(jié)構(gòu)性的腐化,只能靠重構(gòu)。而重構(gòu)說(shuō)白了,就是事后諸葛亮。


        當(dāng)你擁有了更多的信息后再回過(guò)頭來(lái)看當(dāng)時(shí)設(shè)計(jì)的局限性,然后再來(lái)對(duì)之前的設(shè)計(jì)進(jìn)行歸納總結(jié),該分離的分離,該提取公因式的就提取公因式。根據(jù)近期的經(jīng)驗(yàn)再預(yù)測(cè)未來(lái)產(chǎn)品的發(fā)展方向,再去刻意設(shè)計(jì)一些靈活性。


        94fd19af610c039ccebeac6fa3d70026.webp 大神重構(gòu)代碼.gif


        但重構(gòu)的收益到底是什么?重構(gòu)完能帶來(lái)多少需求吞吐率的提升,能給出數(shù)據(jù)嗎?講不出收益,怎么和產(chǎn)品去 battle 和管理層要時(shí)間呢?


        代碼腐化就是技術(shù)債務(wù),但是債務(wù)不總是有害的。也許歷來(lái)每年貸款在北京、上海、深圳投資房產(chǎn)的人,甚至?xí)蠡诟軛U沒(méi)拉滿。所以技術(shù)債務(wù)也不是什么洪水猛獸,它甚至是時(shí)代的紅利。但是債務(wù)總要還。例如現(xiàn)在大家想還房貸都還不了還要排隊(duì)。 那到底什么時(shí)候適合償還技術(shù)債務(wù), 償還多少合適,具體怎么還呢?


        文檔總是過(guò)時(shí)的。寫(xiě)的信息量太少?zèng)]人看,想看的部分沒(méi)人寫(xiě),改了代碼還要同步改文檔容易忘記怎么辦?寫(xiě)文檔太費(fèi)事怎么辦? 如果你感興趣,歡迎留言評(píng)論,我們會(huì)繼續(xù)推出關(guān)于業(yè)務(wù)復(fù)雜性的下篇。


        以上是本次分享全部?jī)?nèi)容,歡迎大家在評(píng)論區(qū)分享交流。


        如果覺(jué)得內(nèi)容 有用,歡迎轉(zhuǎn)發(fā)~在公眾號(hào)后臺(tái)回復(fù)「銀彈」,領(lǐng)取Fred P. Brooks (IBM 大型機(jī)之父、圖靈獎(jiǎng)得主)所著 《沒(méi)有銀彈:軟件工程的本質(zhì)性與附屬性工作》。

        -End-

        原創(chuàng)作者|劉德恩

        技術(shù)責(zé)編|劉德恩


        bd5c4f6aab5d27e6a20db6787e5874a8.webp北漂7年程序猿回看工作選擇 升級(jí)加薪聊績(jī)效過(guò)程中,如果我覺(jué)得自己受到了老板“不公正”的對(duì)待,該如何“懟”回去?
        別癡迷輸入,輸出也很重要
        晉升失敗了,該如何自我安慰?

        瀏覽 35
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            操逼免费大片 | 啊啊啊啊高潮 | 看草逼视频 | 黄上黄在线观看 | 少妇做爱网站 | 国产丁香五月 | 涩涩视频网站在线观看 | 亚洲射 | 亚洲无码AV在线播放 | 黄色激情网站在线观看 |