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

来源: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
  • 1、預覽地址:http://139.155.137.144:9012 2、qq群:801913255 一、前言 隨著網路的發展,企業對於信息系統數據的保密工作愈發重視,不同身份、角色對於數據的訪問許可權都應該大相徑庭。 列如 1、不同登錄人員對一個數據列表的可見度是不一樣的,如數據列、數據行、數據按鈕 ...
  • 前言 上一篇文章寫瞭如何使用RabbitMQ做個簡單的發送郵件項目,然後評論也是比較多,也是準備去學習一下如何確保RabbitMQ的消息可靠性,但是由於時間原因,先來說說設計模式中的簡單工廠模式吧! 在瞭解簡單工廠模式之前,我們要知道C#是一款面向對象的高級程式語言。它有3大特性,封裝、繼承、多態。 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 介紹 Nodify是一個WPF基於節點的編輯器控制項,其中包含一系列節點、連接和連接器組件,旨在簡化構建基於節點的工具的過程 ...
  • 創建一個webapi項目做測試使用。 創建新控制器,搭建一個基礎框架,包括獲取當天日期、wiki的請求地址等 創建一個Http請求幫助類以及方法,用於獲取指定URL的信息 使用http請求訪問指定url,先運行一下,看看返回的內容。內容如圖右邊所示,實際上是一個Json數據。我們主要解析 大事記 部 ...
  • 最近在不少自媒體上看到有關.NET與C#的資訊與評價,感覺大家對.NET與C#還是不太瞭解,尤其是對2016年6月發佈的跨平臺.NET Core 1.0,更是知之甚少。在考慮一番之後,還是決定寫點東西總結一下,也回顧一下.NET的發展歷史。 首先,你沒看錯,.NET是跨平臺的,可以在Windows、 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 添加節點(nodes) 通過上一篇我們已經創建好了編輯器實例現在我們為編輯器添加一個節點 添加model和viewmode ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...
  • 類型檢查和轉換:當你需要檢查對象是否為特定類型,並且希望在同一時間內將其轉換為那個類型時,模式匹配提供了一種更簡潔的方式來完成這一任務,避免了使用傳統的as和is操作符後還需要進行額外的null檢查。 複雜條件邏輯:在處理複雜的條件邏輯時,特別是涉及到多個條件和類型的情況下,使用模式匹配可以使代碼更 ...
  • 在日常開發中,我們經常需要和文件打交道,特別是桌面開發,有時候就會需要載入大批量的文件,而且可能還會存在部分文件缺失的情況,那麼如何才能快速的判斷文件是否存在呢?如果處理不當的,且文件數量比較多的時候,可能會造成卡頓等情況,進而影響程式的使用體驗。今天就以一個簡單的小例子,簡述兩種不同的判斷文件是否... ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...