千萬不要相信程序員在加班時間寫的代碼!

做爲一個最底層的程序員,我先記錄一些只有底層程序員纔會知道的事情。若是多年後,我違背本身進入這個行業的初心,走上管理崗位,也能回想起一些禁忌,避免一些錯誤。程序員

其中最重要的就是這條:不要相信一個程序員在加班時間寫出來的代碼。算法

(軟件工程的學說代表,連正常時間好好寫的代碼,也不要太相信。不過這不是本文的重點,略過不提。)sql

(不懂代碼的人,看到本文中的Java代碼能夠略過,不影響理解。)數據庫

創造力的時限

寫代碼,與寫文章、繪畫、思考複雜問題,並無本質上的區別,都是創造性的活動。bash

**每一個人的創造力,都會隨着身體狀態而波動。**廣爲人知的是,一我的年老體衰後,相比年富力強時,創造力會急劇降低。其實,人天天的狀態起伏,也一樣會劇烈影響這一點。微信

若是是擰螺絲,那麼在精疲力盡、擰不動之前,身體狀態對結果不會產生太大影響。由於擰螺絲的指標很是簡單——擰緊,要作的事也很是機械化——擰,直到它緊,換下一個。架構

但若是是寫代碼,有些事,是不能在狀態很差的時候完成的。併發

好比,在Java裏,遍歷一個外部的List,作一些處理。若是狀態不佳、作事前想的東西少了點,那麼極可能直接這麼作:分佈式

public void handleAList(List<Integer> aList) {
        for (int i = 0; i < aList.size(); ++i) {
            // Do sth with List#get(int)
        }
    }
複製代碼

這樣作是從C/C++帶來的一種很直觀的作法。有什麼問題嗎?高併發

假如外面傳入的aList是一個ArrayList,那麼List.get(int)的時間複雜度是O(1),算上外面那重循環則是O(n);而假如aList是一個LinkedList,那麼List.get(int)的時間複雜度是O(n),算上外面那重循環則是O(n2)!

(爲不懂算法時間複雜度評估的人解釋下:在這個場景下,O(n)表明最優、最快,而O(n2)表明不可接受地慢。)

若是時間充分,那麼能夠去查看handleAList()的調用位置,看看它傳遞的是哪一種List;而若是思考得夠充分,考慮到這兩種狀況都有可能,那麼代碼就會作兼容處理,改爲這樣:

public void handleAList(List<Integer> aList) {
        for (int i : aList) {
            // Do sth with i
        }
    }
複製代碼

這使用了for-each語法,其實是用Iterator來作遍歷,不管對哪一種List都是總共是O(n)的開銷。

注意,這一般不被看作一個bug,普通的黑盒與白盒測試都是沒法發現的。只是你的App會比較卡,或者後臺會比較慢。當須要解決這種性能問題時,可能須要很是經驗豐富的程序員,在海量代碼裏找數週時間——

而這一切,在開發之初,只要那個程序員狀態好一點,就能夠避免。 一我的,天天的創造力是有時限的。在時限外,他再也不是一個優秀的創造者,而是一個笨蛋。

(爲了便於理解,這個例子很是簡單,以致於不夠貼切。對Java來講,優先使用for-each或Iterator來遍歷,已是一個共識,是技術素養的一部分。)

失誤率的飆升

程序員在寫代碼的過程當中,天天作得最多的應該就是等價變換。

if (isSthTrue()) {
        // Take some actions.
    }
複製代碼

變換成

if (!isSthTrue()) return;

    // Take some actions.
複製代碼

這只是最簡單的一種邏輯反轉,實際上還有更多、更復雜的形式。經過這類變化,對代碼作出調整後,程序員能夠把代碼變得更好,或者作到之前不能作的事。

而在加班時間、大腦不那麼清醒的狀況下,極可能會寫成這樣:

if (isSthTrue()) return;

    // Take some actions.
複製代碼

區別僅僅只是少了一個符號,而意義則徹底走樣。

這個例子比較簡單,出錯後也很容易在調試過程當中發現、糾正。可是,請不要懷疑,的確會有程序員爲了這麼個簡單的問題,調試整整一個晚上!

(!i,(字體未配置好時)本就難以區分,眼睛疲勞昏花時,在數百個字符裏掃來掃去,難以分辨(!i中是否少了個符號,也並不奇怪。而若是換成次日早晨,極可能只須要瞥一眼。

大多數管理者,每每會對熬夜的程序員給出一些確定,而且容許次日能夠休息一天(有些甚至只給一早上)。但若是他們知道內情,會發現本身其實虧了一天。若是程序員正常下班,次日花一小時解決這個問題,剩下的七個小時能夠繼續開發。

還有不少比這複雜得多的變換,或其它類型的代碼改動,即便在大腦清醒的狀況下也須要花費一些時間,認真思考、當心調試。而若是來了一個問題,你說**「必需要今天下班前搞定」,那麼程序員會很煩躁,而且愈來愈煩躁。**

煩躁的後果

一件須要冷靜思考、謀定後動的事,若是逼迫人們在煩躁的狀況下去作,那麼每每會獲得意想不到的糟糕結果。

我有一位前同事,技術實力且不論,心性也不太穩(實際上,像我這種少年老成、未老先衰、找不到妹子都不急的青年,還真很少)。他是一個能夠解決問題的人,可是在煩躁的狀況下,也常常作出令我瞠目結舌的事。

好比,有一天,項目組要求某個bug必須解決。他搞到晚上9點還沒搞定,找我幫忙。我當時水平也不好,否則也不會那時還在加班,沒能幫他解決,只是所以而知道這件事。他後來在10點半時採用了一個規避方案,而後下班了事。

具體一點是這樣的:在一個class中,有多個地方調用同一個Method。其它地方沒有問題,惟獨某個位置的結果不正確。他改爲這樣:

private boolean isSthTrue(int sth) {
        // Implementation A
    }

    private boolean isSth1True() {
        // Implementation B
    }

    private boolean isSth2True() {
        // Implementation C
    }
複製代碼

原本**isSthTrue()是能夠作通用判斷的,他沒有在規定時間內找到根本緣由(Root Cause),實際上當時他也根本沒有往發現根本緣由的方向去查找代碼,而是一夜都在作一些無效的調試。最後沒辦法調試出好的結果,因而給出問題的地方一個特殊處理——新增了isSth1True()isSth2True()**去那個出錯的地方頂替。結 果,那個bug的確是解決了,可是後來帶出來了另一個bug。

不過他也達到了目的,當天下班了。

然後來,我在代碼裏發現了另一組更早就有的接口:

private boolean isTrueSth1() {
        // Implemented like B
    }

    private boolean isTrueSth2() {
        // Implemented like C
    }
複製代碼

我問了一下這兩個Method的做者(另外一位同事),他根本沒有看到有isSthTrue()

這件事的最終結果是,解決了一個bug,後來又引發了多個bug,連我也跟着一塊兒焦頭爛額。

image.png
藉着這個例子,回頭再說一下 創造力的時限。

這位同事,之因此不去找Root Cause,是由於項目組的催逼和自身的煩躁,他平時是能夠解決問題的。可是爲何一個簡單問題會這麼難解決,爲何代碼裏以前就有一套他要的Method,他卻新寫一個?

外部代碼環境就不說了,這個class共有2000行。2000行可能並非特別直觀的數目,既不能說多,也不能說少,取決於這個class幹什麼事。

後來,另外一個比較老道的同事,重構(refactor)了這個class,只用了不到500行——這就說明了一個問題,這個class以前就太過冗餘。

約半年後,我水平也提升了些,整體的項目時間也鬆散了些,我花了六週重寫(rewrite)了這個不大的代碼庫。這個class最終只用了100行,部分功能都獨立封裝到了其它class中。

若是以前,在這個代碼庫寫就之初,就能有一個充分的時間作一個好的架構設計,不須要rewrite就能夠只有100行;而若是時間不太充分,卻能給應有的時間好好寫,也起碼能有refactor後的水平,也就是500行。不管是100行,仍是500行,後面出的一大堆問題,都不會出現,或者更容易解決。

這個代碼庫是怎麼來的?

當初某領導,交給了一個比較厲害的同事,只給一週時間。這位同事加班加點,一週當成兩週用,從別的代碼裏剝離、拼湊出來了一個編譯能經過的東西——這就是交給咱們維護的代碼庫。

來自項目最底層的復仇

前面說的,不管是寫出隱蔽的bug,仍是解決一個帶出倆,其實都是這類事情的陽光面。你沒看錯,這是陽光的一面。

還有我不想多說的陰暗面。

前面說的事情,沒有一類是故意的。不管出事的緣由是程序員的技術素養不足、加班狀況下大失水準、仍是原先的代碼就很是容易誘導失誤,都是程序員在認真努力的狀況下,不可自控地犯錯。

還有一類是故意的。 好比,前幾年(2015)攜程那小哥兒,就是怒刪數據庫。固然,他不是爲了加班嚴重而如何如何,而是心愛的運營妹子被公司某高層給……(另有一說,雖然有什麼內部的QQ、微信截圖,但這仍然是謠言,其實是黑客攻擊。)

什麼程度的壓迫,就會獲得什麼程度的反抗。

要知道,即便是很努力地去作,也仍然能夠出各類問題。而若是要故意搗亂,不少手段,雖然不會引發老闆的注意,甚至能夠不被認真的代碼審查者(reviewer)警覺,可是會客觀地影響產品的品質,讓用戶討厭一個產品,或者讓一個爆款產品最終失敗。

反正埋了雷,領了工資,跳下一家即是——要麼給股票、期權,要麼充分洗腦,或至少給出足夠的加班費(幾年後的醫療費),不然就是這個後果。

我只能說,就我我的而言,最多辭職,不會故意亂搞。這關乎職業道德,關乎我是否意念通達、心境澄明。(坐等穿越去修真:P)

可是,我不能用本身的道德準繩去要求別人,對吧?

並且,永遠不要期望一我的在承受不道德的對待時,仍然能謹守原來的道德。

結語

做爲一個軟件項目的領導者,你在要求某個程序員加班時,其實就已經在冒險;而若是你常常這麼幹,不要奇怪爲何項目老是延期,或者一到關鍵時候,總有突發事件。

只要試驗次數夠多,可能性再小的事也會發生;而只要試驗次數更多,小几率事件也會連續發生。

因此,最理智、客觀的觀念就是:欲速則不達,不要相信一個程序員在加班時間寫的代碼。

讀者福利

歡迎工做一到五年的Java工程師朋友們加入Java架構開發:277763288

羣內提供免費的Java架構學習資料(裏面有高可用、高併發、高性能及分佈式、Jvm性能調優、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)合理利用本身每一分每一秒的時間來學習提高本身,不要再用"沒有時間「來掩飾本身思想上的懶惰!趁年輕,使勁拼,給將來的本身一個交代!

相關文章
相關標籤/搜索