如何寫出一篇好的技術方案?
近期在寫某個項目的技術方案時,來來回回修改了許多版,很是苦惱。于是,將自己之前寫的和別人寫的技術方案都翻出來看了幾遍,產(chǎn)生了一些思考,分享給大家。
我們?yōu)槭裁葱枰獙懠夹g方案?總結下來無非是幾點,從不同人的視角來看:
- 產(chǎn)品:驗證技術方案是否能夠 match 上產(chǎn)品方案
- 測試:驗證技術方案對測試方案是否有足夠 & 準確的輸入
- 同事 & leader:參與技術方案評審,驗證技術方案的合理性
- 新人(不單單指新同學也指新接觸這一塊的同學):拿到技術方案可以很快對某一塊的事情熟悉起來
什么樣的技術方案是一個好的技術方案
我們都知道技術方案是指導具體開發(fā)工作的,可以分別從開發(fā)的事前、事中、事后來討論這個問題。
事前
- 明確的目標:整個技術方案要達成什么目的
- 低溝通成本:產(chǎn)品測試能從技術方案上獲取足夠的輸入,不需要反復找你確認
- 技術選型思考:為什么要這么做?和業(yè)內(nèi)方案相比有什么好處和壞處,如何權衡的
事中
- 少調(diào)整:盡可能少的技術方案需要調(diào)整, 否則無法完成開發(fā)任務
事后
- 少補丁:盡可能少的 bug 或者遺漏
- 可擴展 & 可復用:相對簡單的改動就能支持新增需求,類似場景可復用不需要重復開發(fā)
一篇好的技術方案可以貫穿整個研發(fā)的生命周期,并且能起到很好的指導意義,就如同寫小說之前作者必須出一個大綱的邏輯是一致的。
如何寫好一篇好的技術方案
那么,如何寫出一篇好的技術方案呢?下面列舉出筆者認為應該做到的一些點。
清晰的目標
一份技術文檔需要有一個清晰的目標(業(yè)務需求建議自己總結而不是 Copy from
PRD,技術自發(fā)的那肯定得自己總結),那目標怎么來的呢?是從需求里轉化過來的。那么,如何將對應的需求轉化為一個清晰的目標?我認為最重要的是要盡量定義一個可衡量的標準。需求的種類一般來說就兩種,分別是:1.
1.產(chǎn)品類需求——業(yè)務方 or 產(chǎn)品方發(fā)起提給技術,包括但不限于以下幾種:
- 頁面交互:能提升多少的運營操作效率,多少 PV、UV 這種可量化的數(shù)字?
- 業(yè)務 SOP 調(diào)整:帶來的業(yè)務價值是什么?是能降多少本還是提升多少時效?
- 數(shù)據(jù)訂正:訂正能解決什么問題?防止多少錢未結算?又或者是防止多少客訴?
2. 技術類需求——技術自發(fā)提出,包括但不限于以下幾種:
- 性能優(yōu)化:優(yōu)化多少?20%、30% 還是 50%?
- 數(shù)據(jù)隔離:隔離的范圍是什么,涉及多少張表,多少數(shù)據(jù)?可以減少當前的什么問題?減少多少?
- 各種小工具:沒有小工具之前是什么樣?有之后是什么樣?可以帶來什么好處?
- 研發(fā)效能提升:提升多少?有沒有可以量化的指標?而不是為了做而做。
在眾多的需求當中還有一些是我們要去辨別的偽需求——不是用戶真正想要的需求,如用戶想要將一個飛機改造成火箭,但是產(chǎn)品可能提過來的僅僅是把飛機的兩個翅膀砍掉,那么砍掉翅膀就能變成火箭了嗎?明顯不能,所以這種需求一定要注意鑒別。
大綱圖
有句話叫“不謀全局者不足謀一域”,在技術方案中我想也是如此。在一個技術方案中,一個大綱圖是不可或缺的
,有的人叫它技術架構圖,有的人叫它數(shù)據(jù)流轉圖,這都不重要,重要的是我們能從這張圖中看到整體的脈絡,那么這張圖需要有哪幾個要點呢?
- 圖不用很細(比如加工比較復雜我們可以簡單寫**加工),但是要能看到全貌,具體的每個模塊如果需要展開的,那么在對應的詳細設計中體現(xiàn)即可,在這里我們關注的是整體;
- 接口如有歸屬不同的應用要標明;
- 數(shù)據(jù)存儲介質不同要標明;
- 數(shù)據(jù)流轉的箭頭要清晰明確;
- 數(shù)據(jù)加工計算的輸入和輸出要體現(xiàn),同時要體現(xiàn)加工的運行環(huán)境(比如到底是 odps 計算還是內(nèi)存計算,內(nèi)存計算的話是在那個應用)。
大綱圖的邏輯示意參考
模型設計
講到數(shù)據(jù)模型設計,E-R 圖是必不可少的,E-R 圖應該包含以下信息:
- 每個領域對象,如果要持久化,都有表來存儲。我們設計完 ER 圖的時候,應該根據(jù)這條原則做一番檢查,避免漏掉一些表。在大型項目中,漏掉表是很常見的事情,應盡量避免。
- 領域對象之間的關系,如果要持久化,都要在表結構中體現(xiàn)。這種體現(xiàn),可能是 code 字段,可能是外鍵,可能是中間表。我們設計完 ER 圖的時候,也應該根據(jù)這條原則做一番檢查,避免漏掉一些關系。在大型項目中,漏掉關系更是司空見慣,應盡量避免。
- 清晰定義的表名。設計 ER 圖的時候,就要設計出清晰定義的表名。清晰定義的表名,可以幫助大家理解 ER 圖,可以幫助大家映射回領域對象及其關系。在后續(xù)的設計和實施中,將沿用這個表名。
- 清晰定義的字段名、字段類型、字段長度和枚舉值。很多同學容易忽略這點,他們往往清晰定義了表名,卻沒有重視表的字段。認真去做的時候,會發(fā)現(xiàn),這里面有很多工作。例如,字段名是否合適,用什么類型好,字段長度多少合適,是否有枚舉值等等,都需要一一斟酌。如果這點做好了,在實施的時候,可以避免很多麻煩,甚至避免返工。
對外依賴提前確認
技術方案 1:需要依賴緩存、分布式調(diào)度中間件、消費外部的消息,但是沒有把對應的中間件使用方式、數(shù)據(jù)格式貼出來。
技術方案 2:需要依賴緩存、分布式調(diào)度中間件、消費外部的消息,將緩存接入的方法 & 對應的緩存 key-value
設計寫清楚,將分布式調(diào)度中間件接入所需要準備的依賴項梳理好,將外銷消息對應的 topic 和數(shù)據(jù)格式列清楚。
兩個方案對比好壞其實很明顯。如果一開始我們在技術方案里面將外部依賴確定好,那么我們在開發(fā)的時候就一馬平川,反之如果外部依賴都不確定的情況下就進入到開發(fā),那么返工的概率將大大增加,從而降低我們的工作效率。
那么,對外的依賴有哪些以及我們應該要確認什么信息呢?下面列舉了一些常見的依賴情況:
- 外部 hsf 依賴:需要確認對應 hsf 接口的類、方法、version,以及二方包(也可使用泛化調(diào)用);
- 外部消息依賴:需要確認消息的 topic、數(shù)據(jù)格式;
- 分布式調(diào)度、緩存等中間件:當前應用是否接入過該中間件,未接入需要去到官網(wǎng)確認接入文檔,接入的話需要確認是否可以復用接入邏輯。
內(nèi)部前后模塊依賴 & 層次結構
模塊依賴層次從高到低分為:
- 領域依賴(如交易依賴商品);
- 應用依賴(如 cntcp 應用依賴 cngfc 應用);
- 接口依賴(如滾存看板查詢接口依賴于鎖接口 & 渠道集接口)。
我們舉接口依賴的例子來看:一共三個接口分別是滾存看板查詢接口、鎖接口、渠道集接口,滾存看板查詢接口依賴于鎖接口和渠道集接口。
技術方案 1 目錄層次:滾存看板查詢接口、鎖接口、渠道集接口;
技術方案 2 目錄層次:鎖接口、渠道集接口、滾存看板查詢接口。
很明顯,技術方案 2 是更加合理的,A 依賴于 B 那么 B 應該先做。我們在寫技術方案的時候,要考慮什么應該在前什么應該在后,而不是想一步寫一步。要有一個清晰、有序的結構,不然別人看起來就會是雜亂無章的。
一個模塊里面應該有啥
下面列出一個技術方案的模塊里面應該要寫哪些東西,供參考:
1. 具體的接口定義
要求:實現(xiàn)一個飛機運力合同查詢接口,入?yún)檫\力大區(qū)
技術方案 1:
入?yún)ⅲ?br>{
"area": "南美"
}
出參:
{
"date": "***"
}
技術方案 2:
方法名:CapacityService.queryPlan
入?yún)ⅲ?br>{
"cnArea": "南美"
}
出參:
{
"date": "***"
}
技術方案 2 是更好的,為什么?測試、前端 、后續(xù)要接手該接口的人都能夠一下子找到你的接口并清楚知道輸入輸出是什么。另外,1 和 2 的入?yún)⒁粋€ area 一個 cnArea,那么到底哪個更對呢?這里由于系統(tǒng)中在用的都是 cnArea,固沿用 cnArea 是對的(一致性減小溝通成本)。
這里總結對接口定義有幾點要求:
- 完整的類和方法名;
- 入?yún)⒆侄稳绻到y(tǒng)中已有,那便沿用;如果沒有,那么英文的描述需要準確(可同產(chǎn)品業(yè)務商榷);
- 出參字段要求同入?yún)ⅰ?/li>
2. 詳細的時序圖
要求:實現(xiàn)一個學生信息查詢接口。
技術方案 1:寫出查詢結果中執(zhí)行的相關步驟。
step1. 入?yún)⑿r?br>step2. 查詢A表
step3. 對A標返回結果做校驗
step4. 查詢B表
······
技術方案 2:在 1 的基礎上使用時序圖表達出來。
推薦使用技術方案 2,好處是盡管內(nèi)容相同但是時序圖能夠更直觀地看到層次、數(shù)據(jù)流轉等信息。
除了以上比較基礎的 2 點,我覺得的還有一些要點:
- 數(shù)據(jù)加工的詳細圖(如有)——同樣推薦用圖的形式可以更直觀;
- 消息設計(如有),明確消息生產(chǎn)方、消費方、tps、數(shù)據(jù)結構;
- 自測用例(推薦),比較重要的功能點構造一些自測用例。
- ······
技術選型分析
要求:實現(xiàn)一個定時任務,定期將過期的數(shù)據(jù)刪除。
技術方案 1:使用 spring 自帶的定時器進行數(shù)據(jù)清除。
技術方案 2:使用分布式調(diào)度中間件(如 schedulerx)進行定時過期數(shù)據(jù)清除。
乍一看好像都能實現(xiàn),但仔細對比兩個實現(xiàn)方式之后我認為大部分人還是會選擇技術方案 2,為什么?下面列出一些在選擇技術方案時考慮的點。
目標是否可達成 | 實現(xiàn)難度 | 可維護性 | 可擴展性 | |
技術方案1-spring定時器 | 是 | 易 | 易 | 低 |
技術方案2-分布式調(diào)度中間件 | 是 | 易 | 中 | 高 |
安全生產(chǎn)
安全生產(chǎn)包括幾個部分,包括且不限于下面幾個部分:
- 監(jiān)控
- 對賬
- 灰度方案
- 數(shù)據(jù)隔離
- 兼容性評估
- 發(fā)布流程
我們舉一個例子。
需求:在消費者收貨成功時觸發(fā)對商家的結算。
技術方案 1:******,寫了一堆如何觸發(fā)結算、如何更好地支持后續(xù)的可擴展性;
技術方案 2:******,寫的方案可擴展性沒有技術方案 1 高,但是做好了未觸發(fā)結算的監(jiān)控、觸發(fā)結算之后的對賬,并設計好了對應的報表防止出現(xiàn)資損。
其實這也是我們在技術方案中可能會忽略的一點——埋頭于代碼結構如何如何的好,但是有些東西其實是要比單純的代碼更重要。就比如風險控制,完備的監(jiān)控、不可缺少的對賬是保障公司資金安全,更是保障我們自己績效的工具(此處應有表情)。
那么對于監(jiān)控、對賬的具體要求是什么呢?我認為有以下幾點:
監(jiān)控:
- 監(jiān)控目標:寫清楚監(jiān)控的是什么內(nèi)容;
- 監(jiān)控點:如通過打印日志監(jiān)控,那么日志打印在哪個類的哪個方法;
- 監(jiān)控觸發(fā):是通過 sunfire 采集觸發(fā)還是其它,如果是 sunfire 采集最好能把監(jiān)控項地址貼出來;
- 監(jiān)控訂閱人:監(jiān)控告警需要的訂閱人;
- 監(jiān)控觸發(fā)后的解決方法:如果發(fā)生異常該如何解決?如手工觸發(fā)結算。
對賬:
- 對賬目標:寫清楚對賬是為什么;
- 對賬方式:寫清楚是怎么對賬的(如通過 odps 天級定時任務,履行單上的關務資源 code 和日志表中關務 cp 回傳報文的關務資源 code 對比要一致,不一致的形成某個數(shù)據(jù)集,通過錦衣衛(wèi)-資損風險平臺配置);
- 對賬告警訂閱人;
- 對賬異常之后的解決辦法。
還有其它幾個部分也補充說一下:灰度方案,包括但不限于:
- 多方前置準備;
- 灰度切流開關設計;
- 灰度切流節(jié)奏;
- 異常應對。
向前兼容性,包括但不限于:
- 接口的向前兼容:尤其是對外的接口;
- 數(shù)據(jù)結構的向前兼容:如不能隨意改變字段的存儲類型和格式。
環(huán)境隔離:
- 如有租戶隔離 & 預發(fā)線上隔離的情況需要考慮數(shù)據(jù)。
發(fā)布流程,包括但不限于:
- 發(fā)布計劃;
- 檢查項列表;
- 發(fā)布流量監(jiān)控。