[转载]如何编写无法维护的代码(3)
2019獨角獸企業重金招聘Python工程師標準>>>
????????這是一篇轉載文章,最原始的是英文,這是英文原文的地址,然后由伯樂在線的老碼農翻譯,這是翻譯原文的地址,由于翻譯的原文很長,故在轉載時拆分成為多篇。以下為轉載的翻譯:
(接前一篇)
測試
????????在程序里留些bug,讓后繼的維護代碼的程序員能做點有意思的事。精心設計的bug是無跡可尋的,而且誰也不知道它啥時候會冒出來。要做到這一點,最簡單的辦法的就是不要測試代碼。
永不測試
????????永遠不要測試負責處理錯誤、當機或操作系統故障的任何代碼。反正這些代碼永遠也不會執行,只會拖累你的測試。還有,你怎么可能測試處理磁盤錯誤、 文件讀取錯誤、操作系統崩潰這些類型的事件呢?為啥你要用特別不穩定的計算機或者用測試腳手架來模擬這樣的環境?現代化的硬件永遠不會崩潰,誰還愿意寫一 些僅僅用于測試的代碼?這一點也不好玩。萬一將來出了事用戶抱怨,你就怪到操作系統或者硬件頭上。他們永遠不會知道真相的。
永遠不要做性能測試
????????嘿,如果軟件運行不夠快,只要告訴客戶買個更快的機器就行了。如果你真的做了性能測試,你可能會發現一個瓶頸,這會導致修改算法,然后導致整個產品要重新設計。誰想要這種結果?而且,在客戶那邊發現性能問題意味著你可以免費到外地旅游。你只要備好護照和最新照片就行了。
永遠不要寫任何測試用例
????????永遠不要做代碼覆蓋率或路徑覆蓋率測試。自動化測試是給那些窩囊廢用的。搞清楚哪些特性占到你的例程使用率的90%,然后把90%的測試用在這些 路徑上。畢竟說起來,這種方法可能只測試到了大約你代碼的60%,這樣你就節省了40%的測試工作。這能幫助你趕上項目后端的進度。等到有人發現所有這些 漂亮的“市場特性”不能正常工作的時候,你早就跑路了。一些有名的大軟件公司就是這樣測試代碼的,所以你也應該這樣做。如果因為某種原因你還沒走,那就接 著看下一節。
測試是給懦夫用的
????????勇敢的程序員會跳過這個步驟。太多程序員害怕他們的老板,害怕丟掉工作,害怕客戶的投訴郵件,害怕遭到起訴。這種恐懼心理麻痹了行動,降低了生產 率。有科學研究成果表明,取消測試階段意味著經理有把握能提前確定交付時間,這對于規劃流程顯然是有利的。消除了恐懼心理,創新和實驗之花就隨之綻放。程 序員的角色是生產代碼,調試工作完全可以由技術支持和遺留代碼維護組通力合作來進行。
????????如果我們對自己的編程能力有充分信心,那么測試就沒有必要了。如果我們邏輯地看待這個問題,隨便一個傻瓜都能認識到測試根本都不是為了解決技術問 題,相反,它是一種感性的信心問題。針對這種缺乏信心的問題,更有效的解決辦法就是完全取消測試,送我們的程序員去參加自信心培訓課程。畢竟說起來,如果 我們選擇做測試,那么我們就要測試每個程序的變更,但其實我們只需要送程序員去一次建立自信的培訓課就行了。很顯然這么做的成本收益是相當可觀的。
編程語言的選擇
????????計算機語言正在逐步進化,變得更加傻瓜化。使用最新的語言算什么好漢?盡可能堅持使用你會用的最老的語言,先考慮用穿孔紙帶,不行就用匯編,再不行用FORTRAN 或者 COBOL,再不行就用C 還有 BASIC,實在不行再用 C++。
FORTRAN
????????用 FORTRAN 寫所有的代碼。如果老板問你為啥,你可以回答說它有很多非常有用的庫,你用它可以節約時間。不過,用 FORTRAN 寫出可維護代碼的概率是 0,所以,要達到不可維護代碼編程指南里的要求就容易多了。
用 ASM
????????把所有的通用工具函數都轉成匯編程序。
用 QBASIC
????????所有重要的庫函數都要用 QBASIC 寫,然后再寫個匯編的封包程序來處理 large 到 medium 的內存模型映射。
內聯匯編
????????在你的代碼里混雜一些內聯的匯編程序,這樣很好玩。這年頭幾乎沒人懂匯編程序了。只要放幾行匯編代碼就能讓維護代碼的程序員望而卻步。
宏匯編調用C
????????如果你有個匯編模塊被C調用,那就盡可能經常從匯編模塊再去調用C,即使只是出于微不足道的用途,另外要充分利用 goto, bcc 和其他炫目的匯編秘籍。
與他人共事之道
老板才是真行家
????????如果你的老板認為他20年的 FORTRAN 編程經驗對于現代軟件開發具有很高的指導價值,你務必嚴格采納他的所有建議。投桃報李,你的老板也會信任你。這會對你的職業發展有利。你還會從他那里學到很多搞亂程序代碼的新方法。
顛覆技術支持
????????確保代碼中到處是bug的有效方法是永遠不要讓維護代碼的程序員知道它們。這需要顛覆技術支持工作。永遠不接電話。使用自動語音答復“感謝撥打技 術支持熱線。需要人工服務請按1,或在嘀聲后留言。”,請求幫助的電子郵件必須忽略,不要給它分配服務追蹤號。對任何問題的標準答復是“我估計你的賬戶被 鎖定了,有權限幫你恢復的人現在不在。”
沉默是金
????????永遠不要對下一個危機保持警覺。如果你預見到某個問題可能會在一個固定時間爆發,摧毀西半球的全部生命,不要公開討論它。不要告訴朋友、同事或其 他你認識的有本事的人。在任何情況下都不要發表任何可能暗示到這種新的威脅的內容。只發送一篇正常優先級的、語焉不詳的備忘錄給管理層,保護自己免遭秋后 算賬。如果可能的話,把這篇稀里糊涂的信息作為另外一個更緊急的業務問題的附件。這樣就可以心安理得地休息了,你知道將來你被強制提前退休之后一段時間, 他們又會求著你回來,并給你對數級增長的時薪!
每月一書俱樂部
????????加入一個計算機每月一書俱樂部。選擇那些看上去忙著寫書不可能有時間真的去寫代碼的作者。去書店里找一些有很多圖表但是沒有代碼例子的書。瀏覽一 下這些書,從中學會一些迂腐拗口的術語,用它們就能唬住那些自以為是的維護代碼的程序員。你的代碼肯定會給他留下深刻印象。如果人們連你寫的術語都理解不 了,他們一定會認為你非常聰明,你的算法非常深奧。不要在你的算法說明里作任何樸素的類比。
自立門戶
????????你一直想寫系統級的代碼。現在機會來了。忽略標準庫, 編寫你自己的標準,這將會是你簡歷中的一大亮點。
推出你自己的 BNF 范式
????????總是用你自創的、獨一無二的、無文檔的BNF范式記錄你的命令語法。永遠不要提供一套帶注解的例子(合法命令和非法命令之類)來解釋你的語法體 系。那樣會顯得完全缺乏學術嚴謹性。確保沒有明顯的方式來區分終結符和中間符號。永遠不要用字體、顏色、大小寫和其他任何視覺提示幫助讀者分辨它們。在你 的 BNF 范式用和命令語言本身完全一樣的標點符號,這樣讀者就永遠無法分清一段 (…), [...], {…} 或 “…” 到底是你在命令行里真正輸入的,還是想提示在你的BNF 范式里哪個語法元素是必需的、可重復的、或可選的。不管怎么樣,如果他們太笨,搞不清你的BNF 范式的變化,就沒資格使用你的程序。
推出你自己的內存分配
????????地球人兒都知道,調試動態存儲是復雜和費時的。與其逐個類去確認它沒有內存溢出,還不如自創一套存儲分配機制呢。其實它無非是從一大片內存中 malloc 一塊空間而已。用不著釋放內存,讓用戶定期重啟動系統,這樣不就清除了堆么。重啟之后系統需要追蹤的就那么一點東西,比起解決所有的內存泄露簡單得不知道 到哪里去了!而且,只要用戶記得定期重啟系統,他們也永遠不會遇到堆空間不足的問題。一旦系統被部署,你很難想象他們還能改變這個策略。
其他雜七雜八的招
如果你給某人一段程序,你會讓他困惑一天;如果你教他們如何編程,你會讓他困惑一輩子。 — Anonymous
不要重編譯
????????讓我們從一條可能是有史以來最友好的技巧開始:把代碼編譯成可執行文件。如果它能用,就在源代碼里做一兩個微小的改動 — 每個模塊都照此辦理。但是不要費勁巴拉地再編譯一次了。 你可以留著等以后有空而且需要調試的時候再說。多年以后,等可憐的維護代碼的程序員更改了代碼之后發現出錯了,他會有一種錯覺,覺得這些肯定是他自己最近修改的。這樣你就能讓他毫無頭緒地忙碌很長時間。
挫敗調試工具
????????對于試圖用行調試工具追蹤來看懂你的代碼的人,簡單的一招就能讓他狼狽不堪,那就是把每一行代碼都寫得很長。特別要把 then 語句 和 if 語句放在同一行里。他們無法設置斷點。他們也無法分清在看的分支是哪個 if 里的。
公制和美制
????????在工程方面有兩種編碼方式。一種是把所有輸入都轉換為公制(米制)計量單位,然后在輸出的時候自己換算回各種民用計量單位。另一種是從頭到尾都保持各種計量單位混合在一起。總是選擇第二種方式,這就是美國之道!
持續改進
????????要持續不懈地改進。要常常對你的代碼做出“改進”,并強迫用戶經常升級 — 畢竟沒人愿意用一個過時的版本嘛。即便他們覺得他們對現有的程序滿意了,想想看,如果他們看到你又“完善“了它,他們會多么開心啊!不要告訴任何人版本之 間的差別,除非你被逼無奈 — 畢竟,為什么要告訴他們本來永遠也不會注意到的一些bug呢?
“關于”
????????”關于“一欄應該只包含程序名、程序員姓名和一份用法律用語寫的版權聲明。理想情況下,它還應該鏈接到幾 MB 的代碼,產生有趣的動畫效果。但是,里邊永遠不要包含程序用途的描述、它的版本號、或最新代碼修改日期、或獲取更新的網站地址、或作者的email地址 等。這樣,所有的用戶很快就會運行在各種不同的版本上,在安裝N+1版之前就試圖安裝N+2版。
變更
????????在兩個版本之間,你能做的變更自然是多多益善。你不會希望用戶年復一年地面對同一套老的接口或用戶界面,這樣會很無聊。最后,如果你能在用戶不注意的情況下做出這些變更,那就更好了 — 這會讓他們保持警惕,戒驕戒躁。
無需技能
????????寫無法維護代碼不需要多高的技術水平。喊破嗓子不如甩開膀子,不管三七二十一開始寫代碼就行了。記住,管理層還在按代碼行數考核生產率,即使以后這些代碼里的大部分都得刪掉。
只帶一把錘子
????????一招鮮吃遍天,會干什么就吆喝什么,輕裝前進。如果你手頭只有一把錘子,那么所有的問題都是釘子。
規范體系
????????有可能的話,忽略當前你的項目所用語言和環境中被普羅大眾所接受的編程規范。比如,編寫基于MFC 的應用時,就堅持使用STL 編碼風格。
翻轉通常的 True False 慣例
????????把常用的 true 和 false 的定義反過來用。這一招聽起來平淡無奇,但是往往收獲奇效。你可以先藏好下面的定義:
#define?TRUE?0? #define?FALSE?1????????把這個定義深深地藏在代碼中某個沒人會再去看的文件里不易被發現的地方,然后讓程序做下面這樣的比較
if?(?var?==?TRUE?) if?(?var?!=?FALSE?)????????某些人肯定會迫不及待地跳出來“修正”這種明顯的冗余,并且在其他地方照著常規去使用變量var:
if?(?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 的建議 ,然后明目張膽地違反它們。
規避公司的編碼規則
????????某些公司有嚴格的規定,不允許使用數字標識符,你必須使用預先命名的常量。要挫敗這種規定背后的意圖太容易了。比如,一位聰明的 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緩存又沒有被解鎖,NT服務器就會當機。但 是在客戶現場不太可能會有人知道怎么弄好設備驅動,所以他們就沒有選擇(只能請你去免費旅游了)。
定制腳本語言
????????在你的 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; }重新發明了標準庫里已有的簡單函數。
Realocate 這個單詞拼寫錯誤。所以說,永遠不要低估創造性拼寫的威力。
無緣無故地給輸入緩沖區產生一個臨時的副本。
無緣無故地造型。 memcpy() 里有 (void*),這樣即使我們的指針已經是 (void*) 了也要再造型一次。另外,這樣做可以傳遞任何東西作為參數,加10分。
永遠不必費力去釋放臨時內存空間。這樣會導致緩慢的內存泄露,一開始看不出來,要程序運行一段時間才行。
把用不著的東西也從緩沖區里拷貝出來,以防萬一。這樣只會在Unix上產生core dump,Windows 就不會。
很顯然,os 和 ns 的含義分別是”old size” 和 “new size”。
給 buf 分配內存之后,memset 初始化它為 0。不要使用 calloc(),因為某些人會重寫 ANSI 規范,這樣將來保不齊 calloc() 往 buf 里填的就不是 0 了。(雖然我們復制過去的數據量和 buf 的大小是一樣的,不需要初始化,不過這也無所謂啦)
如何修復 “unused variable” 錯誤
????????如果你的編譯器冒出了 “unused local variable” 警告,不要去掉那個變量。相反,要找個聰明的辦法把它用起來。我最喜歡的方法是:
i?=?i;大小很關鍵
????????差點忘了說了,函數是越大越好。跳轉和 GOTO 語句越多越好。那樣的話,想做任何修改都需要分析很多場景。這會讓維護代碼的程序員陷入千頭萬緒之中。如果函數真的體型龐大的話,對于維護代碼的程序員就是哥斯拉怪獸了,它會在他搞清楚情況之前就殘酷無情地將他踩翻在地。
一張圖片頂1000句話,一個函數就是1000行
????????把每個方法體寫的盡可能的長 — 最好是你寫的任何一個方法或函數都不會少于1000行代碼,而且里邊是深度嵌套,這是必須的。
少個文件
????????一定要保證一個或多個關鍵文件無法找到。利用includes 里邊再 includes 就能做到這一點。例如,在你的 main 模塊里,你寫上:
#include?<stdcode.h>stdcode.h 是有的。但是在 stdcode.h 里,還有個引用:
#include?"a:\\refcode.h"然后,refcode.h 就沒地方能找到了。
(【譯者-老碼農-注】為啥找不到呢?仔細看看,現在還有人知道 a:\ 是什么嗎?A盤!傳說中的軟盤…)
到處都寫,無處會讀
????????至少要把一個變量弄成這樣:到處被設置,但是幾乎沒有哪里用到它。不幸的是,現代編譯器通常會阻止你做相反的事:到處讀,沒處寫。不過你在C 或 C++ 里還是可以這樣做的。
【譯注】:原文在后面還有一些內容,翻譯時略有刪減。刪節的內容主要是:
我看不懂的部分;
我覺得不怎么好笑的部分(其實很可能是因為沒看懂所以找不到笑點);
不容易引起現代程序猿共鳴的老舊內容。
(全部內容轉載完畢)
轉載于:https://my.oschina.net/songxinqiang/blog/542485
總結
以上是生活随笔為你收集整理的[转载]如何编写无法维护的代码(3)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: RIP协议无法适应网络环境
- 下一篇: 67、Fragment实现Tab