1. 程式人生 > >及時獲取反饋以提升工作效率

及時獲取反饋以提升工作效率

最近發現自己及整個團隊的開發效率低下,思前想後認為跟反饋鏈太長脫不了干係,於是思考及時反饋的重要性,以及它如何影響我們的效率。這裡做一個小結。

#及時反饋為何重要

  1. 能減少返工的成本
  2. 能減少等待的時間

一般情況下,我們並不知道自己前進的方向是否正確,而及時的反饋能讓我們迅速調整自己的方向。如果反饋不及時,那到時候返工的成本就大大增加了。

例如原型設計,其目的就是在於快速得到(客戶)對產品功能的反饋。如果已經到達產品階段再讓客戶反饋,而客戶一旦否決某項功能,則開發該功能的所有花費都將付之一炬。反之如果是原型階段就被否決,則損失的成本就小得多。

在另一些情況下,我們會很自覺地等待反饋。例如修改了幾行程式碼,需要執行看看到底改得對不對,這時我們需要得到“正確”的反饋後才能繼續前進。這時,得到反饋的快與慢直接就影響了我們等待的時間,從而影響工作的效率。

#在工作流程上的體現

#敏捷開發

首先要說的是“敏捷開發”,相信現在幾乎所有軟體開發人員都聽說過這個詞語,可是它的內涵究竟是什麼呢?

敏捷開發有許多特點(宣言)、方法和工具,這裡我要討論的是它的一些特點:

對我們而言,最重要的是通過 儘早不斷交付 有價值的軟體滿足客戶需要。我們歡迎需求的變化,即使在開發後期。敏捷過程能夠駕馭變化,保持客戶的競爭優勢。經常交付可以工作的軟體,從幾星期到幾個月,時間尺度越短越好。

這裡敏捷開發的原則中的前 3 項,是要縮短迭代週期,持續交付軟體給客戶,並擁抱客戶的需求變化。那麼這些“原則”或“價值觀”背後的動機是什麼呢?我給自己的答案便是要得到“及時”的反饋。

而為什麼“及時”反饋如此重要?是因為絕大多數時候,客戶並無法描述自己的需求,也因此瀑布開發流程越發地無力。客戶並沒有辦法在計劃階段完整地、準確地描述需求,所以往往在交付的時候會與自己的預期出入很大。這也是持續交付和及時反饋的必要性,目的是為了矯正對客戶與開發雙方對需求的理解。

現在我認為這些“對外”(客戶)的內容比一些“對內”(團隊)的方法和工具更加地重要。例如縮短迭代周 期,但產品並沒有快速交付給客戶,那下個迭代開始時,團隊並無法得到客戶的反饋,那縮短迭代週期的意義何在?

也就是說我認為及時反饋就是敏捷開發的核心內涵。

#Design Thinking

中文譯為 設計思考,它是一個方法論,“透過從人的需求出發,為各種議題尋求創新解決方案,並創造更多的可能性”。

例如你需要設計一個產品,但卻無從下手,那麼可以應用 desgin thinking 的方法/流程來按步就班地得到結果:

  1. Empathy (同理心):去了解產品的使用者,包括體驗、問卷、採訪等方法來尋找使用者的真正需求。
  2. Define (需求定義):利用前面收集到的知識做更深入的挖掘,確認使用者的真正需求。
  3. Ideate(創意動腦):發散思維,找出儘可能多的解決方案,例如頭腦風暴,並最終透過不同的投票標準來找出真正合適的解決方案。
  4. Prototype(製作原型):製作原型,來將解決方案以某種形式呈現出來,既用作內部交流,之後也會用作測試。
  5. Test (實際測試):利用原型與使用者溝通,通過情景模擬,測試解決方案是否可用。通過使用者的使用,迴應等等,重新定義需求或改進解決方案,同時更深入瞭解使用者。

可以看到,Design Thinking 的整個流程就是構建了一個環,一個從定義使用者需求,找出解決方案,製作原型並得到使用者反饋的環。而我們也可以看到它與“敏捷開發”也有相似的地方,例如製作原型並得到反饋。

Design Thinking 並不是關於設計,而是解決問題的思維,可以並已經應用到多個領域中,如果讀者感興趣,不妨多做了解。

當然,僅從上面的流程並看不出 Design Thinking 鼓勵“及時”的反饋,但它也有類似敏捷開發的“原則”:

  1. 及早失敗:設計思考鼓勵及早失敗的心態,寧可在早期成本與時間投入相對較少的狀況,早點知道失敗,並作相對應的修正。如此一來,損失會較已完成一定程度,投入巨大資本的狀況更不嚴重。

這就是及時反饋的其中一個優點,減少返工的成本。

#在程式設計中的體現

上面講到的工作流程方面的反映,更多是鼓勵及早失敗。而在實際程式設計時我們希望的是減少等待反饋的時間。因為等待時間變長,不僅浪費等待的時間,程式設計師也需要更多的時間恢復(開始等待時)腦中的狀態。

#REPL

如果用到動態語言應該對 REPL 不陌生,例如瀏覽器的 Console,Python 的命令列直譯器,更別提 REPL 始祖 Lisp 的 REPL 了。

REPL 指的是 Read-Eval-Print-Loop,是一個迴圈,指的是直譯器讀入(Read)程式碼,解釋執行(Eval)並列印(Print)出結果這樣一個迴圈。而進一步擴充套件,可以認為是我們編輯程式碼(Read),部署程式碼(Eval),並檢視結果(Print)的迴圈。

一般來說,解釋型的語言(如 js/python/ruby/lua)通常提供 REPL,讓我們能快速地寫一些程式碼並測試是否可用。而相應的,編譯型語言(如 C/C++/Java)則還需要寫一些測試用例,編譯,執行等等。明顯動態語言的反饋更為及時。

另一方面,相像我們在執行一個 web 程式,現在我們修改了其中的一小段程式碼,我們需要 確認程式碼是否正確,怎麼做呢?很正常的一個步驟是關掉正在執行的程式,編譯修改後的程式碼,重新啟動修改後的程式,再開啟對應的網頁確認結果。這整個流程是很耗時間的, 所以一些 IDE 就提供了熱部署(Hot Swap)的功能,能將修改後的一些類動態地替換到正在執行的 web 程式上,就大大地縮小了反饋的時間,提高工作的效率。

無獨有偶,figwheel 是 ClojureScript 的一個庫,它可以在前端開發中提供“熱部署”的功能。相像你在用 js 寫一個 PPT。發現第 10 頁的圖片位置不對,於是在後臺修改了位置。現在想看看結果,於是在瀏覽器中重新整理頁面,結果又從第 1 頁開始顯示,於是需要連點 10 下才能看到結果正確與否。而 figwheel 則支援修改後,切換到瀏覽器,甚至不需要手工重新整理,就能把修改後的結果反映出來。

我認為這些工具的本質都是在縮短反饋的時間,從而提高效率。換句話說,要想進一步提高效率,可以從縮短反饋時間入手。

想想我們公司一次測試環境的部署要花近兩個小時,無言以對……

#Null 檢查

這裡要講到另一種反饋,程式設計中的靜態檢查。在 Java 開發中,有許多的 bug 是源於沒有正確地檢查變數是否為 null。甚至它的發明者都說它是 值 10 億美元的錯誤

但我們這裡不講語言的設計,而是說我們如何對待它。在工作中,一般我們都是等到測試出錯時才發現有什麼地方忘了判斷,這對於程式設計師來說可能沒什麼,不過是一時不小心,但一般 null 未檢查會很嚴重地影響產品的功能,例如某個功能可能就直接無法使用。

一些 IDE(如 Intellij)可以對某些情形做些判斷並提示變數沒有做 null 檢查,但多數情況是無法提示的;Kotlin 語言要求使用不同的變數型別來表示某個變數可以為 null,如 String 不可為 null,而 String? 則可以為 null,這樣就能在編譯時給出 error;最後像 Rust 語言在語言層面上去除了 null, 而用標準型別 Option 來指代可以為 None 的型別。

這裡重要的內容是,語言的設計讓程式設計師能在編譯期就得到“程式碼有錯”的反饋,而通常如果是在執行時去檢查的話,需要花費很多時間才能定位到 bug 所在,特別浪費時間。

也因此我認為編譯期的錯誤提示是縮短反饋時間的一種體現,並且是提高開發效率的一種很好的方式。再貼一句引用

It has been well understood in software development that the cost to fix a defect increases and in many cases increases dramatically the longer you wait to fix it.

#現實中的無奈與突破

例如公司如果採用敏捷開發,可能是想利用它能更好應對需求改變的特點,當然也可能是想理所當然壓榨員工的時間。但如果是想應對需求改變,那麼客戶的反饋就是很重要的一環。而現實中想得到客戶/使用者的反饋也是有很大成本的,例如客戶不願意花時間看成品並填調查問卷。這就是現實中的無奈。

只是如果一個方法論中的重要一環已經被破除,還有意義繼續執行其它的部分嗎?