---
# System prepended metadata

title: 2022-10-19 MI DMP GUI Refactor/Rebuilding Report by Taiming
tags: [GroundHog]

---

# 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
