簡介
本文以云智慧數字化運維數據平臺DODB產品為例,由云智慧研發團隊通過分析對比產品歷史版本與新版本中的Java代碼行數、Java文件個數、代碼注釋占比、發布包大小、組件依賴等關鍵數據,找尋出歷史版本開發過程中存在的代碼、項目管理及開發人員等方面問題,最終梳理總結出改造代碼、制定規范等可提升研發效能的有效方法。
一、數據對比
通過下方圖表數據可以看出,在新版本中,代碼注釋占比降低了近150%,項目分支數減少了近100%,其他各項數據也均有了明顯的變化。
代碼行數統計
- 過去
- 現在
代碼注釋比例
- 過去
- 現在
sonar掃描
- 過去
- 現在
數據匯總對比
指標 | 過去 | 現在 | 對比結果 |
---|---|---|---|
項目module數量 | 12 | 8 | 減少33% |
代碼總行數 | 174252 | 136484 | 減少22% |
Java代碼行數 | 90310 | 50719 | 減少44% |
Java文件個數 | 982 | 583 | 減少41% |
代碼注釋占比 | 3% | 7% | 提升133% |
sonar-bugs數量 | 92 | 0 | 減少100% |
對外http接口數量 | 160 | 100 | 減少37.5% |
對外dubbo接口數量 | 100 | 25 | 減少75% |
發布包大小 | 226M | 170M | 減少25% |
項目分支數 | 300(參考) | 20 | 減少93% |
寫入性能(單條10K) | 88W/min | 187W/min | 提升113% |
組件依賴 | ignite、vertx | 刪除ignite、vertx | 5.3.1以redis替換ignite,徹底刪除vertx |
微服務 | 1 | 3 | 5.3.1支持服務以存儲模塊或者非存儲模塊獨立運行 |
二、歷史問題回顧
通過上述各項數據的對比分析,本章節對歷史版本開發中存在的代碼、項目管理、開發人員等各方面問題進行了回顧分析,詳細可查看下文:
代碼側問題
- 項目module數量多,部分不再使用的模塊仍存在;
- 各module模塊沒有統一的包名區分,導致大量全限定名一致的類存在,加載順序不一致引發各種問題;
- 項目中存在大量不再使用的代碼塊,增加了新人的學習成本及排查問題難度;例如dubbo接口,外部僅使用25個,對外提供了100個;
- 項目中重復代碼較多,相似業務復制粘貼現象較嚴重,維護成本較高;
- 重復造輪子較多,如HttpclientUtil等;
- 注釋率不夠,大量代碼無注釋,無測試用例;
- Vertx框架改造為SpringBoot容器不徹底,仍有眾多Vertx邏輯和Springboot共存;
- 增加新模塊需要寫很多sql,需要手動編寫Controller、Service、Dao層的“基礎”代碼;
- 項目依賴ignite組集群,導致服務是有狀態的,大多人員對ignite不熟悉,導致解決問題較困難;
項目管理側問題
- 沒有強有力的規范無法支撐更多的成員高效的協同開發;
- 分支管理無規范,導致分支數過多,活躍分支數量少,幾百個分支維護困難;
- 以分支形式交付,交付后分支仍有提交,導致追蹤發版版本困難;
- 無代碼管理規范,多次出現功能遺漏、多功能等導致分支回退;
- 沒有代碼review制度,無法保障代碼質量,沖突強行解決的問題時有發生;
開發人員側問題
- 大多成員對代碼不夠熟悉,或僅熟悉部分代碼;
- 很多成員不敢修改代碼,更不敢刪除代碼;打著不影響其它功能的旗號,復制粘貼大量的重復代碼和邏輯,導致維護困難;
- 早期代碼設計較為合理,隨著人員不斷增加,很多代碼重copy輕設計,只求完成功能,不考慮維護性拓展性,毫無設計可言;
- 部分人員git不熟練,導致強行覆蓋遠程分支的情況時有發生,無權限管控;
部分問題示例
- 全限定名完全一致的類
- 重復造輪子
三、解決問題方案
為解決產品歷史版本開發過程中存在的代碼、項目管理等各方面問題,云智慧研發團隊制定了開發、接口等相關規范;同時,通過限制代碼倉庫權限等其他操作也保證了規范的嚴格執行。
制定相關規范
無規矩不成方圓,切實可行的規范是保障團隊戰斗力的前提。規范制定應本著提高團隊水平,又不限制成員積極性為目標。
- 開發規范
- DODB接口規范
- 開發規范實行計劃
- DODB版本tag命名規范
- 開發設計模板
- 提測模板
- 會議規范
保障規范嚴格執行
工欲善其事必先利其器,規范里提供了相應的工具,用好可達到事半功倍的目的;同時有相關提交流程保障規范落地,不能讓規范流于形式。
- 設置gitlab權限,保障強制代碼review
禁止任何人向保護分支提交代碼,必須走merge流程
merge列表界面
merge詳情界面
- 完成后遠程分支強制刪除
- 以tag追蹤生產交付,要求測試交付時一定要有tag
禁止刪除tag
標簽tag列表
標簽tag詳情
四、如何對現有代碼進行改造
改造原則
- 不影響現有開發和交付進度,必須互不影響,類似于空中加油機,既要加油,飛機也不能停;
- 保證兼容性升級,改造后原有數據和業務不受影響;
- 對外接口(如dubbo)原則上不能修改,保證調用方功能正常,特殊情況可溝通配合(如dubbo方法重載必須修改);
- 不需要的代碼一律刪除,以后需要從歷史版本找回,禁止批量注釋掉代碼;
- 代碼設計要遵循 高內聚低耦合 的原則,保證可重用性、移植性;
- 目標一致,改造循序漸進,保證充分測試。
部分改造邏輯
- 對不再使用的代碼直接刪除,歷史可在gitlog中查看,例如bdp-plugin-sdk、bdp-plugin-zabbix等模塊直接刪除;
- 集中解決重復造輪子的問題,對部分通用邏輯,如HttpClientUtils、db操作等使用已有工具類替換;
- 書寫數據庫代碼繁瑣,引入mybatis-plus操作mysql
<!-- mybatis-plus操作數據庫 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
- 對外dubbo接口bdp-rpc和bdp-rpc-model合并為bdp-rpc-core對外提供,通過編譯各調用方項目,刪除未使用的接口
- 徹底刪除vertx和ignite依賴
- 對各module的功能和命名做了統一規范,對原有代碼做統一修改調整
模塊名稱 | 功能 | 包名前綴 |
---|---|---|
bdp-api | Restful API和業務邏輯實現、只能被bdp-standalone依賴 | com.cloudwise.bdp.api |
bdp-base | 業務對象和接口定義 | com.cloudwise.bdp.base |
bdp-commons | 通用工具common util | com.cloudwise.bdp.commons |
bdp-pipeline | pipeline數據處理實現 | com.cloudwise.bdp.pipeline |
bdp-rpc-core | rpc接口聲明及rpc實體類定義,不能依賴其它模塊,對外rpc接口請慎重修改 | com.cloudwise.bdp |
bdp-standalone | 主項目,DODB主入口類為DodbServiceApplication | com.cloudwise.bdp.standalone |
bdp-store-ck | ClickHouse存取實現 | com.cloudwise.bdp.store.ck |
bdp-store-common | 數據層存儲接口聲明 | com.cloudwise.bdp.store.common |
- 調用gitlab-api對長期不活躍分支集中清理備份
/**
* github-api
* https://github.com/help/api/api_resources.md
*/
private static final String GITLAB_URL = "https://github.com/api/v4/projects/2393/repository";
private static final String PRIVATE_PARAM = "*****************";
/**
* 清理分支,已合并分支直接刪除,其余刪除前以tag形式備份
*/
@Test
public void cleanBranches() {
Map<String, Object> paramMap = Maps.newHashMap();
paramMap.put("private_token", PRIVATE_PARAM);
paramMap.put("per_page", 10086);
String body = HttpUtil.get(GITLAB_URL + "/branches", paramMap);
List<Branche> branches = JSON.parseArray(body, Branche.class);
// 按最后一次提交時間由小到大排列
branches.sort(Comparator.comparingLong(o -> o.getCommit().getCommitted_date().getTime()));
log.error("分支數量:{}", branches.size());
branches.forEach(item -> log.info("{}", item));
// 清理長期不活躍分支
.....
}
發布包進行瘦身
首先了解發布包是如何構建的,參看assembly.xml配置
- 發布包里有什么
- 不要把配置文件重復打入jar包中
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<includes>
<include>com/cloudwise/bdp/**</include>
</includes>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>${start-class}</mainClass>
<classpathPrefix>../lib/</classpathPrefix>
</manifest>
</archive>
</configuration>
</plugin>
- 重點關注lib包,可從以下幾個方面排查
重點關注過大的包是否是項目必須的
是否有重復依賴問題
例如netty-all是眾多netty-*的合集,不要去重復依賴,如果版本不一致還會引發問題;
batik-all是batik-*的合集,排除各個子包
排查后通過相關插件追查依賴源,精確排除
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>${maven.dependency.version}</version>
</plugin>
1、開發人員應清楚依賴包是做什么的,不要不管三七二十一依賴一堆沒用的包,無謂增加發布包大小,還會帶來隱患;
2、代碼審核時,依賴文件(maven工程的pom.xml文件)的修改需要重點關注,禁止隨意引入和修改依賴;
3、各產品線可在統一各依賴版本的基礎上,調整構建方式,大幅減小集成包的大小。
改造總結
- 優化不求一步到位,可逐步進行,目標明確即可;
- 復雜是一切問題的根源,能用一行代碼解決的問題,就不用兩行;
- 一切功能都是靠代碼實現的,寫好代碼很重要,不能把寫好代碼當成一件小事,只是為了完成所需功能而堆砌代碼很簡單,但編寫清晰易懂且能完成所需功能的代碼并不簡單;
- 你永遠無法編寫出“完美”的代碼,要用工具和流程規范來保證這一切,要充分測試;
本文鏈接:http://m.abandonstatusquo.com/40943.html
網友評論comments