1. 程式人生 > >10種軟件開發中 over-engineering 的錯誤套路

10種軟件開發中 over-engineering 的錯誤套路

ica couchdb 吃飯 del 開源庫 gis 演進 做出 20px

別把「不要過度使用 Generic」誤解成「不用 Generic」。也別把「不要寫一些不必要的 Wrapper」誤解成「不寫不論什麽 Wrapper」。我僅僅是在講 over-engineering 這個事。僅僅是在提倡不搞野路子編程。


套路1:攻城獅自覺得比業務人員聰明

攻城獅認為自己最聰明,由於東西是他們寫出來的嘛!

然而這常常就是 over-engineering 的根源。

即使我們考慮好100件事情。還是會有第101件我們沒想到的事情冒出來。

就算我們搞定了1000個問題,還是會帶出10000個新問題。我們認為一切盡在掌握,可是事實是要打臉的。

在我15年的 coding 生涯裏,我還從來沒見著哪塊業務光用需求就能概括清楚了。業務總是會變得五花八門。這不是業務人員的錯,這是業務的天性使然。

長話短說:別和業務爭。他們是贏定了的

溫馨提示:假設你時間不多。這篇文章 Get 到上面這點就夠了

套路2:過度復用業務邏輯

業務需求越來越多時(早就料到有這麽一天),我們的反應是:

技術分享圖片

我們挖空心思搞抽象,搞復用,結果搞出一堆 Fat Models Fat Controllers。

然並卵,業務需求從來不知足,僅僅會越來越復雜,這個套路 hold 不住。
所以。應該反其道而行之:

技術分享圖片

在合理的系統中。共享邏輯會隨著時間沈澱下來。即使外部功能變多,也依舊保持「身材」。

假設你看到的情況與此相反,恭喜你,你手上的系統早晚難逃被所有重寫的命運。由於實在難以做局部的改進了。


在水平劃分業務之前。最好先考慮垂直劃分。比方不相幹的服務,流程相關的邏輯,語言相關的模塊。等等。這樣一來改個局部的地方就比較easy,不用想太多。

長話短說:不要搞組合共享,要隔離

溫馨提示:去代碼庫裏挑一個和外部系統(Endpoint/Page/Job/etc)打交到的 Action 來看看,算一算這段代碼的運行過程中須要切換多少個 Context?

套路3:不論什麽東西都要 Generic

(本套路有時候是與套路2一起發作的。有時也單獨發作)

  • 連接數據庫?寫一個 Generic Adapter
  • 查詢數據庫?Generic Query
  • 傳參數?Generic Params
  • 構建參數?Generic Builders
  • Map the Response?Generic Data Mapper
  • 處理用戶請求?Generic Request
  • 幹全部的雜活?Generic Executor
  • 等等等等

有時候攻城獅就是管不住自己的欲望:不先去解決詳細的業務問題。而是要浪費時間搞一個完美的抽象。
設計僅僅能是追著真實生活的需求跑,所以哪天即時我們吃了狗屎運搞出個完美的設計。完畢的時間也就是過期的時間——業務變了。最吼的設計就是易於重構的設計。

這篇文章提倡的是大家應該寫刪了不心疼的代碼,而不是老是想著被擴展的代碼

長話短說:錯誤的抽象比反復還要糟糕

假設要做合理的抽象,適度的反復是必須的,由於僅僅有我們從足夠多的反復中總結的抽象才是好抽象(別一開始沒多少反復就著急搞抽象)。抽象的質量。往往是系統裏最薄弱的一環。


正經提示:在微服務之間搞抽象簡直是災難,會導致分布式巨無霸怪胎產生

套路4:影子封裝器(Wrapper)

曾經用到外部庫時,總要包裝包裝,但往往僅僅是影子封裝法。我們也就僅僅在實現功能和打包第三方庫之間玩玩小把戲。

所以呢,我們所謂的封裝事實上是和底層實現綁得死死的(有時候僅僅是1:1的搬功能,有時候甚至花10倍功夫來包裝,而僅僅實現了底層庫1/10的功能)。假設以後我們把底層庫換了,包裝器就得跟著改。有時候還在包裝器裏帶點業務邏輯,搞成個四不像,最後成了個懵逼膠水層。


如今是2016年了,醒一醒吧!第三方庫都已經做得又快又好了。寫這些庫的人都非常聰明:質量好、測試好、有錢有閑寫得好。用這些庫的時候。就僅僅管依照 Initialize-Instrument-Implement 的模式來用即可了。

長話短說:寫 Wrapper 屬於意外事件,不要當作常態。不要為了包裝而包裝

溫馨提示:不是開玩笑。有人就是想搞能「海納百川」的 Wrapper,理由就是,能夠隨時更換實現庫啊!這是一種病,參照後文中的「可X性」套路

套路5:用工具思維對待質量問題

盲目地套用代碼質量概念。並不能實際提高代碼質量。比方:把全部變量都寫成 private final。給全部的服務類都加一個 interface,等等。


看下 Hello World 企業版 或者 FizzBuzz企業版 ,代碼寫得巨多,且搞笑。

細看每一個類都嚴格遵守實現準則了,設計模式也用得蠻多(工廠,構建器,策略,等等),編碼技巧上也下了苦功夫(泛型,枚舉等等)。拿工具一檢測,代碼質量給滿分。
可是,從大處一看。這貨也就打印一句廢話。

長話短說:別迷戀細節,要從大局出發

自己主動化代碼質量檢測工具能幫我們盯覆蓋率這些東西,可是分辨不出測試是不是測了須要測的東西。

性能檢測工具能測性能。也不知道程序是並行還是串行跑出來的。僅僅有人才把握得了大局。
否則。就會催生出千層餅現象:把代碼邏輯切割到10到20層面餅裏面,但每一層對全局的了解都是懵逼狀態。

為什麽會醬紙?就是由於我們單純追求代碼可測試性,或者單一職責原則這些東西,忽視了大局觀。


在曾經。大家喜歡搞繼承。A繼承B繼承C繼承D等等。

技術分享圖片

在如今。大家還是喜歡搞這一套,僅僅只是加上了配套的接口和實現。

每一層都有接口和實現去調下一層,由於這非常規範嘛。

技術分享圖片

有一些非常好的原則。就是用來對付繼承和其它OOP濫用的現象的。非常多攻城獅不知道這些原則的來源。僅僅管照本宣科。是有問題的。

長話短說:同樣的概念在不同的領域有不同的含義。不要見得風就是雨。拿著錘子眼裏僅僅有釘子

在別的領域。要學會說人話。好的開發人員要靠這個混。新瓶裝老酒是行不通的。永遠不要以套用概念的名義把設計搞亂。

套路6:Adopter 狂熱癥

無處不在的泛型。

這個世道上。HelloWorldPrinter 都要寫成 HelloWorldPrinter< String, Writer >

假設問題明顯能用普通的方法、專門的數據類型來解決。就不要亂搞泛型。

無處不在的策略模式。

連 if 都想用策略模式來做。

腦子有毛病?

無處不在的 DSL。想用 DSL重寫全部東西。

不知所雲……

喜歡用 Mock。測試時不論什麽東西都想 mock 一下。

假設玩砸了……

元編程好屌,什麽地方都搞一搞。

能講講理由麽!

枚舉好屌,擴展方法好屌,Traits好屌,好屌好屌。用起來!

騷年,這是不正確的!

長話短說:不要什麽地方都長話短說

套路7:癡迷「可…性」

  • 可配置性
  • 可伸縮性
  • 可維護性
  • 可擴展性
  • ……

瞎搞。不可理喻。

頑冥不化。

栗子1:來做個可擴展性屌屌的 CMS 吧!

業務人員能夠隨心所欲地給實體加字段。


結果:業務人員從來不用。假設非用不可,他們會抓一個碼農來幫他們用。本來就想要幾個經常使用字段。快點出結果。結果你給我搞一個狂拽酷炫然並卵的界面出來。

栗子2:設計一個可配置性極好的數據庫。

改個文件就能自由切換底層數據庫哦!
結果:10年以後。我僅僅見過1家公司動真格去換底層數據庫。到了真換的時候,那種所謂的配置隔離文件起不了什麽作用。有太多運維工作要做了,不兼容性、功能不匹配,等等。等到我們的客戶要求我們光換個配置文件就把半數表格換到 NoSQL 去,我們就瘋了:配置文件僅僅是千千萬萬要修改的地方之中的一個,客戶你玩兒我呢?
今天根本沒有辦法光憑一個配置層就能把傳統數據庫和新的 文檔/KV 數據庫(Redis CouchDB DynamoDB)統一起來。甚至不同的傳統關系數據庫之間也不行(MySQL Postgres SQLite)(這一段我不想翻譯了,我認為這個是個常識)

技術分享圖片

長話短說:不要對「可…性」的癡迷坐視無論。要清晰地定義問題場景/用例/需求/解法

套路8:不務正業的 「玩具小發明」

從頭開始做東西總是讓人感覺良好。可是這些東西非常快就沒人用了。

幾個栗子:

  • 玩具程序庫(HTTP,迷你 ORM/ODM,緩存,配置,等等)
  • 玩具框架(CMS,事件流,並發。後臺任務,等等)
  • 玩具工具(構建、部署工具,等等)

不要忘了:

  • 熟悉問題域不是一件輕松的事。要花大量的精力和技巧。

    一個成功的 Service Runner 庫是建立在對 daemon work、進程管理和I/O等等的專業知識深入了解的基礎上的。一個 CMS 也不是顯示幾個字段那麽簡單,這些字段有內在的聯系,還要校驗、向導、適配顯示等問題要解決。

    一個看起來非常easy、就僅僅做 「重試」這麽一件事的程序庫,其實也不是那麽easy做好的。

  • 維護這些個玩具庫須要持續花費時間和精力。再小的開源庫要維持運轉也是一個easy的事情。
  • 就算把這些東西開源了,除了你自己和那些要靠它們吃飯的人。不會再有別人去看的。

  • 那幫人開源了這些東西,心愜意足地在簡歷裏寫上「XXX創始人」之後。一般就會慢慢撒手無論了。

  • 維護當前的東西須要在當下花時間。可是創造新東西是在透支未來的時間。(譯者註:向未來的人借時間債)

長話短說:復用已有的!復制優秀的!

學會貢獻!學會三思!

最後,假設真要不得不去做一些超前的事。要帶著創業心態去做。要和現有的同行競爭。要爭取內部同事的信任和支持。不要認為都是自己人就無所作為,把一切看作理所當然了。

套路9:安於現狀

假設有些東西一開始以某種方式去實現了。那麽全部人會默認依照這樣的思路接著做下去。

沒有人會去質疑一下原因。能幹活的代碼就是「好代碼」。即使實現的過程有些迫不得已,大家也會繞著彎去適應現狀。

一個健康的系統應該是持續演進的。不健康的系統特色就是一直在修修補補。假設一塊代碼非常久都沒人提交了,說明這塊代碼已經開始變臭了。

系統的每一部分都要與時俱進。

這篇文章寫得非常好:不要把重構任務一直放在 backlog 裏!

一個團隊每天實際叠代的狀況。和他們理想中的叠代方式對照:

技術分享圖片

長話短說:重構是每天日常工作的一部分。沒有什麽代碼是不能改的。

套路10:胡亂預計

真心的,常常會見到素養還不錯的攻城獅/團隊卻寫出了一坨屎。

看到他們寫的代碼,我們會想。臥槽。這真是那個號稱非常牛逼的人做出來的麽?!

質量不光靠技巧,還要靠時間來湊。

越聰明人的人,越easy自信滿滿導致預計失控。

(WTF?)最後呢。僅僅好靠一堆 hack 技巧和自殺性工作時長來把事情了結。

長話短說:還沒寫代碼之前,錯誤的預計就能毀了project質量

假設你能耐心看到這裏,我謹在此表示由衷的感謝!還是別忘了,我僅僅是在講 over-engineering 這個事,僅僅是在提倡不搞野路子編程。


10種軟件開發中 over-engineering 的錯誤套路