不要相信程序員在加班時(shí)間寫的代碼

作為一個(gè)最底層的程序員,我先記錄一些只有底層程序員才會(huì)知道的事情。如果多年后越锈,我違背自己進(jìn)入這個(gè)行業(yè)的初心仗嗦,走上管理崗位,也能回想起一些禁忌甘凭,避免一些錯(cuò)誤稀拐。

其中最重要的就是這條:不要相信一個(gè)程序員在加班時(shí)間寫出來的代碼

(軟件工程的學(xué)說表明丹弱,連正常時(shí)間好好寫的代碼德撬,也不要太相信。不過這不是本文的重點(diǎn)躲胳,略過不提蜓洪。)

(不懂代碼的人,看到本文中的Java代碼可以略過坯苹,不影響理解隆檀。)

創(chuàng)造力的時(shí)限

寫代碼,與寫文章北滥、繪畫刚操、思考復(fù)雜問題,并沒有本質(zhì)上的區(qū)別再芋,都是創(chuàng)造性的活動(dòng)菊霜。

每個(gè)人的創(chuàng)造力,都會(huì)隨著身體狀態(tài)而波動(dòng)济赎。廣為人知的是鉴逞,一個(gè)人年老體衰后,相比年富力強(qiáng)時(shí)司训,創(chuàng)造力會(huì)急劇下降构捡。其實(shí),人每天的狀態(tài)起伏壳猜,也同樣會(huì)劇烈影響這一點(diǎn)勾徽。

如果是擰螺絲,那么在精疲力盡统扳、擰不動(dòng)以前喘帚,身體狀態(tài)對(duì)結(jié)果不會(huì)產(chǎn)生太大影響。因?yàn)閿Q螺絲的指標(biāo)非常簡單——擰緊咒钟,要做的事也非常機(jī)械化——擰吹由,直到它緊,換下一個(gè)朱嘴。

但如果是寫代碼倾鲫,有些事,是不能在狀態(tài)不好的時(shí)候完成的。

比如乌昔,在Java里隙疚,遍歷一個(gè)外部的List,做一些處理磕道。如果狀態(tài)不佳甚淡、做事前想的東西少了點(diǎn),那么很可能直接這么做:

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

這樣做是從C/C++帶來的一種很直觀的做法捅厂。有什么問題嗎?

假如外面?zhèn)魅氲?code>aList是一個(gè)ArrayList资柔,那么List.get(int)的時(shí)間復(fù)雜度是O(1)焙贷,算上外面那重循環(huán)則是O(n);而假如aList是一個(gè)LinkedList贿堰,那么List.get(int)的時(shí)間復(fù)雜度是O(n)辙芍,算上外面那重循環(huán)則是O(n2)!

(為不懂算法時(shí)間復(fù)雜度評(píng)估的人解釋下:在這個(gè)場景下羹与,O(n)代表最優(yōu)故硅、最快,而O(n2)代表不可接受地慢纵搁。)

如果時(shí)間充分吃衅,那么可以去查看handleAList()的調(diào)用位置,看看它傳遞的是哪種List腾誉;而如果思考得夠充分徘层,考慮到這兩種情況都有可能,那么代碼就會(huì)做兼容處理利职,改成這樣:

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

這使用了for-each語法趣效,實(shí)際上是用Iterator來做遍歷,無論對(duì)哪種List都是總共是O(n)的開銷猪贪。

注意跷敬,這通常不被看做一個(gè)bug,普通的黑盒與白盒測試都是無法發(fā)現(xiàn)的热押。只是你的App會(huì)比較卡西傀,或者后臺(tái)會(huì)比較慢。當(dāng)需要解決這種性能問題時(shí)楞黄,可能需要非常經(jīng)驗(yàn)豐富的程序員池凄,在海量代碼里找數(shù)周時(shí)間——而這一切,在開發(fā)之初鬼廓,只要那個(gè)程序員狀態(tài)好一點(diǎn)肿仑,就可以避免。

一個(gè)人,每天的創(chuàng)造力是有時(shí)限的尤慰。在時(shí)限外馏锡,他不再是一個(gè)優(yōu)秀的創(chuàng)造者,而是一個(gè)笨蛋伟端。

(為了便于理解杯道,這個(gè)例子非常簡單,以至于不夠貼切责蝠。對(duì)Java來說党巾,優(yōu)先使用for-each或Iterator來遍歷,已經(jīng)是一個(gè)共識(shí)霜医,是技術(shù)素養(yǎng)的一部分齿拂。)

失誤率的飆升

程序員在寫代碼的過程中,每天做得最多的應(yīng)該就是等價(jià)變換肴敛。

    if (isSthTrue()) {
        // Take some actions.
    }

變換成

    if (!isSthTrue()) return;

    // Take some actions.

這只是最簡單的一種邏輯反轉(zhuǎn)署海,實(shí)際上還有更多、更復(fù)雜的形式医男。通過這類變化砸狞,對(duì)代碼做出調(diào)整后,程序員可以把代碼變得更好镀梭,或者做到以前不能做的事刀森。

而在加班時(shí)間、大腦不那么清醒的情況下丰辣,很可能會(huì)寫成這樣:

    if (isSthTrue()) return;

    // Take some actions.

區(qū)別僅僅只是少了一個(gè)符號(hào)撒强,而意義則完全走樣。

這個(gè)例子比較簡單笙什,出錯(cuò)后也很容易在調(diào)試過程中發(fā)現(xiàn)飘哨、糾正。但是琐凭,請(qǐng)不要懷疑芽隆,的確會(huì)有程序員為了這么個(gè)簡單的問題,調(diào)試整整一個(gè)晚上统屈!

(胚吁、!i愁憔,(字體未配置好時(shí))本就難以區(qū)分腕扶,眼睛疲勞昏花時(shí),在數(shù)百個(gè)字符里掃來掃去吨掌,難以分辨(!i中是否少了個(gè)符號(hào)半抱,也并不奇怪脓恕。而如果換成第二天早晨,很可能只需要瞥一眼窿侈。

大多數(shù)管理者炼幔,往往會(huì)對(duì)熬夜的程序員給出一些肯定首繁,并且允許第二天可以休息一天(有些甚至只給一早上)卷胯。但如果他們知道內(nèi)情,會(huì)發(fā)現(xiàn)自己其實(shí)虧了一天藤肢。如果程序員正常下班圆兵,第二天花一小時(shí)解決這個(gè)問題跺讯,剩下的七個(gè)小時(shí)可以繼續(xù)開發(fā)。

還有很多比這復(fù)雜得多的變換殉农,或其它類型的代碼改動(dòng)抬吟,即使在大腦清醒的情況下也需要花費(fèi)一些時(shí)間,認(rèn)真思考统抬、小心調(diào)試。而如果來了一個(gè)問題危队,你說“必須要今天下班前搞定”聪建,那么程序員會(huì)很煩躁,并且越來越煩躁茫陆。

煩躁的后果

一件需要冷靜思考金麸、謀定后動(dòng)的事,如果逼迫人們?cè)跓┰甑那闆r下去做簿盅,那么往往會(huì)得到意想不到的糟糕結(jié)果挥下。

我有一位前同事,技術(shù)實(shí)力且不論桨醋,心性也不太穩(wěn)(實(shí)際上棚瘟,像我這種少年老成、未老先衰喜最、找不到妹子都不急的青年偎蘸,還真不多)。他是一個(gè)可以解決問題的人瞬内,但是在煩躁的情況下迷雪,也經(jīng)常做出令我瞠目結(jié)舌的事。

比如虫蝶,有一天章咧,項(xiàng)目組要求某個(gè)bug必須解決。他搞到晚上9點(diǎn)還沒搞定能真,找我?guī)兔α扪稀N耶?dāng)時(shí)水平也很差扰柠,不然也不會(huì)那時(shí)還在加班,沒能幫他解決误澳,只是因此而知道這件事耻矮。他后來在10點(diǎn)半時(shí)采用了一個(gè)規(guī)避方案,然后下班了事忆谓。

具體一點(diǎn)是這樣的:在一個(gè)class中裆装,有多個(gè)地方調(diào)用同一個(gè)Method。其它地方?jīng)]有問題倡缠,唯獨(dú)某個(gè)位置的結(jié)果不正確哨免。他改成這樣:

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

    private boolean isSth1True() {
        // Implementation B
    }

    private boolean isSth2True() {
        // Implementation C
    }

本來isSthTrue()是可以做通用判斷的,他沒有在規(guī)定時(shí)間內(nèi)找到根本原因(Root Cause)昙沦,實(shí)際上當(dāng)時(shí)他也根本沒有往發(fā)現(xiàn)根本原因的方向去查找代碼琢唾,而是一晚上都在做一些無效的調(diào)試。最后沒辦法調(diào)試出好的結(jié)果盾饮,于是給出問題的地方一個(gè)特殊處理——新增了isSth1True()isSth2True()去那個(gè)出錯(cuò)的地方頂替采桃。結(jié)果,那個(gè)bug的確是解決了丘损,但是后來帶出來了另外一個(gè)bug普办。

不過他也達(dá)到了目的,當(dāng)天下班了徘钥。

而后來衔蹲,我在代碼里發(fā)現(xiàn)了另外一組更早就有的接口。

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

    private boolean isTrueSth2() {
        // Implemented like C
    }

我問了一下這兩個(gè)Method的作者(另一位同事)呈础,他根本沒有看到有isSthTrue()舆驶。

這件事的最終結(jié)果是,解決了一個(gè)bug而钞,后來又引起了多個(gè)bug沙廉,連我也跟著一起焦頭爛額。

不解決問題地debug

借著這個(gè)例子臼节,回頭再說一下創(chuàng)造力的時(shí)限蓝仲。

這位同事,之所以不去找Root Cause官疲,是因?yàn)轫?xiàng)目組的催逼和自身的煩躁袱结,他平時(shí)是可以解決問題的。但是為什么一個(gè)簡單問題會(huì)這么難解決途凫,為什么代碼里之前就有一套他要的Method垢夹,他卻新寫一個(gè)?

外部代碼環(huán)境就不說了维费,這個(gè)class共有2000行果元。2000行可能并不是特別直觀的數(shù)目促王,既不能說多,也不能說少而晒,取決于這個(gè)class干什么事蝇狼。

后來,另一個(gè)比較老道的同事倡怎,重構(gòu)(refactor)了這個(gè)class迅耘,只用了不到500行——這就說明了一個(gè)問題,這個(gè)class之前就太過冗余监署。

約半年后颤专,我水平也提高了些,總體的項(xiàng)目時(shí)間也松散了些钠乏,我花了六周重寫(rewrite)了這個(gè)不大的代碼庫栖秕。這個(gè)class最終只用了100行,部分功能都獨(dú)立封裝到了其它c(diǎn)lass中晓避。

如果之前簇捍,在這個(gè)代碼庫寫就之初,就能有一個(gè)充分的時(shí)間做一個(gè)好的架構(gòu)設(shè)計(jì)俏拱,不需要rewrite就可以只有100行垦写;而如果時(shí)間不太充分,卻能給應(yīng)有的時(shí)間好好寫彰触,也起碼能有refactor后的水平,也就是500行命辖。無論是100行况毅,還是500行,后面出的一大堆問題尔艇,都不會(huì)出現(xiàn)尔许,或者更容易解決。

這個(gè)代碼庫是怎么來的终娃?

當(dāng)初某領(lǐng)導(dǎo)味廊,交給了一個(gè)比較厲害的同事,只給一周時(shí)間棠耕。這位同事加班加點(diǎn)余佛,一周當(dāng)成兩周用,從別的代碼里剝離窍荧、拼湊出來了一個(gè)編譯能通過的東西——這就是交給我們維護(hù)的代碼庫辉巡。

來自項(xiàng)目最底層的復(fù)仇

前面說的,無論是寫出隱蔽的bug蕊退,還是解決一個(gè)帶出倆郊楣,其實(shí)都是這類事情的陽光面憔恳。你沒看錯(cuò),這是陽光的一面净蚤。

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

前面說的事情,沒有一類是故意的今瀑。無論出事的原因是程序員的技術(shù)素養(yǎng)不足程梦、加班情況下大失水準(zhǔn)、還是原先的代碼就非常容易誘導(dǎo)失誤放椰,都是程序員在認(rèn)真努力的情況下作烟,不可自控地犯錯(cuò)。

還有一類是故意的砾医。

比如拿撩,去年(2015)攜程那小哥兒,就是怒刪數(shù)據(jù)庫如蚜。當(dāng)然压恒,他不是為了加班嚴(yán)重而如何如何,而是心愛的運(yùn)營妹子被公司某高層給……(另有一說错邦,雖然有什么內(nèi)部的QQ探赫、微信截圖,但這仍然是謠言撬呢,實(shí)際上是黑客攻擊伦吠。)

什么程度的壓迫,就會(huì)得到什么程度的反抗魂拦。

要知道毛仪,即使是很努力地去做,也仍然可以出各種問題芯勘。而如果要故意搗亂箱靴,很多手段,雖然不會(huì)引起老板的注意荷愕,甚至可以不被認(rèn)真的代碼審查者(reviewer)警覺衡怀,但是會(huì)客觀地影響產(chǎn)品的品質(zhì),讓用戶討厭一個(gè)產(chǎn)品安疗,或者讓一個(gè)爆款產(chǎn)品最終失敗抛杨。

反正埋了雷,領(lǐng)了工資荐类,跳下一家便是——要么給股票蝶桶、期權(quán),要么充分洗腦掉冶,或至少給出足夠的加班費(fèi)(幾年后的醫(yī)療費(fèi))真竖,否則就是這個(gè)后果脐雪。

我只能說,就我個(gè)人而言恢共,最多辭職战秋,不會(huì)故意亂搞。這關(guān)乎職業(yè)道德讨韭,關(guān)乎我是否意念通達(dá)脂信、心境澄明。(坐等穿越去修真:P)

但是透硝,我不能用自己的道德準(zhǔn)繩去要求別人狰闪,對(duì)吧?

而且濒生,永遠(yuǎn)不要指望一個(gè)人在承受不道德的對(duì)待時(shí)埋泵,仍然能謹(jǐn)守原來的道德。

結(jié)語

作為一個(gè)軟件項(xiàng)目的領(lǐng)導(dǎo)者罪治,你在要求某個(gè)程序員加班時(shí)丽声,其實(shí)就已經(jīng)在冒險(xiǎn);而如果你經(jīng)常這么干觉义,不要奇怪為什么項(xiàng)目總是延期雁社,或者一到關(guān)鍵時(shí)候,總有突發(fā)事件晒骇。

只要試驗(yàn)次數(shù)夠多霉撵,可能性再小的事也會(huì)發(fā)生;而只要試驗(yàn)次數(shù)更多洪囤,小概率事件也會(huì)連續(xù)發(fā)生徒坡。

所以,最理智箍鼓、客觀的觀念就是:欲速則不達(dá),不要相信一個(gè)程序員在加班時(shí)間寫的代碼呵曹。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末款咖,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子奄喂,更是在濱河造成了極大的恐慌铐殃,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件跨新,死亡現(xiàn)場離奇詭異富腊,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)域帐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門赘被,熙熙樓的掌柜王于貴愁眉苦臉地迎上來是整,“玉大人,你說我怎么就攤上這事民假「∪耄” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵羊异,是天一觀的道長事秀。 經(jīng)常有香客問我,道長野舶,這世上最難降的妖魔是什么易迹? 我笑而不...
    開封第一講書人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮平道,結(jié)果婚禮上睹欲,老公的妹妹穿的比我還像新娘。我一直安慰自己巢掺,他們只是感情好句伶,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著陆淀,像睡著了一般考余。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上轧苫,一...
    開封第一講書人閱讀 49,079評(píng)論 1 285
  • 那天楚堤,我揣著相機(jī)與錄音,去河邊找鬼含懊。 笑死身冬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的岔乔。 我是一名探鬼主播酥筝,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼雏门!你這毒婦竟也來了嘿歌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤茁影,失蹤者是張志新(化名)和其女友劉穎宙帝,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體募闲,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡步脓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仍侥。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蚁廓,靈堂內(nèi)的尸體忽然破棺而出访圃,到底是詐尸還是另有隱情相嵌,我是刑警寧澤腿时,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站看铆,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏弹惦。R本人自食惡果不足惜否淤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一石抡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧助泽,春花似錦啰扛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至诫睬,卻和暖如春煞茫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背摄凡。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來泰國打工续徽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人架谎。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓炸宵,卻偏偏與公主長得像辟躏,于是被迫代替她去往敵國和親谷扣。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,517評(píng)論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)会涎,斷路器裹匙,智...
    卡卡羅2017閱讀 134,600評(píng)論 18 139
  • 回答一個(gè)問題概页, 假設(shè)你點(diǎn)擊一下鼠標(biāo),就有可能得到练慕, 其他人對(duì)你文章的評(píng)價(jià)幾個(gè)關(guān)注你的朋友 那你會(huì)點(diǎn)那個(gè)鼠標(biāo)嗎惰匙? 請(qǐng)...
    喳喳Aha閱讀 574評(píng)論 0 3
  • O 這是我在辦公室養(yǎng)的一株小榕樹,當(dāng)初買回來的時(shí)候還是枝繁葉茂的铃将,可不久之后就開始掉葉子项鬼,難道是我太過呵護(hù),水澆得...
    葳葳一笑浥清清閱讀 255評(píng)論 2 1
  • 長篇小說《百年?duì)t火》第十四章 2015-02-18 12:5643 十四 母親開始吃早餐是在春天發(fā)病之后的事劲阎。在此...
    294af09a07b2閱讀 350評(píng)論 0 0