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

来源: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
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...