99.999999999% 做技術的都會被問到或者被吐槽到:“你的程序怎麼又出 Bug 了!”
反正,我作為程序員的内心世界是:如同一萬隻草泥馬飛奔而過,難以壓抑内心的激動,每次都差點忍不住想說“你寫篇幾百字的作文還有錯别字呢,我碼個幾萬行的代碼還不允許出錯了?“
可能同樣是做技術的你此時在不斷點頭,哈哈。但是這麼講畢竟也緩解不了矛盾,我們還是得擺事實講道理不是?
啥都不怕,就怕程序員有文化!所以,我想來帶你好好分析一下這個事情,當你再遇到這個情況的時候,可以拿這些觀點來反駁。(不是做技術的也可以了解下程序員的難處,誰沒個難處呢,多多包容)
什麼是 Bug
任何一個「問題」的産生,本身是沒有好壞之分的,但是為什麼會有的就不被 Care,甚至還會很喜歡,而有的會被吐槽呢?根本原因是因為産生了利益損失。
比如年前拼多多出問題送了很多無門檻券。作為一個用戶,自然很喜歡,誇你誇到飛起,怎麼會吐槽你呢。但是作為利益損失方,必然破口大罵,害我傾家蕩産!
所以,如果沒有産生利益損失,我想其他人也不會來找你吐槽。但是「問題」就等于「Bug」嗎?我認為并不是,「問題」不等于「 bug」。
因為程序員的職責是什麼?拿造房子來比喻的話,我認為最核心的工作真的和“搬磚”(非貶義詞)無異,就是根據設計師(産品經理)設計好的設計圖砌磚(編碼),建成和設計圖紙上一模一樣的建築。
所以,如果一個東西造出來與設計不符,那麼它可以說是 Bug 或者缺陷(缺斤少兩不完整)。
否則,并不是 Bug,但可以被稱之為「漏洞」(完全沒考慮到的),表示不在預料之内的情況。
之前看到過一個形象的比喻:你家裡的窗可以從外面打開,那叫漏洞。你家裡的窗打不開,那叫 Bug。
但是要承認,Bug 是必然存在的。為什麼?它是如何出現的呢?
Bug 是如何出現的
正如前面所說,程序員做的是“造房子”的事情。這件事完整的步驟分為 3 步:
第一步,“與産品經理讨論并确定功能”主要是溝通,靠“看”和“理解”。
但是溝通本身是一個有損耗的過程,特别是在職責非常明确的組織中,産品經理啪啦啪啦講了很多,到實際做的時候你必然還是會去翻閱需求原型、需求文檔之類的重新理解一下。這個時候就是一個非常危險的時期。
比如像下面這個的答案是什麼?
答案是 17?不對。我猜你可能沒注意到這些地方:
為了讓你有深刻的印象,這個舉例可能比較刻意和誇張一些,但是我想在你的身邊,由于沒注意到或者理解有誤的現象肯定很常見。
溝通是相互的,這鍋隻讓程序員背的話的确太委屈了點。
第二步,“将每個單獨的元件抽象出來”這主要是一個人抽象能力的體現。
但是抽象是啥?抽象是“透過現象看到本質”的能力,這個能力理論上是可以無限增長的。
随着你對相關信息的掌握越多,這個能力會越強,會無限趨近于 100%,但永遠不會真正達到 100%,因為沒人知道怎麼才算 100%。
所以,當你具備的信息沒那麼多的時候,是不是就抽象的不是那麼合理?不合理會導緻什麼?
雖然不會直接産生 Bug,但是會更容易産生 Bug。但是人不都是需要經曆這麼一個成長的過程麼?
可以說,精通一項能力的背後都是踩着無數的 Bug 過來的。要麼在來這個組織之前已經踩過了,要麼在這個組織裡踩。因此,前者的薪資也比後者高。
所以,如果過分苛求沒有 Bug,等于是在扼殺每個人成長的機會,并且在透支未來的可能性。人會變得非常保守、不敢嘗試新事物。
但是外部環境在不斷變化,新事物總會被動的需要去接納(技術的更新越來越快,趨勢不可逆),然而對新事物的接受能力又得不到鍛煉,一旦遇到這種情況,在接觸新事物的時候會産生更多的問題(欠下的債總要還的)。
第三步,“将相關的元件實現并進行組合,完成建設”這就是實際的 Coding 過程,而 Coding 是一個主觀的,完全由人主觀掌控的事情。
人畢竟不是機器,不可能不犯錯,就如前面提到的寫文章的時候出現錯别字一樣。
可能你會說,有測試人員啊,測試的工作不就是通過逆向思維來給程序員查缺補漏嗎?
的确是的,但測試的介入隻是降低錯誤率,隻是讓不出現 Bug 的概率小數點後多幾位,指望發現 100% 的問題還是不太現實的。至少在當下的條件下是這樣,為什麼呢?因為代碼的本質是各種邏輯的組合。
比如,一個完整的業務流程有 10 個環節,每個環節有 3 種可能性,這是一個什麼複雜度的系統?
3 ^ 10 = 59049 個分支(理論上存在的可能性數量),想要 100% 覆蓋這些場景,付出的成本幾乎是不可接受的。然而我們實際的系統中遇到的個别場景甚至還要複雜的多。
其實每個正在運行的系統都有 Bug,包括我們每天在使用一些熱門系統(玩遊戲的小夥伴們肯定熟悉“卡 Bug”這個詞)。隻是這些 Bug 有沒有被執行到,有沒有被發現,被多少人發現而已。
那麼,我們隻能舉手投降嗎?那倒不至于,辦法還是有的。
減少 Bug 的慣性想法
首先最容易想到的一點是,增加測試人員。這也是最容易看得到“成本”的一種方式,畢竟招一個人就得支出一份工資啊。
所以,增加測試人員這個方案是最不容易被老闆們采納的方案。除非你可以說服這個人力成本的投入小于獲得的價值。
另外,這個方案還增加了溝通成本,溝通的「隐性成本」其實非常大,但是往往容易被忽略。
其次會想到的就是程序員代碼寫的嚴謹一點,仔細一點啊。這也是一種缺啥補啥的慣性思維。
先撇開到底能不能達到嚴謹一點,仔細一點的目的。那怕達到了,他會産生什麼結果呢?
可能是下面 3 種:
可以确定的是,這些工作會增加兩樣硬性的東西,投入的時間和整體的複雜度。時間很好理解,我們就來聊聊複雜度。
一個常識是,越簡單的東西越不容易産生 Bug。比如 1 1=2,出現 Bug 的可能性無非就是加号寫成了減号,1 寫成了 4 之類。
但是,1 1=2,并且 1*1=1,并且 1/1=1 等等這些驗證條件越多,那麼由于驗證條件自身的錯誤而産生問題的可能性反而更多。
所以,代碼的複雜度和産生 Bug 的概率是成正比的,并且具有「邊際效用遞減」的效果。這就意味着,做更多的驗證帶來的收益會越來越小。
因此,這個方案哪怕真能執行到位,也不是一個特别好的方案。那有沒有相對靠譜一些的辦法呢?有,但需要我們換一個角度來看待這個問題。
換一個角度看待 Bug
既然無法 100% 避免 Bug,那我們可以換個角度考慮一下,如何讓解決 Bug 的過程更快,甚至快到你都沒有察覺。
解決 Bbug 主要就是做兩件事:
每天都在解決 Bug 的程序員們應該知道,這事最費時間的是“找 Bug”的過程。
因為“修複 Bug”是一個技術性問題,這個對不同人的差異其實是很小的,因為程序員們每天在寫的代碼都是差不多的,非常同質化的,況且還有标準答案“文檔”可以參考。
比如,都知道 string.concat() 是拼接,string.split() 是分割。該用分割的地方不小心用了拼接,那改掉就好。
但是“找 Bug”就不是這樣了。比如,你剛剛改完一行代碼後發布出現的問題,你不用找就知道問題出現在哪。但是讓你排查一個剛接手沒多久的系統肯定是一臉懵逼。
根本原因在于,這個過程不像技術性問題具有确定性,它是充滿不确定性的,處在一個“混沌”的環境中。所以,對待 Bug 的重點就變成了:如何更快的發現和找到 Bug。
關于這點我的建議是:
首先,打好日志。日志其實就是我們在編碼的時候安插在程序中的“記錄員”,它替我們記錄着我們認為容易出現問題的地方所産生的信息。
但是系統無時無刻都在運行着,必然會産生大量的日志信息,如何從這些信息中快速的找到關鍵信息,就是需要考慮的問題。
另外,如果每個人都随意的用自己喜歡的記錄日志的方式,那麼從風格迥異的日志中找你需要的信息就變得很頭疼,時間不一緻,格式不一緻等等。
所以,要做好打日志這個事情,就需要定義一個标準,比如必須要有時間,包含當前上下文的參數等等。
我們還可以給日志做一下歸類,定義不同的日志級别,在記錄的時候帶上前綴。比如【info】、【warning】、【error】之類。
如此一來,平時更着重關注的就是 Error 級别的信息,而且由于将其他級别的信息剝離了出去,使得這裡的數據量大大減少,更便于查看。
不過,日志記錄畢竟是一個在做“預判”,如果日志中沒有記錄到怎麼辦呢?這裡提醒大家不要先想着怎麼調試。
如果你面對的系統是一個單體應用倒還好。如果你面對的是一個大型的分布式系統,調試的效率低不說,這事你一個人可能還完不成。
而且,如果你直接調試生産環境的話,說不準還會産生什麼副作用,攤上新的問題。
找 Bug 本質上是一個排除法的過程,設斷點調試也是如此。但是從起點開始一步一步的做,排除法效率太低了。
應該先通過自己的經驗、擁有的部分信息先邏輯推理一下,縮小排查的範圍。哪怕你最終還是需要調試的話,先做這個事情也會讓後續的工作更高效一些。
第二點,利用工具。這裡的“工具”不要簡單的理解成利用“調試工具”。正如上面提到的,找 Bug 的本質是一個排除法的過程,那麼任何能夠幫你更高效的做排除法的工具都是可以利用的。
比如:
另外,如果能主動的告訴你哪裡出現 Bug 了,就更棒了。所以,我們可以搭建一套查看方便,信息同步及時的日志框架,以便讓有價值的信息第一時間呈現在你的面前。如果有高效的篩選功能就更好了。
很多日志框架我沒用過,就不發表什麼言論了,但是 Elasticsearch Logstash Kibana 這套用起來還是很爽的,體系也比較成熟,部署起來也很簡單,大家可以嘗試一下。再配上 ElastAlert 或者 Sentinl,可以把實時預警機制也包含了。
最後,每次的叠代規模盡可能的小。這個說起來容易,做起來難,因為這是由整個團隊的文化來決定的。這個點的内容完全可以單獨開一篇講,這裡就簡要闡述下。
MVP(Minimum Viable Product)式的小步快跑,其實除了讓系統或者産品的功能演進更科學之外,還可以讓每次叠代所面臨的風險更小。正如前面提到的,你改一行代碼發布上去,如果出問題,你說問題在哪?
相對的,再想象一下,一次性發布一個開發了半年的版本,前一晚能睡的安穩不?
總結
好了,我們總結一下。這篇先闡述了我對“什麼是 Bug”的理解,然後分析了 Bug 是如何産生的,以及我們可能會做的一些慣性選擇。
最後給你的建議是,以如何更快的找到 Bug 為出發點來考慮。通過「打好日志」、「學會利用工具」、「每次的叠代規模盡可能的小」這 3 種方式來進行。
不過話說回來,雖然我們無法避免出 Bug(一個項目開發完後沒測出 Bug?你問任何一個技術人員都說“做夢呢”),但是争取讓 Bug 更少是我們的本職工作。
因為對 Bug 容忍度低的另一層含義是,大家對系統的依賴越來越重,越來越多的事情在通過程序完成,而不是人力。但是再有人咄咄逼人,就把這篇文章丢給他!
作者:張帆來源:跨界架構師
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!