動態語言企業應用優缺點淺析

動態語言的興起已經有些年頭了。現在,人們早已不再去爭論動態語言是否能夠取代靜態語言,因爲這種爭論毫無意義。越來越多的開發者開始在動態語言更爲擅長的領域應用它們。比如,DjangoRuby on Rails等開發框架的盛行使得像Python和Ruby這樣的動態語言可以在Web開發領域大放異彩,PHP和JavaScript也早已在Web開發領域佔有一席之地。

不過目前動態語言在企業開發中的應用還不夠廣泛,很多企業只是用它來做一些粘合系統的工作,並沒有承擔起主力開發語言的重任。尤其是在底層系統開發方面,動態語言遠沒有在Web開發方面那麼風光。在運行時效率和虛擬機穩定性方面的不足,使得動態語言註定無法與編譯型語言競爭,並取代它們在高性能領域的地位。然而,動態語言也有自己的優勢所在。如何克服自己的劣勢,將優勢發揚光大,便是每一位動態語言開發者所面臨的機遇和挑戰。

我所在的團隊用了近兩年的時間,將一個電信領域的公司絕大部分的生產系統用動態語言(主要是Python)重寫。包括短/彩信消息網關、業務訂閱服務、座席查詢系統、銷售支撐系統,乃至搜索引擎等多個核心繫統,都在重寫之列。重寫的理由很多,一方面原有系統無論是從性能上,還是從應對需求變化的能力上,都已經不能滿足業務發展的需要;另外一方面,動態語言的諸多優勢,也是我們重寫的動力。這裏僅以開發這些系統時獲得的經驗,來談談動態語言在應用時的優缺點。

動態語言的優勢

動態語言的優勢有很多,歸納起來主要有以下幾個方面:

1. 生產力。動態語言在開發效率方面有着無與倫比的優勢,這也與動態語言“優化人的時間而不是機器的時間”這個理念相吻合。利用傳統的靜態語言要開發幾周的功能和特性,使用動態語言也許幾天甚至幾個小時就可以實現。不僅如此,動態語言在開發原型系統和常用工具方面的開發效率也非常高,尤其值得一提的是原型系統。更快地讓原型系統運轉起來,不僅可以儘早驗證一些假設,也能夠更好地與迭代開發相結合,更及時地與需求方進行溝通,幫助需求方挖掘和了解自己真正的需求。開發效率可以說是動態語言最爲吸引人的地方,這也被認爲是將來開發語言的前進方向。這些年隨着敏捷開發的盛行,越來越多的開發者意識到,原來動態語言的特性和敏捷開發的價值觀也相當契合:縮短反饋時間,對變化的響應能力更強。所以許多動態語言團隊選擇了敏捷開發的實踐來組織團隊。我們在實際開發的過程中,以兩週爲一個迭代週期,基本上1-2個迭代就可以完成一個系統的開發和上線,而原型系統的開發通常能在1-3天內完成。

2. 代碼量。曾有報道說,用Ruby on Rails寫同樣的項目,代碼量大概只有Java的1/10。且先不說這個說法是否有誇張的成分,但就實際來看,動態語言的確從代碼量上來說,要比 Java/C/C++等傳統靜態編譯型語言要少的多(當然語言的表達能力與動態靜態關係並不大,靜態函數式語言的表達能力也很強),可能幾千行的項目就算得上是個大項目。例如,我們開發的系統中較爲複雜的消息網關,生產代碼行數大概在7000行左右;而訂閱服務系統的生產代碼行數不到3000行;藉助於Xapian的 Python-bindings,我們的搜索引擎系統代碼行數只有800行左右。代碼量少的好處非常多:首先,這意味着將來需要花在維護上的代價更低;其次,因爲代碼少了,犯錯誤的機會也就少了,因此代碼量少還意味着BUG的減少;再次,代碼量的減少也有助於優化系統,有句話說的好,“There is no code faster than no code”。開源項目Nebula Device有一個設計哲學,就是“每一次Release要比前一次代碼量更少”,竊以爲高論也。

3. 測試。因爲動態語言很容易實現反射等動態特性(JUnit也是等到Java支持了反射以後纔出現的),因此測試也更爲容易實現。Python和Ruby的標準庫中都帶有unittest的框架,這幾乎可以讓你無成本地使用單元測試來加固代碼。因爲動態語言本身不具有編譯過程,因此犯下某些低級錯誤的機率大大增加,也爲重構帶來了重重困難。沒有單元測試的重構如同夢魘一般,動態語言尤甚。因此,在開發語言以動態語言爲主的開源項目中,單元測試總是佔有相當大的比重。還有建議稱測試代碼與生產代碼的比率(Unit Test To Code Ratio)要達到2:1以上。另外,動態語言的測試環境更容易搭建,實現Mock也更爲簡單。

4. 原生數據結構。現在主流的動態語言多爲腳本語言發展而來,而在這些語言中,集合、列表和詞典這樣的數據結構都是原生的,而靜態語言的數據結構往往是通過程序類庫來實現的。比如Python就提供了set、tuple、list和dict等原生數據結構,同時還提供了大量操作(如數組分片等),讓這些數據結構使用起來非常方便。原生數據結構使得對數據的操控融入到了語言的語法當中,讓程序更爲易讀,這也讓基於代碼的溝通更爲順暢。

5. 簡單易學。動態語言的語法相對簡單,學習成本看似也比較低。有人舉例說,Python和Ruby寫個Hello World只需要一行即可,這是很多靜態語言所達不到的(把多行代碼寫成一行的不算)。當然你可以認爲這只不過是句玩笑話,不過單就語法而言,動態語言的學習門檻要比很多靜態語言要低的多。可是,開發不僅僅只是語法而已。很多動態語言的初學者,能夠用動態語言寫一些簡單的小程序小工具,卻很難構建起龐大複雜的商業系統,究其原因,主要是還是因爲系統設計和麪向對象的功底欠缺所導致的。如何設計,如何抽象,如何重構,這些能力與語言無關,而是個人的修爲。正如陸游所言,“功夫在詩外”,這些能力也不是一朝一夕、通過學學語言就能夠輕易練就的。當然,動態語言的各種特性(如Duck Typing)也使得在靜態語言中不得不使用的設計模式可以很自然地表達,這些差異也增加了動態語言學習的隱性成本。

不足之處

任何事物都具有兩面性,動態語言也不例外,雖然優勢顯而易見,動態語言的不足之處也有很多。這裏列舉一些我們在開發過程中所遇到的問題,以及一些初步的解決方案,來供大家參考。

1. 運行效率。運行效率低下使得動態語言飽受詬病。“跑得太慢”這頂帽子已經在動態語言的頭上扣了許多年。甚至有Benchmark表明,在某些應用場景下,動態語言的運行效率和C/C++、Java等成熟的靜態語言相比,相差數十倍甚至上百倍,這也爲動態語言的普及埋下陰影。不少開發者因爲運行效率的問題,紛紛表示 “對動態語言很失望”。其實我倒是覺得大可不必糾結在這個問題上,原因有兩點。第一,很多動態語言的應用場景使得運行效率的重要程度大大降低。就拿 Ruby on Rails來說,在Web開發這個應用場景裏,數據庫的響應時間無疑是最大時延,與之相比代碼運行時間就微不足道了。而且通過Cache和優化,基本上可以消除代碼運行效率低對項目的影響。又如我們的消息網關係統,最耗時的部分就是網絡通信和文件I/O,而這兩部分動態語言和靜態語言相比並無明顯劣勢,運行效率的問題可以完全忽略。第二,如果遇到很耗CPU或者很耗內存的運算,完全可以通過C/C++實現的擴展來解決。無論是Python還是Ruby,都支持採用C/C++編寫擴展。通過這些擴展,可以極大地提高運行效率,從而彌補動態語言在運行效率上的不足。

2. BUG難於發現。動態語言由於沒有構建的過程,因此很多錯誤只有等到運行時纔會發現。而這些錯誤很可能是些低級錯誤,比如拼寫錯誤、沒有import相關的類庫,或者括號不匹配等等。如果每次修復這樣的BUG都要通過去測試環境中部署來驗證的話,則會浪費了大量時間。因此動態語言往往需要充分的自動化測試套件,才能夠確保代碼基本可用。另外,使用動態語言的時候,一個良好的代碼靜態檢查工具也是很有必要的。它不但可以糾正一些低級錯誤,而且還可以幫助你發現代碼中的Bad Smells,大大提高開發效率。對於Python來說,PyflakesPylint都是不錯的選擇;而Ruby也有衆多工具可供使用。測試充分的代碼也更容易重構,在重構動態語言項目時要萬分小心,因爲動態語言極容易犯錯,稍不留意就會引入新的BUG。保持小步前進的步伐,每次修改後都執行測試,最好再通過持續集成環境來幫助發現測試失敗的情況,這樣重構起來才能得心應手。

3. 專業人員少。不少使用動態語言的公司都會遭遇一個問題,那就是使用動態語言的資深開發人員很少,不但很難招聘到靠譜的員工,核心人員的離隊也會對公司造成很大的損失。這是因爲完全使用動態語言進行開發的公司少的可憐,只有極少數的開發者能夠參與其中並獲得相關的開發經驗。絕大多數的動態語言使用者還處在愛好者階段,跟着Tutorials寫寫Demo,或者隨手寫個Utils等等。因爲高水平的動態語言開發者的確是可遇不可求,因此尋找有經驗的開發者也許要花上不少的時間和成本。當團隊有了較爲有經驗的開發者以後,就需要通過內部培訓、結對編程等手段,幫助公司裏沒有經驗的開發者迅速積累經驗,逐漸成爲動態語言方面的靠譜人才。其實,對於動態語言的圈子,還有一個有趣的說法:因爲學習動態語言的人往往都是在其他領域有了很深的積累後,在有餘力的情況下才接觸動態語言的,因此往往相對都比較靠譜,動態語言的圈子反而能夠幫助僱主們甄選出一批高素質的開發者。

4. 不夠成熟。動態語言的發展歷史雖然不比靜態語言差到哪裏(比如Ruby和Java就同爲1995年始創),然而由於其較爲小衆,因此無論是虛擬機的實現上,語言本身的機制上,還是相關的配套工具上都算不得十分成熟。例如,Ruby雖然以其優美靈活的語法爲人所稱道,但也因爲其虛擬機效率低下和內存泄露問題所爲人詬病,使用 Ruby on Rails的網站往往需要加配監控程序,一旦發現某個VM內存超標立刻重啓;Python的虛擬機雖然還算穩定,但長久以來一直受GIL(Global Interpreter Lock)問題所困擾,完全無法發揮多核的優勢,這在家用PC都早已多核的今天的確是個不小的問題(事實上Ruby也存在GIL問題)。不過,雖然官方實現不夠成熟,現在已經有很多逐漸成熟的其他選擇可供使用。比如JRuby就充分利用了Java成熟的虛擬機和Ruby優良的語法特性,還可以允許開發者使用Java背後龐大的類庫。通過multiprocessingStackless Python,甚至手工將任務切成多份,分發給多個進程運行,都可以規避掉GIL的問題,更充分地利用系統性能。當然,隨着時間的推移,動態語言的實現將會越來越成熟,不但MRI逐漸完善,MagLevRubinius等一系列優秀的Ruby虛擬機也開始登上舞臺;Python 3000甚至打破了向後兼容性,試圖將Python以前的設計錯誤全面改寫。回頭去看Java等一批成熟開發語言的發展路線,有誰沒有經歷過不成熟的青春期呢?

小結

通過實踐我們發現,動態語言既不是什麼洪水猛獸,也不是什麼奇巧玩物,它們已經逐漸成長爲稱手的兵器,幫助開發者們快速完成項目,進而達成商業目標。使用動態語言,已經讓我們切切實實感受到了它的開發效率爲我們所帶來的好處。在商業機會瞬息萬變的今天,誰能以最快的速度實現自己的想法,誰能儘快應對市場帶來的變化,誰就能離成功更進一步。

誠然,動態語言目前還存在很多問題。但瑕不掩瑜,如果在使用時可以意識到這些問題,並善加處理的話,動態語言也可以成爲複雜商業系統的主角,在企業開發中佔據自己的地位。而且隨着開源社區的努力,很多問題正逐一被解決。我們有理由相信,在不遠的未來,動態語言一定會有一片更爲廣闊的天空。

感謝田樂(Tin)和賴翥翔(Jason Lai)對本文提出了大量的反饋意見,感謝霍泰穩爲本文找到如此貼切的標題。

發佈了129 篇原創文章 · 獲贊 8 · 訪問量 33萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章