「經驗總結」高效開發,老程式碼可以這樣動
theme: condensed-night-purple highlight: atelier-lakeside-light
前言
一般專案存在2年及以上,程式碼就會變得臃腫且不好維護。如果有人員變動,新成員加入,可能面臨的第一個難題就是,如何在老程式碼中加新需求。
我之前剛入職,接手老專案,內心活動是這樣的:
- 新功能的新增會不會影響老功能?
- 工期如此緊張,我還有時間做功能設計嗎?要不直接上手碼程式碼得了?
- 新增的功能跟已有的很相似,我是提公共還是在下面直接貼上一份改改?如果提公共,原來的功能要不要測試一下?
- 緊急的線上的bug,但是程式碼第一次見,今天必須修復上線,時間太緊了怎麼辦?
- 業務邏輯太複雜,平時迭代頻繁,我有時間捋程式碼嗎?
- 老的功能不熟悉,新的需求要修改原來的邏輯,但是邏輯挺繞的,互動多,UI還不好實現,我應該怎麼排期才合理?
雖然看上去像個戲精,但是句句都是真情實感。接手老專案遇到的這些疑問,不僅實際發生的頻率較高,而且很大一部分會影響開發效率和質量。
雖然沒有研究出什麼武學奧祕「降龍十八掌」,但是我用畢生所學,總結了「老程式碼的神行百變」。
功能變通
老頁面新增新功能,並不是所有時候都會影響原有的功能,所以開發之前做一下功能分類,會有「事半功倍」的效果。
功能隔離
有時候,需要新增的功能點,可能只是一個單選項或者下拉框,這個時候封裝個元件也沒什麼必要。但是畢竟是在老的頁面上新增,既要不影響現有功能,又要考慮之後做功能擴充套件。
我一般會做功能隔離。得益於JSX的靈活寫法,封裝程式碼段十分方便。相對獨立的功能就不過多說了,這種情況下做功能隔離很簡單。主要是有依賴關係的功能,如何做隔離?
其實功能隔離也有點解耦的思維在裡面,雖然功能有依賴性,但是不一定要把程式碼寫在一起。
比如產品訂單,所有產品型別的訂單都在訂單管理中,A產品的產品經理說,A產品的訂單撤單的時候要給二次確認提示,但是B產品沒有這個要求。
我把是否進行二次確認彈窗的邏輯放在了彈窗外側的列表操作上。撤單彈窗裡面中直接對二次彈窗的處理。這樣一來,撤單彈窗中的程式碼不用關心哪條業務線需要進行二次確認操作。外側的撤單按鈕不需要關心撤單彈窗裡面是怎麼操作的,只需要把控制二次確認彈窗的變數reconfirmFlag(是否二次確認布林值)傳入彈窗即可。
這樣,未來如果再加其他的產品型別,只需求維護列表撤單操作程式碼裡面的條件判斷即可。
列表撤單操作
js
record.reconfirmFlag = false;
// =>true: 帽子產品需要二次確認彈窗
if (record.type === 'hat') {
record.reconfirmFlag = true;
}
撤單彈窗確定操作
js
/**
* 提交操作
* @param {Object} value 介面入參
* @return {void} 無
*/
const handleSubmit = value => {
let { data } = props;
if (data.reconfirmFlag) {
// 二次確認提示
} else {
// 提交操作
}
};
相似功能的提煉
新增的功能和現有的功能非常相似,也是在開發中經常遇到的情況。可能習慣性的或者為了求穩直接把老程式碼複製一份,修改修改完事。
看似又快又穩的操作,會導致程式碼變得越來越冗餘,老程式碼變得越來越難維護。
怎麼提煉?
相似功能主要有兩點,同和異。相同部分是需要提煉的內容。對於差異的部分,我之前習慣用條件判斷,現在習慣用列舉的方式。
不同產品分類展示產品資訊
```js /* * 產品介紹 * @param {string} type 產品分類 / const goodContent = type => { const good = { hat: { title: '帽子', name: '帽子A', color: '黑色', }, shoses: { title: '鞋子', name: '鞋子B', color: '白色', }, }; const goodItem = good[type]; return (
return (
); ```
不同型別圖片展示和編輯
```js
/*
* 圖片展示
* @param {Array} imgList 圖片列表
* @return {void} 無
/
const colImgContent = imgList => {
return (
<>
{imgList.length != 0 &&
imgList.map((img, index) => (
))}
);
};
/* * 圖片展示 * @param {string} type 圖片分類 / const imgContent = type => { const imgObj = { hat: { title: '帽子產品展示', imgList: [], }, shoses: { title: '鞋子產品展示', imgList: [], }, }; const item = imgObj[type]; return (
return (
); ```
老功能要不要測?
當然,測試是肯定要測的。不過提測的時間點可能會因情況而異。
我一般在需求設計階段就會把需要額外測試的地方標註出來。但是有些需求任務量小不需要設計評審,這種情況下如果要增加額外的測試功能點,需要提前跟測試的同事溝通,一般不是翻天覆地的修改,測試的同事是很好溝通的。如果改動影響範圍太大,測試的同事要做風險評估之後,才能確定是否可以進行程式碼改造。
注:可能每個公司的工作流程不一樣,請大家參考公司的實際流程進行提測要求和提測操作。
精準定位
線上的緊急bug、臨時上線的緊急需求等,即考驗開發者的抗壓能力,又考驗自身的技術能力。有些技術難題,確實需要過硬的技術功底。但是有些問題,找準問題的關鍵點,其實不難解決。
不是逗悶子,先來看幾個例子。
無法喚起的收銀臺
前情提要
同事離職之後,葉一一暫時接手了同事負責的工作,還沒有完全熟悉那部分業務。這天,葉一一拎著早餐來上班,產品經理過來說一個活動的收銀臺無法喚起了,讓葉一一幫忙看一下。
第一次知道這個活動的葉一一是這樣做的
雖然葉一一對這個活動非常陌生,好在所有的活動功能都在專案的activity目錄下。找到對應的頁面,葉一一沒有急吼吼的去熟悉業務,她在做其他業務線購買流程時很清楚一點,進行提交操作之後才會喚起支付收銀臺,所以葉一一先找到提交按鈕,然後再去找喚起收銀臺的程式碼,發現這個活動的收銀臺是跳轉第三方的,而跳轉第三方url中帶有test字元的域名,葉一一就知曉了問題所在。
果然不出所料
葉一一問產品,跳轉第三方收銀臺的連結,測試環境和正式環境的分別是什麼。拿到連結之後,果然測試環境帶"test"線上不帶。葉一一加上環境判斷區分不同環境的跳轉連結,再上線之後,線上就能正常跳轉收銀臺了。
後續
葉一一後來熟悉這個活動功能的時候發現,活動流程還挺長的,到達購買頁面之前至少有5、6個步驟。所以,如果葉一一沒有精準定位問題,到達購買頁面還得費點功夫。
js
// 區分線上和測試環境的地址
let prodEnv = env === 'production'
let urlPrefix = prodEnv ? 'https://api' : 'https://testapi';
const url = `${urlPrefix}.com`;
測試通過之後的線上報錯
前情提要
葉一一剛接手了從別的組並過來的專案,還沒來得及看程式碼,就來活了,有大概10個頁面需要改動。葉一一看不是改文案就是在原來的基礎上新增模組,就愉快的定好排期開始幹活。
測試通過
很順利的開發完功能,自測沒有什麼問題,葉一一就提測了。測試過程也很順利,上線日,程式碼早就準備好,就等晚上上線了。
線上驗收
線上驗收時,測試的小夥伴發現有個介面報錯了,後端的同事說這是一個老介面沒有做過調整。葉一一找到出現問題的介面,發現是一個很簡單的獲取資料的介面,都不需要入參。怎麼會有問題呢,測試環境明明沒有問題。
「全組的等待」+「就差這一個問題就驗收通過」的雙重壓力並沒有讓葉一一慌亂。葉一一遇到過多次玄妙問題,只要找準問題關鍵,不難解決。
正式環境和測試環境唯一的區別就是介面加密,會不會加密的介面需要什麼特殊處理?葉一一本地執行加密命令,果然介面報錯。葉一一又對比了其他介面,唯一區別就是這個介面沒有入參。於是葉一一嘗試在入參傳了一個空物件,果然介面成功並返回資料。葉一一讓後端幫忙看加密的邏輯,確實有特殊的邏輯處理。
後續
後來葉一一發現其他專案沒有這個問題,是因為在axios封裝時做了特殊處理,新接手的專案沒有進行處理。
```js // 處理過 config.params = JSON.stringify(params);
// 未做處理 config.params = params; ```
小結
有些問題,在特殊場景才會出現,這個時候通過對位元殊場景和常規場景的異同,找到不同點,能夠幫助快速解決問題。
業務變通
熟悉業務
與其擔心不熟悉業務,不好改需求或者評估開發排期,不如抽時間熟悉業務。業務熟悉起來也可以間接的提升開發速度和開發質量。
業務線太多怎麼辦?
我個人是「好記性不如爛筆頭」這一派的。我無法一次記住全部的業務功能,那就寫寫文件,將功能梳理出來。
如果業務線多,那就一條一條的捋。切記「貪多嚼不爛」,以及避免「迷路蜜蜂」的狀態。
單個業務如何梳理?
不同的業務,梳理的側重點也會有所不同。
- 流程較長的業務,需要捋順整個流程。可以通過繪製流程圖的方式,輔助理解和記憶。
- 互動複雜的業務,需要新定邊框,再細化互動設計。互動是比較分散的,但是型別比較確定,彈層是彈層,下拉項是下拉項。整體的互動風格先定好,可以減少後期的修改。
- 內部邏輯複雜的業務,需要拆分層級。先拆成最小顆粒度,再做功能組合,有點像搭積木,先把形狀分類好,再搭結構,最終完成「圖紙」上的模樣。
- 偏活動類的業務,梳理文件中,最好列出來活動入口、參與規則(可能會影響前端的功能)、參與記錄的檢視方式等。
已整理業務文件數量
| 型別 | 文件數量/個 | | ------- | ------ | | 產品線文件 | 12 | | 理賠組業務文件 | 7 |
抽離業務元件
先繪製出全部業務模組,分門別類。然後橫向對比業務功能,思索今後的發展,確定哪些功能是可以抽離業務元件的。
抽離的本意,一方面提升程式碼的複用率,不必重複開發;另一方面,可以將複雜功能拆解,便於後續的快速迭代。
抽離的好處,功能寫的又快又好,一次開發,造福未來。
我之前寫過一篇關於業務元件的思考,這裡不展開說了,指路☞ 【工作小記】關於業務元件的思考 。
思維變通
設計文件
我的設計文件主要包括三部分內容,開發前準備、開發排期、需求拆分和重點功能設計。
開發前準備
準備的內容主要是功能開發之外的事情。
比如小程式開發是否需要新增業務域名,新增的專案是否需要運維的同事幫忙配置域名,公眾號新增頁面上線之後是否需要提供入口連結等。這些可以再設計文件中進行記錄,避免遺忘。
開發排期
我的開發排期中包含每一項開發需要的具體時間,以頁面為維度,以小時為最小的時間單位,然後彙總成天數。實際的開發週期和聯調週期,還需要結合手上有沒有其他並行的需求和後端同事的開發週期。
需求拆分和重點功能設計
我會把所有的需求羅列出來,避免漏掉哪個。但是功能設計一般只會做重點功能,或者一些元件封裝的設計。
重點功能設計主要是處理方案和重點功能程式碼塊,如果流程複雜的會附上流程圖;如果狀態值較多的項,附上列舉值對應表格。
抽絲剝繭
並不是所有的功能是複雜的,把功能抽離到最小功能,肯定有是簡單的部分,而複雜的內容只佔其中一角。
比如一個列表頁,搜尋項和列表展示比較簡單,刪除操作頁很簡單。但是新增的表單很複雜。
如果卡在複雜的功能處,不妨先完成簡單的部分。如果經常卡在複雜的功能上,就要自我反思一下了,平時有沒有做功能整理和技術提升,來幫助自己更好更快的開發複雜功能。
排期變通
正向排期總是失誤,不妨試試反問式排期方法。
- 為什麼只是在原來功能中加了一個小需求,但是寫的排期卻這麼長?它難在相容老功能還是新增的功能?
- 為什麼修改的項這麼多,但是寫的排期時間卻這麼短?複雜程度是不是真的不高,不要被想當然坑了自己?
- 別的頁面需要的時間都很短,為什麼唯獨這個頁面需要這麼長的時間?它的複雜點到底在哪?
- 為什麼聯調的時間反而比開發的時間長?是我聯調時間寫長了?還是開發時間寫短了?還是聯調介面太多,引數太複雜?
總結
對於老程式碼的改動,確實耗時又費心神。所以我有時候會在我的設計文件中,寫一些鼓勵自己的俏皮話。一定要好玩一點不要太中規中矩,才能逗笑自己的同時,激發幹活的熱情。
凡事留一線,程式碼好擴充套件。
功能設計好,bug見得少。
今天努力捋順的需求,都是日後寶貴的財富。
我正在參與掘金技術社群創作者簽約計劃招募活動,點選連結報名投稿。
- await-to-js 原始碼分析,體驗一把捕獲異常的優雅
- CSS偽類的第三集,原來偽類也可組CP
- 從:is()說起,開啟CSS偽類第二集
- 一組純CSS開發的聊天背景圖,幫助避免發錯訊息的尷尬
- 「CSS特效」我的發呆專屬,反覆解鎖手機螢幕
- 「技術分享」以Antd為例,快速打通UI元件開發的任督二脈
- 「功能實現」我封裝了一個表單元件,感覺離財富自由又近了一步
- 「經驗總結」高效開發,老程式碼可以這樣動
- 前端開發提效小技巧之業務功能篇
- 人生有忙忙碌碌,也有詩和遠方 | 2022年中總結
- 【端午節】新奇體驗,我用react實現網頁遊戲的全過程(包括規則設計)
- 【暑假記憶】消暑神器,我用CSS復刻了一個遊戲機
- 突圍?我願稱之為向上的攀登者
- 【孟夏之遇】望孟夏之短夜兮,螢星相伴
- 【技術學習】SVG-邊學邊做
- 【TS實踐】自己動手豐衣足食的TS專案開發
- 【碼上掘金】通過FileReader讀取Excel檔案內容
- 【Taro開發】四月芳菲,Taro觀賞指南
- 【Node.js】青梅煮酒,聊聊zlib壓縮
- 【知識點】關於iframe跨域通訊