如何處理前任程序員留下的代碼

摘要:作為軟件工程師不可避免會遇到的一個場景是:我們在改變或添加一個功能到不是我們創(chuàng)建的、我們不熟悉的荠耽、與我們負責的系統(tǒng)部分無關(guān)的代碼中時,會遇到麻煩倘屹。雖然這可能會是一個繁瑣而艱巨的任務(wù),但是由于使用其他開發(fā)人員編寫的代碼有很大的靈活性慢叨,所以我們可以從中得到大大的好處纽匙,包括增加我們的影響范圍,修復(fù)軟件腐爛以及學(xué)習(xí)我們以前不了解的系統(tǒng)部分(更何況拍谐,還可以學(xué)習(xí)其他程序員的技術(shù)和技巧)烛缔。

作為軟件工程師不可避免會遇到的一個場景是:我們在改變或添加一個功能到不是我們創(chuàng)建的、我們不熟悉的轩拨、與我們負責的系統(tǒng)部分無關(guān)的代碼中時践瓷,會遇到麻煩。雖然這可能會是一個繁瑣而艱巨的任務(wù)亡蓉,但是由于使用其他開發(fā)人員編寫的代碼有很大的靈活性,所以我們可以從中得到大大的好處巫员,包括增加我們的影響范圍,修復(fù)軟件腐爛以及學(xué)習(xí)我們以前不了解的系統(tǒng)部分(更何況,還可以學(xué)習(xí)其他程序員的技術(shù)和技巧)膳灶。

考慮到使用其他開發(fā)人員編寫的代碼既有其厭煩之處锐膜,又有其優(yōu)勢所在,所以我們必須小心不要犯一些嚴重的錯誤:

我們的自我意識:我們可能會覺得自己知道得最多,但通常事實并非如此伟葫。我們要更改的是我們知之甚少的代碼——我們不知道原作者的意圖常拓、導(dǎo)致此代碼的決策以及原作者在寫代碼時可用的工具和框架,等等。謙遜的品質(zhì)價值千金,你值得擁有叹誉。

原作者的自我意識:我們即將接觸的代碼是由另一個開發(fā)人員所編寫的,另一種風(fēng)格、約束、期限和個人生活(消耗他或她工作之外的時間)。只有當我們開始質(zhì)疑他或她做出的決定或質(zhì)疑代碼為什么這么不干凈的時候,那人才會自我反省斥难,不至于夜郎自大镀裤。我們應(yīng)該盡一切努力讓原作者幫助我們工作颗搂,而不是妨礙我們。

對未知的恐懼:很多時候,我們將要接觸的代碼是我們知之甚少或完全一無所知的猜嘱。令人害怕的是:我們將對我們所做的任何改變負責步咪,但是我們基本上就像是在沒有光線的黑暗屋子里走動一樣悯周。其實我們不需要擔心,而是應(yīng)該構(gòu)建一種使我們能夠在大小不一的改變中感到舒適的結(jié)構(gòu),并允許我們確保沒有破壞現(xiàn)有的功能溪北。

由于開發(fā)人員,包括我們自己,是人,所以在處理其他開發(fā)人員編寫的代碼時听想,處理好很多人的天性問題是很有用的蛙粘。在這篇文章中,我們將通過我們可以使用的五種技術(shù)來確保將對人性的理解成為我們的優(yōu)勢,從現(xiàn)有代碼和原作者汲取盡可能多的幫助,并使得其他開發(fā)人員編寫的代碼最后變得比原來更優(yōu)秀叮雳。雖然這里列出的5個方法并不全面,但是使用下面的技術(shù)將確保在結(jié)束改動其他開發(fā)人員編寫的代碼時擒悬,我們有信心保持現(xiàn)有功能的工作狀態(tài)僧凤,同時確保我們的新功能與現(xiàn)有的代碼庫協(xié)調(diào)一致验懊。

1.確保測試的存在

要想確保在其他開發(fā)人員編寫的代碼中所存在的現(xiàn)有功能實際能夠按照預(yù)期的方式工作垄懂,并且我們對其進行的任何更改都不會影響到功能的實現(xiàn),唯一真正令人信心十足的方式是用測試來支持代碼。當我們遇到另一位開發(fā)人員編寫的代碼時俺祠,代碼有兩種所處的狀態(tài):(1)沒有足夠的測試水平,或(2)有足夠的測試水平加叁。遇到前一種情況豫柬,我們得負責創(chuàng)建測試,而在后一種情況下哩陕,我們可以使用現(xiàn)有的測試來確保我們做出的任何更改都不會破壞代碼缺猛,并盡可能多地從測試去了解代碼的意圖湖雹。

創(chuàng)建新測試

這是一個悲傷的例子:我們在改變其他開發(fā)人員的代碼時摔吏,要對更改結(jié)果負責诗箍,但是我們沒有辦法保證我們在進行更改時不破壞任何東西。抱怨是沒有用的。無論我們發(fā)現(xiàn)代碼處在什么樣的條件下格二,我們總歸是要接觸代碼,因此如果代碼壞掉了翰舌,就是我們的責任嚣潜。所以我們在改變代碼時,一定要掌控自己的行為椅贱。確定不會破壞代碼的唯一方法是自己寫測試懂算。

雖然這是乏味的,但它允許我們通過編寫測試來學(xué)習(xí)庇麦,這是它的主要優(yōu)點计技。假設(shè)代碼現(xiàn)在可以正常工作,而我們需要編寫測試山橄,以便預(yù)期的輸入會導(dǎo)致預(yù)期的輸出垮媒。在我們完成這個測試的過程中,我們逐漸了解到代碼的意圖和功能。例如涣澡,給出以下代碼

publicclass?SuccessfulFilterTest?{

privatestaticfinaldoubleTHRESHOLD_NET_SALARY?=?68330.0;

@Test

publicvoid?under30AndNettingThresholdEnsureSuccessful()?{

Person?person?=?new?Person(29,?THRESHOLD_NET_SALARY);

Assert.assertTrue(new?SuccessfulFilter().test(person));

}

@Test

publicvoid?exactly30AndNettingThresholdEnsureUnsuccessful()?{

Person?person?=?new?Person(30,?THRESHOLD_NET_SALARY);

Assert.assertFalse(new?SuccessfulFilter().test(person));

}

@Test

publicvoid?under30AndNettingLessThanThresholdEnsureSuccessful()?{

Person?person?=?new?Person(29,?THRESHOLD_NET_SALARY?-?1);

Assert.assertFalse(new?SuccessfulFilter().test(person));

}

}

我們對代碼的意圖以及為什么在代碼中使用Magic number知道得并不多贱呐,但是我們可以創(chuàng)建一組測試,已知輸入產(chǎn)生已知輸出入桂。例如奄薇,通過做一些簡單的數(shù)學(xué)和解決構(gòu)成成功的閾值薪水問題,我們發(fā)現(xiàn)如果一個人的年齡在30歲以下抗愁,且每年大概賺68,330美元馁蒂,那么他被認為是成功的(按照本規(guī)范的標準)。雖然我們不知道那些magic number是什么蜘腌,但是我們知道它們確實減少了初始的薪水值沫屡。因此,68,330美元的閾值是扣除前的基本工資撮珠。通過使用這些信息沮脖,我們可以創(chuàng)建一些簡單的測試,例如:

publicclass?SuccessfulFilterTest?{

privatestaticfinaldoubleTHRESHOLD_NET_SALARY?=?68330.0;

@Test

publicvoid?under30AndNettingThresholdEnsureSuccessful()?{

Person?person?=?new?Person(29,?THRESHOLD_NET_SALARY);

Assert.assertTrue(new?SuccessfulFilter().test(person));

}

@Test

publicvoid?exactly30AndNettingThresholdEnsureUnsuccessful()?{

Person?person?=?new?Person(30,?THRESHOLD_NET_SALARY);

Assert.assertFalse(new?SuccessfulFilter().test(person));

}

@Test

publicvoid?under30AndNettingLessThanThresholdEnsureSuccessful()?{

Person?person?=?new?Person(29,?THRESHOLD_NET_SALARY?-?1);

Assert.assertFalse(new?SuccessfulFilter().test(person));

}

}

通過這三個測試芯急,我們現(xiàn)在對現(xiàn)有代碼的工作方式有了大致的了解:如果一個人不到30歲勺届,且每年賺$ 68,300,那么他被認為是成功人士娶耍。雖然我們可以創(chuàng)建更多的測試來確保臨界情況(例如空白年齡或工資)功能正常免姿,但是一些簡短的測試不僅使我們了解了原始功能,還給出了一套自動化測試榕酒,可用于確保在對現(xiàn)有代碼進行更改時胚膊,我們不會破壞現(xiàn)有功能。

使用現(xiàn)有測試

如果有足夠的代碼測試組件想鹰,那么我們可以從測試中學(xué)到很多東西紊婉。正如我們創(chuàng)建測試一樣,通過閱讀測試辑舷,我們可以了解代碼如何在功能層面上工作肩榕。此外,我們還可以知道原作者是如何讓代碼運行的惩妇。即使測試是由原作者以外的人(在我們接觸之前)撰寫的,也依然能夠為我們提供關(guān)于其他人對代碼的看法筐乳。

雖然現(xiàn)有的測試可以提供幫助歌殃,但我們?nèi)匀恍枰獙Υ顺直A魬B(tài)度。測試是否與代碼的開發(fā)更改一起與時俱進是很難說的蝙云。如果是的話氓皱,那么這是一個很好的理解基礎(chǔ);如果不是,那么我們要小心不要被誤導(dǎo)。例如波材,如果初始的工資閾值是每年75,000美元股淡,而后來更改為我們的68,330美元,那么下面這個過時的測試可能會使我們誤入歧途:

@Test

publicvoid?under30AndNettingThresholdEnsureSuccessful()?{

Person?person?=?new?Person(29,?75000.0);

Assert.assertTrue(new?SuccessfulFilter().test(person));

}

這個測試還是會通過的廷区,但沒有了預(yù)期的作用唯灵。通過的原因不是因為它正好是閾值,而是因為它超出了閾值隙轻。如果此測試組件包含這樣一個測試用例:當薪水低于閾值1美元時埠帕,過濾器就返回false,這樣第二個測試將會失敗玖绿,表明閾值是錯誤的敛瓷。如果套件沒有這樣的測試,那么陳舊的數(shù)據(jù)會很容易誤導(dǎo)我們弄錯代碼的真正意圖斑匪。當有疑問時呐籽,請相信代碼:正如我們之前所表述的那樣,求解閾值表明測試沒有對準實際閾值蚀瘸。

另外狡蝶,要查看代碼和測試用例的存儲庫日志(即Git日志):如果代碼的最后更新日期比測試的最后更新日期更近(對代碼進行了重大更改,例如更改閾值)苍姜,則測試可能已經(jīng)過時牢酵,應(yīng)謹慎查看。注意衙猪,我們不應(yīng)該完全忽視測試馍乙,因為它們也許仍然能為我們提供關(guān)于原作者(或最近撰寫測試的開發(fā)人員)意圖的一些文檔,但它們可能包含過時或不正確的數(shù)據(jù)垫释。

2.與編寫代碼的人交流

在涉及多個人的任何工作中丝格,溝通至關(guān)重要。無論是企業(yè)棵譬,越野旅行還是軟件項目显蝌,缺乏溝通是損害任務(wù)最有效的手段之一。即使我們在創(chuàng)建新代碼時進行溝通订咸,但是當我們接觸現(xiàn)有的代碼時曼尊,風(fēng)險會增加。因為此時我們對現(xiàn)有的代碼并不太了解脏嚷,因此我們所了解的內(nèi)容可能是被誤導(dǎo)的骆撇,或只代表了其中的一小部分。為了真正了解現(xiàn)有的代碼父叙,我們需要和編寫它的人交流神郊。

當開始提出問題時肴裙,我們需要確定問題是具體的,并且旨在實現(xiàn)我們理解代碼的目標涌乳。例如:

這個代碼片段最適合放到系統(tǒng)的哪里?

你有什么設(shè)計或圖表嗎?

我應(yīng)該注意什么陷阱?

這個組件或類是做什么的?

有沒有什么你想放到代碼里蜻懦,但當時沒有做的?為什么?

始終要保持謙虛的態(tài)度,積極尋求原作者真正的答案夕晓。幾乎每個開發(fā)人員都碰到過這樣的場景宛乃,他或她看著別人的代碼,自問自答:“為什么他/她要這樣做?為什么他們不這樣做?”然后花幾個小時來得出本來只要原作者回答就能得到的結(jié)論运授。大多數(shù)開發(fā)人員都是有才華的程序員烤惊,所以即使如果我們遇到一個看似糟糕的決定,也有可能有一個很好的理由(可能沒有吁朦,但研究別人的代碼時最好假設(shè)他們這樣做是有原因的;如果真的沒有柒室,我們可以通過重構(gòu)來改變)。

溝通在軟件開發(fā)中起次要副作用逗宜。1967年最初由Melvin Conway創(chuàng)立的康威定律規(guī)定:

設(shè)計系統(tǒng)的任何組織…都將不可避免地產(chǎn)生一種設(shè)計雄右,該設(shè)計結(jié)構(gòu)反映了組織的通信結(jié)構(gòu)。

這意味著纺讲,一個龐大擂仍、緊密溝通的團隊可能會生成一體化,緊密耦合的代碼熬甚,但一些較小的團隊可能會生成更獨立逢渔、松散耦合的代碼(有關(guān)此相關(guān)性的更多信息,請參閱《Demystifying Conway’s Law》)乡括。對于我們來說肃廓,這意味著我們的通信結(jié)構(gòu)不僅影響特定的代碼段,也影響整個代碼庫诲泌。因此盲赊,與原作者密切溝通絕對是一個好辦法,但我們應(yīng)該自檢不要太過于依賴于原作者敷扫。這不僅可能會惹惱原作者哀蘑,還可能在我們的代碼中產(chǎn)生無意識的耦合。

雖然這有助于我們深入研究代碼葵第,但這是在假設(shè)可以接觸原作者的情況下绘迁。在很多時候,原作者可能已經(jīng)離開了公司卒密,或恰巧不在公司(例如正在休假)缀台。在此種情況下我們該做什么?詢問可能對代碼有所了解的人。這個人不一定要曾真正工作于代碼栅受,他可以是在原作者編寫代碼時就在周圍将硝,也可以是認識原作者。哪怕僅是從原開發(fā)者周圍的人中得到只言片語屏镊,也可能會啟迪其他未知的代碼片段依疼。

3.刪除所有警告

心理學(xué)中有一個眾所周知的概念,稱為“破窗理論”而芥,Andrew Hunt和Dave Thomas在《The Pragmatic Programmer》(第4-6頁)中詳細描述了這個概念律罢。這個理論最初是由James Q.Wilson和George L. Kelling提出的,描述如下:

假設(shè)有一個建筑物有幾扇破了的窗戶棍丐。如果窗戶沒有修好误辑,那么破壞者會趨向于打破更多的窗戶。最終歌逢,他們甚至可能會破門而入巾钉,如果建筑物是沒人住的,那么他們可能會非法占有或者在里面點火秘案。也可以考慮人行道的情況砰苍。如果道路上面有垃圾堆積,那么不久之后阱高,就會有更多的垃圾累積赚导。最終,人們甚至?xí)_始往那里扔外賣垃圾赤惊,甚至打破汽車吼旧。

這個理論指出,如果似乎已經(jīng)沒人關(guān)心這個物品或事物未舟,那么我們就會忽視對物品或事物的照顧圈暗,這是人的天性。例如处面,如果一棟建筑物看上去已經(jīng)凌亂不堪厂置,那么它更有可能被肆意破壞。在軟件方面魂角,這個理論意味著如果開發(fā)人員發(fā)現(xiàn)代碼已經(jīng)是一團糟昵济,那么人的本性會讓他弄壞代碼。從本質(zhì)上說野揪,我們心里想的是(即使心理活動沒有這么豐富)访忿,“既然最后一個人不在乎這代碼,我為什么要在乎?”或“都是亂糟糟的代碼斯稳,誰知道是誰寫的海铆。”

但是挣惰,這不應(yīng)該成為我們的借口卧斟。只要我們接觸以前屬于其他人的代碼殴边,那么我們就要對這些代碼負責,并且如果它不能有效工作的話珍语,我們得擔負后果锤岸。為了戰(zhàn)勝這種人的天性行為,我們需要采取一些小措施以避免我們的代碼更少地被弄臟(及時更換破掉的窗戶)板乙。

一個簡單方法是刪除來自我們正在使用的整個包或模塊中的所有警告是偷。至于未使用或添加注釋的代碼,刪除它募逞。如果我們稍后需要這部分代碼蛋铆,那么在存儲庫中,我們總是可以從先前的提交中檢索它放接。如果存在無法直接解決的警告(例如原始類型警告)刺啦,那么使用@SuppressWarnings注解注釋該調(diào)用或方法。這樣可以確保我們對代碼進行過仔細的考慮:它們不是因為疏忽而發(fā)出的警告透乾,而是我們明確地注意到了警告(如原始類型)洪燥。

一旦我們刪除或明確地禁止所有警告,那么我們就必須確保代碼保持免除警告乳乌。這有兩個主要作用:

迫使我們仔細考慮我們創(chuàng)建的任何代碼捧韵。

減少代碼腐敗的變化,現(xiàn)在的警告會導(dǎo)致以后的錯誤汉操。

這對其他人再来,以及我們自己都有心理暗示作用——我們其實關(guān)心我們正在處理的代碼。它不再是條單行線——我們強逼著自己更改代碼磷瘤,提交芒篷,然后永不回頭。相反采缚,我們認識到我們需要對這代碼負責针炉。這對之后的軟件開發(fā)也是有幫助的——它向?qū)淼拈_發(fā)人員展示,這不是一間窗戶都破了的倉庫:而是一個維護良好的代碼庫扳抽。

4.重構(gòu)

在過去幾十年中篡帕,重構(gòu)已經(jīng)成為了一個非常重要的術(shù)語,并且最近被當作是對當前工作代碼做任何改變的代名詞贸呢。雖然重構(gòu)確實涉及對當前正在工作的代碼的更改镰烧,但并非整個大局。Martin Fowler在他關(guān)于這個話題的重要著作——《Refactoring》一書中將重構(gòu)定義為:

對軟件的內(nèi)部結(jié)構(gòu)進行更改楞陷,使其更容易理解并且修改起來更便宜怔鳖,而不改變其可觀察的行為。

這個定義的關(guān)鍵在于它涉及的更改不會改變系統(tǒng)可觀察的行為固蛾。這意味著當我們重構(gòu)代碼時结执,我們必須要有方法來確保代碼的外部可見行為不會改變度陆。在我們的例子中,這意味著是在我們繼承或自己開發(fā)的測試套件中献幔。為了確保我們沒有改變系統(tǒng)的外部行為坚芜,每當我們進行改變時,都必須重新編譯和執(zhí)行我們的全部測試斜姥。

此外,并不是我們所做的每一個改變都被認為是重構(gòu)沧竟。例如铸敏,重命名方法以更好地反映其預(yù)期用途是重構(gòu),但添加新功能不是悟泵。為了看到重構(gòu)的好處杈笔,我們將重構(gòu)SuccessfulFilter。執(zhí)行的第一個重構(gòu)是提取方法糕非,以更好地封裝個人凈工資的邏輯:

publicclass?SuccessfulFilter?implements?Predicate?{

@Override

publicboolean?test(Person?person)?{

returnperson.getAge()?<?30?&&?getNetSalary(person)?>?60000;

}

privatedoublegetNetSalary(Person?person)?{

return(((person.getSalary()?-?(250?*?12))?-?1500)?*?0.94);

}

}

在我們進行這種改變之后蒙具,我們重新編譯并運行我們的測試套件,測試套件將繼續(xù)通過⌒喾剩現(xiàn)在更容易看出禁筏,成功是通過一個人的年齡和凈薪酬定義的,但是getNetSalary方法似乎并不像Person類一樣屬于SuccessfulFilter(指示標志就是該方法的唯一參數(shù)是Person衡招,該方法的唯一調(diào)用是Person類的方法篱昔,因此對Person類有很強的親和力)。 為了更好地定位這個方法始腾,我們執(zhí)行一個Move方法將其移動到Person類:

publicclass?Person?{

privateintage;

privatedoublesalary;

publicPerson(intage,doublesalary)?{

this.age?=?age;

this.salary?=?salary;

}

publicvoid?setAge(intage)?{

this.age?=?age;

}

publicintgetAge()?{

returnage;

}

publicvoid?setSalary(doublesalary)?{

this.salary?=?salary;

}

publicdoublegetSalary()?{

returnsalary;

}

publicdoublegetNetSalary()?{

return((getSalary()?-?(250?*?12))?-?1500)?*?0.94;

}

}

publicclass?SuccessfulFilter?implements?Predicate?{

@Override

publicboolean?test(Person?person)?{

returnperson.getAge()?<?30?&&?person.getNetSalary()?>?60000;

}

}

為了進一步清理此代碼州刽,我們對每個magic number執(zhí)行符號常量替換magic number行為。為了知道這些值的含義浪箭,我們可能得和原作者交流穗椅,或者向具有足夠領(lǐng)域知識的人請教,以引領(lǐng)正確的方向奶栖。我們還將執(zhí)行更多的提取方法重構(gòu)匹表,以確保現(xiàn)有的方法盡可能簡單驼抹。

publicclass?Person?{

privatestaticfinalintMONTHLY_BONUS?=?250;

privatestaticfinalintYEARLY_BONUS?=?MONTHLY_BONUS?*?12;

privatestaticfinalintYEARLY_BENEFITS_DEDUCTIONS?=?1500;

privatestaticfinaldoubleYEARLY_401K_CONTRIBUTION_PERCENT?=?0.06;

privatestaticfinaldoubleYEARLY_401K_CONTRIBUTION_MUTLIPLIER?=?1?-?YEARLY_401K_CONTRIBUTION_PERCENT;

privateintage;

privatedoublesalary;

publicPerson(intage,doublesalary)?{

this.age?=?age;

this.salary?=?salary;

}

publicvoid?setAge(intage)?{

this.age?=?age;

}

publicintgetAge()?{

returnage;

}

publicvoid?setSalary(doublesalary)?{

this.salary?=?salary;

}

publicdoublegetSalary()?{

returnsalary;

}

publicdoublegetNetSalary()?{

returngetPostDeductionSalary();

}

privatedoublegetPostDeductionSalary()?{

returngetPostBenefitsSalary()?*?YEARLY_401K_CONTRIBUTION_MUTLIPLIER;

}

privatedoublegetPostBenefitsSalary()?{

returngetSalary()?-?YEARLY_BONUS?-?YEARLY_BENEFITS_DEDUCTIONS;

}

}

publicclass?SuccessfulFilter?implements?Predicate?{

privatestaticfinalintTHRESHOLD_AGE?=?30;

privatestaticfinaldoubleTHRESHOLD_SALARY?=?60000.0;

@Override

publicboolean?test(Person?person)?{

returnperson.getAge()?<?THRESHOLD_AGE?&&?person.getNetSalary()?>?THRESHOLD_SALARY;

}

}

重新編譯和測試桑孩,發(fā)現(xiàn)系統(tǒng)仍然按照預(yù)期的方式工作:我們沒有改變外部行為,但是我們改進了代碼的可靠性和內(nèi)部結(jié)構(gòu)框冀。有關(guān)更復(fù)雜的重構(gòu)和重構(gòu)過程流椒,請參閱Martin Fowler的Refactoring Guru網(wǎng)站。

5.當你離開的時候明也,代碼比你發(fā)現(xiàn)它的時候更好

最后這個技術(shù)在概念上非常簡單宣虾,但在實踐中很困難:讓代碼比你發(fā)現(xiàn)它的時候更好惯裕。當我們梳理代碼,特別是別人的代碼時绣硝,我們大多會添加功能蜻势,測試它,然后前行鹉胖,不關(guān)心我們會不會貢獻軟件腐爛握玛,也不在乎我們添加到類的新方法會不會導(dǎo)致額外的混亂。因此甫菠,本文的全部內(nèi)容可總結(jié)為以下規(guī)則:

每當我們修改代碼時挠铲,請確保當你離開的時候,代碼比你發(fā)現(xiàn)它的時候更好寂诱。

前面提到過拂苹,我們需要對類造成的損壞和對改變的代碼負責,如果它不能工作痰洒,那么修復(fù)是我們的職責瓢棒。為了戰(zhàn)勝伴隨軟件生產(chǎn)而出現(xiàn)的熵,我們必須強制自己做到離開時的代碼比我們發(fā)現(xiàn)它的時候更佳丘喻。為了不逃避這個問題脯宿,我們必須償還技術(shù)債務(wù),確保下一個接觸代碼的人不需要再付出代價泉粉。說不定嗅绰,將來可能是我們自己感謝自己這個時候的堅持呢。

本文作者:小峰原創(chuàng)翻譯

來源:51CTO

原文鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末搀继,一起剝皮案震驚了整個濱河市窘面,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌叽躯,老刑警劉巖财边,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件搞动,死亡現(xiàn)場離奇詭異樊零,居然都是意外死亡,警方通過查閱死者的電腦和手機紊撕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門黑滴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來憨募,“玉大人,你說我怎么就攤上這事袁辈〔艘ィ” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長尾膊。 經(jīng)常有香客問我媳危,道長,這世上最難降的妖魔是什么冈敛? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任待笑,我火速辦了婚禮,結(jié)果婚禮上抓谴,老公的妹妹穿的比我還像新娘暮蹂。我一直安慰自己,他們只是感情好癌压,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布椎侠。 她就那樣靜靜地躺著,像睡著了一般措拇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上慎宾,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天丐吓,我揣著相機與錄音,去河邊找鬼趟据。 笑死券犁,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的汹碱。 我是一名探鬼主播粘衬,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼咳促!你這毒婦竟也來了稚新?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤跪腹,失蹤者是張志新(化名)和其女友劉穎褂删,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冲茸,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡屯阀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了轴术。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片难衰。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖逗栽,靈堂內(nèi)的尸體忽然破棺而出盖袭,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布苍凛,位于F島的核電站趣席,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏醇蝴。R本人自食惡果不足惜宣肚,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望悠栓。 院中可真熱鬧霉涨,春花似錦、人聲如沸惭适。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽癞志。三九已至往枷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間凄杯,已是汗流浹背错洁。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留戒突,地道東北人屯碴。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像膊存,于是被迫代替她去往敵國和親导而。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

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

  • 作為軟件工程師不可避免會遇到的一個場景是:我們在改變或添加一個功能到不是我們創(chuàng)建的隔崎、我們不熟悉的今艺、與我們負責的系統(tǒng)...
    肆虐的悲傷閱讀 320評論 0 0
  • 身為一個軟件工程師,我們不可避免的會遇到這樣一些問題:不得不修改別人的代碼爵卒,或者在別人的代碼中添加新的功能洼滚。我們并...
    OSC開源社區(qū)閱讀 668評論 1 1
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)技潘,斷路器遥巴,智...
    卡卡羅2017閱讀 134,601評論 18 139
  • 最重要的工作,是規(guī)律生活和健康管理享幽。 我從事文字工作铲掐,經(jīng)營舊書店“COW BOOKS”,擔任《生活手帖》的主編值桩。在...
    彧言彧語閱讀 243評論 0 0
  • 前文再續(xù),書接上一回携栋!在昨天的收盤點評《生蠔說:想不想連續(xù)上攻搭盾?想的話磨一兩天》中,我們講到:“ 1婉支、你想大...
    果園生蠔閱讀 410評論 0 0