<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          你為什么不敢重構(gòu)代碼?

          共 5847字,需瀏覽 12分鐘

           ·

          2021-05-12 12:26


          源 /頂級(jí)程序員      文/ 黑石



          代碼重構(gòu)有兩大難點(diǎn),一個(gè)是「考古」,也就是如何快速梳理出代碼的原有邏輯,還有一點(diǎn)就是「發(fā)布」,如何讓新的代碼可以穩(wěn)定的發(fā)布到線上,而不產(chǎn)生故障。下面我們就聊聊我一個(gè)朋友的故事,看看他是怎么把代碼穩(wěn)定搞上線的。為了表達(dá)更為親切,你現(xiàn)在就是我那個(gè)朋友。

          重構(gòu)代碼對(duì)很多人來(lái)說(shuō),絕對(duì)是一件臟活、累活。沒(méi)有可以大幅度提效的方法,難以沉淀有效的體系化的可復(fù)用的技術(shù)抓手,對(duì)業(yè)務(wù)來(lái)說(shuō)沒(méi)有明顯的增量,精力和時(shí)間消耗巨大,沒(méi)有測(cè)試用例,也不一定能得到測(cè)試的支持,自測(cè)很難做到充分,最后開發(fā)完了很難上線,主要原因是害怕!當(dāng)然并不是我們不自信,是真的恐懼。

          一、你為什么不敢發(fā)代碼?

          通過(guò)代碼還原當(dāng)時(shí)完整的產(chǎn)品邏輯太難了

          你重構(gòu)的代碼是誰(shuí)的?鬼知道是誰(shuí)的!能讓你重構(gòu)的代碼大概率不是你寫的代碼,而且是遠(yuǎn)古代碼,用的是一種過(guò)時(shí)的技術(shù)棧。當(dāng)然一般情況下,當(dāng)年的開發(fā)、測(cè)試、甚至產(chǎn)品早已不見了蹤跡,只能在注釋的代碼里看見了了數(shù)語(yǔ)。言語(yǔ)中透露著無(wú)奈,用一個(gè)程序員的良心提醒著后來(lái)人,「小心前面的臟東西」??戳诉@些話,你只能收回口中馬上要吐出的芬芳,默默離開工位,倒點(diǎn)熱水。
          從此你會(huì)發(fā)現(xiàn),注釋不僅能夠幫你讀懂代碼,還能有警示作用,告訴你重構(gòu)代碼的同時(shí),記得把 bug 一并改了。你想要通過(guò)注釋來(lái)梳理出原始需求的愿望宣告失敗,接下來(lái)你只能死磕了,祈禱千萬(wàn)不要漏掉業(yè)務(wù)邏輯。

          沒(méi)有自測(cè)用例

          別以為大公司制度完善,測(cè)試都有完整的測(cè)試用例,現(xiàn)實(shí)會(huì)狠狠的夾你腦門。頻繁的迭代,功能早已面目全非,老的用例根本不可用,更何況根本找不到老的測(cè)試用例。沒(méi)有用例怎么自測(cè)呢?全靠個(gè)人想象。

          沒(méi)有測(cè)試同學(xué)跟進(jìn)

          多一個(gè)人多一分力量,讓一個(gè)有經(jīng)驗(yàn)的測(cè)試參與到功能回歸中來(lái),無(wú)疑會(huì)給你的重構(gòu)事業(yè)吃上定心丸,但真實(shí)的情況是,測(cè)試同學(xué)根本不想?yún)⑴c這種臟活累活。他自己手里的需求還測(cè)不過(guò)來(lái),怎么會(huì)把時(shí)間奉獻(xiàn)給一個(gè)前端發(fā)起的重構(gòu)工作上呢。無(wú)增量,無(wú)抓手,純體力,他們同樣心知肚明。

          沒(méi)有穩(wěn)定發(fā)布方案

          在沒(méi)有上述保障的前提下,如果你還能硬著頭皮上線,就會(huì)遇到更大的難題,如何上線?直接全量替換嗎?如果線上出問(wèn)題怎么辦?好在前端的回滾是非常迅速的,但是即使再迅速的回滾,從發(fā)布完成到發(fā)現(xiàn)問(wèn)題回滾,在提醒用戶重新刷新頁(yè)面,這個(gè)過(guò)程也足以造成難以估量的后果,尤其是那些高頻使用,且極易產(chǎn)生臟數(shù)據(jù)的場(chǎng)景。這就是沒(méi)有一個(gè)有效的發(fā)布方案所導(dǎo)致的常見后果,這個(gè)后果還有可能導(dǎo)致你背上故障,這一年加過(guò)的班,熬過(guò)的夜,掉的頭發(fā),什么也換不來(lái),只能催生你換個(gè)地方重新做人的念頭。
          綜上因素直接導(dǎo)致開發(fā)者極度缺乏安全感,一個(gè)不敢上線自己代碼的程序員,就像半夜被自己一個(gè)月大孩子的哭聲吵醒,那時(shí)那刻你只想裝死摸魚。更何況你的工作往往不是只有重構(gòu)這一件事,寫寫新需求他不香嗎?就這樣你眼看著一個(gè)頁(yè)面重構(gòu)了兩個(gè)星期,遲遲不能收尾,你變得越來(lái)越不自信,越來(lái)越害怕了起來(lái),不敢面對(duì)那些重構(gòu)了一半的代碼,開始恐懼老板的問(wèn)題:「重構(gòu)搞的怎么樣了?」,你簡(jiǎn)直不像個(gè)程序員。
          終于到了年底,你的重構(gòu)事業(yè)還未完成,更可怕的是,這件事還被打上了「承諾型」OKR 的標(biāo),于是你痛定思痛,做了個(gè)夢(mèng)。
          時(shí)間回到年初你剛剛接到重構(gòu)任務(wù)的時(shí)候。

          二、尋求組織保障

          你的重構(gòu)工作是把 177 個(gè) jQuery 頁(yè)面用 React 重寫一遍。你立馬想到,自己一個(gè)人一年時(shí)間,一定是做不完的,此時(shí)此刻,切記不要滿口答應(yīng),一定實(shí)事求是,甚至向著最壞的方向想,讓老板充分認(rèn)識(shí)到這項(xiàng)任務(wù)的艱巨性,不要抱有太高的期望。最重要的是保證人力的投入,必須有更多的同學(xué)一起參與進(jìn)來(lái),有效的分工才有可能完成這項(xiàng)艱巨的任務(wù)。有人參與進(jìn)來(lái),也只是基礎(chǔ),因?yàn)樗麄儤O有可能會(huì)像上面描述的一樣,從興致勃勃到唯唯諾諾,因此一定要確保時(shí)間的投入,必要時(shí)把老板也拉進(jìn)來(lái)跟你一起做,老板一旦參與進(jìn)來(lái),就會(huì)更有體感,能體會(huì)到大家的不易。接下來(lái),就應(yīng)了那就老話,「別忘了,你是一個(gè) owner!」做好基礎(chǔ)設(shè)施建設(shè),讓每個(gè)同學(xué)有趁手的工具,有安全的保障,去除他們的后顧之憂至關(guān)重要。因此,你要做下面幾件事。

          三、劃分重構(gòu)頁(yè)面優(yōu)先級(jí)

          你通過(guò)細(xì)致的研究發(fā)現(xiàn),這些頁(yè)面中,有 77 個(gè)頁(yè)面是用戶使用較多的頁(yè)面,也是相對(duì)比較復(fù)雜的頁(yè)面,剩下的 100 個(gè)頁(yè)面,大部分是給開發(fā)用的增刪改查頁(yè)面,用戶的使用頻率不高。于是你做了如下劃分:

          優(yōu)先級(jí)劃分好優(yōu)先級(jí)以后,就要對(duì)不同優(yōu)先級(jí)的頁(yè)面使用不同的穩(wěn)定發(fā)布策略。
          • 復(fù)雜高頻頁(yè)面:重兵壓上,細(xì)致還原原始需求,摳代碼,拉測(cè)試同學(xué)一起整理測(cè)試用例,按照測(cè)試用例自測(cè),測(cè)試同學(xué)回歸所有功能。但其實(shí)這部分頁(yè)面中,也可以分為兩種頁(yè)面:
            • 編輯頁(yè)面:這樣的頁(yè)面是風(fēng)險(xiǎn)最高的頁(yè)面,一旦因?yàn)楹蠖私涌跊](méi)有做完整的數(shù)據(jù)校驗(yàn),就會(huì)編輯出臟數(shù)據(jù),或者錯(cuò)誤的數(shù)據(jù)被保存,導(dǎo)致線上運(yùn)行異常,這種后果將是不堪設(shè)想的,即使非常短的時(shí)間內(nèi)回滾,也會(huì)造成難以挽回的故障,因此必須要像新需求一樣測(cè)試到位。
            • 展示頁(yè)面:這樣的頁(yè)面不會(huì)影響運(yùn)行時(shí),不會(huì)產(chǎn)生臟數(shù)據(jù),是風(fēng)險(xiǎn)相對(duì)低一點(diǎn)點(diǎn)的頁(yè)面,本著不麻煩合作方的原則,畢竟資源有限,可以讓測(cè)試幫你出完整的用例,然后你自己自測(cè),或者多找?guī)讉€(gè)同學(xué)幫你自測(cè)。
          • 高頻簡(jiǎn)單頁(yè)面:自測(cè),當(dāng)然最好是能綁架幾個(gè)經(jīng)常用這個(gè)功能的開發(fā),來(lái)幫你點(diǎn)點(diǎn),但是自己測(cè)總是會(huì)有可能會(huì)有遺漏,因此就需要下面的步驟來(lái)保證了。
          • 低頻運(yùn)維頁(yè)面:選擇性重構(gòu),因?yàn)楹芏囗?yè)面基本上不會(huì)有迭代,且使用頻率較低,基本上不需要重構(gòu),即使是有新的需求,也可以在做新需求的時(shí)候順便重構(gòu)下,以為并不能占用太多時(shí)間。
          將頁(yè)面劃分完畢后,你會(huì)發(fā)現(xiàn)重構(gòu)的工作量降低了很多,因?yàn)楸局笩o(wú)需求,勿變更」的原則,很多頁(yè)面都可以不需要重構(gòu)。且上述重構(gòu)完的頁(yè)面都必須做灰度發(fā)布。

          四、單測(cè)

          前端不太喜歡寫單測(cè),你大概總結(jié)了一下,主要有下面幾方面的原因:
          • 當(dāng)下的收益不高。
          • 相比后端接口的單測(cè),前端單測(cè)寫起來(lái)相對(duì)復(fù)雜。
          • 前端更多是面向 UI 的編程,但 UI 變動(dòng)大,難以使用 TDD (測(cè)試驅(qū)動(dòng)開發(fā)) 的開發(fā)模式。
          • 沒(méi)有寫單測(cè)的習(xí)慣,可能是因?yàn)閱螠y(cè)增加了工作量,且沒(méi)有寫純函數(shù)的意識(shí),不利于測(cè)試。
          • 單測(cè)的工具難學(xué)又難用。
          你發(fā)現(xiàn)前端不喜歡寫單測(cè),有各種各樣的原因,但是當(dāng)你重構(gòu)那些復(fù)雜頁(yè)面,尤其是 jQuery 技術(shù)棧重構(gòu)為 React 技術(shù)棧的時(shí)候,單測(cè)真的非常有用。


          比如這里有一個(gè)編輯頁(yè)面,包含兩部分:基本信息和運(yùn)行邏輯,在重構(gòu)運(yùn)行邏輯時(shí)候,你首先要保證的是重構(gòu)過(guò)后的頁(yè)面在保存的時(shí)候,保存的數(shù)據(jù)結(jié)構(gòu)必須跟之前的接口參數(shù)必須一致,所以在重構(gòu)運(yùn)行邏輯這個(gè)組件的時(shí)候就會(huì)有很多數(shù)據(jù)轉(zhuǎn)換邏輯。


          可以看到為了保證你的新組件不影響保持原有功能,就要保證原始數(shù)據(jù)通過(guò)新組件的一頓操作最終保留了原來(lái)的結(jié)構(gòu),此時(shí)你就可以寫單測(cè)來(lái)保證這個(gè)過(guò)程。
          describe('utils', () => {
            it('流程圖:轉(zhuǎn)換為提交的數(shù)據(jù) transformForm', () => {
              const result = transformForm(canvasData);
              expect(result).toEqual(settingData);
            });

            it('流程圖:轉(zhuǎn)換為需要的數(shù)據(jù) parseRuleSetData', () => {
              const [result] = parseRuleSetData(settingData, rules);
              expect(result).toEqual(canvasData);
            });

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

          前端單元測(cè)試寫起來(lái)復(fù)雜,其實(shí)只是 UI 的單測(cè)復(fù)雜而已,如果你把代碼做好了足夠的拆分,拆出更多函數(shù),更多 hooks ,單測(cè)就是輕而易舉了。

          五、測(cè)試用例

          你在的團(tuán)隊(duì),一直測(cè)試資源都不是充足,測(cè)試用例似乎一直都是一種可遇不可求的東西,尤其是在敏捷開發(fā)的趨勢(shì)下,產(chǎn)品功能變動(dòng)快,很少有測(cè)試會(huì)一直去維護(hù)那個(gè)最初的測(cè)試用例,往往是寫過(guò)用過(guò)就再也找不到了。但測(cè)試用例在重構(gòu)這個(gè)場(chǎng)景下,真的非常重要,他解決的核心問(wèn)題是把測(cè)試同學(xué)拉到重構(gòu)的質(zhì)量保障中,一起梳理老的邏輯。這份寶貴的測(cè)試用例,可以成為你自測(cè)的依據(jù),也可以為你提供對(duì)于同一個(gè)功能的不同視角,如果你通過(guò)代碼看到的是實(shí)現(xiàn)細(xì)節(jié)的邏輯,那測(cè)試看到的就是整個(gè)鏈路的流程圖。很多中后臺(tái)系統(tǒng)都有管理態(tài)和運(yùn)行態(tài)之分,管理態(tài),前端是非常熟悉的,但是運(yùn)行態(tài),測(cè)試往往更加熟悉。

          六、自測(cè)

          拿到測(cè)試用例,你就可以自測(cè)了,但是這里有個(gè)坑,就是如果你完全依賴測(cè)試同學(xué)給你的測(cè)試用例。只要保證測(cè)試用例驗(yàn)證通過(guò)就行了,這種想法會(huì)出大問(wèn)題,因?yàn)樨?fù)責(zé)這塊功能的測(cè)試可能是個(gè)新手,可能并不是一直負(fù)責(zé)這塊功能的測(cè)試,他們的測(cè)試用例可能只是浮于表面的。所以你需要把通過(guò)代碼考古發(fā)現(xiàn)的測(cè)試用例里沒(méi)有的邏輯,暴露給測(cè)試同學(xué),并補(bǔ)充到測(cè)試用例里。并且如果發(fā)現(xiàn)有一些看不懂的邏輯,就應(yīng)該搞懂他,那些你不懂的死角,往往上線后就會(huì)有大問(wèn)題,不要心存僥幸。自測(cè)非常重要,但是往往你會(huì)覺(jué)得開發(fā)完了,就算是把這個(gè)事做完了,然后就去忙別的事了,并沒(méi)有好好的自測(cè),心想還有測(cè)試呢,等他們提問(wèn)題,我再改吧。這是一種很普遍的程序員心理,其實(shí)很難避免,畢竟事情有很多。這個(gè)時(shí)候你可以找同組的開發(fā)同學(xué)幫你點(diǎn)一點(diǎn),先解決那些顯而易見的問(wèn)題,也算是一個(gè)認(rèn)真負(fù)責(zé)的程序員了,不要讓測(cè)試同學(xué)給你提太多低級(jí) bug。

          七、回歸測(cè)試

          能有測(cè)試同學(xué)幫你做功能的回歸測(cè)試真是一件可遇而不可求的事,一定要珍惜,拿出你的大塊時(shí)間配合好。這其中最重要的就是多交流,測(cè)試同學(xué)也不一定知道所有的邏輯,在做回歸測(cè)試的時(shí)候,就需要開發(fā)和測(cè)試反復(fù)核對(duì)每個(gè)邏輯死角,弄清楚,才敢上線。當(dāng)然,能夠有測(cè)試幫你回歸的功能都是極易引起故障的功能,這里就有一個(gè)技巧就是如何拉測(cè)試參與你的重構(gòu)中來(lái)。像這樣重要的功能如果測(cè)試知道里面的邏輯,你可以懷著請(qǐng)教的心態(tài)去問(wèn)對(duì)方,如果對(duì)方并不了解,那你就可以講給他聽,一個(gè)負(fù)責(zé)任的測(cè)試,應(yīng)該都非常想了解自己負(fù)責(zé)系統(tǒng)的重要模塊的來(lái)龍去脈。

          八、灰度發(fā)布

          即使你做了再多的測(cè)試,都有可能有沒(méi)有考慮到的遺漏點(diǎn),這個(gè)時(shí)候灰度就非常重要了,灰度就必須要有灰度工具才行。重構(gòu)一般是以頁(yè)面或者區(qū)塊為粒度按照人來(lái)進(jìn)行的。所以你的灰度工具必須要包含這些功能:
          • 配置用戶或者用戶組
          • 配置老路由和新路由
          • 配置灰度狀態(tài)提示
          • 新老頁(yè)面的自動(dòng)打點(diǎn)
          灰度配置頁(yè)面,新老動(dòng)態(tài)路由的參數(shù)需要保持一致,這樣才能把參數(shù)傳遞下去。

          展示灰度提示,并提供一個(gè)快速「返回舊版」的按鈕,為了更快速解決問(wèn)題,可以給出開發(fā)者聯(lián)系方式。

          當(dāng)用戶訪問(wèn)老路由的時(shí)候,按照灰度配置驗(yàn)證當(dāng)前用戶是否在灰度中,如果在灰度中,則立即跳轉(zhuǎn)到新的路由,并顯示灰度提示。如果重構(gòu)的是頁(yè)面中的區(qū)塊,則可以提供灰度命中的方法,在頁(yè)面調(diào)用區(qū)塊的部分做判斷。
          灰度策略可以按照以下用戶級(jí)別分布進(jìn)行:
          • L1:所有項(xiàng)目開發(fā),測(cè)試,設(shè)計(jì)師,內(nèi)部運(yùn)營(yíng)人員
          • L2:核心用戶,建立釘釘群,觀察用戶反饋,及時(shí)解決用戶問(wèn)題。
          • L3:適當(dāng)加入更多用戶,直到全量后,刪除灰度策略的配置。
          發(fā)布后,注意觀察打點(diǎn)數(shù)據(jù):
          image.png
          打點(diǎn)的時(shí)候需要注意,要按照動(dòng)態(tài)路由來(lái)打點(diǎn),并分成命中灰度的,點(diǎn)擊使用舊版的,不在灰度內(nèi)的三個(gè)維度來(lái)看數(shù)據(jù),同時(shí)每天調(diào)整灰度用戶,這樣就能保證頁(yè)面是有人用的。如果有很多用戶使用了返回舊版的功能,那你就得找找這些用戶了解下情況了,到底是有 bug 還是交互不舒服,一對(duì)一的解決用戶問(wèn)題,在反復(fù)去優(yōu)化你的頁(yè)面,慢慢擴(kuò)大用戶灰度范圍,直到老的路由訪問(wèn)數(shù)據(jù) PV 為 0。

          九、全量上線

          全量上線并不是灰度所有人,而是真正下線老的頁(yè)面,并刪除老的代碼,只有到這一步才算重構(gòu)完成了。

          十、總結(jié)

          經(jīng)歷千難萬(wàn)險(xiǎn),你終于把重構(gòu)好的頁(yè)面上線了,經(jīng)歷了這個(gè)過(guò)程,感慨良多,只求以后再也不要做重構(gòu)了,好好做需求不香嗎?后頭看看整個(gè)過(guò)程,要想重構(gòu)的頁(yè)面上線,不僅要下苦功夫,還要克服人性的一些弱點(diǎn),要做到這幾點(diǎn):
          • Double Check:讓其他人參與進(jìn)來(lái),多一個(gè)人就能幫你發(fā)現(xiàn)更多問(wèn)題。重構(gòu)面前,不要相信自己,相信伙伴。
          • 邏輯無(wú)死角:不要還有不懂的代碼,不清楚的邏輯,按照程序員的第六感,不確定的都會(huì)出大問(wèn)題。
          • 集中注意力:重構(gòu)不能碎片化進(jìn)行,要集中大塊時(shí)間來(lái)做,并一做到底,不然過(guò)個(gè)幾天,你自己的代碼都會(huì)不認(rèn)識(shí)。
          • 一跟到底:開發(fā)完成不是重點(diǎn),全量上線,并下掉老的頁(yè)面才是結(jié)束。
          致敬每一位重構(gòu)路上的勇士。


          好文推薦


          因故意引入漏洞,整所大學(xué)被禁止為L(zhǎng)inux內(nèi)核做貢獻(xiàn),回應(yīng)來(lái)了!


          今天,我的電腦被勒索了,嘿嘿嘿!


          好家伙!30% 國(guó)外程序員每天“摸魚”四五個(gè)小時(shí),國(guó)內(nèi)似乎更嚴(yán)重…


          —  —


          一鍵三連「分享」、「點(diǎn)贊」和「在看」

          技術(shù)干貨與你天天見~




          瀏覽 25
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色一区二区三区 | 人人摸人人添 | 毛片av免费看 | 国产成人精品 视频 | 中文A∨视频 |