天天看點

你為什麼不敢重構代碼?

點選上方 前端瓶子君,關注公衆号

回複算法,加入前端程式設計面試算法每日一題群

你為什麼不敢重構代碼?

來源:ES2049 / 黑石

https://juejin.cn/post/6951373058544730125

代碼重構有兩大難點,一個是「考古」,也就是如何快速梳理出代碼的原有邏輯,還有一點就是「釋出」,如何讓新的代碼可以穩定的釋出到線上,而不産生故障。下面我們就聊聊我一個朋友的故事,看看他是怎麼把代碼穩定搞上線的。為了表達更為親切,你現在就是我那個朋友。

重構代碼對很多人來說,絕對是一件髒活、累活。沒有可以大幅度提效的方法,難以沉澱有效的體系化的可複用的技術抓手,對業務來說沒有明顯的增量,精力和時間消耗巨大,沒有測試用例,也不一定能得到測試的支援,自測很難做到充分,最後開發完了很難上線,主要原因是害怕!當然并不是我們不自信,是真的恐懼。

一、你為什麼不敢發代碼?

通過代碼還原當時完整的産品邏輯太難了

你重構的代碼是誰的?鬼知道是誰的!能讓你重構的代碼大機率不是你寫的代碼,而且是遠古代碼,用的是一種過時的技術棧。當然一般情況下,當年的開發、測試、甚至産品早已不見了蹤迹,隻能在注釋的代碼裡看見了了數語。言語中透露着無奈,用一個程式員的良心提醒着後來人,「小心前面的髒東西」。看了這些話,你隻能收回口中馬上要吐出的芬芳,默默離開工位,倒點熱水。

你為什麼不敢重構代碼?

image.png

你為什麼不敢重構代碼?

image.png

你為什麼不敢重構代碼?

image.png

從此你會發現,注釋不僅能夠幫你讀懂代碼,還能有警示作用,告訴你重構代碼的同時,記得把 bug 一并改了。你想要通過注釋來梳理出原始需求的願望宣告失敗,接下來你隻能死磕了,祈禱千萬不要漏掉業務邏輯。

沒有自測用例

别以為大公司制度完善,測試都有完整的測試用例,現實會狠狠的夾你腦門。頻繁的疊代,功能早已面目全非,老的用例根本不可用,更何況根本找不到老的測試用例。沒有用例怎麼自測呢?全靠個人想象。

沒有測試同學跟進

多一個人多一分力量,讓一個有經驗的測試參與到功能回歸中來,無疑會給你的重構事業吃上定心丸,但真實的情況是,測試同學根本不想參與這種髒活累活。他自己手裡的需求還測不過來,怎麼會把時間奉獻給一個前端發起的重構工作上呢。無增量,無抓手,純體力,他們同樣心知肚明。

沒有穩定釋出方案

在沒有上述保障的前提下,如果你還能硬着頭皮上線,就會遇到更大的難題,如何上線?直接全量替換嗎?如果線上出問題怎麼辦?好在前端的復原是非常迅速的,但是即使再迅速的復原,從釋出完成到發現問題復原,在提醒使用者重新重新整理頁面,這個過程也足以造成難以估量的後果,尤其是那些高頻使用,且極易産生髒資料的場景。這就是沒有一個有效的釋出方案所導緻的常見後果,這個後果還有可能導緻你背上故障,這一年加過的班,熬過的夜,掉的頭發,什麼也換不來,隻能催生你換個地方重新做人的念頭。

綜上因素直接導緻開發者極度缺乏安全感,一個不敢上線自己代碼的程式員,就像半夜被自己一個月大孩子的哭聲吵醒,那時那刻你隻想裝死摸魚。更何況你的工作往往不是隻有重構這一件事,寫寫新需求他不香嗎?就這樣你眼看着一個頁面重構了兩個星期,遲遲不能收尾,你變得越來越不自信,越來越害怕了起來,不敢面對那些重構了一半的代碼,開始恐懼老闆的問題:「重構搞的怎麼樣了?」,你簡直不像個程式員。

終于到了年底,你的重構事業還未完成,更可怕的是,這件事還被打上了「承諾型」OKR 的标,于是你痛定思痛,做了個夢。

時間回到年初你剛剛接到重構任務的時候。

二、尋求組織保障

你的重構工作是把 177 個 jQuery 頁面用 React 重寫一遍。你立馬想到,自己一個人一年時間,一定是做不完的,此時此刻,切記不要滿口答應,一定實事求是,甚至向着最壞的方向想,讓老闆充分認識到這項任務的艱巨性,不要抱有太高的期望。最重要的是保證人力的投入,必須有更多的同學一起參與進來,有效的分工才有可能完成這項艱巨的任務。有人參與進來,也隻是基礎,因為他們極有可能會像上面描述的一樣,從興緻勃勃到唯唯諾諾,是以一定要確定時間的投入,必要時把老闆也拉進來跟你一起做,老闆一旦參與進來,就會更有體感,能體會到大家的不易。接下來,就應了那就老話,「别忘了,你是一個 owner!」做好基礎設施建設,讓每個同學有趁手的工具,有安全的保障,去除他們的後顧之憂至關重要。是以,你要做下面幾件事。

三、劃分重構頁面優先級

你通過細緻的研究發現,這些頁面中,有 77 個頁面是使用者使用較多的頁面,也是相對比較複雜的頁面,剩下的 100 個頁面,大部分是給開發用的增删改查頁面,使用者的使用頻率不高。于是你做了如下劃分:

你為什麼不敢重構代碼?

優先級劃分好優先級以後,就要對不同優先級的頁面使用不同的穩定釋出政策。

  • 複雜高頻頁面:重兵壓上,細緻還原原始需求,摳代碼,拉測試同學一起整理測試用例,按照測試用例自測,測試同學回歸所有功能。但其實這部分頁面中,也可以分為兩種頁面:
    • 編輯頁面:這樣的頁面是風險最高的頁面,一旦因為後端接口沒有做完整的資料校驗,就會編輯出髒資料,或者錯誤的資料被儲存,導緻線上運作異常,這種後果将是不堪設想的,即使非常短的時間内復原,也會造成難以挽回的故障,是以必須要像新需求一樣測試到位。
    • 展示頁面:這樣的頁面不會影響運作時,不會産生髒資料,是風險相對低一點點的頁面,本着不麻煩合作方的原則,畢竟資源有限,可以讓測試幫你出完整的用例,然後你自己自測,或者多找幾個同學幫你自測。
  • 高頻簡單頁面:自測,當然最好是能綁架幾個經常用這個功能的開發,來幫你點點,但是自己測總是會有可能會有遺漏,是以就需要下面的步驟來保證了。
  • 低頻運維頁面:選擇性重構,因為很多頁面基本上不會有疊代,且使用頻率較低,基本上不需要重構,即使是有新的需求,也可以在做新需求的時候順便重構下,以為并不能占用太多時間。

将頁面劃分完畢後,你會發現重構的工作量降低了很多,因為本着「無需求,勿變更」的原則,很多頁面都可以不需要重構。且上述重構完的頁面都必須做灰階釋出。

你為什麼不敢重構代碼?

四、單測

前端不太喜歡寫單測,你大概總結了一下,主要有下面幾方面的原因:

  • 當下的收益不高。
  • 相比後端接口的單測,前端單測寫起來相對複雜。
  • 前端更多是面向 UI 的程式設計,但 UI 變動大,難以使用 TDD (測試驅動開發) 的開發模式。
  • 沒有寫單測的習慣,可能是因為單測增加了工作量,且沒有寫純函數的意識,不利于測試。
  • 單測的工具難學又難用。

你發現前端不喜歡寫單測,有各種各樣的原因,但是當你重構那些複雜頁面,尤其是 jQuery 技術棧重構為 React 技術棧的時候,單測真的非常有用。

你為什麼不敢重構代碼?

比如這裡有一個編輯頁面,包含兩部分:基本資訊和運作邏輯,在重構運作邏輯時候,你首先要保證的是重構過後的頁面在儲存的時候,儲存的資料結構必須跟之前的接口參數必須一緻,是以在重構運作邏輯這個元件的時候就會有很多資料轉換邏輯。

你為什麼不敢重構代碼?

可以看到為了保證你的新元件不影響保持原有功能,就要保證原始資料通過新元件的一頓操作最終保留了原來的結構,此時你就可以寫單測來保證這個過程。

describe('utils', () => {
  it('流程圖:轉換為送出的資料 transformForm', () => {
    const result = transformForm(canvasData);
    expect(result).toEqual(settingData);
  });

  it('流程圖:轉換為需要的資料 parseRuleSetData', () => {
    const [result] = parseRuleSetData(settingData, rules);
    expect(result).toEqual(canvasData);
  });

  it('流程圖:反複轉換 transformForm - parseRuleSetData', () => {
    const [result] = parseRuleSetData(visualSettings, rulesData);
    const newResult = transformForm(result);
    expect(newResult).toEqual(visualSettings);
  });
});
複制代碼
           

前端單元測試寫起來複雜,其實隻是 UI 的單測複雜而已,如果你把代碼做好了足夠的拆分,拆出更多函數,更多 hooks ,單測就是輕而易舉了。

五、測試用例

你在的團隊,一直測試資源都不是充足,測試用例似乎一直都是一種可遇不可求的東西,尤其是在靈活開發的趨勢下,産品功能變動快,很少有測試會一直去維護那個最初的測試用例,往往是寫過用過就再也找不到了。但測試用例在重構這個場景下,真的非常重要,他解決的核心問題是把測試同學拉到重構的品質保障中,一起梳理老的邏輯。這份寶貴的測試用例,可以成為你自測的依據,也可以為你提供對于同一個功能的不同視角,如果你通過代碼看到的是實作細節的邏輯,那測試看到的就是整個鍊路的流程圖。很多中背景系統都有管理态和運作态之分,管理态,前端是非常熟悉的,但是運作态,測試往往更加熟悉。

六、自測

拿到測試用例,你就可以自測了,但是這裡有個坑,就是如果你完全依賴測試同學給你的測試用例。隻要保證測試用例驗證通過就行了,這種想法會出大問題,因為負責這塊功能的測試可能是個新手,可能并不是一直負責這塊功能的測試,他們的測試用例可能隻是浮于表面的。是以你需要把通過代碼考古發現的測試用例裡沒有的邏輯,暴露給測試同學,并補充到測試用例裡。并且如果發現有一些看不懂的邏輯,就應該搞懂他,那些你不懂的死角,往往上線後就會有大問題,不要心存僥幸。自測非常重要,但是往往你會覺得開發完了,就算是把這個事做完了,然後就去忙别的事了,并沒有好好的自測,心想還有測試呢,等他們提問題,我再改吧。這是一種很普遍的程式員心理,其實很難避免,畢竟事情有很多。這個時候你可以找同組的開發同學幫你點一點,先解決那些顯而易見的問題,也算是一個認真負責的程式員了,不要讓測試同學給你提太多低級 bug。

七、回歸測試

能有測試同學幫你做功能的回歸測試真是一件可遇而不可求的事,一定要珍惜,拿出你的大塊時間配合好。這其中最重要的就是多交流,測試同學也不一定知道所有的邏輯,在做回歸測試的時候,就需要開發和測試反複核對每個邏輯死角,弄清楚,才敢上線。當然,能夠有測試幫你回歸的功能都是極易引起故障的功能,這裡就有一個技巧就是如何拉測試參與你的重構中來。像這樣重要的功能如果測試知道裡面的邏輯,你可以懷着請教的心态去問對方,如果對方并不了解,那你就可以講給他聽,一個負責任的測試,應該都非常想了解自己負責系統的重要子產品的來龍去脈。

八、灰階釋出

即使你做了再多的測試,都有可能有沒有考慮到的遺漏點,這個時候灰階就非常重要了,灰階就必須要有灰階工具才行。重構一般是以頁面或者區塊為粒度按照人來進行的。是以你的灰階工具必須要包含這些功能:

  • 配置使用者或者使用者組
  • 配置老路由和新路由
  • 配置灰階狀态提示
  • 新老頁面的自動打點

灰階配置頁面,新老動态路由的參數需要保持一緻,這樣才能把參數傳遞下去。

你為什麼不敢重構代碼?

展示灰階提示,并提供一個快速「傳回舊版」的按鈕,為了更快速解決問題,可以給出開發者聯系方式。

你為什麼不敢重構代碼?

當使用者通路老路由的時候,按照灰階配置驗證目前使用者是否在灰階中,如果在灰階中,則立即跳轉到新的路由,并顯示灰階提示。如果重構的是頁面中的區塊,則可以提供灰階命中的方法,在頁面調用區塊的部分做判斷。

灰階政策可以按照以下使用者級别分布進行:

  • L1:所有項目開發,測試,設計師,内部營運人員
  • L2:核心使用者,建立釘釘群,觀察使用者回報,及時解決使用者問題。
  • L3:适當加入更多使用者,直到全量後,删除灰階政策的配置。

釋出後,注意觀察打點資料:

你為什麼不敢重構代碼?

image.png

打點的時候需要注意,要按照動态路由來打點,并分成命中灰階的,點選使用舊版的,不在灰階内的三個次元來看資料,同時每天調整灰階使用者,這樣就能保證頁面是有人用的。如果有很多使用者使用了傳回舊版的功能,那你就得找找這些使用者了解下情況了,到底是有 bug 還是互動不舒服,一對一的解決使用者問題,在反複去優化你的頁面,慢慢擴大使用者灰階範圍,直到老的路由通路資料 PV 為 0。

九、全量上線

全量上線并不是灰階所有人,而是真正下線老的頁面,并删除老的代碼,隻有到這一步才算重構完成了。

十、總結

經曆千難萬險,你終于把重構好的頁面上線了,經曆了這個過程,感慨良多,隻求以後再也不要做重構了,好好做需求不香嗎?後頭看看整個過程,要想重構的頁面上線,不僅要下苦功夫,還要克服人性的一些弱點,要做到這幾點:

  • Double Check:讓其他人參與進來,多一個人就能幫你發現更多問題。重構面前,不要相信自己,相信夥伴。
  • 邏輯無死角:不要還有不懂的代碼,不清楚的邏輯,按照程式員的第六感,不确定的都會出大問題。
  • 集中注意力:重構不能碎片化進行,要集中大塊時間來做,并一做到底,不然過個幾天,你自己的代碼都會不認識。
  • 一跟到底:開發完成不是重點,全量上線,并下掉老的頁面才是結束。

緻敬每一位重構路上的勇士。

最後

歡迎關注【前端瓶子君】✿✿ヽ(°▽°)ノ✿

回複「算法」,加入前端程式設計源碼算法群,每日一道面試題(工作日),第二天瓶子君都會很認真的解答喲!

回複「交流」,吹吹水、聊聊技術、吐吐槽!

回複「閱讀」,每日刷刷高品質好文!

如果這篇文章對你有幫助,「在看」是最大的支援

》》面試官也在看的算法資料《《

“在看和轉發”就是最大的支援