黑客馬拉松
軟體開發聽上去高大上,但實際很簡單,全部活動可以分為兩類:造輪子,搭積木。這和建築行業很相似 —— 甚至相似到軟體業懶得自己編詞,借用建築業的architect這樣的title為自己所用。我的理解「造輪子」就是做一些基礎性的工作,如os,compiler,database,protocol(如tcp/ip),algorithm(如DH,RSA),framework(如rails)等,「搭積木」則更多是應用性的工作,利用手邊的組件和工具,做出新的產品和工具。「造輪子」需要的知識和能力一般而言要高於「搭積木」(也不盡然),所以大拿們總會說:"dont reinvent the wheels"。不過,技術總是在不斷進步,總得有人「造輪子」,所以隔些時候基礎領域就會有新思想新公司橫空出世。比如PageRank,BigTable和它背後的google,電動汽車的電池技術/電動機技術和它背後的Tesla。
黑客馬拉松(Hackathon,Hacker + Marathon)就是一個鼓勵「搭積木」而非「造輪子」的活動。你可以在固定的時間(現在多為一天)內隨心所欲,去做自己想做的產品。固定的時間就限定了你能做的事情 —— 「造輪子」這種基礎性的工作,一天下來可能想還沒想清楚呢就過去了,所以最終比拼的是「搭積木」的能力 —— 誰搭出來了,誰搭得漂亮。
Juniper內部的hackathon起源於一年前,現在已經是第三屆了。老闆們知道「老」程序員們(Juniper員工年齡都偏大)熬夜編程是個挑戰,於是慷慨地給了一天開發時間,半天準備presentation的時間,和半天的pitch時間。今天我就結合自己這次hackathon的經歷,講講在hackathon中如何選題,如何利用好一天的時間鏖戰(包括pivot),以及如何pitch。
選題
既然是隨心所欲,選題就要選自己想做的東西。如果能往公司的產品上靠(內部hackathon),或者往大會的主旨上靠(外部hackathon)最好;如果不能,也沒關係。hackathon的精神是交流學習及努力交付。如果你找不到什麼題材,又想參加hackathon,不妨往這幾個方向去想:
- 提升可用性(availability)
- 提升效率(efficiency)
- 提升可測性(debugability)
- 提升性能(performance)
- 提升娛樂性(recreationability)
找到你想做的點之後,要把產品的範圍縮小縮小再縮小。千萬不要高估你一天之中能做出來的東西。如果你聚精會神毫無打擾地寫一天能寫1000行代碼,那hackathon你的代碼量要控制在500行內。如果一個team好幾個人做開發,假設三人,那麼總代碼量最好乘以一個減去溝通成本的有效編碼的係數,如0.7,也就是三個人一起寫,總代碼量控制在1000行。這裡舉的數字都是估值,具體要看你自己對自己和團隊的估計。
產品的範圍集中在一個小點上的好處是容易交付。沒有什麼比做了一天沒有任何可交付的軟體讓人痛心疾首的了。如果很不幸你低估了自己的能力,很快就做出來可交付的產品,那麼空閑的時間可以繼續完善產品(而不是擴大產品的範疇),讓核心功能更突出。
這次hackathon我的選題和visualization相關。我就說說其中和Juniper產品無關的部分吧。在過去的數年裡,我們漸漸知道data visualization的巨大威力 —— 下圖是當本拉登被擊斃後,twitter上的傳播路徑 —— 複雜的數據流動,通過一張簡單的圖就說得無比清楚:
其實類似的路徑在代碼中也存在。一個公司的代碼庫裡面有數百萬,數千萬行代碼,有誰能把它捋清楚,有誰能在很短的時間內了解其中的細節呢?可不可以將代碼庫可視化,讓其能夠自己告訴你其中的各種邏輯/調用關係?
這就是我想嘗試的。
開工
開工首要解決的問題就是原料,也就是尋找可以用來「搭積木」的「輪子」。在hackathon前備好料最好不過,當天備料也未嘗不可。
備料是個很重要的過程,你使用的「輪子」決定了你「搭積木」的高度。很多時候一個產品都少不了和數據打交道,如果使用django的admin(加south),處理數據,存儲數據,撰寫基礎數據都易如反掌 —— 而且你只需要寥寥數行代碼(不包括model的代碼)就能得到大部分功能。別人也許還在吭哧吭哧地做CRUD,這廂你已經開始聚焦你要解決的核心問題了。
做hackathon經常遇見的問題是做了半天發現手頭的產品礙於主觀或客觀原因無法繼續下去,走進了死胡同。這時要果斷pivot —— 注意不是另起爐灶。pivot這詞不好翻譯成中文,借用Tao神的形象解釋(如果你看了這幾期『途客們地旅行夢』,你應該對他有點印象)—— pivot在籃球場上就是指一隻腳作為軸,另一隻腳來回探索尋找投籃或傳球的機會。當產品進入死胡同,不要立即否定,退後一點做做其它方向上的嘗試,也許能找到突破口。記住努力交付是hackathon的精神之一,pivot的目的就是為了交付。
回到我的項目。我們知道,對代碼進行profile分為靜態和動態兩種。我想達到的目標是能夠可視化代碼中運行時的路徑(這個足夠cool!)。比如說對linux kernel的代碼做研究(舉個例子,我真正做的不是這個),我想知道一個個數據包在kernel里走過的全部流程,然後以此繪製熱點圖,Petri Net等等。
對這個需求點,我需要的「輪子」有:
(1) systemtap,一個linux下媲美DTrace的probing工具。systemtap可以生成kernel module將你需要的代碼注入到被監測的點(通過TRAP),達到不改變已有系統的代碼,runtime獲取信息的效果(你可以把它想像成病毒 - 獲取程序的控制權,執行病毒代碼,然後恢復原有程序的執行)。
(2) jointjs(DavidDurman/joint)。獲得了運行時的數據後,我需要將其可視化,而jointjs就是我找到的一個最稱手的工具(但不完美)。我本來是想用d3.js或者sigma.js來做這事的,但前者太基礎,需要寫很多代碼,後者表述的圖形種類太少,所以我最終選定了jointjs。
(3) websocketd。這是一個可以把stdout輸出轉化為websocket數據傳遞給browser進行展示的好工具。比如下圖是我做得一個對vmstat的可視化(實時變化的):
找齊了「輪子」後,我打算這麼「搭積木」:使用websocketd來運行systemtap抓取數據(systemtap把數據輸出到stdout,websocketd將其發到瀏覽器),然後前端生成jointjs的繪圖代碼,生成實施的數據流圖。代碼量估計不會很大,就幾百行的樣子(systemtap腳本+javascript)。
可惜人算不如天算,我沒斗得過Murphys Law(凡事可能發生,就必然發生),我工作的幾台vm都罷工了,要麼業務跑不通,要麼vm本身無法訪問。下面的systemtap腳本運行也出了不少問題(示例而已,已經大幅裁剪和移除和工作相關的內容):
global ignorednglobal startnnprobe begin {n printf ("Start probling...n")n start = 0n}nprobe process("mydaemon").function("*").call {n if (!start) {n ignored[probefunc()] = 1n } else {n if ([probefunc()] in ignored) {n // do nothingn } else {n printf ("%s -> %sn", thread_indent(1), probefunc())n }n }n}nnprobe process("mydaemon").function("*").return {n if (start) {n if ([probefunc()] in ignored) {n } else {n printf ("%s <- %sn", thread_indent(-1), probefunc())n }n }n}nnprobe timer.s(5) {n start = 1n printf("Function call probing started.n")n}nnprobe end {n printf ("probing stopped.n")n}n
這個腳本監控某個process,任其運行5s,把所有遇到的function call都存入ignored中,5s之後的函數調用關係會被列印出來。之所以選擇5s,是想屏蔽系統的噪音 - 初始化代碼,各種scheduling,time event代碼,列印真正的業務。由於我工作的vm不穩定,業務跑不通,所以無法抓到有效的數據。
整整試了一個早上(大概8:00-11:30),最終我放棄和系統抗爭。動態profile走不通,我決定pivot到不那麼sexy的靜態profile。靜態profile就是生成代碼的調用關係 —— 我最初打算做一個python腳本,用戶輸入一個函數名,我為她生成兩張圖,一張是往前回溯,展示所有調用這個函數的完整路徑;另一張是往後追溯,展示所有以它為根的整個調用路徑。初見成效後,我便用django將生成的結果管理起來了。
要獲得整個代碼庫的調用關係可以寫yacc/lex進行語法分析,但單單做這一件事就要耗去不止一天時間。我想了很多其它方法,也在stackoverflow上到處尋找幫助,但都無疾而終。人在緊急情況下會產生「急智」,就好像突然開竅一般,我突然想到了用cscope生成的索引文件cscope.out。非c/c++,或者非unix平台下工作的程序員可能不知道cscope —— 其實只要你使用IDE,IDE就會生成代碼庫的索引,跟cscope原理基本一樣。我開始打算寫個python腳本分析cscope.out里的內容,去尋找函數間的關係,但我沒能在cscope的官網上找到相關的文檔告訴我該怎麼做,也沒有找到操作它的API。退而求其次,我只好求助於cscope命令本身。使用過cscope的人大多是用vim/emacs或者直接"cscope -d"使用,我想99%的人不知道cscope還能這麼用:
$ cscope -d -L2 <function-name> # print all callee symbols to stdoutn$ cscope -d -L3 <function-name> # print all caller symbols to stdoutn
這下我只需要寫個python腳本分析這兩個命令的輸出就OK了。通過分析輸出並不斷遞歸這個過程直到stdout沒有輸出(到達根節點,沒有調用關係了),我就能獲取某個函數的整個調用關係。當然,這麼寫代碼是權宜之計(workaround),效率很低(因為不斷地和cscope進程交互),所以真正高效的做法是找到合適的api,或者自己寫yacc/lex。
使用cscope.out的另外一個好處是對代碼的分析可以脫離代碼本身,任何一個裝有cscope的環境就可以進行分析。
靜態profile我需要的其它「輪子」還包括graphviz(繪圖),django(存儲所生成的graph,提供web訪問方式)。其中一個function的callee可視化後是這個樣子(不用費心啦,你看不清我長什麼樣子滴^_^):
挺嚇人的吧 - 這原圖三十多兆,以我高大上的15" new mbp還繪製了半個小時。最終這些圖表能夠在django做的website中展示出來,還能查詢。
前天晚上我寫了個多線程的腳本,8個核打滿,一晚上直到把我的mbp電池耗盡才繪製了400多個函數的caller graph和callee graph。繪圖的效率太低,這performance也就頂多給hackathon做個演示。昨天我本來想修改一下代碼,先將中間結果保存在圖形資料庫neo4j裡面(使用neo4django),然後再考慮繪圖的事(或直接用jointjs展示到前端),可惜時間不夠就放棄了,我還需要寫slides做pitch呢。
pitch
pitch是整個hackathon中最耀眼的環節,但又是最不好玩的環節。產品最終是要被展示出來的,這是我喜歡pitch的地方;但pitch的功利性太強,反而沖淡了hack一天帶來的愉悅感。
一位做產品的前輩曾跟我說:「做出來的產品要麼很受歡迎(大把粉絲),要麼有很多爭議(很多仇家)。不管怎樣,千萬不要做出來後大家覺得無關緊要」。我雖然對此論調持保留意見,但我覺得這話用在pitch上比較對路。不管怎麼說,一個大家漠不關心的pitch不是好pitch。到了這一步,idea不酷不重要,demo不豐滿不重要,重要的是pitch要夠吸引人。這很悲哀,但也很現實 —— 和忽悠投資人是一個道理。
所以hackathon最後要多花些時間打磨pitch。我參加過的在北京舉行的techrunch hackathon最後team就敗在了pitch環節 —— 我們頭腦發熱做了個情景舞台劇模擬用戶的使用場景 —— 天知道當時大家是怎麼想的。所以儘管你做出了很cool的產品,如果pitch不好,吸引力還是會大打折扣的。
我現在做pitch除非公司強制,否則絕對不會用powerpoint/keynote —— 並不是這兩個工具不好,而是它們已經跟不上互聯網的思維了 —— 演示文檔要隨時隨地可達,並且可以嵌入任何內容。我自己一般使用reveal.js和impress.js。當然不是直接用,而是使用我自己結合wintersmith做的一套生成工具。這樣我可以用markdown,jade,yml等格式撰寫slides,然後編譯成html,在chrome里播放。這次pitch我使用的是impress.js。類prezi的炫麗展示效果加上裡面引用data visualization的一些高大上的圖(包括ben laden的這張開山鼻祖圖),足夠吸引觀眾的眼球。
結語
其實參加hackathon最有意思的事情是挑戰自己的能力極限,逼出自己的潛能 —— 這是hackathon向marathon致敬的地方。我個人感覺重要的不是受到讚許或者別人注目的快樂,衝線那一刻的快樂,獲獎的快樂等等,這些快樂都無法持續很久,也許第二天起床後你就忘記了;真正重要的是沉浸於其中,專註地圍繞著一個目標不斷學習不斷改進,最終交付的整個過程中獲得的快樂。我在參加這次hackathon之前從未用過 graphviz(pygraphviz),jointjs,neo4django等等工具,都是現看文檔現學習,很受用,很享受learning by doing的快樂。
題圖是baltimore hackathon的宣傳海報,我覺得它很好地反應了hackathon的精髓。
如果你對本如果你對本文感興趣,歡迎訂閱公眾號『程序人生』(搜索微信號 programmer_life)。每篇文章都力求原汁原味,早8點與您相會。
推薦閱讀:
TAG:迷思 |