你已經是一個成熟的碼農了,這些思維習慣你要有
不想成為好程序員的碼農不是好工程師。出色的碼農都具備怎樣的思維習慣?這裡有 25 條成熟的小建議。
「即使進行小的軟體變更也很困難!」
「進行變更會破壞軟體的特性。」
「修復一個 bug 的同時又引入了一個新的 bug...」
「實現的是沒有必要的代碼。」
「代碼太複雜了!幾乎不可能向其中添加新的特性。」
「這產品永遠不會交付不了了!」
「拋棄舊代碼吧!重頭開始寫。」
你是否對上面的這些話感到熟悉呢?
世界上每個角落的開發者,每一分鐘都在說著(或想著)這些,亞歷山大到想哭!這是為什麼呢?
這些都是開發者們經常討論的常見問題。每個開發團隊都經歷過這些故事。
有許多小的因素會逐漸侵蝕開發人員的項目。它們不會立即造成破壞,而是日積月累,甚至一年或更長的時間內都看不出來。所以當有人指出這些因素時,通常聽起來沒有什麼危害。
即使你開始實施,也可能看起來一切正常。但隨著時間的推移,尤其是隨著這些東西越積越多,情況也會變得越來越複雜,直到你成為這個「常見恐怖故事」的又一個受害者。
為了避免成為受害者之一,你應該牢記幾條軟體的基本定律。你應該培養一種開發者必備的思維模式。這種思維模式會幫助你在日常編程過程中做出更好的決定。你可以讓你的軟體儘可能的簡單,防止它成為一個無法管理的複雜系統。
接下來,本文將列出 25 條開發者必須掌握的要點。
1. 構想軟體的用途
首先,你需要明白軟體的用途。也就是說,實際上所有軟體的用途只有一個:幫助人們!
請記住:軟體的用途並不是炫耀你有多聰明。
不能構想出軟體用途的開發者將寫出糟糕的軟體。什麼是糟糕的軟體呢?就是那些對人們沒什麼幫助的系統。
在對軟體做出決策的時候,你應該牢記下面的指導原則:我們如何才能為人們提供幫助?你甚至可以通過這種方式對軟體特性的請求進行優先順序排序。
2. 明確軟體設計的目標
每個程序員都是一個設計師。
當難以創建或修改軟體時,開發者往往會將大部分時間花在讓軟體「能用就行」,而不是關注如何幫助用戶。
軟體設計的目的是儘可能簡化開發人員的工作,這樣他們就可以專註於重要的事情。你將創建能夠幫助用戶的軟體,並且你的軟體將在很長一段時間內持續對用戶提供幫助。
然而,如果你設計了一個糟糕的系統,你的軟體生命周期將會很短。
這就向我們指明了軟體設計最重要的目標:
設計出開發人員能夠儘可能容易創建和維護的系統,從而使這些軟體能夠持續地為用戶提供幫助。
所以設計時有兩個關鍵點:方便自己,幫助他人。
3. 正確理解自己開發的東西
無法完全理解自己作品的開發者往往會開發出複雜的系統。這會招致一個惡性循環:對軟體的誤解會導致複雜性增加,反過來又會進一步加深對軟體的誤解,循環往複。
事實上,提升設計技能的最佳方法之一就是:保證你對開發的系統和工具有充分的理解。
對軟體的理解程度是優秀開發者與蹩腳開發者之間最關鍵的差別。
蹩腳的開發者並不能理解他們所做的東西,而優秀的開發者則對此有很清晰的認識。就是這麼簡單。
4. 簡潔
簡潔是終極的複雜 ——達芬奇。
編程是化繁為簡的藝術。「蹩腳的開發者」無法降低程序複雜性,而「優秀的開發者」會盡其所能簡化其代碼,使其易於被其他開發者理解。
一名優秀的開發者會創建一些易於理解的東西,這樣就很容易發現所有的 bug。
現在的開發者都是聰明人,沒有人喜歡被當做傻瓜來對待。具有諷刺意味的是,有時候這種心態卻會導致他們創造出複雜的東西。他們大體上是這麼想的:
「其它的開發者會能夠理解我寫的這些代碼。所以我應該寫出一些難以理解的絕妙代碼,這樣他們就會認為我很聰明。」
這是一種不良心態導致的錯誤,並不一定是由於缺乏編程技巧。大多數編程中的失敗都是由這樣的心態造成的。
炫耀你有多聰明並不能幫你寫出好程序。
剛接觸你的代碼的開發者對這樣的代碼並沒有什麼了解,他們必須研究一陣子。
所以,你應該問自己:「我是想讓人們理解我的代碼並感到快樂,還是希望他們感到困惑和沮喪呢?」
事實上,如果其他閱讀代碼的開發者能夠很容易地理解它,這說明你的代碼寫得很棒。
複雜與智慧無關,簡單卻與之相關。——Larry Bossidy
那麼問題來了:「應該讓代碼有多簡潔?」
你的答案應該是:「讓它變成傻瓜式的代碼,任何人都能讀懂。」
5. 控制複雜度
控制複雜度是計算機編程的本質。—— Brian Kernighan
複雜性是許多軟體失敗的根源。一開始,你要做的可能是一個在一個月內就能完成的項目。
接著,你增加了程序的複雜性,使得這項任務需要三個月才能做完。
然後,你又開始加入了一些滿足其它用途的新的特性。由於你無緣無故地擴展了軟體的用途,這件事開始變得十分複雜,需要六個月才能完成。
但是,這還沒完。
接下來,你考慮了每個特性並讓整個軟體變得更複雜,導致軟體需要九個月才能完成。然後,由於代碼的複雜度提高,又引入了許多新的 bug。
自然而然地,你開始著手修復這些 bug,然而你並沒有考慮到這些修復對程序的其餘部分有何影響。
最終,即使是很小的更改也會變得十分困難。當 bug 修復開始引入新的 bug 時,你將不得不面對最流行的編程恐怖故事:從頭開始重寫代碼!
那麼,你是如何成為這個恐怖故事的受害者的呢?或者說,更多人應該關心:我們如何才能避免成為這個受害者?
其實很簡單。首先,你需要確切地弄清楚你軟體的用途及其定義。其次,你需要使你所編寫的每段代碼儘可能簡潔。第三,當一個新的特性或變更請求出現在討論表中時,你需要基於你軟體的用途對它們進行評估,並提出問題。
作為一名開發者,你的第一反應應該是抵制(不必要的)軟體變更。這將防止你向軟體中添加不必要的代碼。當你確信這項變更是必要的時,你可以去實現它。
有許多因素會提高軟體的複雜度,但本文提到的這些是最常見的因素。除此之外,你還應該遵循一條原則:你的主要目的是降低複雜度,而不是增加複雜度。
6. 維護
維護是軟體開發中最重要的事情之一。很遺憾,開發人員通常會忽略它的重要性。快速編碼和快速交付看起來比代碼維護更重要。這就是錯誤的所在——忽視未來的代碼維護。
總有一些變更需要實現。不僅必須實現,你還要隨著時間的推移維護它們。作為開發人員,考慮未來變更的維護是你的主要職責之一。
所有的變更都需要維護。
簡潔性和複雜性是影響代碼維護的兩個主要因素。任何軟體的易維護性都與其各個部分的簡潔性成正比。維護的工作量與軟體的複雜度成正比。
關於維護,你應該遵循的一條原則是:減少維護的工作比減少實現的工作更重要。
7. 一致性
一致性是簡潔性的重要組成部分。如果你在某處用一種方式做了某件事,那麼你應該在所有的地方都用這種方法做這件事。例如,如果你將一個變數命名為「thisIsVariable」,那麼你所有的變數都應該以這種方式命名(「otherVariable」、「anAnotherVariable」等。而不是「other_variable」)。
缺乏一致性的代碼很難被理解。不要讓開發者每次閱讀一段來自你的系統的新代碼時,都要重新學習你系統的規則。
在所有的團體運動中,最好的隊伍都是一致的,併產生化學反應。——Roger Staubach。
8. 優先順序
你應該如何為你的軟體做出決策呢?
當你面臨許多可能的方向時,你如何決定哪種選擇是最好的?你應該關注什麼?應該實現哪些特性?
要回答這些問題,有三個重要的因素可以幫助你做出更好的決策:
- 變更的意願(D):你有多想做出該變更?
- 變更的價值(V):該變更帶來了多少價值,或者說對你的用戶有多大幫助?
- 執行變更所需要的工作量(E):變更需要做多少工作?
計算的公式很簡單:D=V/E
任何變更的意願都直接與變更的價值成正比,與進行變更所需的工作量成反比。
當你對工作進行優先順序排序時,應該遵循以下的原則:那些為你帶來很大價值而需要的工作量很少的變更,要比那些沒有價值卻需要付出很多努力的變更要好!
9. 解決問題
第一步是理解。你需要清楚地知道要求是什麼。大多數難題之所以難,是因為你不理解它們。把你的問題寫下來,試著向別人解釋。
如果你不能用簡單的語言來解釋某件事,你就沒有理解它。——理查德·費曼
第二步是計劃。不要馬上開始行動,稍微停一下。給你的大腦一些時間來分析問題和處理信息,但不要花太多時間在計划上。
三思而後行。
第三步是分而治之。不要試圖一次解決一個大問題。當你從整體上看問題時,你會感到害怕。把它分解成更小的任務,逐個解決每個子問題。一旦你解決了每個子問題,你就可以把這些點串聯起來了。
10. 夠用即可
「完美是良好的敵人。」——Voltaire
無論是創建一個新項目,還是向現有的系統添加一個新特性,開發者都傾向於從一開始就對所有事情進行詳細的規劃。
他們希望第一個版本是完美的。他們並不關注將要解決的問題以及他們的軟體將如何幫助人們。他們從能想到的每一個小細節開始。接著會進行假設和預測,然後他們會想「如果... 會怎麼樣?」
他們不得不預測未來,因為他們現在是如此著迷於腦海中想像出的項目,他們的項目必須像他們想像的那樣完美。
事實上,他們並不知道等待他們的是什麼,也不知道追求完美會讓他們付出多少代價。
讓我告訴你會發生什麼吧:
你將寫出一些實際上並不需要的代碼
你將因為加入了不必要的代碼而增加複雜度
你將會焦頭爛額
你將錯過 deadline
你將處理由於高複雜度引起的許多 bug
你想讓這一切發生嗎?我想不會吧。
那麼你應該怎麼做呢?
從小的開發原型做起,不斷改進它,然後進行擴展。
你應該將增量開發作為行動指南。你可以帶著這種思想通過下面的步驟設計一個計算器:
- 計劃做出一個只做加法而不做其它事情的系統。
- 實現它。
- 改進現有系統的設計,這樣你可以加入其它的操作。
- 計劃減法並重複步驟 2 和 3。
- 計劃乘法並重複步驟 2 和 3。
- 計劃除法並重複步驟 2 和 3。
11. 預測
「預測就是簡單地預想未來會發生什麼事情。這可能是真實的,基於某種客觀數據,也可能是基於假設」。
想到他們的代碼可能會在未來進行變更,一些開發者試圖通過設計通用的解決方案來解決這個問題,他們相信這個解決方案能夠適應未來所有可能的情況。
軟體過於通用意味著會寫出很多沒有必要的代碼。
你無法預測未來,因此,無論你的解決方案多麼通用,它都不足以滿足你未來的實際需求。最有可能的是,你所預測的情況永遠不會到來,而你為解決未來問題而編寫的代碼將增加複雜度,使變更代碼片段變得困難,最終它將成為一種負擔,可能會毀掉你的軟體。
不要預測未來。只要實現你現在應該實現的功能即可。
12. 假設
什麼是假設?
「假設是某些你接受為真或相信為真的事,儘管你沒有確鑿的證據。」
假設是軟體項目的一大殺手。讓我們看看假設是如何扼殺一個軟體項目的。
開發者知道他們必須開發一個系統來做 X。然後他們認為系統將來會要求他們去做 Y,然後他們去實現 Y。於是他們就寫了數千行代碼來設計 Y。
到了後來,開發者會認識到當前的需求與他們所想的完全不同。但現在,該軟體有了一些不必要的代碼。這些代碼很難扔掉,因為所有的東西都是相互交織的。重構代碼需要花費幾個月的時間,而現在他們認為從頭開始重寫整個軟體將導致他們浪費幾個月的時間。
為了避免成為這樣的開發者,你應該遵循這個原則:基於你現在所知道的事情設計代碼,而不是你認為將來會發生的事。
13. 不要重造「輪子」
舉個例子,假如現在已經有一個很完美的垃圾回收器了,你還想自己發明一個,那麼你將花費大量的時間來開發這個垃圾回收器,而你本來可以只專註於開發你的軟體。
只有當下面的任何一種情況成立,你才應該重新去發明「輪子」:
- 你需要一些還不存在的東西。
- 所有現有的「輪子」都太糟糕了,或者都無法滿足你的需求。
- 現有的「輪子」沒有得到適當的維護。
你要遵循的簡單原則是:盡量不要重新發明「輪子」。
14. 學會拒絕
作為開發人員,你對於變更申請的第一反應應該是「不!」
永遠要學會抵制添加更多的代碼、更多的特性,直到你確信它們是必需的,並且有必要實現他們。因為不必要的變更會增多軟體中的缺陷。
你怎麼知道它們是必需的呢?
請回顧並牢記軟體的用途。然後記住「優先順序排序」!
15. 自動化
不要把時間浪費在重複的勞動上。將這些任務設置好,然後忘掉它們。它們可以在你睡覺的時候工作。
當你發現自己在一次又一次地做某件事時,盡量把它自動化!
16. 代碼衡量
根據代碼行數來衡量編程進度就像用重量來衡量飛機建造進度一樣。——比爾蓋茨
我看到有些開發者根據代碼的行數來衡量他們的軟體質量。他們認為代碼行越多意味著他們做得越好。他們的軟體包含數十萬行代碼,就意味著他們開發的軟體非常龐大。
那麼問題來了:它真的有那麼大嗎?還是哪裡出了點問題?
最有可能的答案是:他們的設計有問題。大多數簡單的解決方案不需要很多代碼。你可以使用少量代碼實現軟體的簡潔性並解決問題。
我並不是說代碼越少越好。當你想避免上述狀況而使用更少的代碼時,很容易掉進「陷阱」中,然後寫出一些別人難以理解的「聰明」代碼。你應該找到一個平衡點。
最好的代碼是一小段易於理解和閱讀的代碼。
17. 工作效率
你如何衡量自己的工作效率?
通過編寫更多行的代碼?還是通過扔掉數百行代碼?
你的主要目標應該是讓代碼庫儘可能的小,問題不在於「如何編寫更多代碼?」而應該是「如何刪除更多代碼?」
「我工作效率最高的一天扔掉了 1000 行代碼。」——Ken Thompson
18. 代碼測試
什麼時候應該將日誌和錯誤處理記錄添加到項目中?
你應該在很早的階段就添加日誌。這將幫助你輕鬆地找到問題並節省你的時間。
我發現在代碼測試階段,容易發現很多錯誤。舉個例子,有兩個條件,一個簡單的 if-else 代碼塊。開發者將輸入傳給軟體,軟體將進入 if 代碼塊中,開發人員對其進行了測試,並將代碼提交給源代碼管理。這個部分的測試就完成了!
但是其它程序塊呢?當軟體被交付到生產環境中時,會導致很多錯誤。當你測試代碼時,必須至少執行一次所有的新代碼行,並且應該在進行整體測試之前測試對每部分進行測試(執行集成測試和系統測試之前進行單元測試)。
當你要處理一個 bug 時,首先你應該重現它。你不應該猜測錯誤的來源並給予你的假設應用補丁。你很可能想錯了。在應用補丁之前你應該親眼看到這個 bug。
你應該要靠譜一些。當團隊中的其他開發者看到你向版本控制工具提交了新代碼時,每個人都應該明白你的代碼已經經過了測試,能夠正常工作。
沒有經過測試的代碼是不能正常工作的代碼。
19. 低估開發難度
開發者不太擅長估計軟體的開發難度。
通常,他們會低估而非高估事情的難度。他們會低估開發少量代碼或特性所需的時間和工作量。最終,這種低估會導致他們錯過 deadline。
這個問題的解決方案是:把大項目分解成多個小項目,事情越小就越好估計。你可能仍然會出錯,但是你所犯的錯誤會比估計一個大型項目時要少得多。
請記住:每件事所花的時間都比你想像得更長。
20. 遠離重寫代碼
我相信,如果你接受了上面提到的軟體開發基本原則的話,你不會走到這一步。然而,如果你不小心犯了這些錯誤,並在考慮重寫代碼的話,那麼你要知道的一件事是:重寫代碼通常是開發人員的錯覺,在大多數情況下這並不是正確的解決方案。
為什麼這是一種錯覺?
因為讀代碼比寫代碼難。這就是為什麼重用代碼如此困難的原因,也是為什麼當我們讀到其他開發者寫的代碼時,我們的潛意識會悄悄告訴我們「把它扔掉,重新寫」。
在很多情況下,你可能會考慮從頭開始重寫代碼。但是,給你條簡單的建議:重構應該是第一選擇。
21. 文檔和注釋
關於注釋的一個常見誤解是:開發者應該添加註釋來說明代碼的作用。這是錯誤的!從代碼中就可以明顯看出它在做什麼。如果這一點不明顯,說明代碼的可讀性很差,你要讓代碼變得更簡單。
當你不能讓代碼變得更簡單時,你應該添加註釋來解釋這種複雜性。
注釋的真正目的是解釋「為什麼」要做某事,而不是代碼「在做什麼」。如果你不解釋這一點,其他程序員可能會感到困惑,所以當他們變更你的代碼時,可能會刪除其中的重要部分。
因此,寫一條注釋來解釋「為什麼」,而不是解釋「是什麼」。
另一件需要注意的事是撰寫文檔。用文檔來解釋你的軟體架構、每個模塊和組件很重要。這是從高層次來查看軟體所必需的。當一個新的開發者加入你的團隊時,他將更容易理解整個軟體。
當開發者對軟體的其他部分一無所知時,他們很容易在自己負責的部分犯錯誤,這也會影響其他部分。
22. 選擇技術(工具,程序庫,等等)
首先,你要牢記這條規則:不要過於依賴外部技術或儘可能減少對它們的依賴。
這是為什麼呢?因為它們是複雜性的另一個常見來源。它們會扼殺你的積極發展,讓一切變得更加困難。當你嚴重依賴於外部技術時,你就不是自由的。
如果該技術存在重大缺陷怎麼辦?你必須等待開發人員來修復這個 bug,如果這個技術是你項目的核心,你基本上就會卡在這裡,無法繼續工作。因此,為你的項目選擇正確的技術非常重要。
在開始使用某些技術之前,你應該考慮以下幾個因素:
- 它的發展前景如何?
- 它會被繼續維護下去嗎?
- 換掉它容易嗎?
- 技術社區對它看法如何?
如果你能找到這些問題的正確答案,就能降低選擇錯誤技術的風險。
23. 提升自我
保持學習的狀態。嘗試不同的編程語言和工具,閱讀軟體開發方面的書籍。它們會為你提供另一種視角。每天的小小進步都會讓你的知識和技能產生質變。
要有開放的心態。不要痴迷於一種技術。使用所需的技術去解決特定的問題。不要參與沒意義的討論,比如「微軟和 Linux 哪個好用?」
要知道每個特定的問題都有特定的解決方案
24. 不要逞英雄
很多時候,及時放棄比逞英雄要好。
比如說,你覺得某個任務你能在兩個小時之內完成。但是四個小時過去了,你仍然只完成了四分之一。
你的本能應該是這樣想的:「我現在還不能放棄啊,我已經在這上面花了四個小時了!」
所以你開始逞英雄,下定決心要完成它(但尷尬的是,但仍然沒有成功)。於是,你開始閉門造車。
不要過於偏執!要學會及時止損。不要羞於求助。
25. 不要立馬提問,適時尋求建議
當你要實現某些東西,但又不確定解決方案時,不要去問別人怎麼做,至少不要馬上去問。相反,去嘗試任何你能想到的辦法。當你對某種概念或語言越不熟悉時,這一點越重要。
當你自己不能想通任何問題的時候,去搜索!找出答案並試一試。修改這些答案,看看你是否能夠理解它們為什麼起作用,並改寫它們,讓它們適應自己的代碼。
但同時也一定要學會尋求建議。
當你嘗試了所有方法,特別是有了一個可行的解決方案之後,就是你尋求建議的最佳時機了。
你可以請同行和高級開發者幫你檢查代碼
※我是怎麼走上推薦系統這條路的……
※CSP行人檢測:無錨點框的檢測新思路
TAG:機器之心 |