系統漸漸淪為“屎山”,原因是..

来源:https://www.cnblogs.com/kdaddy/p/18079482
-Advertisement-
Play Games

相信有很多小伙伴都有小貓這樣的體會,尤其是接手一個老的系統的時候,總是會吐槽當前的系統很爛,恨不得馬上將其完完全全重構掉。 ...


分享是最有效的學習方式。

博客:https://blog.ktdaddy.com/

背景

小貓維護現有的系統也有一段時間了,踩坑也不少,事故不少。感興趣的小伙伴可以瞭解一下,往期的小貓踩坑記合集

這天,小貓找到了商城系統的第一任開發老A開始聊天。

“你們這系統是真坑,我都吃過好多次虧了,太爛了...”小貓開玩笑地吐槽道。

“我們當時其實還是花了很長的一段時間去做設計以及評審的,每一步都是有嚴格把控的,當時系統總體骨架還是相當清晰的,也沒有那麼多新的概念,你說現在系統不行了,可不能怪我們啊。一會我給你原始設計文檔看看。後面經過了很多研發的手了,甚至運營和產品都換了好幾撥了,每任運營可能對當前系統的要求都不一樣吧,大家理解可能都不同......”老A侃侃而言著。

小貓抿了口剛倒的茶,意味深長地看著老A...

寫在前面

相信有很多小伙伴都有小貓這樣的體會,尤其是接手一個老的系統的時候,總是會吐槽當前的系統很爛,恨不得馬上將其完完全全重構掉。

前段時間老貓還遇到一個比較逗的小伙伴,他想表達的意思大概是“代碼寫的爛也就算了,他居然還在註釋里撒謊...”,結果他樓下哥們還在一個勁追問他的註釋是怎麼撒謊的,老貓當時邊吃午飯邊在刷手機,老貓看到評論後,笑到噴飯,當然在此也對這位小伙伴表示同情。

其實很多時候一個系統的腐爛和破敗並不是在開始的時候就出現了,而是在持續地迭代升級中漸漸腐化繼而淪為“屎山”。

接下來咱們就來盤點一下到底是一些什麼原因將一個原本架構清晰的系統腐敗淪喪為複雜“屎山”的,然後咱們作為後來人又該如何應對?

“屎山”特征

既然說到系統淪為“屎山”,那麼什麼樣的系統會被定義成“屎山”呢?其實所謂的“屎山”即為非常複雜的系統,難以維護。John OusterhOut在A Philosophy of Software Design這本書中就已經提及了“複雜性就是使得軟體難於理解和修改的因素”。

John OusterhOut將“屎山”(這裡要說明一下,這哥們並沒有說複雜系統叫“屎山”,哈哈,只是咱們都習慣這麼叫了)歸為三大類:

  • Change Amplification(變更放大)
  • Cognitive Load(認知負荷)
  • Unknown Unknowns(未知的未知)

上述幾類特種總結有點抽象,咱們來具體化闡述一下。

變更放大

變更放大其實就是說明明一個相當簡單的需求,卻要動到很多地方的代碼。

相信大家在日常開發中應該也會遇到這樣的問題。老貓也經常遇到,例如明明從需求的理解來說,只要加一個固定的校驗邏輯,這個事情就應該可以搞定了,結果發現,改一個地方的校驗邏輯還不行,可能還要動到各個地方的校驗邏輯。這種情況往往是由於開發在寫代碼過程中復用沒有到位,或者本身流程問題導致的。軟體可拓展性變得很差。

認知負荷

認知負荷說白了就是相關的開發人員在進行開發任務的時候,需要花費很多時間去學習所需要的知識(當然這裡大部分指的是技術知識)才能完成一系列開發任務,於此同時,如果某個知識點沒有掌握好,可能會導致未知的Bug。

打個比方,大部分的開發還是比較傾向於自己熟悉的編程語言或者是開發框架,以及中間件的。例如,前後端分離雖然好,DDD雖然好,但是對於簡單的內部管理系統而言,明明一個mvc就能搞定的事情,非得搞成前後端分離,加上DDD設計分層。明明一個人天就能搞定的事情,非得搞成三人天,另外的維護者可能還得花時間去研究相關技術,這種盲目追求最新技術增加系統本身實現複雜度就是一種本末倒置的行為。

當然認知負荷有的時候可能也不一定是新技術帶來的,也有可能是純粹的技術實現爛,例如不恰當的介面設計、混亂的命名,還有“愛撒謊”的註釋等等。

未知的未知

未知的未知是最要命的,例如,當我們從產品那邊得到一個需求的時候,我們甚至不曉得為了完成這個需求我們到底需要修改哪些代碼才能完成,當前開發甚至還不清楚相關的業務知識。

這種很多時候體現在咱們接手一個新項目的時候,尤其是項目比較複雜的情況下,並且此時的項目沒有任何的技術文檔,這種情況下我們往往是抓瞎的,非常被動,即使對代碼調整完畢之後心裡也還是會沒底,涉及的一些業務場景甚至都沒有理清楚。也不曉得調整完畢之後會不會出現新的問題。

“屎山”誘因

從技術側聊聊,複雜系統發展的誘因,UML之父Grady Booch在《面向對象分析和設計》的觀點是,軟體的複雜性是一個固有屬性,並不是偶然屬性,軟體的發展必然會伴隨複雜性。

誘因有下麵三個:

  1. 模糊性:模糊性產生了最直接的複雜度。
    老貓的理解,關於模糊性包含其實有兩層,一個是需求模糊性,第二個技術模糊性。產品經理對實際的業務把控沒有做到很精準,存在模糊性,導致系統本身的業務覆蓋點經常發生變更,這種是導致系統複雜的罪魁禍首之一。第二技術側的模糊性,技術側的模糊性當然就包含研發人員本身對業務把控不到位,另外的在定義API以及方法命名變數命名的時候存在模糊,無法通過命名直接理解想要表達的意思。

  2. 依賴性:模糊產生複雜性,而依賴導致複雜的傳遞,不斷外移的複雜性將導致最終系統無限腐化,質量失控,修複成本指數級增長。打個比方一個不合理的實現方法被我們認為是一套標準的實現方式,然後後面的很多業務代碼為了方便都會去復用這段邏輯。但是這種不合理的實現方法還在不停的迭代,所以之後系統會發展成什麼樣子,大家可想而知了。

  3. 遞增性:一個軟體系統無論多麼複雜,都是從第一行代碼開始的。然後慢慢“生長”。隨著業務發展,需求不斷產生,功能逐漸豐富,軟體系統隨之演進,同時廢棄而未被及時清除的代碼也是日益膨脹。最終形成一個複雜的系統。
    這點相信理解起來還是比較簡單的。

系統“腐爛”的真相

就像上面小貓和老A的對話那樣,其實很多時候,系統的腐爛並並不是發生在最開始。

很多後端研發在接手新的系統之後,往往對其設計的理解其實是不夠深入的,來了需求之後就是一頓“兵來將擋水來土掩”,可以說是一種戰術性編程,或者說的難聽些“應付式編程”。

這種編程的特點有下麵這幾種:

  1. 快。這類程式員為了快速解決產品需求,總是以腐化系統為代價去解決問題。經過他們之手維護的系統可拓展性差。
  2. 高產。這類程式員代碼量極大,可能不擇手段,完全不會考慮復用,很多時候解決問題就是cv大法。
  3. 坑。他們往往只是專註於功能堆砌卻忽略設計原則和設計規範,有時候命名規範甚至都懶得遵循,成本放到未來,後人買單。咱們經常提到的倒霉的小貓就是經常買單的那位。

上述共同特點就是缺乏設計,完全聚焦於快速交付,註重短期價值不考慮未來發展。

那麼為什麼會這樣的呢?可能會受到以下三點的影響:

  1. 研發人員本身的水平以及認知還有責任心。研發人員本身認知不夠,意識不到系統其實是需要考慮拓展性的,這種往往也是沒有辦法的,另外一種是研發人員抱有僥幸心理,雖然意識到拓展性的問題以及設計問題,但是比較懶,本著“多一事不如少一事,反正我只是過客”的心態去做系統。這類往往在外包系統中體現更為放大。

  2. 互聯網背景下,老闆為了快速適應市場,會進行大量業務試錯,這就會要求程式員快速開發。很多程式員想要好好設計一下系統,可是無奈妥協於項目經理的一而再再而三的問你上線時間。這種情況下,設計可能就成了一種奢侈。

  3. 考評體系不合理。老貓有個朋友,之前一天他和我們吐槽,他們目前領導需要拉出他們每天寫代碼的量去看看他們每天干了多少活。這種真的是滑天下之大稽,在這樣的考評體系下麵,程式員還會好好寫代碼麽。當然這種往往是發生在領導屁都不懂研發的公司。這類領導也是老貓最最鄙視的。技術上明明屁都不懂,還要裝x去指指點點。

“屎山”應對之道

上面聊了這麼多,我們也大概知道了為什麼我們的系統會逐漸淪為“屎山”,可能是在軟體發展過程中的必然,其中也摻雜著各種人為因素以及非人為因素。
當然事情還是要去解決的。那麼我們應當如何應對呢?

  • 尋找合適的架構

    當咱們接到一個複雜系統的時候,其實首先需要理清楚相關的架構,知道系統是如何進行模塊拆分的,另外它們的協作關係和通信方式。具體操作,大家可以訪問老貓之前寫的系統梳理大法

  • 遵循設計原則
    組件層面,咱們的設計原則需要遵循復用/發佈等同原則,共同閉包原則,共同復用原則,無依賴環原則,穩定依賴原則和穩定抽象原則。
    代碼層面,可以參考老貓之前梳理的開發中需要遵循的設計原則

  • 避免破窗效應

    這裡的“破窗效應”其實是出自David Thomas Andrew Hunt的著作《程式員修煉之道》,一扇破窗,只要一段時間不去修理,建築中的居民就會潛移默化地產生一種被遺棄的感覺————當權者不關心這幢建築。然後其他窗戶也開始損壞,居民開始亂丟廢物,牆上開始亂塗鴉,建築開始出現嚴重結構性的損壞。

    聊到咱們軟體系統側其實也是一樣的,在系統發展的過程中,只有在我們修複歷史遺留的問題時,才是真正對其進行了維護。如果我們使用一些極端的手段保持古老陳腐的代碼繼續工作的時候,這其實是一種苟且。例如為了臨時解決問題寫hotfix介面等等。

    在我們開發的過程中,一旦系統有了設計缺陷,咱們其實應該及時優化,否則會形成不好的示範,更多的後來者會傾向於做出類似設計,從而加速系統腐化。

總結

上述就是老貓對系統淪為“屎山”的一些看法,另外的,希望大家比較再提“防禦性編碼”這類概念。這種思想就不應該是一個合格程式員提出的。老貓對這類還是比較抵觸的。“難不成螺絲釘以為自己螺紋角度獨特就不會被取代了?”,咱們把自己負責的東西儘量做到完美,是金子總能發光的,小伙伴們,你們覺得呢?

我是老貓,10year+資深研發,讓我們一起聊聊技術,聊聊職場,聊聊人生~ 更多精彩,歡迎關註公眾號“程式員老貓”。 個人博客:https://blog.ktdaddy.com/
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 基礎與面試題:viewpoint 與 rem、百分比高度、px 一、定義 ​ 一個表總結: 名稱 定義 使用示例 viewpoint 是指用戶在網頁上實際可見和可交互的區域,通常指的是瀏覽器視窗或移動設備的屏幕尺寸。 width:100vw;height:100vh rem (root em)是相對 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 一、HTTP HTTP (HyperText Transfer Protocol),即超文本運輸協議,是實現網路通信的一種規範 在電腦和網路世界有,存在不同的協議,如廣播協議、定址協議、路由協議等等...... 而HTTP是一個傳輸協議 ...
  • it-tools —— 一個為開發人員提供方便的線上工具集合的開源項目,包含了加密、轉換器、Web、開發、圖片和視頻等十幾種工具,功能齊全,部署方便。 ...
  • 所有主要的瀏覽器都內置了一個XML解析器,用於訪問和操作XML XML 解析器 在訪問XML文檔之前,必須將其載入到XML DOM對象中 所有現代瀏覽器都有一個內置的XML解析器,可以將文本轉換為XML DOM對象 解析文本字元串 以下示例將一個文本字元串解析為XML DOM對象,並使用JavaSc ...
  • 0x01 概述 (1)簡介 Tailwind CSS 官網:https://www.tailwindcss.cn/ Tailwind CSS 是一個 CSS 框架,使用初級“工具”類創建佈局 如 Bootstrap 等傳統 CSS 框架,其使用的類通常與組件直接相關;然而,Tailwind 則採用了 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 一、前言 與link類似 在VUE項目中應用typescript,我們需要引入一個庫vue-property-decorator, 其是基於vue-class-component庫而來,這個庫vue官方推出的一個支持使用class方式來開 ...
  • 目錄前言無法調用析構函數的原因改進方法內嵌回收類智能指針局部靜態變數參考文章 前言 在《單例模式學習》中提到了,在單例對象是通過new關鍵字動態分配在堆上的情況下,當程式退出時,不會通過C++的RAII機制自動調用其析構函數。本文討論一下這種現象的原因以及解決方法。 無法調用析構函數的原因 在DCL ...
  • 目錄前言餓漢式懶漢式懶漢式DCLP局部靜態式(Meyers' Singleton)單例模板參考文章 前言 單例模式,其核心目標是確保在程式運行的過程中,有且只有存在一個實例才能保證他們的邏輯正確性以及良好的效率。因此單例模式的實現思路就是確保一個類有且只有一個實例,並提供一個該實例的全局訪問點。 單 ...
一周排行
    -Advertisement-
    Play Games
  • GoF之工廠模式 @目錄GoF之工廠模式每博一文案1. 簡單說明“23種設計模式”1.2 介紹工廠模式的三種形態1.3 簡單工廠模式(靜態工廠模式)1.3.1 簡單工廠模式的優缺點:1.4 工廠方法模式1.4.1 工廠方法模式的優缺點:1.5 抽象工廠模式1.6 抽象工廠模式的優缺點:2. 總結:3 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 本章將和大家分享ES的數據同步方案和ES集群相關知識。廢話不多說,下麵我們直接進入主題。 一、ES數據同步 1、數據同步問題 Elasticsearch中的酒店數據來自於mysql資料庫,因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這個就是Elasticsearch與my ...
  • 引言 在我們之前的文章中介紹過使用Bogus生成模擬測試數據,今天來講解一下功能更加強大自動生成測試數據的工具的庫"AutoFixture"。 什麼是AutoFixture? AutoFixture 是一個針對 .NET 的開源庫,旨在最大程度地減少單元測試中的“安排(Arrange)”階段,以提高 ...
  • 經過前面幾個部分學習,相信學過的同學已經能夠掌握 .NET Emit 這種中間語言,並能使得它來編寫一些應用,以提高程式的性能。隨著 IL 指令篇的結束,本系列也已經接近尾聲,在這接近結束的最後,會提供幾個可供直接使用的示例,以供大伙分析或使用在項目中。 ...
  • 當從不同來源導入Excel數據時,可能存在重覆的記錄。為了確保數據的準確性,通常需要刪除這些重覆的行。手動查找並刪除可能會非常耗費時間,而通過編程腳本則可以實現在短時間內處理大量數據。本文將提供一個使用C# 快速查找並刪除Excel重覆項的免費解決方案。 以下是實現步驟: 1. 首先安裝免費.NET ...
  • C++ 異常處理 C++ 異常處理機制允許程式在運行時處理錯誤或意外情況。它提供了捕獲和處理錯誤的一種結構化方式,使程式更加健壯和可靠。 異常處理的基本概念: 異常: 程式在運行時發生的錯誤或意外情況。 拋出異常: 使用 throw 關鍵字將異常傳遞給調用堆棧。 捕獲異常: 使用 try-catch ...
  • 優秀且經驗豐富的Java開發人員的特征之一是對API的廣泛瞭解,包括JDK和第三方庫。 我花了很多時間來學習API,尤其是在閱讀了Effective Java 3rd Edition之後 ,Joshua Bloch建議在Java 3rd Edition中使用現有的API進行開發,而不是為常見的東西編 ...
  • 框架 · 使用laravel框架,原因:tp的框架路由和orm沒有laravel好用 · 使用強制路由,方便介面多時,分多版本,分文件夾等操作 介面 · 介面開發註意欄位類型,欄位是int,查詢成功失敗都要返回int(對接java等強類型語言方便) · 查詢介面用GET、其他用POST 代碼 · 所 ...
  • 正文 下午找企業的人去鎮上做貸後。 車上聽同事跟那個司機對罵,火星子都快出來了。司機跟那同事更熟一些,連我在內一共就三個人,同事那一手指桑罵槐給我都聽愣了。司機也是老社會人了,馬上聽出來了,為那個無辜的企業經辦人辯護,實際上是為自己辯護。 “這個事情你不能怪企業。”“但他們總不能讓銀行的人全權負責, ...