# 2022-10-19 MI DMP GUI Refactor/Rebuilding Report by Taiming ###### tags: `GroundHog` ## 前言 DMP GUI 歷經幾任不同的 RD 開發後,累積了不少的已淘汰掉的 code,但仍存在/被引用在不同段落之中。這會導致接手 GUI 的前端工程師很難追蹤 bug,且很容易發生改 A 錯 B 的情形,不僅加大 QA 跟前端 RD 的 loading,亦拖累開發速度。為了導正這件事,DMP Team 決議暫停開發新功能,並且直接重做一個新的 GUI Project 來替代掉原本的舊 GUI Project。 ==從介面上,當然也能夠很明顯看出差異性。但事實上,背後做的調校是更多的,本次報告主要希望能夠凸顯舊 GUI 的問題,以及轉換到新 GUI 之後得到的好處及轉變。== ## 介面調整 ![](https://i.imgur.com/RsR0jlr.png) ![](https://i.imgur.com/Hdkn488.png) ## 程式碼行數/檔案數分析 ```shell= cloc --exclude-list-file=.clocignore . ``` **舊 GUI: dmp-gui** ![](https://i.imgur.com/sSKNTju.png) **新 GUI: dmp-gui-front-end** ![](https://i.imgur.com/yXLsrm3.png) ### 小結 在擁有同樣的功能,甚至更好的功能的前提下,程式碼行數減少,以及檔案數量減少,代表: - `有效減少使用者必須傳輸的資料量`,從而減少了使用者載入應用程式/站點所需的時間。這對於使用低頻寬連線的使用者而言尤其重要。 - `可讀性也能有效提升`(ex: 原本了解一個功能需要讀 500行,現在瞭解同樣的功能只需要讀 200 行。其中舊 GUI 最大的一個檔案有破千行)。 - `可維護性提升`,原本要維護 76 萬行程式碼,現在只要維護 2 萬行。 統計: - 程式碼行數: - 減少為原本的 1/36 - 減少了 97.22% - 檔案數量: - 減少為原本的 1/33.6 - 減少了 97% - 註解數量: - 減少為原本的 1/636 - 減少了 99.8% - 空白行數量: - 減少為原本的 1/94.68 - 減少了 98.9% 並且,使用 Typescript,因為多增加了型別檢查,理應當程式碼要變多,但是整體上程式碼還是減少,所以理論上減少的程式碼是更多的。 --- ## 版本升級 - node v10 --> v14 - 由於 `semantic-ui` 套件大量使用,造成過往 node 一直無法升級。現在完全棄用 `semantic-ui`,因此能夠無痛升級。未來也能無痛升到 v16。 - react v16.8.4 --> v18.2.0 - react17 是一個過渡版本,為了 react18 做鋪路。 - react18 是最新穩定的版本。 --- ## 檔案結構優化 **舊 GUI:** 如同四個室友住在一起,後來三個都搬出去了,剩下自己留下來,但是房間裡面還有許多前室友的遺留物。你不知道該怎麼整理他,該留?該丟?而且有些是跟其他室友共用的東西,有些是不確定是什麼東西的東西,不知道丟了會不會出問題。 ![](https://i.imgur.com/SmHYMsU.png) - 在前端裡面有 backend 的 code. ex: backend-core/ - 有許多檔案結構的設計,看起來是以前為了跟 DSP 共用而設計(以前 DMP & DSP 在同一包)。 ex: - frontend-hooks - frontend-model - frontend-queries - frontend-xxx ![](https://i.imgur.com/WenZZRW.png) **以前 DSP 與 DMP 同一包的證據:** ![](https://i.imgur.com/PBNajJi.png) ![](https://i.imgur.com/gEFepZm.png) ![](https://i.imgur.com/vUEHYvb.png) ![](https://i.imgur.com/yeIZC25.png) **檔案結構沒有分類** - 沒有區分頁面元件與共用元件 - 檔案結構沒有分層,全部攤平在同一層,所以要找 code 很難找 - ex: 如同抽屜打開來,書本、咖啡杯、文具、便當、襪子、零食、充電線全部塞在一起,沒有分類。 ![](https://i.imgur.com/akTOuJY.png) **新 GUI:** 解決上述問題,檔案區分清楚,檔案結構與頁面結構完美對應。從檔案結構即可理解頁面邏輯: ![](https://i.imgur.com/SJvXW7E.png) ### 小結 - 移除不必要的檔案(ex: DSPxxx, xxxDSP) - 過去結構設計以 DSP&DMP 共用為考量,因此檔案結構在 DSP&DMP 獨立之後難以理解。這部份在新 GUI 完全解決。 - 檔案分類清楚(ex: pages, components, hooks, constants, configs, routes, types, utils...)。原本是散落在各個資料夾當中。 - 檔案結構與頁面結構完美對應。從檔案結構即可理解頁面邏輯。因此能夠增加開發速度與減少除錯時間 - 個人實測:過往平均找到一個頁面上的元件所在的檔案需要十分鐘,甚至常常需要半小時以上。現在在一分鐘內(甚至秒等級)就能夠鎖定頁面上的原始碼。提升的速度至少 10 倍以上。 --- ## 關於檔案搜尋 可讀的 class name。DOM 結構變得語意化,可理解。 過往,在頁面上的一個 button,要找他所在的 file 真的比登天還難。因為線索很少,雷同的地方又很多。 ![](https://i.imgur.com/a3qDYC2.png) 由於過往在不同環境會需要不同的 webpack 打包,因此產生出許多檔案,讓鎖定程式碼這件事情變得很困擾: ![](https://i.imgur.com/Rkgqw4W.png) ![](https://i.imgur.com/Dn1R5Vp.png) ![](https://i.imgur.com/WzIL3pX.png) ### 小結 屏棄過往複雜的打包方式,讓程式碼變得乾淨,大大降低鎖定程式碼所需要的時間。 - 個人實測:過往平均找到一個頁面上的元件所在的檔案需要十分鐘,甚至常常需要半小時以上。現在在一分鐘內(甚至秒等級)就能夠鎖定頁面上的原始碼。提升的速度至少 10 倍以上。 --- ## 套件的整理 舊 GUI 當中,安裝許多過時的、重覆的、沒有用到的、不必要的套件,但因為程式碼的相依性以及複雜度,造成無法乾淨的移除。在新 GUI 當中大大改善這邊的功能。 **UI library 好多套:** - antd - semantic-ui (目前安裝的也太過老舊) - @ghtinc/react (Groundhog 自己的) - MUI <--- 差點要裝了 **utility library 裝兩套:** - lodash - Ramda - 可以將 ramda 想成 functional programming 的 lodash,他們的 API 有許多相似性,差別在於 ramda 本身有 FP 的功能,任何的 API 只要你沒有傳入參數,ramda 就會自動做 curry,這提供了相當大的彈性。 - https://medium.com/d-d-mag/淺析幾個-ramda-當中的-api-c399a3f73c68 **使用不再維護的套件:** - moment (MomentJs recently announced that the library is now deprecated.) - 已經被棄用 - 因為太肥大而被人詬病 - 跟 antd 相依 - luxon - 同樣是處理時間的套件 - A powerful, modern, and friendly wrapper for JavaScript dates and times. https://momentjs.com/docs/#/-project-status/ ``` moment - 327KB, but also needs moment-timezone (185KB) Total: 513KB day.js - 2KB, but also needs the utc and timezone plugins (4KB), and all locales (26 KB) Total: ~32KB ``` https://terodox.tech/migrating-away-from-momentjs-part1/ **使用沒有必要的套件:** ex: 已經用 graphql,就沒有必要再用 redux, redux-thunk, reselect...。 ### 小結 - packge.json 從 199 行減少為 97 行。 - 其中安裝的套件數量從 167 個減少為 59個。減少為原本的 1/2.8,將近 1/3。 --- ## 大幅減少環境變數 舊 GUI 的環境變數太氾濫,甚至有些重複的功能卻不同命名,或者有些不需要放到環境變數裡面,這次新 GUI 將環境變數統一並縮減。使用到相關的環境變數的程式碼也調整完畢。 舊 GUI: ```yml= dmp_gui: image: repository: "{{ DOCKER_REGISTRY }}/ghtinc/dmp-gui" tag: DMPv2.1.35 env: - NODE_ENV=production - PRODUCT=indosat - WEB_PORT=4040 - LANGUAGE=en - DATE_FORMAT=dd-LLL-yy - DATE_HOUR_FORMAT=dd-LLL-yy HH:mm - LOCATION=[-4.67,109.94] - LABEL_INDEX_AGE=1 - LABEL_INDEX_GENDER=2 # graphql api - DMP_GUI_BACKEND_URL=https://data-ads.indosatooredoo.com - AUDIENCE_ID_TYPES=ifa,gid,brgid,im3 - CUSTOM_IMAGE_TYPE=indosat - CUSTOM_HTML_TITLE=indosat ``` 新 GUI: ```yml= dmp_gui_frontend: image: repository: "{{ DOCKER_REGISTRY }}/.../dmp_gui_frontend" tag: v0.1.28 env: - REACT_APP_PRODUCT=indosat # graphql api - REACT_APP_DMP_GUI_BACKEND_URL=https://data-ads.indosatooredoo.com ``` --- <!-- ## Bug 發現數量 12/22~10/6 old gui high bugs: 24 8/25~10/19 new gui high bugs: 13 12/07-10/7(304 天) old gui bugs: 149 8/25~10/19(55 天) new gui bugs: 44 --> ## 程式碼優化/移除壞味道(Bad Smell) 舊 GUI: - eslint/prettier 形同虛設 - 許多沒有被用到的引入 ![](https://i.imgur.com/xvVyps3.png) **過於複雜的 React 生命週期,useEffect 連續技......** - 程式執行的流程難以預測 - 參數的結果難以預測 - 造成很難修正 bug(修這個壞那個),也很難增加功能。 ![](https://i.imgur.com/WRq4IQK.png) ![](https://i.imgur.com/64XZgIJ.png) ![](https://i.imgur.com/VNJYwyi.png) ![](https://i.imgur.com/svY0rzr.png) ![](https://i.imgur.com/wbAtQX8.png) ![](https://i.imgur.com/eUKdsJg.png) **使用 React Hooks 取代 class component(React16.8 以前的寫法)** - class components 程式碼較為冗長 - class components 需要撰寫 JS 當中最令人難以理解和頭痛的 `this` - class components 編譯速度沒有 functional components 快(少了繼承 class 轉成 ES5) - funtional components 可以避免生命週期方法的調用,減少人為錯誤的發生。 ![](https://i.imgur.com/8J6TdgE.png) **一言不合就 reload** - 創建資料 - 更新資料 - 刪除資料 - 換頁 - 切換 advertiser - 有東西不預期壞掉了 其實我們只需要針對更新的「區塊」作更新就好了,但是舊 GUI 就算只有小區塊的更新,也會「整個頁面刷新」,因此效能和使用者體驗很差。 ![](https://i.imgur.com/qDaVE02.png) **避免使用非主流語法** 屏棄使用 pipeline operator 模式 - 會增加專案的學習門檻。 - 沒有明顯感受到好處,反而增加理解程式碼的困難度。 - 專案會需要特別為了支援特別的語法做更多的設定。 - 而且此語法只有在這個專案適用,同樣的程式碼移到別的專案上就不能執行。 - ES6 已經很強大,並且很通用。 ![](https://i.imgur.com/tJkqUQa.png) ![](https://i.imgur.com/Nyn0Qze.png) ![](https://i.imgur.com/ea4IxqC.png) **統一 style 的寫法** - 舊 GUI 處理 style 有各種寫法 - ex: `*.css, *.scss, *.less, object css, styled-components...etc.` - 新 GUI 全站統一使用 styled-components. ### 小結 - 使用 Typescript 達到 90% 以上(舊 GUI 的 Typescript 為 6.3%)。詳如檔案分析圖。 - 許多js的錯誤往往是在runtime時才報錯,有了typescript,我們能夠在編譯時期就發現錯誤。 - 增加可讀性:有了型別之後,我們能更好得理解程式碼,進而提升整體的閱讀速度。 - 減少難以預測的生命週期觸發 - 重新調校 eslint/prettier - 屏棄 class components,全面使用 functional components + React Hooks - 參數命名優化 - 可讀性優化 - 命名一致性優化(Agency vs Brand, Visualization vs Project...etc) - 減少一個檔案內程式碼的行數,大部分一個檔案在 250 行內。 - 移除沒有使用到的引入 - 修正錯誤的程式邏輯 - ex: 一言不合就 reload - 避免使用非主流語法 ## 總結 - 大幅度減少專案的大小,以及清除不需要的程式碼 - 程式碼行數:減少為原本的 1/36 - 檔案數量:減少為原本的 1/33.6 - 版本升級,增加專案的支援度、安全性 - node v10 --> v14 - react v16.8.4 --> v18.2.0 - 相依性套件也會跟著升級 - 檔案結構優化 - 有條理、可理解的檔案結構 - 大幅改善鎖定 root cause 的時間,實測至少 10 倍。 - DOM 結構變得語意化,可理解。 - 大幅改善鎖定 root cause 的時間,實測至少 10 倍。 - 套件的整理 - 移除重覆、沒有被使用到、不必要的套件 - 安裝的套件數量從 167 個減少為 59個。減少為原本的 1/2.8,將近 1/3 - 大幅減少亂用而氾濫的環境變數 - 程式碼優化/移除壞味道(Bad Smell) - 使用 Typescript 達到 90% 以上(舊 GUI 的 Typescript 為 6.3%)。 - 許多js的錯誤往往是在runtime時才報錯,有了typescript,我們能夠在編譯時期就發現錯誤。 - 增加可讀性:有了型別之後,我們能更好得理解程式碼,進而提升整體的閱讀速度。 - 統一 style 的寫法 - 舊 GUI 處理 style 有各種寫法 - ex: `*.css, *.scss, *.less, object css, styled-components...etc`. - 新 GUI 全站統一使用 styled-components. - 減少難以預測的生命週期觸發 - 重新調校 eslint/prettier - 屏棄 class components,全面使用 functional components + React Hooks - 參數命名優化 - 可讀性優化 - 大量的錯字修正 - 命名一致性優化(Agency vs Brand, Visualization vs Project...etc) - 原本畫面上的文字、程式碼、API 參數,是同一個東西,但命名都不一樣,因此很難理解、很難追蹤。 - 錯誤的參數命名 - ex: 明明是 label,卻命名成 category 或 type 或 tag。明明是 category,卻命名成 label。 - 減少一個檔案內程式碼的行數,大部分一個檔案在 250 行內。 - 移除沒有使用到的引入 - 修正錯誤的程式邏輯 - ex: 一言不合就 reload - 避免使用非主流語法 ## Ref: Spec: https://hackmd.io/@Taiming/By7-E9t55 Kader suggestion: https://hackmd.io/@acEKxk9GRZCnOXjvxEOGfQ/Hy1KI_rbs