# 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 之後得到的好處及轉變。==
## 介面調整


## 程式碼行數/檔案數分析
```shell=
cloc --exclude-list-file=.clocignore .
```
**舊 GUI: dmp-gui**

**新 GUI: dmp-gui-front-end**

### 小結
在擁有同樣的功能,甚至更好的功能的前提下,程式碼行數減少,以及檔案數量減少,代表:
- `有效減少使用者必須傳輸的資料量`,從而減少了使用者載入應用程式/站點所需的時間。這對於使用低頻寬連線的使用者而言尤其重要。
- `可讀性也能有效提升`(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:**
如同四個室友住在一起,後來三個都搬出去了,剩下自己留下來,但是房間裡面還有許多前室友的遺留物。你不知道該怎麼整理他,該留?該丟?而且有些是跟其他室友共用的東西,有些是不確定是什麼東西的東西,不知道丟了會不會出問題。

- 在前端裡面有 backend 的 code. ex: backend-core/
- 有許多檔案結構的設計,看起來是以前為了跟 DSP 共用而設計(以前 DMP & DSP 在同一包)。 ex:
- frontend-hooks
- frontend-model
- frontend-queries
- frontend-xxx

**以前 DSP 與 DMP 同一包的證據:**




**檔案結構沒有分類**
- 沒有區分頁面元件與共用元件
- 檔案結構沒有分層,全部攤平在同一層,所以要找 code 很難找
- ex: 如同抽屜打開來,書本、咖啡杯、文具、便當、襪子、零食、充電線全部塞在一起,沒有分類。

**新 GUI:**
解決上述問題,檔案區分清楚,檔案結構與頁面結構完美對應。從檔案結構即可理解頁面邏輯:

### 小結
- 移除不必要的檔案(ex: DSPxxx, xxxDSP)
- 過去結構設計以 DSP&DMP 共用為考量,因此檔案結構在 DSP&DMP 獨立之後難以理解。這部份在新 GUI 完全解決。
- 檔案分類清楚(ex: pages, components, hooks, constants, configs, routes, types, utils...)。原本是散落在各個資料夾當中。
- 檔案結構與頁面結構完美對應。從檔案結構即可理解頁面邏輯。因此能夠增加開發速度與減少除錯時間
- 個人實測:過往平均找到一個頁面上的元件所在的檔案需要十分鐘,甚至常常需要半小時以上。現在在一分鐘內(甚至秒等級)就能夠鎖定頁面上的原始碼。提升的速度至少 10 倍以上。
---
## 關於檔案搜尋
可讀的 class name。DOM 結構變得語意化,可理解。
過往,在頁面上的一個 button,要找他所在的 file 真的比登天還難。因為線索很少,雷同的地方又很多。

由於過往在不同環境會需要不同的 webpack 打包,因此產生出許多檔案,讓鎖定程式碼這件事情變得很困擾:



### 小結
屏棄過往複雜的打包方式,讓程式碼變得乾淨,大大降低鎖定程式碼所需要的時間。
- 個人實測:過往平均找到一個頁面上的元件所在的檔案需要十分鐘,甚至常常需要半小時以上。現在在一分鐘內(甚至秒等級)就能夠鎖定頁面上的原始碼。提升的速度至少 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 形同虛設
- 許多沒有被用到的引入

**過於複雜的 React 生命週期,useEffect 連續技......**
- 程式執行的流程難以預測
- 參數的結果難以預測
- 造成很難修正 bug(修這個壞那個),也很難增加功能。






**使用 React Hooks 取代 class component(React16.8 以前的寫法)**
- class components 程式碼較為冗長
- class components 需要撰寫 JS 當中最令人難以理解和頭痛的 `this`
- class components 編譯速度沒有 functional components 快(少了繼承 class 轉成 ES5)
- funtional components 可以避免生命週期方法的調用,減少人為錯誤的發生。

**一言不合就 reload**
- 創建資料
- 更新資料
- 刪除資料
- 換頁
- 切換 advertiser
- 有東西不預期壞掉了
其實我們只需要針對更新的「區塊」作更新就好了,但是舊 GUI 就算只有小區塊的更新,也會「整個頁面刷新」,因此效能和使用者體驗很差。

**避免使用非主流語法**
屏棄使用 pipeline operator 模式
- 會增加專案的學習門檻。
- 沒有明顯感受到好處,反而增加理解程式碼的困難度。
- 專案會需要特別為了支援特別的語法做更多的設定。
- 而且此語法只有在這個專案適用,同樣的程式碼移到別的專案上就不能執行。
- ES6 已經很強大,並且很通用。



**統一 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