(點選上方公眾號,可快速關註)
來源:koala bear,
wsfdl.com/architecture/2017/12/18/daemon_normalize_arch.html
本人接觸過數個 Open Source 專案,如 OpenStack/Kubernetes 等,深感這些優秀的開源專案存在著一些共性,如:美觀的程式碼,完整的測試,設計理念,框架和架構等等。一般來說,遵循這些優良原則的專案在易讀性,可維護性,特別是(功能和規模的)可擴充套件性會更強些。
本文探討性的梳理部分開源專案某些共同的優秀之處,但是軟體工程領域,不同業務/語言的專案千差萬別,故“標準”二字也是相對的,更適合於 golang/python 語言編寫的 LAM(Linux/Apache/Mysql)後端專案,爭議之處請多包涵。
目錄組織與命名
目錄組織
不同語言下的目錄組織可能會有很大的差異,但是都遵循以下共識:
-
語意貼切:檔案/目錄的命名要盡可能表達其功能/用途
-
集中原則:功能相近的檔案/目錄應集中存放
例如:
your_project /
|– contrib / # 存放一些如 rpm.spec,指令碼等等
|– doc / # 檔案相關
|– examples / # 樣例說明
|– etc(or conf) / # 配置檔案
|– src /
|– api / # api 相關程式碼
|– cmd(or cli) / # command 相關程式碼,
|– db / # database 相關程式碼
|– rpc / # rpc 相關程式碼
|– tests / # 測試檔案目錄
|– utils(or common) / # 一些公共的方法
|– … # 其它目錄/檔案
|– README # 專案說明
|– .gitigore # 忽略的 git 檔案和目錄
|– … # 其它目錄/檔案
-
api:存放 api 相關程式碼,比如 route,filter,middleware,serialize/de-serialize等。
-
contrib:存放一些編包的檔案,git 的 patch 檔案,systemd 的配置檔案,以及指令碼等等。
-
db:存放 database(如:sql/NoSQL) 相關程式碼,包括 model, connection, session,orm/sql。是程式碼層面訪問 db 的唯一入口,建議所有訪問 database 的操作都必須在程式碼層面經過 db 目錄下的程式碼。
-
utils:一般存放一些公共的方法,如 time/date,encoding/decoding,exec,regex,uuid,string/json 等等。
推薦閱讀:
-
What is the best project structure for a Python application?
https://stackoverflow.com/questions/193161/what-is-the-best-project-structure-for-a-python-application
-
What is a sensible way to layout a Go project
https://stackoverflow.com/questions/14867452/what-is-a-sensible-way-to-layout-a-go-project
命名
匈牙利命名法和 unix 命名法是常見的命名風格,一般來說,c 和 python 以 unix 命名法居多,golang 和 java 以匈牙利命名法居多。程式設計過程中,遵循一致的命名風格,會使得程式碼更為美觀,可讀性強。
程式設計中貼切的縮寫常見單詞,可使得程式碼更為簡潔美觀,但是縮寫最好應遵循某些約定成俗,避免產生歧義。
一般情況下不建議採用拼音命名和拼音縮寫。
推薦閱讀:
-
Google Python Style Guides
https://google.github.io/styleguide/pyguide.html
-
Google Shell Style Guide
https://google.github.io/styleguide/shell.xml
-
一個查單詞縮寫的網站
http://www.abbreviations.com/
釋出與執行
為什麼應用的釋出和執行也應該遵循一定的規則呢?首先,使用者體驗更好。比如,軟體工程師習慣性在 /etc 尋找配置檔案,/var/log 尋找日誌檔案。其次,遵循這些規則有利於被運維軟體管理,如 ansible,chef 等等。最後,linux 及相關軟體在設計時就已經遵循這些規則,如果你的應用不遵循,可能會產生某些衝突。
版本號
釋出版本時,版本號的命名需要遵循某種規則,其中 Semantic Versioning 2.0.0 定義了一套簡單的規則。重點如下: 版本號的格式為 X.Y.Z(又稱 Major.Minor.Patch),遞增的規則為:
-
X 表示主版本號,當 API 的相容性變化時,X 需遞增。
-
Y 表示次版本號,當增加功能時(不影響 API 的相容性),Y 需遞增。
-
Z 表示修訂號,當做 Bug 修複時(不影響 API 的相容性),Z 需遞增。
-
X, Y, Z 必須為非負整數,且不得包含前導零,必須按數值遞增,如 1.9.0 -> 1.10.0 -> 1.11.0
-
當 API 的相容性變化時,X 必須遞增,Y 和 Z 同時設定為 0;當新增功能(不影響 API 的相容性)或者 API 被標記為 Deprecated 時,Y 必須遞增,同時 Z 設定為 0;當進行 bug fix 時,Z 必須遞增。
-
版本一經釋出,不得修改其內容,任何修改必須在新版本釋出!
推薦閱讀:
-
Semantic Versioning 2.0.0
https://fedoraproject.org/wikiHow_to_create_an_RPM_package/zh-cn
打包
不同語言的應用可能有不同的打包和釋出規範,如 java 的 jar 包,python 的 wheel 包,它們極大的簡化了應用的安裝/升級/管理等等,非常方便的解決了版本和依賴問題。但是上述的打包方式在通用性上有所欠缺,只能被特定語言的工具管理,比如 jar 包需要被 maven 等管理,wheel 包需要 pip 等管理。為此 Redhat 定義了 redhat package management(即 rpm 包),所有語言的應用都可以製作成 rpm 包,然後用 rpm 和 yum 等常用工具進行管理。與此類似,ubuntu 也定義了 debian 包。
個人認為,應用最好以軟體包的形式釋出,避免直接複製原始碼或者二進位制檔案到線上。
推薦閱讀:
-
How to create an RPM package
https://fedoraproject.org/wikiHow_to_create_an_RPM_package/zh-cn
-
Python application 的打包和釋出
http://wsfdl.com/python/2015/09/06/Python應用的打包和釋出上.html
執行
一個應用可能包涵配置檔案,程式碼庫,可執行檔案等等,這些檔案的存放也所講究,一般來說:
-
可執行檔案應存放於 /usr/bin 或者 /usr/local/bin 目錄下
-
程式碼或者庫應放於 /usr/lib 或者 /var/lib 目錄下
-
配置檔案應存放於 /etc/your_project/ 目錄下
-
日誌檔案應存放於 /var/log/your_project 目錄下
-
systemd 相關指令碼應該放置於 /usr/lib/systemd/system/ 目錄下
-
logrotate 相關指令碼應該放置於 /etc/logrotate.d/ 目錄下
個人不建議把應用放在 /home 目錄下。
測試
測試可分為單元測試/整合測試/效能測試。需不需要測試,需要哪些測試,測試改寫率為多少和很多因素有關,個人認為:
-
程式碼越多,測試越重要
-
動態語言,測試越重要
-
開發人員越多,測試越重要
從經驗上看,很多專案一般都會有整合測試,但是很少有單元測試。這裡想談談單元測試的重要性,它很大程度上保證了新加的程式碼不會影響原有的邏輯。首先這種程式碼級別首先更為精細,能夠針對性的改寫重要功能程式碼。其次,單元測試的執行一般不依賴具體環境,能夠隨時編寫隨時測試。再次,單元測試能夠在一定程度上維護程式碼的結構。最後,都說動態一時爽,重構火葬場,而完整的單元測試是避免火葬場的重要手段。
很多語言都有測試框架和工具,這些工具功能強大,如:執行測試,生成測試報告,程式碼風格規範檢查,整合 jenkins 等等。建議根據需要選擇合適的測試框架。
推薦閱讀:
-
tox vision: standardize testing in Python
https://tox.readthedocs.io/en/latest/
-
談談開發中單元測試的重要性
http://ufdouble.com/2016/07/談談開發中單元測試的重要性/
看完本文有收穫?請轉發分享給更多人
關註「ImportNew」,提升Java技能