怎樣編寫沒法維護的代碼
讓本身穩拿鐵飯碗 ;-)
Roedy Green
簡單介紹
永遠不要(把本身遇到的問題)歸因於(他人的)惡意,這偏偏說明了(你本身的)無能。html
-- 拿破崙前端
爲了造福大衆,在Java編程領域創造就業機會,兄弟我在此傳授大師們的祕籍。這些大師寫的代碼極其難以維護。後繼者就是想對它作最簡單的改動都需要花上數年時間。而且,假設你能對比祕籍潛心修煉,你甚至可以給本身弄個鐵飯碗。因爲除了你以外,沒人能維護你寫的代碼。java
再而且。假設你能練就祕籍中的全部招式,那麼連你本身都沒法維護你的代碼了!算法
你不想練功過分走火入魔吧。數據庫
那就不要讓你的代碼一眼看去就全然沒法維護,僅僅要它實質上是那樣便可了。不然。你的代碼就有被重寫或重構的風險。編程
總體原則
Quidquid latine dictum sit, altum sonatur.
(隨便用拉丁文寫點啥都會顯得高大上。)小程序
想挫敗維護代碼的程序猿,你必須先明確他的思惟方式。後端
他接手了你的龐大程序,沒有時間把它全部讀一遍,更別說理解它了。他無非是想高速找到改動代碼的位置、改代碼、編譯,而後就能交差,並但願他的改動不會出現意外的反作用。數組
他查看你的代碼只是是管中窺豹,一次僅僅能看到一小段而已。緩存
你要確保他永遠看不到全貌。要儘可能和讓他難以找到他想找的代碼。但更重要的是。要讓他不能有把握忽略不論什麼東西。
程序猿都被編程慣例洗腦了,還爲此自鳴得意。每一次你處心積慮地違背編程慣例,都會迫使他必須用放大鏡去細緻閱讀你的每一行代碼。
你可能會認爲每個語言特性都可以用來讓代碼難以維護。事實上否則。你必須精心地誤用它們才行。
命名
"當我使用一個單詞的時候" Humpty Dumpty 之前用一種輕視的口氣說, "它就是我想表達的意思。很少也很多。「
- Lewis Carroll -- 《愛麗絲魔鏡之旅》, 第6章
編寫沒法維護代碼的技巧的重中之重是變量和方法命名的藝術。怎樣命名是和編譯器無關的。這就讓你有巨大的自由度去利用它們迷惑維護代碼的程序猿。
妙用 寶寶起名大全
買本寶寶起名大全。你就永遠不缺變量名了。比方
Fred 就是個好名字。而且鍵盤輸入它也省事。假設你就想找一些easy輸入的變量名,可以試試
adsf 或者
aoeu之類。
單字母變量名
假設你給變量起名爲a,b,c。用簡單的文本編輯器就無法搜索它們的引用。
而且,沒人能猜到它們的含義。
創造性的拼寫錯誤
假設你必須使用描寫敘述性的變量和函數名。那就把它們都拼錯。
還可以把某些函數和變量名拼錯,再把其它的拼對(好比 SetPintleOpening 和 SetPintalClosing) ,咱們就能有效地將grep或IDE搜索技術玩弄於股掌之上。
這招超級管用。還可以混淆不一樣語言(比方colour -- 英國英語,和 color -- 美國英語)。
抽象
在命名函數和變量的時候,充分利用抽象單詞,好比 it, everything, data, handle, stuff, do, routine, perform 和數字。好比 e.g.
routineX48,
PerformDataFunction,
DoIt,
HandleStuff還有
do_args_method。
首字母大寫的縮寫
用首字母大寫縮寫(比方GNU 表明 GNU's Not Unix) 使代碼簡潔難懂。真正的漢子(無論男女)歷來不說明這樣的縮寫的含義,他們生下來就懂。
辭典大輪換
爲了打破沉悶的編程氣氛。你可以用一本辭典來查找儘可能多的同義詞。好比 display, show, present。在凝視裏含糊其辭地暗示這些命名之間有細微的區別,事實上根本沒有。
只是,假設有兩個命名類似的函數真的有重大區別。那卻是必定要確保它們用一樣的單詞來命名(好比。對於 "寫入文件", "在紙上書寫" 和 "屏幕顯示" 都用 print 來命名)。
在不論什麼狀況下都不要屈服於編寫明白的項目詞彙表這樣的無理要求。你可以辯講解,這樣的要求是一種不專業的行爲,它違反告終構化設計的信息隱藏原則。
首字母大寫
隨機地把單詞中間某個音節的首字母大寫。好比
ComputeReSult()。
重用命名
在語言規則贊成的地方,儘可能把類、構造器、方法、成員變量、參數和局部變量都命名成同樣。更高級的技巧是在{}塊中重用局部變量。這樣作的目的是迫使維護代碼的程序猿認真檢查每個演示樣例的範圍。特別是在Java代碼中,可以把普通方法假裝成構造器。
使用非英語字母
在命名中偷偷使用不易察覺的非英語字母,好比
typedef struct { int i; } ínt;
看上去沒啥不正確是吧?嘿嘿嘿...這裏的第二個 ínt 的
í 其實是東北歐字母。並不是英語中的 i 。在簡單的文本編輯器裏。想看出這一點點差異差點兒是不可能的。
巧妙利用編譯器對於命名長度的限制
假設編譯器僅僅區分命名的前幾位,比方前8位,那麼就把後面的字母寫得不同。
比方。事實上是同一個變量,有時候寫成 var_unit_update() ,有時候又寫成 var_unit_setup(),看起來是兩個不一樣的函數調用。而在編譯的時候,它們事實上是同一個變量 var_unit。
下劃線。一位真正的朋友
可以拿 _ 和 __ 做爲標示符。
混合多語言
隨機地混用兩種語言(人類語言或計算機語言都行)。
假設老闆要求使用他指定的語言。你就告訴他你用本身的語言更有利於組織你的思路,萬一這招不管用,就去控訴這是語言鄙視,並威脅起訴老闆要求鉅額精神損失賠償。
擴展 ASCII 字符
擴展 ASCII 字符用於變量命名是全然合法的,包含 ß, Ð, 和 ñ 等。在簡單的文本編輯器裏。除了拷貝/粘貼。基本上無法輸入。
其它語言的命名
使用外語字典做爲變量名的來源。
好比,可以用德語單詞 punkt 取代 point。
除非維護代碼的程序猿也像你同樣熟練掌握了德語. 否則他就僅僅能盡情地在代碼中享受異域風情了。
數學命名
用數學操做符的單詞來命名變量。好比:
openParen
= (slash
+ asterix)
/ equals;
(左圓括號 = (斜槓 + 星號)/等號;)
使人眩暈的命名
用帶有全然不相關的感情色彩的單詞來命名變量。好比:
marypoppins
= (superman
+ starship)
/ god;
(歡樂滿人間 = (超人 + 星河戰隊)/上帝;)
這一招可以讓閱讀代碼的人陷入迷惑之中,因爲他們在試圖想清楚這些命名的邏輯時。會不自覺地聯繫到不一樣的感情場景裏而沒法自拔。
什麼時候使用 i
永遠不要把
i 用做最內層的循環變量。
用什麼命名都行,就是別用i。把 i 用在其它地方就隨便了,用做非整數變量尤爲好。
慣例 -- 明修棧道,暗度陳倉
忽視 Java 編碼慣例。Sun 就是這樣作的。幸運的是,你違反了它編譯器也不會打小報告。這一招的目的是搞出一些在某些特殊狀況下有細微區別的名字來。假設你被強迫遵循駝峯法命名。你仍是可以在某些模棱兩可的狀況下顛覆它。
好比。inputFilename 和 inputfileName 兩個命名都可以合法使用。在此基礎上本身發明一套複雜到變態的命名慣例,而後就可以痛扁其它人,說他們違反了慣例。
小寫的 l 看上去很是像數字 1
用小寫字母 l 標識 long 常數。好比 10l 更easy被誤以爲是 101 而不是 10L 。
禁用所有能讓人準確區分 uvw wW gq9 2z 5s il17|!j oO08 `'" ;,. m nn rn {[()]} 的字體。要作個有創造力的人。
把全局命名重用爲私有
在A 模塊裏聲明一個全局數組,而後在B 模塊的頭文件中在聲明一個同名的私有數組,這樣看起來你在B 模塊裏引用的是那個全局數組,事實上卻不是。不要在凝視裏提到這個反覆的狀況。
誤導性的命名
讓每個方法都和它的名字蘊含的功能有一些差別。
好比。一個叫 isValid(x)的方法在推斷完參數x的合法性以後,還順帶着把它轉換成二進制並保存到數據庫裏。
假裝
當一個bug需要越長的時間纔會暴露,它就越難被發現。
- Roedy Green(本文做者)
編寫沒法維護代碼的還有一大祕訣就是假裝的藝術,即隱藏它或者讓它看起來像其它東西。很是多招式有賴於這樣一個事實:編譯器比肉眼或文本編輯器更有分辨能力。如下是一些假裝的最佳招式。
把代碼假裝成凝視,反之亦然
如下包含了一些被凝視掉的代碼,但是一眼看去卻像是正常代碼。
for(j
=0; j
<array_len; j
+ =8)
{
total
+= array
[j
+0
];
total
+= array
[j
+1
];
total
+= array
[j
+2
];
/* Main body of
total += array[j+3]; * loop is unrolled
total += array[j+4]; * for greater speed.
total += array[j+5]; */
total
+= array
[j
+6
];
total
+= array
[j
+7
];
}
假設不是用綠色標出來。你能注意到這三行代碼被凝視掉了麼?
用鏈接符隱藏變量
對於如下的定義
可以把 "xy_z" 打散到兩行裏:
#define local_var xy\
_z // local_var OK
這樣全局搜索 xy_z 的操做在這個文件中就一無所得了。 對於 C 預處理器來講。第一行最後的 "\" 表示繼續拼接下一行的內容。
文檔
不論什麼傻瓜都能說真話,而要把謊編圓則需要至關的智慧。
- Samuel Butler (1835 - 1902)
不對的文檔每每比沒有文檔還糟糕。
- Bertrand Meyer
既然計算機是忽略凝視和文檔的,你就可以在裏邊冠冕堂皇地編織彌天大謊。讓可憐的維護代碼的程序猿完全迷失。
在凝視中撒謊
實際上你不需要主動地撒謊,僅僅要沒有及時保持凝視和代碼更新的一致性就可以了。
僅僅記錄顯而易見的東西
往代碼裏摻進去相似於
/* 給 i 加 1 */ 這種凝視。但是永遠不要記錄包或者方法的整體設計這種乾貨。
記錄 How 而不是 Why
僅僅解釋一個程序功能的細節,而不是它要完畢的任務是什麼。
這種話,假設出現了一個bug。修復者就搞不清這裏的代碼應有的功能。
該寫的別寫
比方你在開發一套航班預約系統,那就要精心設計,讓它在添加還有一個航空公司的時候至少有25處代碼需要改動。永遠不要在文檔裏說明要改動的位置。
後來的開發者要想改動你的代碼門都沒有。除非他們能把每一行代碼都讀懂。
計量單位
永遠不要在文檔中說明不論什麼變量、輸入、輸出或參數的計量單位,如英尺、米、加侖等。計量單位對數豆子不是過重要,但在project領域就至關重要了。
同理,永遠不要說明不論什麼轉換常量的計量單位,或者是它的取值怎樣得到。要想讓代碼更亂的話。你還可以在凝視裏寫上錯誤的計量單位,這是赤裸裸的欺騙,但是頗有效。假設你想作一個惡貫滿盈的人。最好仍是本身發明一套計量單位,用本身或某個小人物的名字命名這套計量單位。但不要給出定義。萬一有人挑刺兒。你就告訴他們,你這麼作是爲了把浮點數運算湊成整數運算而進行的轉換。
坑
永遠不要記錄代碼中的坑。
假設你懷疑某個類裏可能有bug。天知地知你知就好。
假設你想到了重構或重寫代碼的思路,看在老天爺的份上。千萬別寫出來。
切記電影《小鹿斑比》裏那句臺詞 "假設你不能說好聽的話,那就什麼也不要說。"。
萬一這段代碼的原做者看到你的凝視怎麼辦?萬一老闆看到了怎麼辦?萬一客戶看到了怎麼辦?搞很差最後你本身被解僱了。一句」這裏需要改動「的匿名凝視就好多了,尤爲是當看不清這句凝視指的是哪裏需要改動的狀況下。切記可貴糊塗四個字,這樣你們都不會感受受到了批評。
說明變量
永遠不要 對變量聲明加凝視。有關變量使用的方式、邊界值、合法值、小數點後的位數、計量單位、顯示格式、數據錄入規則等等,後繼者全然可以本身從程序代碼中去理解和整理嘛。假設老闆強迫你寫凝視,就把方法體代碼混進去,但絕對不要對變量聲明寫凝視,即便是暫時變量!
在凝視裏唆使離間
爲了阻撓不論什麼僱傭外部維護承包商的傾向,可以在代碼中散佈針對其它同行軟件公司的攻擊和抹黑。特別是可能接替你工做的當中不論什麼一家。
好比:
/* 優化後的內層循環
這套技巧對於SSI軟件服務公司的那幫蠢材來講過高深了。他們僅僅會
用 <math.h> 裏的笨例程,消耗50倍的內存和處理時間。
*/
class clever_SSInc
可能的話,除了凝視以外。這些攻擊抹黑的內容也要摻到代碼裏的重要部分,這樣假設管理層想清理掉這些攻擊性的言論而後發給外部承包商去維護,就會破壞代碼結構。
程序設計
編寫沒法維護代碼的基本規則就是:在儘量多的地方,以儘量多的方式表述每一個事實。
- Roedy Green
編寫可維護代碼的關鍵因素是僅僅在一個地方表述應用裏的一個事實。假設你的想法變了,你也僅僅在一個地方改動。這樣就能保證整個程序正常工做。因此,編寫沒法維護代碼的關鍵因素就是重複地表述同一個事實,在儘量多的地方,以儘量多的方式進行。使人高興的是。像Java這種語言讓編寫這種沒法維護代碼變得很是easy。好比。改變一個被引用很是多的變量的類型差點兒是不可能的,因爲所有造型和轉換功能都會出錯,而且關聯的暫時變量的類型也不合適了。
而且。假設變量值要在屏幕上顯示,那麼所有相關的顯示和數據錄入代碼都必須一一找到並手工進行改動。相似的還有很是多。比方由C和Java組成的Algol語言系列。Abundance甚至Smalltalk對於數組等結構的處理,都是大有可爲的。
Java 造型
Java的造型機制是上帝的禮物。你可以心安理得地使用它,因爲Java語言自己就需要它。每次你從一個Collection 裏獲取一個對象。你都必須把它造型爲原始類型。
這樣這個變量的類型就必須在無數地方表述。
假設後來類型變了,所有的造型都要改動才幹匹配。
假設倒黴的維護代碼的程序猿沒有找全(或者改動太多),編譯器能不能檢測到也很差說。
相似的,假設變量類型從short 變成 int,所有匹配的造型也都要從(short) 改爲 (int)。
利用Java的冗餘
Java要求你給每個變量的類型寫兩次表述。 Java 程序猿已經習慣了這樣的冗餘,他們不會注意到你的兩次表述有細微的區別,好比
Bubblegum
b = new Bubblegom();
不幸的是 ++ 操做符的盛行讓如下這樣的僞冗餘代碼得手的難度變大了:
永遠不作校驗
永遠不要對輸入數據作不論什麼的正確性或差別性檢查。這樣能表現你對公司設備的絕對信任,以及你是一位信任所有項目夥伴和系統管理員的團隊合做者。
老是返回合理的值。即便數據輸入有問題或者錯誤。
有禮貌,無斷言
避免使用 assert() 機制,因爲它可能把三天的debug盛宴變成10分鐘的快餐。
避免封裝
爲了提升效率,不要使用封裝。方法的調用者需要所有能獲得的外部信息。以便了解方法的內部是怎樣工做的。
複製粘貼改動
以效率的名義。使用 複製+粘貼+改動。這樣比寫成小型可複用模塊效率高得多。在用代碼行數衡量你的進度的小做坊裏。這招尤爲管用。
使用靜態數組
假設一個庫裏的模塊需要一個數組來存放圖片,就定義一個靜態數組。沒人會有比512 X 512 更大的圖片。因此固定大小的數組就可以了。
爲了最佳精度,就把它定義成 double 類型的數組。
傻瓜接口
編寫一個名爲 "WrittenByMe" 之類的空接口。而後讓你的所有類都實現它。
而後給所有你用到的Java 內置類編寫包裝類。
這裏的思想是確保你程序裏的每個對象都實現這個接口。最後,編寫所有的方法,讓它們的參數和返回類型都是這個 WrittenByMe。這樣就差點兒不可能搞清楚某個方法的功能是什麼,並且所有類型都需要好玩的造型方法。更出格的玩法是,讓每個團隊成員編寫它們本身的接口(好比 WrittenByJoe),程序猿用到的不論什麼類都要實現他本身的接口。這樣你就可以在大量無心義接口中隨便找一個來引用對象了。
巨型監聽器
永遠不要爲每個組件建立分開的監聽器。
對所有button老是用同一個監聽器,僅僅要用大量的if...else 來推斷是哪個button被點擊便可了。
好事成堆TM
狂野地使用封裝和OO思想。
好比
myPanel
.add( getMyButton
() );
private JButton
getMyButton()
這段很是可能看起來不怎麼可笑。別操心,僅僅是時候未到而已。
友好的朋友
在C++ 裏儘可能多使用friend聲明。再把建立類的指針傳遞給已建立類。現在你不用浪費時間去考慮接口了。另外。你應該用上keywordprivate 和 protected 來代表你的類封裝得很是好。
使用三維數組
大量使用它們。用扭曲的方式在數組之間移動數據,比方,用arrayA裏的行去填充arrayB的列。
這麼作的時候,不管三七二十一再加上1的偏移值,這樣很是靈。讓維護代碼的程序猿抓狂去吧。
混合與匹配
存取方法和公共變量神馬的都要給他用上。這種話,你無需調用存取器的開銷就可以改動一個對象的變量。還能宣稱這個類是個"Java Bean"。對於那些試圖加入日誌函數來找出改變值的源頭的維護代碼的程序猿,用這一招來迷惑他尤爲有效。
沒有祕密!
把每個方法和變量都聲明爲 public。畢竟某我的某天可能會需要用到它。一旦方法被聲明爲public 了,就很是難縮回去。
對不?這樣不論什麼它覆蓋到的代碼都很是難改動了。它還有個使人愉快的反作用。就是讓你看不清類的做用是什麼。假設老闆質問你是否是瘋了,你就告訴他你遵循的是經典的透明接口原則。
全堆一塊
把你所有的無用的和過期的方法和變量都留在代碼裏。畢竟提及來。既然你在1976年用過一次,誰知道你啥時候會需要再用到呢?固然程序是改了,但它也可能會改回來嘛。你"不想要又一次發明輪子"(領導們都會喜歡這種口氣)。假設你還原封不動地留着這些方法和變量的凝視。而且凝視寫得又高深莫測,甭管維護代碼的是誰。恐怕都不敢對它輕舉妄動。
就是 Final
把你所有的葉子類都聲明爲 final。畢竟提及來,你在項目裏的活兒都幹完了,顯然不會有其它人會經過擴展你的類來改進你的代碼。這樣的狀況甚至可能有安全漏洞。
java.lang.String 被定義成 final 或許就是這個緣由吧?假設項目組其它程序猿有意見。告訴他們這樣作能夠提升執行速度。
避免佈局
永遠不要用到佈局。
當維護代碼的程序猿想添加一個字段,他必須手工調整屏幕上顯示所有內容的絕對座標值。
假設老闆強迫你使用佈局,那就寫一個巨型的 GridBagLayout 並在裏面用絕對座標進行硬編碼。
全局變量。怎麼強調都只是分
假設上帝不肯意咱們使用全局變量,他就不會發明出這個東西。
不要讓上帝失望,儘可能多使用全局變量。每個函數最起碼都要使用和設置當中的兩個。即便沒有理由也要這麼作。畢竟,不論什麼優秀的維護代碼的程序猿都會很是快搞清楚這是一種偵探工做測試,有利於讓他們從笨蛋中脫穎而出。
再一次說說全局變量
全局變量讓你可以省去在函數裏描寫敘述參數的麻煩。充分利用這一點。在全局變量中選那麼幾個來表示對其它全局變量進行操做的類型。
局部變量
永遠不要用局部變量。
在你感受想要用的時候,把它改爲一個實例或者靜態變量。並沒有私地和其它方法分享它。這樣作的優勢是,你之後在其它方法裏寫相似聲明的時候會節省時間。
C++程序猿可以百尺竿頭更進一步。把所有變量都弄成全局的。
配置文件
配置文件通常是以 keyword = 值 的形式出現。在載入時這些值被放入 Java 變量中。最明顯的迷惑技術就是把有細微區別的名字用於keyword和Java 變量.甚至可以在配置文件中定義執行時根本不會改變的常量。
參數文件變量和簡單變量比,維護它的代碼量起碼是後者的5倍。
子類
對於編寫沒法維護代碼的任務來講,面向對象編程的思想簡直是天賜之寶。
假設你有一個類,裏邊有10個屬性(成員/方法),可以考慮寫一個基類,裏面僅僅有一個屬性,而後產生9層的子類,每層添加一個屬性。
等你訪問到終於的子類時,你才幹獲得全部10個屬性。
假設可能,把每個類的聲明都放在不一樣的文件中。
編碼迷局
迷惑 C
從互聯網上的各類混亂C 語言競賽中學習。追隨大師們的腳步。
追求極致
老是追求用最迷惑的方式來作普通的任務。好比。要用數組來把整數轉換爲對應的字符串。可以這麼作:
char *p;
switch (n)
{
case 1:
p = "one";
if (0)
case 2:
p = "two";
if (0)
case 3:
p = "three";
printf("%s", p);
break;
}
一致性的小淘氣
當你需要一個字符常量的時候,可以用多種不一樣格式: ' ', 32, 0x20, 040。
在C或Java裏10和010是不一樣的數(0開頭的表示16進制),你也可以充分利用這個特性。
造型
把所有數據都以 void * 形式傳遞,而後再造型爲合適的結構。
不用結構而是經過位移字節數來造型也很是好玩。
嵌套 Switch
Switch 裏邊還有 Switch。這樣的嵌套方式是人類大腦難以破解的。
利用隱式轉化
牢記編程語言中所有的隱式轉化細節。充分利用它們。數組的索引要用浮點變量。循環計數器用字符,對數字運行字符串函數調用。不管怎麼說,所有這些操做都是合法的,它們無非是讓源碼更簡潔而已。
不論什麼嘗試理解它們的維護者都會對你感激涕零,因爲他們必須閱讀和學習整個關於隱式數據類型轉化的章節,而這個章節很是多是他們來維護你的代碼以前全然忽略了的。
分號!
在所有語法贊成的地方都加上分號,好比:
使用八進制數
把八進制數混到十進制數列表裏,就像這樣:
array
= new int
[]
{
111
,
120
,
013
,
121
,
};
嵌套
儘量深地嵌套。優秀的程序猿能在一行代碼裏寫10層(),在一個方法裏寫20層{}。
C數組
C編譯器會把
myArray
轉換成
*(myArray + i)。它等同於
*(i + myArray) 也等同於
i[myArray]。 高手都知道怎麼用好這個招。
可以用如下的函數來產生索引,這樣就把代碼搞亂了:
int myfunc(int q, int p) { return p%q; }
...
myfunc(6291, 8)[Array];
遺憾的是,這一招僅僅能在本地C類裏用。Java 還不行。
放長線釣大魚
一行代碼裏堆的東西越多越好。這樣可以省下暫時變量的開銷,去掉換行和空格還可以縮短源文件大小。記住,要去掉運算符兩邊的空格。優秀的程序猿老是能突破某些編輯器對於255個字符行寬的限制。
異常
我這裏要向你傳授一個編程中不爲人知的祕訣。異常是個討厭的東西。
良好的代碼永遠不會出錯,因此異常其實是沒必要要的。
不要把時間浪費在這上面。子類異常是給那些知道本身代碼會出錯的低能兒用的。在整個應用裏,你僅僅用在main()裏放一個try/catch。裏邊直接調用 System.exit()便可了。在每個方法頭要貼上標準的拋出集合定義,究竟會不會拋出異常你就不用管了。
使用異常的時機
在非異常條件下才要使用異常。
比方終止循環就可以用 ArrayIndexOutOfBoundsException。
還可以從異常裏的方法返回標準的結果。
狂熱奔放地使用線程
如題。
測試
在程序裏留些bug。讓後繼的維護代碼的程序猿能作點有意思的事。精心設計的bug是無跡可尋的。而且誰也不知道它啥時候會冒出來。
要作到這一點,最簡單的辦法的就是不要測試代碼。
永不測試
永遠不要測試負責處理錯誤、當機或操做系故障的不論什麼代碼。反正這些代碼永遠也不會運行,只會拖累你的測試。還有,你怎麼可能測試處理磁盤錯誤、文件讀取錯誤、操做系統崩潰這些類型的事件呢?爲啥你要用特別不穩定的計算機或者用測試腳手架來模擬這種環境?現代化的硬件永遠不會崩潰,誰還願意寫一些只用於測試的代碼?這一點也很差玩。假設用戶抱怨,你就怪到操做系統或者硬件頭上。他們永遠不會知道真相的。
永遠不要作性能測試
嘿,假設軟件執行不夠快。僅僅要告訴客戶買個更快的機器便可了。假設你真的作了性能測試,你可能會發現一個瓶頸,這會致使改動算法,而後致使整個產品要又一次設計。誰想要這樣的結果?而且。在客戶那邊發現性能問題意味着你可以免費到外地旅遊。你僅僅要備好護照和最新照片便可了。
永遠不要寫不論什麼測試用例
永遠不要作代碼覆蓋率或路徑覆蓋率測試。本身主動化測試是給那些窩囊廢用的。搞清楚哪些特性佔到你的例程使用率的90%,而後把90%的測試用在這些路徑上。
畢竟提及來。這樣的方法可能僅僅測試到了大約你代碼的60%,這樣你就節省了40%的測試工做。這能幫助你遇上項目後端的進度。
等到有人發現所有這些美麗的「市場特性」不能正常工做的時候,你早就跑路了。
一些有名的大軟件公司就是這樣測試代碼的。因此你也應該這樣作。假設因爲某種緣由你還沒走,那就接着看下一節。
測試是給懦夫用的
勇敢的程序猿會跳過這個步驟。
太多程序猿懼怕他們的老闆,懼怕丟掉工做,懼怕客戶的投訴郵件,懼怕遭到起訴。這樣的恐懼心理麻痹了行動,減小了生產率。有科學研究成果代表,取消測試階段意味着經理有把握能提早肯定交付時間。這對於規劃流程顯然是有利的。消除了恐懼心理,創新和實驗之花就隨之綻開。
程序猿的角色是生產代碼。調試工做全然可以由技術支持和遺留代碼維護組通力合做來進行。
假設咱們對本身的編程能力有充分信心,那麼測試就沒有必要了。假設咱們邏輯地看待這個問題,隨便一個傻瓜都能認識到測試根本都不是爲了解決技術問題,相反,它是一種感性的信心問題。針對這樣的缺少信心的問題,更有效的解決的方法就是全然取消測試,送咱們的程序猿去參加自信心培訓課程。畢竟提及來,假設咱們選擇作測試,那麼咱們就要測試每個程序的變動。但事實上咱們僅僅需要送程序猿去一次創建自信的培訓課便可了。很是顯然這麼作的成本收益是至關可觀的。
編程語言的選擇
計算機語言正在逐步進化,變得更加傻瓜化。
使用最新的語言是不人性的。儘量堅持使用你會用的最老的語言,先考慮用穿孔紙帶,不行就用匯編。再不行用FORTRAN 或者 COBOL,再不行就用C 還有 BASIC。實在不行再用 C++。
FØRTRAN
用 FORTRAN 寫所有的代碼。
假設老闆問你爲啥,你可以回答說有很是多它很是實用的庫,你用了可以節約時間。只是。用 FORTRAN 寫出可維護代碼的機率是0。因此。要達到不可維護代碼編程指南里的要求就easy多了。
用 ASM
把所有的通用工具函數都轉成彙編程序。
用 QBASIC
所有重要的庫函數都要用 QBASIC 寫,而後再寫個彙編的封包程序來處理 large 到 medium 的內存模型映射。
內聯彙編
在你的代碼裏混雜一些內聯的彙編程序,這樣很是好玩。這年頭差點兒沒人懂彙編程序了。
僅僅要放幾行彙編代碼就能讓維護代碼的程序猿望而卻步。
宏彙編調用C
假設你有個彙編模塊被C調用,那就儘量經常從彙編模塊再去調用C,即便僅僅是出於微不足道的用途,另外要充分利用 goto, bcc 和其它炫目的彙編祕籍。
與他人共事之道
老闆纔是真行家
假設你的老闆以爲他20年的 FORTRAN 編程經驗對於現代軟件開發具備很是高的指導價值,你務必嚴格採納他的所有建議。投桃報李。你的老闆也會信任你。這會對你的職業發展有利。你還會從他那裏學到很是多搞亂程序代碼的新方法。
顛覆技術支持
確保代碼中處處是bug的有效方法是永遠不要讓維護代碼的程序猿知道它們。
這需要顛覆技術支持工做。永遠不接電話。使用本身主動語音答覆「感謝撥打技術支持熱線。需要人工服務請按1,或在嘀聲後留言。」,請求幫助的電子郵件必須忽略,不要給它分配服務追蹤號。對不論什麼問題的標準答覆是「我預計你的帳戶被鎖定了,有權限幫你恢復的人現在不在。」
沉默是金
永遠不要對下一個危機保持警覺。
假設你預見到某個問題可能會在一個固定時間爆發。摧毀西半球的全部生命。不要公開討論它。不要告訴朋友、同事或其它你認識的有本事的人。
在不論什麼狀況下都不要發表不論什麼可能暗示到這樣的新的威脅的內容。
僅僅發送一篇正常優先級的、語焉不詳的備忘錄給管理層。保護本身免遭秋後算帳。假設可能的話,把這篇稀裏糊塗的信息做爲另一個更緊急的業務問題的附件。
這樣就可以問心無愧地歇息了,你知道未來你被強制提早退休以後一段時間,他們又會求着你回來。並給你對數級增加的時薪!
每個月一書俱樂部
增長一個計算機每個月一書俱樂部。選擇那些看上去忙着寫書不可能有時間真的去寫代碼的做者。去書店裏找一些有許多圖表但是沒有代碼樣例的書。瀏覽一下這些書,從中學會一些迂腐拗口的術語,用它們就能唬住那些自覺得是的維護代碼的程序猿。你的代碼確定會給他留下深入印象。假設人們連你寫的術語都理解不了。他們必定會以爲你很聰明,你的算法很深奧。不要在你的算法說明裏做不論什麼樸素的類比。
自立門戶
你一直想寫系統級的代碼。現在機會來了。忽略標準庫, 編寫你本身的標準
,這將會是你簡歷中的一個亮點。
推出你本身的 BNF 範式
老是用你自創的、獨一無二的、無文檔的BNF範式記錄你的命令語法。永遠不要提供一套帶註解的樣例(合法命令和非法命令之類)來解釋你的語法體系。
那樣會顯得全然缺少學術嚴謹性。確保沒有明顯的方式來區分終結符和中間符號。
永遠不要用字體、顏色、大寫和小寫和其它不論什麼視覺提示幫助讀者分辨它們。
在你的 BNF 範式用和命令語言自己全然同樣的標點符號。這樣讀者就永遠沒法分清一段 (...), [...], {...} 或 "..." 到底是你在命令行裏真正輸入的,仍是想提示在你的BNF 範式裏哪一個語法元素是必需的、可反覆的、或可選的。
不管怎麼樣,假設他們太笨。搞不清你的BNF 範式的變化。就沒資格使用你的程序。
推出你本身的內存分配
地球人兒都知道,調試動態存儲是複雜和費時的。與其逐個類去確認它沒有內存溢出,還不如自創一套存儲分配機制呢。事實上它無非是從一大片內存中 malloc 一塊空間而已。
用不着釋放內存,讓用戶按期從新啓動動系統,這樣不就清除了堆麼。從新啓動以後系統需要追蹤的就那麼一點東西。比起解決所有的內存泄露簡單得不知道到哪裏去了!
而且。僅僅要用戶記得按期從新啓動系統。他們也永遠不會遇到堆空間不足的問題。
一旦系統被部署。你很是難想象他們還能改變這個策略。
其它雜七雜八的招
假設你給某人一段程序,你會讓他困惑一天;假設你教他們怎樣編程。你會讓他困惑一生。 -- Anonymous
-
不要重編譯
讓咱們從一條多是有史以來最友好的技巧開始:把代碼編譯成可運行文件。假設它能用,就在源碼裏作一兩個微小的修改 -- 每個模塊都照此辦理。
但是不要費勁巴拉地再編譯一次了。 你可以留着等之後有空而且需要調試的時候再說。
多年之後,等可憐的維護代碼的程序猿更改了代碼以後發現出錯了。他會有一種錯覺,認爲這些確定是他本身近期修改的。這樣你就能讓他毫無頭緒地忙碌很是長時間。
-
挫敗調試工具
對於試圖用行調試工具追蹤來看懂你的代碼的人,簡單的一招就能讓他狼狽不堪,那就是把每一行代碼都寫得很是長。特別要把 then 語句 和 if 語句放在同一行裏。他們沒法設置斷點。他們也沒法分清在看的分支是哪一個 if 裏的。
-
公制和美製
在project方面有兩種編碼方式。一種是把所有輸入都轉換爲公制(米制)計量單位,而後在輸出的時候本身換算回各類民用計量單位。還有一種是從頭至尾都保持各類計量單位混合在一塊兒。老是選擇另一種方式,這就是美國之道!
-
持續改進
要持續不懈地改進。要常常對你的代碼作出「改進」,並強迫用戶常常升級 -- 畢竟沒人願意用一個過期的版本號嘛。即使他們認爲他們對現有的程序愜意了,想一想看,假設他們看到你又「無缺「了它,他們會多麼開心啊!不要告訴不論什麼人版本號之間的區別,除非你被逼無奈 -- 畢竟,爲何要告訴他們原本永遠也不會注意到的一些bug呢?
-
」關於「
」關於「一欄應該僅僅包括程序名、程序猿姓名和一份使用方法律用語寫的版權聲明。理想狀況下。它還應該連接到幾 MB 的代碼,產生有趣的動畫效果。但是,裏邊永遠不要包括程序用途的描寫敘述、它的版本號號、或最新代碼改動日期、或獲取更新的站點地址、或做者的email地址等。這樣,所有的用戶很是快就會執行在不一樣的版本號上,在安裝N+1版以前就試圖安裝N+2版。
-
變動
在兩個版本號之間,你能作的變動天然是多多益善。你不會但願用戶年復一年地面對同一套老的接口或用戶界面,這樣會很是無聊。最後。假設你能在用戶不注意的狀況下作出這些變動。那就更好了 -- 這會讓他們保持警戒,戒驕戒躁。
-
無需技能
寫沒法維護代碼不需要多高的技能。喊破嗓子不如甩開膀子,不管三七二十一開始寫代碼便可了。記住,管理層還在按代碼行數考覈生產率,即便之後這些代碼裏的大部分都得刪掉。
-
僅僅帶一把錘子
一招鮮吃遍天,輕裝前進。假設你手頭僅僅有一把錘子。那麼所有的問題都是釘子。
-
規範體系
有可能的話,忽略當前你的項目所用語言和環境中被普羅大衆所接受的編程規範。比方。編寫基於MFC 的應用時,就堅持使用STL 編碼風格。
-
翻轉一般的 True False 慣例
把常用的 true 和 false 的定義反過來用。這一招聽起來平淡無奇,但是每每收穫奇效。
你可以先藏好如下的定義:
#define TRUE 0
#define FALSE 1
把這個定義深深地藏在代碼中某個沒人會再去看的文件中不易被發現的地方,而後讓程序作如下這種比較
某些人確定會火燒眉毛地跳出來「修正」這樣的明顯的冗餘,並且在其它地方照着常規去使用變量var:
另外一招是爲 TRUE 和 FALSE賦予一樣的值。儘管大部分人可能會看穿這樣的騙局。給它們分別賦值 1 和 2 或者 -1 和 0 是讓他們瞎忙乎的方式裏更靜止的,而且這樣作看起來也不失對他們的尊重。你在Java 裏也可以用這一招,定義一個叫 TRUE 的靜態常量。在這樣的狀況下。其它程序猿更有可能懷疑你乾的不是好事。因爲Java裏已經有了內建的標識符 true。
-
第三方庫
在你的項目裏引入功能強大的第三方庫,而後不要用它們。潛規則就是這樣。儘管你對這些好的工具仍然一無所知。卻仍是可以在你簡歷的「其它工具」一節中寫上這些沒用過的庫。
-
不要用庫
僞裝不知道有些庫已經直接在你的開發工具中引入了。假設你用VC++編程,忽略MFC 或 STL 的存在,手工編寫所有字符串和數組的實現;這樣有助於保持你的指針技術,並本身主動阻止不論什麼擴展代碼功能的企圖。
-
建立一套Build順序
把這套順序規則作得很晦澀,讓維護者根本沒法編譯不論什麼他的改動代碼。祕密保留 SmartJ ,它會讓 make腳本形同廢物。
相似地,偷偷地定義一個 javac 類,讓它和編譯程序同名。說到大招,那就是編寫和維護一個定製的小程序,在程序裏找到需要編譯的文件,而後經過直接調用 sun.tools.javac.Main 編譯類來進行編譯。
-
Make 的不少其它玩法
用一個 makefile-generated-batch-file 批處理文件從多個文件夾複製源文件,文件之間的覆蓋規則在文檔中是沒有的。這樣。無需不論什麼炫酷的源碼控制系統,就能實現代碼分支。並阻止你的後繼者弄清哪一個版本號的 DoUsefulWork() 纔是他需要改動的那個。
-
蒐集編碼規範
儘量蒐集所有關於編寫可維護代碼的建議。好比 SquareBox 的建議 。而後明目張膽地違反它們。
-
規避公司的編碼規則
某些公司有嚴格的規定。不一樣意使用數字標識符,你必須使用預先命名的常量。要挫敗這樣的規定背後的意圖太easy了。比方,一位聰明的 C++ 程序猿是這麼寫的:
#define K_ONE 1
#define K_TWO 2
#define K_THOUSAND 999
-
編譯器警告
必定要保留一些編譯器警告。在 make 裏使用 「-」 前綴強制運行,忽視不論什麼編譯器報告的錯誤。這樣。即便維護代碼的程序猿不當心在你的源碼裏形成了一個語法錯誤,make 工具仍是會又一次把整個包build 一遍,甚至可能會成功!
而不論什麼程序猿要是手工編譯你的代碼,看到屏幕上冒出一堆事實上可有可無的警告。他們確定會認爲是本身搞壞了代碼。
相同,他們必定會感謝你讓他們有找錯的機會。學有餘力的同窗可以作點手腳讓編譯器在打開編譯錯誤診斷工具時就無法編譯你的程序。
固然了,編譯器或許能作一些腳本邊界檢查,但是真正的程序猿是不用這些特性的,因此你也不應用。既然你用本身的寶貴時間就能找到這些靜止的bug。何須還畫蛇添足讓編譯器來檢查錯誤呢?
-
把 bug 修復和升級混在一塊兒
永遠不要推出什麼「bug 修復"版本號。必定要把 bug 修復和數據庫結構變動、複雜的用戶界面改動。還有管理界面重寫等混在一塊兒。那樣的話。升級就變成一件很困難的事情,人們會慢慢習慣 bug 的存在並開始稱他們爲特性。那些真心但願改變這些」特性「的人們就會有動力升級到新版本號。
這樣從長期來講可以節省你的維護工做量,並從你的客戶那裏得到不少其它收入。
-
在你的產品公佈每個新版本號的時候都改變文件結構
沒錯,你的客戶會要求向上兼容。那就去作吧。只是必定要確保向下是不兼容的。這樣可以阻止客戶重新版本號回退,再配合一套合理的 bug 修復規則(見上一條),就可以確保每次新版本號公佈後,客戶都會留在新版本號。學有餘力的話,還可以想辦法讓舊版本號壓根沒法識別新版本號產生的文件。那樣的話,老版本號系統不但沒法讀取新文件。甚至會否定這些文件是本身的應用系統產生的!舒適提示:PC 上的 Word 文字處理軟件就典型地精於此道。
-
抵消 Bug
不用費勁去代碼裏找 bug 的根源。僅僅要在更高級的例程裏增長一些抵銷它的代碼便可了。這是一種很是棒的智力測驗,相似於玩3D棋,而且能讓未來的代碼維護者忙乎很是長時間都想不明確問題究竟出在哪裏:是產生數據的低層例程,仍是莫名其妙改了一堆東西的高層代碼。
這一招對天生需要多回合運行的編譯器也很是好用。你可以在較早的回合全然避免修復問題。讓較晚的回合變得更加複雜。
假設運氣好,你永遠都不用和編譯器前端打交道。學有餘力的話。在後端作點手腳,一旦前端產生的是正確的數據,就讓後端報錯。
-
使用旋轉鎖
不要用真正的同步原語,多種多樣的旋轉鎖更好 -- 重複休眠而後測試一個(non-volatile的) 全局變量,直到它符合你的條件爲止。相比系統對象,旋轉鎖使用簡便,」通用「性強,」靈活「多變,實爲居家旅行必備。
-
任意安插 sync 代碼
把某些系統同步原語安插到一些用不着它們的地方。本人之前在一段不可能會有第二個線程的代碼中看到一個臨界區(critical section)代碼。本人當時就質問寫這段代碼的程序猿,他居然義正詞嚴地說這麼寫是爲了代表這段代碼是很是」關鍵「(也是critical)的!
-
優雅降級
假設你的系統包括了一套 NT 設備驅動,就讓應用程序負責給驅動分配 I/O 緩衝區。而後在不論什麼交易過程當中對內存中的驅動加鎖,並在交易完畢後釋放或解鎖。這樣一旦應用非正常終止,I/O緩存又沒有被解鎖,NTserver就會當機。
但是在客戶現場不太可能會有人知道怎麼弄好設備驅動,因此他們就沒有選擇(僅僅能請你去免費旅遊了)。
-
定製腳本語言
在你的 C/S 應用裏嵌入一個在執行時按字節編譯的腳本命令語言。
-
依賴於編譯器的代碼
假設你發現在你的編譯器或解釋器裏有個bug,必定要確保這個bug的存在對於你的代碼正常工做是相當重要的。畢竟你又不會使用其它的編譯器,其它不論什麼人也不一樣意!
-
一個貨真價實的樣例
如下是一位大師編寫的真實樣例。讓咱們來瞻仰一下他在這樣短短几行 C 函數裏展現的高超技巧。
void* Realocate(void*buf, int os, int ns)
{
void*temp;
temp = malloc(os);
memcpy((void*)temp, (void*)buf, os);
free(buf);
buf = malloc(ns);
memset(buf, 0, ns);
memcpy((void*)buf, (void*)temp, ns);
return buf;
}
-
怎樣修復 "unused variable" 錯誤
假設你的編譯器冒出了 "unused local variable" 警告,不要去掉那個變量。相反,要找個聰明的辦法把它用起來。
我最喜歡的方法是:
i = i;
-
大小很是關鍵
差點忘了說了,函數是越大越好。跳轉和 GOTO 語句越多越好。那樣的話。想作不論什麼改動都需要分析很是多場景。這會讓維護代碼的程序猿陷入千頭萬緒之中。假設函數真的體型龐大的話,對於維護代碼的程序猿就是哥斯拉怪獸了,它會在他搞清楚狀況以前就殘酷無情地將他們踩翻在地。
-
一張圖片頂1000句話。一個函數就是1000行
把每個方法體寫的儘量的長 -- 最好是你寫的不論什麼方法或函數都沒有少於1000行代碼的,而且裏邊深度嵌套,這是必須的。
-
少個文件
必定要保證一個或多個重要文件是找不到的。利用includes 裏邊再 includes 就能作到這一點。好比。在你的 main 模塊裏,你寫上:
Stdcode.h 是有的。但是在 stdcode.h 裏,還有個引用:
而後,refcode.h 就沒地方能找到了。
-
處處可寫,無處可讀
至少要把一個變量弄成這樣:處處被設置,但是差點兒沒有哪裏用到它。不幸的是,現代編譯器通常會阻止你作相反的事:處處讀,沒處寫。只是你在C 或 C++ 裏仍是可以這樣作的。