偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

如何不 Review 每一行代碼,同時(shí)保持代碼不被寫亂?

新聞 前端
本文的目標(biāo)是以盡可能濃縮的篇幅提供可模仿的步驟來(lái)達(dá)成“如何不 Review 每一行代碼,同時(shí)保持代碼不被寫亂”的目標(biāo)。

 [[404268]]

本文的讀者

  • 你為代碼總是被同事們寫亂了而苦惱,但是又無(wú)法 Review 每一行代碼

  • 你要開發(fā)一個(gè) SaaS,實(shí)現(xiàn)各種復(fù)雜功能的組合,但是又不能像互聯(lián)網(wǎng)公司一樣堆很多人來(lái)開發(fā)微服務(wù)

  • 你模仿過主流的微服務(wù),DDD 等做法,但并沒有達(dá)到理想的效果,不介意嘗試一些非主流的新辦法

本文的目標(biāo)是以盡可能濃縮的篇幅提供可模仿的步驟來(lái)達(dá)成“如何不 Review 每一行代碼,同時(shí)保持代碼不被寫亂”的目標(biāo)??偣踩?/p>

  • 第一步:不要拆分代碼倉(cāng)庫(kù),不要拆微服務(wù)。Monorepo is all you need,F(xiàn)eature Toggle is all you need。

  • 第二步:管控集成類需求的代碼審查:主板加插件。

  • 第三步:管控規(guī)范型需求的代碼審查:獨(dú)家收口。

第一步:不要拆分代碼倉(cāng)庫(kù),不要拆微服務(wù)

拆分微服務(wù)以及代碼倉(cāng)庫(kù)的缺點(diǎn)

  • 利用組織邊界來(lái)強(qiáng)化代碼的分工邊界會(huì)導(dǎo)致將來(lái)調(diào)整阻力很大。我們對(duì)于代碼應(yīng)該如何組織的認(rèn)識(shí)是隨著新需求不斷調(diào)整的。不要輕易動(dòng)用“組織架構(gòu)”這樣的核武器來(lái)達(dá)成小目標(biāo)。

  • 拆分了代碼倉(cāng)庫(kù)之后不利于在編譯期做集成,做集成后的整體驗(yàn)證。即便運(yùn)行時(shí)集成有萬(wàn)般好處,也沒有必要喪失掉編譯期集成的選項(xiàng)。

  • 跨代碼倉(cāng)庫(kù)的代碼閱讀,開發(fā)時(shí)的輔助和檢查都會(huì)變困難。

  • 微服務(wù)控制變更風(fēng)險(xiǎn)的灰度邊界是固化的,也就是微服務(wù)的大小。切得越細(xì),每次變更的東西就越少,風(fēng)險(xiǎn)就越小。這不夠靈活。

  • 微服務(wù)的彈性邊界是固化的,如果某種視頻編輯需要特別多的內(nèi)存,我們希望獨(dú)立伸縮,就得把這部分代碼切割出來(lái)變成一個(gè)獨(dú)立的微服務(wù)。

拆分微服務(wù)和代碼倉(cāng)庫(kù)相比單體架構(gòu),最重要的目標(biāo)是減少分支沖突,控制發(fā)布變更的風(fēng)險(xiǎn)。但是拆分微服務(wù)和代碼倉(cāng)庫(kù)并不是最佳的解決方案。Monorepo + Feature Toggle 是更好的解決方案。

  • Monorepo:所有的代碼都在一個(gè)倉(cāng)庫(kù)里。這樣就不存在不同模塊的倉(cāng)庫(kù)有不同的版本問題。大家都是統(tǒng)一的一個(gè)版本。升級(jí)線上系統(tǒng)的過程拆分成:部署+發(fā)布,兩個(gè)步驟。部署的時(shí)候,整個(gè) Monorepo 的代碼都部署到目標(biāo)機(jī)器上了,但并不代表發(fā)布了。

  • Feature Toggle:特性開關(guān)來(lái)精確控制哪些邏輯分支被發(fā)布出去。這樣部署就和發(fā)布解耦了。要灰度多少比例,可以精確控制。要一個(gè)特性開關(guān)的兩個(gè)版本的邏輯分支共存,也可以實(shí)現(xiàn)。

使用 Monorepo + Feature Toggle 可以提供所有拆分微服務(wù)達(dá)成的目標(biāo),同時(shí)克服以上微服務(wù)拆分帶來(lái)的缺點(diǎn)

  • 通過目錄結(jié)構(gòu)來(lái)控制代碼所有權(quán)。你可以要求這個(gè)目錄下的代碼必須經(jīng)過你的 Code Review。調(diào)整目錄結(jié)構(gòu)比調(diào)整代碼倉(cāng)庫(kù)容易得多,比調(diào)整組織架構(gòu)要容易得多。

  • 可以保持編譯期集成這個(gè)選項(xiàng)。

  • 可以更容易實(shí)現(xiàn)開發(fā)時(shí)輔助和檢查工具,可以很方便地閱讀跨模塊的代碼

  • 變更風(fēng)險(xiǎn)更小,不僅僅開關(guān)回滾很快,而且開關(guān)可以靈活地定向灰度,而且一個(gè)開關(guān)的控制范圍大小也可大可小,粒度非常靈活。

  • 彈性邊界更靈活,不需要因?yàn)橐?dú)立擴(kuò)縮容,就得把代碼切分出去

經(jīng)常聽說(shuō)的一個(gè)說(shuō)法是最終是要拆分成微服務(wù),多倉(cāng)庫(kù)的。單體應(yīng)用單倉(cāng)庫(kù)只是一個(gè)過度形態(tài)。這會(huì)導(dǎo)致我們認(rèn)為為啥 不 一步到位呢。 但事實(shí)并非如此,微服務(wù)和多倉(cāng)庫(kù)并不一定適合所有人。 你可以用 Monorepo + Feature Toggle 用一輩子。

具體如何實(shí)踐 Monorepo + Feature Toggle 按照 https://www.branchbyabstraction.com/ 和 https://trunkbaseddevelopment.com/ 的指導(dǎo)去做就可以了。

第二步:管控集成類需求的代碼審查

當(dāng)我們把代碼都放一個(gè)代碼倉(cāng)庫(kù)里之后,立即要面臨的問題是代碼不會(huì)寫亂么?你怎么控制什么代碼寫在哪里?每一行代碼寫之前都來(lái)問你,每一行代碼寫完了都需要你來(lái) Review 么?

所以,我們需要一種強(qiáng)制檢查代碼寫在了正確的位置的自動(dòng)化機(jī)制。這個(gè)機(jī)制就叫“依賴管理”。對(duì)應(yīng)常見的編程語(yǔ)言

  • 如果是 TypeScript,這個(gè)叫 package.json

  • 如果是 Golang,這個(gè)叫 go.mod

  • 如果是 Java,這個(gè)叫 POM.xml

當(dāng)我們把代碼拆分成多個(gè)包(或者叫模塊),并使得這些包(模塊)形成特定的依賴關(guān)系,就可以通過編譯器檢查控制什么代碼必須寫在什么地方,從而不需要靠人去檢查。這個(gè)依賴關(guān)系如下圖所示

  • 插件:盡可能完整的實(shí)現(xiàn)一個(gè)獨(dú)立的功能,比如面向最終用戶的完整的頁(yè)面

  • 主板:當(dāng)插件與插件之間有功能上的集成需要的時(shí)候,通過繞路主板來(lái)實(shí)現(xiàn),而不能直接在插件和插件之間有引用關(guān)系

這樣做的好處是可以減少 Review 的負(fù)擔(dān)。不需要盯著每一行代碼了,只需要重點(diǎn)盯著主板的修改就可以了。實(shí)現(xiàn)的步驟是

  • 先決定每個(gè)插件里封裝什么的數(shù)據(jù)庫(kù)表。如果是前端模塊,則是封裝什么后端的數(shù)據(jù)接口

  • 因?yàn)椴寮荒芤貌寮詫?duì)應(yīng)的頁(yè)面和功能就會(huì)自然選擇有這些數(shù)據(jù)庫(kù)表的插件里來(lái)寫。因?yàn)閷懺谄渌寮锏脑捑驮L問不到了

  • 然后對(duì)于需要來(lái)自多個(gè)插件數(shù)據(jù)才能實(shí)現(xiàn)的功能,我們通過主板來(lái)實(shí)現(xiàn)

比如說(shuō)我們決定有一個(gè)團(tuán)購(gòu)插件,有一張表 GroupPurchaseCampaign 記錄了團(tuán)購(gòu)活動(dòng)的參與商品和規(guī)則。那么要展示團(tuán)購(gòu)活動(dòng)列表的時(shí)候,就會(huì)自然有限選擇在團(tuán)購(gòu)插件里來(lái)寫,因?yàn)檫@個(gè)插件里可以訪問這張表。這里說(shuō)的“訪問”是指可以 import GroupPurchaseCampaign 這個(gè)類型的意思。插件不能 import 另外另外一個(gè)插件定義的類型,但是不意味著運(yùn)行時(shí)不能訪問別的插件的數(shù)據(jù)。運(yùn)行時(shí)的數(shù)據(jù)都是通的。限制的是編譯期,誰(shuí)可以 import 誰(shuí)。

當(dāng)需要主板進(jìn)來(lái)實(shí)現(xiàn)”集成類需求”的時(shí)候,應(yīng)該如何做。分為以下三類

  • 一個(gè)界面需要同時(shí)展示來(lái)自兩個(gè)插件的數(shù)據(jù)。例如商品詳情頁(yè),需要常規(guī)商品數(shù)據(jù),需要當(dāng)前的券活動(dòng),需要當(dāng)前的限時(shí)折扣活動(dòng)等。在主板里把界面分成多個(gè)槽,然后不同的槽由不同的插件來(lái)實(shí)現(xiàn)。

  • 一個(gè)操作需要多個(gè)插件的數(shù)據(jù)進(jìn)行綜合決策判斷。例如計(jì)算價(jià)格的函數(shù),需要綜合商品的原價(jià),需要取得購(gòu)物車選擇券,需要判斷是否滿減等。在主板里把價(jià)格的計(jì)算流程里留出槽,然后不同的槽由不同的插件來(lái)實(shí)現(xiàn)。

  • 一個(gè)插件的界面里需要展示來(lái)自其他插件的數(shù)據(jù)。例如退款申請(qǐng)界面,需要展示商品圖片等。這個(gè)不同之處在于整個(gè)頁(yè)面絕大部分都是由一個(gè)插件自己實(shí)現(xiàn)的,只是在局部的地方需要其他插件的數(shù)據(jù)。所以就不值得把整個(gè)頁(yè)面都下沉到主板里去寫。實(shí)現(xiàn)方法是在主板里聲明一個(gè)ProductCard組件,然后這個(gè)組件由常規(guī)商品插件實(shí)現(xiàn),再由退貨插件來(lái)使用用。

主板起到的作用和 C 編程里的“頭文件”的作用是一樣的,就是給模塊之間相互調(diào)用提供聲明。主板的代碼要盡可能的少,絕對(duì)不要在主板里提供 CRUD 的裸數(shù)據(jù)接口,主板里定義的是界面的槽,流程的槽,而不是直接把數(shù)據(jù)庫(kù)的原始數(shù)據(jù)暴露出去。

技術(shù)上如何實(shí)現(xiàn):在一個(gè)包里提供聲明, 在 另外一個(gè)包里寫實(shí)現(xiàn)。 這個(gè)有兩類做法:

  • 通過運(yùn)行時(shí)多態(tài)來(lái)實(shí)現(xiàn)。在主板里定義 interface,在插件里寫實(shí)現(xiàn) interface 的類的或者函數(shù)。然后在啟動(dòng)的時(shí)候,做一次 “AutoWire” 的綁定操作。這個(gè)綁定最簡(jiǎn)單的方式可以是對(duì)類型為函數(shù)指針的全局變量做一下賦值操作。也可以由 Spring 這樣的依賴注入框架來(lái)做 AutoWire。

  • 通過編譯期做源代碼的復(fù)制粘貼。需要在編譯之前先對(duì)源文件做一下處理,然后再喂給編譯器。

無(wú)論是哪種具體實(shí)現(xiàn)技術(shù),都不要實(shí)現(xiàn)成如下圖所示這樣

在插件之上 不應(yīng)該有 一個(gè)額外的包(模塊)包含業(yè)務(wù)邏輯了。插件對(duì)主板的插入應(yīng)該是一個(gè) AutoWire,純機(jī)械不含業(yè)務(wù)的過程。業(yè)務(wù)編排這樣的概念一定不要出現(xiàn)在依賴關(guān)系的最頂層。我們已經(jīng)在最底下的主板實(shí)現(xiàn)了所謂的“業(yè)務(wù)編排”了。

SaaS 可以把自己的功能拆解到多個(gè)插件來(lái)實(shí)現(xiàn)。但是經(jīng)常有“按需”組裝,或者付費(fèi)購(gòu)買的需求。我們并不需要?jiǎng)討B(tài)來(lái)組裝代碼來(lái)獲得“按需”組裝的產(chǎn)品效果。代碼可以是一份,只是通過運(yùn)行時(shí)的開關(guān)來(lái)控制某些插件是否啟用。這些開關(guān)可以是配置文件,也可以是數(shù)據(jù)庫(kù)表來(lái)控制。在沒有啟用的時(shí)候,界面上完全隱藏相關(guān)的組件(就是 if/else 判斷),用戶也察覺不到這個(gè)功能的存在。付費(fèi)購(gòu)買其實(shí)就是付費(fèi)買這個(gè)開關(guān),也不需要像 Apple Store 那樣真的去做什么代碼下載和安裝。當(dāng)然給外包公司做二次開發(fā)就是完全另外一個(gè)話題了,與本主題無(wú)關(guān)。是否打開某個(gè)插件可以是全局性的(給每個(gè)商家或者租戶啟用),也可以是“訂單”級(jí)別。一個(gè)所謂的訂單履約流程,需要組合多個(gè)插件的功能。對(duì)于每個(gè)訂單來(lái)說(shuō),都有一堆 bit 開關(guān)來(lái)決定某個(gè)插件是否啟用了,以及對(duì)應(yīng)的業(yè)務(wù)數(shù)據(jù)是什么。比如 GroupPurchase + OrderSelfPickup + Order 可以組合成團(tuán)購(gòu)自提的訂單。訂單在此處只是一個(gè)例子,不同類型的業(yè)務(wù)有自己的領(lǐng)域概念。

第三步:管控規(guī)范型需求的代碼審查

有了主板加插件,Monorepo 已經(jīng)切分出了多個(gè)子目錄了。每個(gè)開發(fā)者也基本上能夠知道什么需求寫在什么目錄下,哪些目錄是自己經(jīng)常修改的。接下來(lái)的問題是,如果每個(gè)開發(fā)者都各寫各的,那他們之間有重復(fù)實(shí)現(xiàn)怎么辦?誰(shuí)來(lái)避免同一個(gè)東西,被不同產(chǎn)品經(jīng)理提出多遍,再由不同的開發(fā)者用不同的姿勢(shì)實(shí)現(xiàn)多變,導(dǎo)致浪費(fèi)和返工?這個(gè)也是一個(gè) Code Review 的問題。不能指望有一個(gè)人來(lái) Review 每一行代碼。

解決辦法就是我們希望有一個(gè)人來(lái)“收口”,然后由這個(gè)人來(lái)保證收口之后的代碼沒有重復(fù)的實(shí)現(xiàn),建立合理的抽象。如下圖所示

所謂“收口”,就是要阻止上圖中這樣的繞過“這層抽象”,去訪問“底層API”的行為。比如說(shuō),所有的編程語(yǔ)言都提供了 Http 調(diào)用的能力。但是我們希望封裝一個(gè) Http Restful API 的調(diào)用 SDK。在這個(gè) SDK 里我們統(tǒng)一實(shí)現(xiàn)重試,統(tǒng)一實(shí)現(xiàn)熔斷摘除故障節(jié)點(diǎn)這樣的一些功能。避免每個(gè)調(diào)用 Restful 接口的地方都重復(fù)地 try catch,重復(fù)地寫不一致地重試邏輯。那就需要有一個(gè)人來(lái)封裝這樣的庫(kù),同時(shí)強(qiáng)制所有“應(yīng)該使用這個(gè)庫(kù)的地方”都使用了這個(gè)庫(kù)。

實(shí)現(xiàn)方案要比管控集成類需求要稍微麻煩一些。集成類需求可以用包之間的依賴關(guān)系來(lái)約束什么代碼寫在哪里,規(guī)范型需求的問題是假設(shè)一個(gè)業(yè)務(wù)包,比如團(tuán)購(gòu)。它依賴了 Http Restful SDK,而 Http Restful SDK 又依賴了 Http 的庫(kù)。那么就意味著團(tuán)購(gòu)這個(gè)包通過依賴的傳遞性,也依賴了 Http 的庫(kù)。在現(xiàn)有的編程語(yǔ)言里,都無(wú)法禁止團(tuán)購(gòu)的包通過傳遞性依賴獲得的調(diào)用 Http 庫(kù)的權(quán)利。這個(gè)時(shí)候我們就需要通過自制 lint 工具的方式,在編譯期額外做更嚴(yán)格的依賴關(guān)系檢查。通過 lint 檢查,強(qiáng)迫所有訪問 Http 庫(kù)的代碼都“收口”到某個(gè)目錄里。然后我們就可以通過 Review 這個(gè)目錄的改動(dòng),確保重試邏輯只寫了一份,而不是散落到各個(gè)地方。

這樣的 lint 規(guī)則可以檢查以下類型的訪問

  • 對(duì)某個(gè) API 是否可以調(diào)用:比如 Http 庫(kù)的 API

  • 對(duì)某個(gè)自定義類型是否可以調(diào)用其指定的方法:比如 Datetime 類型,或者業(yè)務(wù)上自己封裝一個(gè) Money 類型

  • 對(duì)某個(gè) API 的某幾個(gè)參數(shù)是否可以傳值:比如組件庫(kù)中的 Button 組件提供了 style 屬性,我們不希望把這么靈活的屬性暴露出去

  • 對(duì)語(yǔ)言和框架某些特性是否可以使用:比如 vue 文件中可以寫 style,但是我們不希望所有的目錄都可以寫 style

再舉一個(gè)例子。通過 lint 檢查,我們可以確保所有包含樣式的前端組件都寫在某個(gè)目錄下,比如說(shuō) RegularUi 和 SpecialUi。其他目錄中的組件,只能是通過組裝 RegularUi 和 SpecialUi 目錄中的組件來(lái)完成自己的設(shè)計(jì)稿還原。當(dāng)然這樣就是一種“收口”。我們可以通過 Review 對(duì) RegularUi 和 SpecialUi 這兩個(gè)目錄下文件的修改,來(lái)發(fā)現(xiàn)是不是有兩個(gè)開發(fā)者在嘗試實(shí)現(xiàn)極度類似的頁(yè)面組件,也可以促成兩個(gè)產(chǎn)品經(jīng)理互相交流一下,是不是把兩個(gè)組件合并成一樣的行為,避免不必要的實(shí)現(xiàn)成本。

“收口”的代價(jià)是不可避免會(huì)出現(xiàn)很多一次性的需求,個(gè)性化的需求。比如優(yōu)惠券的界面就是要和其他界面不一樣。因?yàn)閷?duì)樣式做了收口,所以就不能直接寫在優(yōu)惠券這個(gè)包里面。于是就有了 SpecialUi 這個(gè)目錄,用于寫被收口了,但是并不可復(fù)用的東西。SpecialUi 里的組件數(shù)量的多寡,體現(xiàn)了 Ui 不一致性的嚴(yán)重程度。如果每個(gè)頁(yè)面都不一樣,都非常有藝術(shù)感。那說(shuō)明這樣的產(chǎn)品并不適合對(duì)樣式進(jìn)行收口,就應(yīng)該各寫各的,每個(gè)頁(yè)面都純手工打造。

“收口”的 lint 檢查的關(guān)鍵是要去掉對(duì)人的主觀判斷的依賴。我們不需要判斷這里來(lái)是不是一定能復(fù)用 RegularUi。我們寧愿過度收口,導(dǎo)致 SpecialUi 的出現(xiàn),也要避免人為主觀判斷的介入。這種“過度的”收口,是規(guī)范型需求能實(shí)現(xiàn)自動(dòng)化檢查的關(guān)鍵。一旦我們?cè)试S酌情出現(xiàn)一些例外的情況下,那么又變成了需要 Review 每一行代碼了。

“收口”之后的一個(gè)風(fēng)險(xiǎn)是強(qiáng)行抽象。明明不適合復(fù)用同一個(gè)組件的場(chǎng)合,仍然復(fù)用了同一個(gè)組件。導(dǎo)致組件變得更復(fù)雜,導(dǎo)致組件經(jīng)常被修改。一個(gè)對(duì)策是控制組件或者函數(shù)的參數(shù)個(gè)數(shù),參數(shù)應(yīng)該盡可能地少。如果某個(gè)函數(shù)在 Monorepo 中有10處調(diào)用,但是其名為 IsVipUserPriviliged 參數(shù)僅僅在 1 處調(diào)用有傳值。那么這個(gè) IsVipUserPriviliged 參數(shù)大概率是不應(yīng)該被添加進(jìn)來(lái)的,是強(qiáng)行抽象的產(chǎn)物。對(duì)于 IsVipUserPriviliged 的處理,更適合直接寫在調(diào)用的地方,而不是被寫到可復(fù)用的目錄里。

收益

在這三步都完成之后,你獲得了一個(gè)“機(jī)器人”。它幫你在每個(gè)開發(fā)者提交代碼的時(shí)候檢查代碼是不是寫到了正確的位置。 在 通過了這個(gè)機(jī)器人檢查的基礎(chǔ)上,你只需要關(guān)注重點(diǎn)的一些目錄就可以了,對(duì)其他的修改僅僅需要抽查。 這個(gè)機(jī)器人能夠像拆分了微服務(wù)一樣,確保代碼不寫亂。 同時(shí)不像微服務(wù)那樣,拆分之后就很難調(diào)整了。 因?yàn)榇a仍然一個(gè)倉(cāng)庫(kù)里,只是分了目錄,隨時(shí)都可以再調(diào)整。

 

責(zé)任編輯:張燕妮 來(lái)源: taowen
相關(guān)推薦

2016-12-02 08:53:18

Python一行代碼

2020-09-09 16:00:22

Linux進(jìn)程

2022-04-10 23:43:11

代碼發(fā)送郵件后端

2014-02-12 13:43:50

代碼并行任務(wù)

2022-04-09 09:11:33

Python

2017-04-05 11:10:23

Javascript代碼前端

2023-09-12 10:10:57

開發(fā)者工具開源

2021-10-19 17:52:16

Git命令算數(shù)

2020-08-19 10:30:25

代碼Python多線程

2021-11-02 16:25:41

Python代碼技巧

2021-08-31 09:49:37

CPU執(zhí)行語(yǔ)言

2017-04-13 19:20:18

Python代碼并行任務(wù)

2020-07-20 09:20:48

代碼geventPython

2020-10-23 09:35:41

開源 Java 代碼

2025-06-13 08:35:00

前向聲明C++代碼

2022-04-11 11:38:44

Python代碼游戲

2020-09-28 12:34:38

Python代碼開發(fā)

2019-12-25 14:08:50

Pandas數(shù)據(jù)計(jì)算

2020-08-12 14:54:00

Python代碼開發(fā)

2025-04-28 09:06:00

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)