有經驗和沒有經驗的程序員的區別
近來我一直都需要帶些畢業生(怎么我的隊伍就那么多新人呢,呵呵),發現很多人在解決問題的方法上都存在一些問題。解決問題,其實是每個程序員每天都在干的事情。但是方法好還是不好,我覺得正是有經驗和沒有經驗的程序員之間很重要的區別。剛好最近又看到了余晟公眾號(yurii-says)的《砍伐大樹 v.s. 收割莊稼》里面闡述的解決問題的四個步驟。所以,我想根據身邊的案例詳細一點解釋一下這四個步驟。
認識和定位問題
一般來說,新手進入一個公司和團隊之后,team lead 會分配一兩個簡單的任務讓新人練練手。所以,首先要做的事情就是搭環境,和認識已有的系統和代碼。
在搭環境和做任務的過程中,新人經常遇到問題或異常后就蒙了,不知道怎么繼續下去。然后就求救說:“出錯了,怎么辦?”。我都會反問一句:“哪里出錯了?你定位到問題在哪里了嗎?”。可是一般收到的答案都是“不知道”。像我們公司的系統架構,把后端分了兩層,一層專門服務于表現層,另一層專門訪問數據庫和提供 web service。系統的架構就簡單如下圖所示。如果在瀏覽器發起的一個 Ajax 請求有異常返回,那異常到底是在哪一層,或者是哪一個傳輸環節?如果在傳輸環節可能是因為對象類型,屬性不一致導致。如果在 Domain 層可能是業務邏輯,數據庫操作出現問題等。
認識和定位問題的能力應該是程序員技能寶典里面的 101,并且是要持續修煉的能力。 看日志 和 看代碼 是獲取這種能力的兩種最基礎的手段。
看日志
在沒有源碼的情況下,看日志可以說是唯一定位問題的方法了。再說,即便有源碼,也不可能全都看一通,或者順著源頭一直 debug 下去,直到遇到問題為止。所以,定位問題的起點一般都是先看日志。
日志來源基本可以分為幾大類:
- 程序:項目代碼
- 框架:團隊或者公司內部框架,因為不少公司還有專門制造內部輪子的部門
- 中間件,外部組件:廣義地指所有外部的組件,包括數據庫,Web 服務器,Application 服務器,以及云平臺等。
一般來說,我們都先從第一種著手。因為一個好的項目,第一大類的日志應該是要把內部框架和所有外部系統組件的異常都封裝好。異常處理設計的好,從這里的日志出發就可以較容易地追溯至真正問題所在。可是如果看完第一大類的日志都沒能確定問題的原因,很可能是自身項目沒有做好異常的捕捉處理,把外部系統或組件的異常給吞吃和隱藏了;又或者是因為一些運行時異常沒有被捕捉到,然后就被寫到外層的框架或者容器的日志里,比如說 Weblogic/Tomcat。
所以,經驗首先就在于懂得在什么地方找日志;先看什么,后看什么;以及在項目自身日志不健全情況下,如何尋求第二或者第三大類的日志,看出錯時間點的前后到底有什么信息可以幫助推斷真正問題所在。
看代碼
當定位到要改動的源碼的位置時,很多新人面對著一個從未接觸過的龐大的項目代碼庫,在 IDE 里面跳轉多幾次就亂了。他們遇到每一個方法調用都想進去看個明白。但是,一開始就想看懂整個系統的代碼是不現實的。除了代碼量龐大,或者找不到源碼作者咨詢以外,我們可能還有時間的壓力,必須盡快找到問題和解決方案。那么,我們就必須有 猜代碼 的能力。如何忽略細節,不迷失在茫茫代碼海洋里,迅速定位和看懂自己的任務需要涉及和修改的代碼是哪一塊,是非常重要的。就像瀏覽文章一樣,找段落大意,猜不同的代碼大概是完成什么功能,然后再決定是否應該深入去研究某一塊,而不是一次搞懂所有的代碼。
認清問題
其實就是 知其然,知其所以然 。
從技術上來看
準確定位到問題或者異常發生在什么地方了,并不代表要修改的代碼就是且只是那一塊。我們不能頭痛醫頭,腳痛醫腳。
比如說,我們對外提供的一個 Web Service,因為瀏覽器或者調用方傳的參數不全,報了 NullPointerException,我們要怎么改?改調用方還是提供方?首先,根據健壯性原則,也就是 Postel’s law ,看起來應該是改提供方這邊:
Be conservative in what you do, be liberal in what you accept from others
但是,除了改提供方,我們還要考慮為什么調用方會提供少了一個參數,是所有的情況下都少了嗎?還是某一些場景才會?尤其是當調用方是從一個具有復雜的業務操作邏輯的頁面過來的時候,我們更需要全面分析。這才不至于說見一個洞,補一個洞,其實還漏了幾個洞。
從做產品或者功能設計來看
明白用戶的功能使用場景和背后的動機,是產品功能的設計,如何取舍技術難點等的重要決策依據。
舉個栗子,船運公司的單據錄入部門其實有一份標準的操作指南。錄入人員要根據托運人是誰,運什么貨,從哪運到哪,要不要報關等一大堆的信息,決定應該怎么錄入單據。那份操作指南其實就像一個問答系統,或者更像是一顆決策樹一樣。從成百上千種分門別類,如貨物,航線,費用,報關等的問題找到那對應的決策路徑來獲知最后該怎么做。
那么,如果我們要做一個指南維護頁面,怎么讓用戶容易找到他想要修改的條目,和清晰展現出來就要有一些考量了。對查找這個功能,開發人員一開始把那些門類和問題都做為下拉框展示出來以作為查詢條件,并且還算聰明地實現了級聯過濾,不至于用戶因為門類和問題數目過多而不好找到他想要的東西。但是用戶反映說那個問題的下拉框不能有級聯。為什么?原因其實是有些問題是通用的。它們有可能出現在很多門類當中。所以,一種場景是他們已經知道要改的是某一個具體的問題,他想找出相關的所有門類。那么,我們把問題那個下拉框做成級聯,他們就悲劇了。因為他們根本就不知道什么門類下有那個問題,怎么可能要求他們先選好門類,再選問題來查找呢?就問題這個下拉框,換另一種思路,做自動補全,讓用戶輸入關鍵字就能過濾就好了。
可行性分析
在定位好問題和界定范圍后,下一步就要看能采取什么方法來解決問題。
拿最近讓新人做的一個開發任務來說吧。有一個頁面,需要把數據庫的數據,按照樹的結構渲染出來和進行數據維護。這里,我們要考慮的東西有什么呢?
-
頁面生成樹要用什么前端技術,它的特點是什么?它生成樹形結構的表現形式時,需要的數據結構是什么樣的,是不是就是對應的樹狀數據結構還是沒有要求?對樹的結點進行頁面上的操作時,數據就會自動更新嗎,還是說我們要另外再獨立更新數據?
-
頁面的數據結構和數據庫的結構一樣嗎?中間要經過多少次的轉換?轉換放在前臺還是后臺做?頁面到底有一些什么業務操作,以及這些業務操作其實最后反映到數據庫層面的形態是什么樣的?界面操作后如何把信息帶回后臺,要帶回什么數據(比如 ID,操作類型標記等)?
-
后臺的數據更新邏輯是怎么樣的?每次更新都需要整顆樹的全部信息進行全量更新嗎?
動手編碼之前,對這些問題有初步的認識是相當重要的。有初步的想法之后,怎么做才能用最少的時間和精力來驗證是否可行?如果還沒有搞清楚,一下子就上手編碼是很危險的。這個開發任務,也正是因為上面說的某幾個方面沒有做好,中間一些實現推倒重來了幾次,進度比預期長了很多。
另一種非常重要的情況就是,我們想到或者碰到的技術難點,可能在實際應用場景根本碰到的情況就不高。如果避開那種場景來考慮的話,系統設計和實現方式可能就大大不同了。對于怎么舍棄一些技術細節問題,曹政的公眾號(caozsay)的《如何應對并發(3) - 需求裁剪》就針對一個案例說得很清楚,大家可以看看。
還有,可行路徑的選擇,當然也受計劃的影響。一個月,還是三個月時間的實現方案當然會有所不同。
估算
估算和可行性分析其實密切相關。只有經過充分的可行性分析,估算才相對有意義。我覺得不是說有經驗的人,估算的時間就比較準。而是有經驗的人大概知道什么地方有坑,把上面的可行性分析和任務拆分做得更細,考慮得更全面一些。
其實人對時間的估算是非常不準確的,也不應當僅僅依賴于時間估算來做計劃或者確定項目期限。我個人對估算的想法在之前寫的 Kanban 系列里面有一篇文章也講了一下。有興趣可以看看。
- Dive into Kanban (3) - How Kanban address the estimation headache
</ul> </div>
來自: http://www.thinkingincrowd.me/2016/03/08/Novice-vs-Experienced/