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

来源: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
  • 隨著Aspire發佈preview5的發佈,Microsoft.Extensions.ServiceDiscovery隨之更新, 服務註冊發現這個屬於老掉牙的話題解決什麼問題就不贅述了,這裡主要講講Microsoft.Extensions.ServiceDiscovery(preview5)以及如何 ...
  • 概述:通過使用`SemaphoreSlim`,可以簡單而有效地限制非同步HTTP請求的併發量,確保在任何給定時間內不超過20個網頁同時下載。`ParallelOptions`不適用於非同步操作,但可考慮使用`Parallel.ForEach`,儘管在非同步場景中謹慎使用。 對於併發非同步 I/O 操作的數量 ...
  • 1.Linux上安裝Docken 伺服器系統版本以及內核版本:cat /etc/redhat-release 查看伺服器內核版本:uname -r 安裝依賴包:yum install -y yum-utils device-mapper-persistent-data lvm2 設置阿裡雲鏡像源:y ...
  • 概述:WPF界面綁定和渲染大量數據可能導致性能問題。通過啟用UI虛擬化、非同步載入和數據分頁,可以有效提高界面響應性能。以下是簡單示例演示這些優化方法。 在WPF中,當你嘗試綁定和渲染大量的數據項時,性能問題可能出現。以下是一些可能導致性能慢的原因以及優化方法: UI 虛擬化: WPF提供了虛擬化技術 ...
  • 引言 上一章節介紹了 TDD 的三大法則,今天我們講一下在單元測試中模擬對象的使用。 Fake Fake - Fake 是一個通用術語,可用於描述 stub或 mock 對象。 它是 stub 還是 mock 取決於使用它的上下文。 也就是說,Fake 可以是 stub 或 mock Mock - ...
  • 為.net6在CentOS7上面做準備,先在vmware虛擬機安裝CentOS 7.9 新建CentOS764位的系統 因為CentOS8不更新了,所以安裝7;簡單就一筆帶過了 選擇下載好的操作系統的iso文件,下載地址https://mirrors.aliyun.com/centos/7.9.20 ...
  • 經過前面幾篇的學習,我們瞭解到指令的大概分類,如:參數載入指令,該載入指令以 Ld 開頭,將參數載入到棧中,以便於後續執行操作命令。參數存儲指令,其指令以 St 開頭,將棧中的數據,存儲到指定的變數中,以方便後續使用。創建實例指令,其指令以 New 開頭,用於在運行時動態生成並初始化對象。方法調用指... ...
  • LiteDB 是一個輕量級的嵌入式 NoSQL 資料庫,其設計理念與 MongoDB 類似,但它是完全使用 C# 開發的,因此與 C# 應用程式的集成非常順暢。與 SQLite 相比,LiteDB 提供了 NoSQL(即鍵值對)的數據存儲方式,並且是一個開源且免費的項目。它適用於桌面、移動以及 We ...
  • 1 開源解析和拆分文檔 第三方的工具去對文件解析拆分,去將我們的文件內容給提取出來,並將我們的文檔內容去拆分成一個小的chunk。常見的PDF word mark down, JSON、HTML。都可以有很好的一些模塊去把這些文件去進行一個東西去提取。 優勢 支持豐富的文檔類型 每種文檔多樣化選擇 ...
  • OOM是什麼?英文全稱為 OutOfMemoryError(記憶體溢出錯誤)。當程式發生OOM時,如何去定位導致異常的代碼還是挺麻煩的。 要檢查OOM發生的原因,首先需要瞭解各種OOM情況下會報的異常信息。這樣能縮小排查範圍,再結合異常堆棧、heapDump文件、JVM分析工具和業務代碼來判斷具體是哪 ...