在 PawPal 的醫院搜尋裡,原本有一個「診療動物」篩選。
使用者可以依照狗、貓、兔、鳥類等不同動物種類來篩選醫院。
一開始看起來很合理。
既然是找動物醫院,如果可以直接篩選「這間醫院能不能看我的寵物」,好像會更方便。
但做到後來,我開始發現一個問題:
我們當時掌握的醫院資料,沒有足夠完整的診療動物資訊可以支撐這個篩選。
如果資料本身不夠完整,就算畫面上有這個功能,篩選結果的意義也會變得有限。
而且多一個沒有完整資料支撐的條件,反而讓原本的醫院搜尋變得更複雜。
所以最後決定:
把「診療動物」篩選拿掉。
而我一開始對這件事的想法非常簡單:
不就是把畫面上的那幾顆按鈕刪掉嗎?
結果真正開始整理之後,我才發現:
一個功能出現在畫面上,不代表它只存在在畫面裡。
如果只看使用者看到的畫面,「診療動物」就是一排篩選按鈕。
所以我原本想像的流程大概只有:
找到「診療動物」
↓
刪掉標題
↓
刪掉按鈕
↓
完成
但實際去整理程式之後,才發現這個篩選早就不只存在於 SearchBar。
它還一路連到:
UI
↓
Pinia state
↓
store action
↓
API query
↓
constants
↓
tests
這時我才開始理解:
畫面只是這個功能最外面的一層。
把最外面的東西拿掉,不代表裡面的邏輯就會跟著消失。
PawPal 的醫院搜尋狀態是透過 Pinia Store 管理的。
原本其中有一個:
animalType
當使用者選擇不同診療動物時,也會透過:
setAnimalType
去改變篩選條件。
所以如果今天只有把按鈕從畫面上拿掉,但是這些 state 和 action 都還留在 Store 裡,就會變成:
畫面已經不能操作
但程式還保留著相關邏輯
除此之外,前端原本在建立醫院查詢時,也會把這個條件轉成:
animal_type
送給後端。
一般醫院清單和附近醫院查詢都有這一層。
所以除了畫面之外,也一起把前端不再需要的:
animalType
setAnimalType
animal_type query mapping
HOSPITAL_ANIMAL_TYPES
整理掉。
其中 HOSPITAL_ANIMAL_TYPES 原本就是專門提供診療動物篩選按鈕使用的選項資料。
當這個前端功能已經不存在,這份專門提供篩選按鈕使用的常數也失去了原本的用途。
做到這裡,我才比較有感地發現:
移除功能不是找到一個元件刪掉,而是要一路確認它還留下了哪些關聯。
不過這裡也有一個很重要的差別。
前端不再送出 animal_type,不代表後端也已經不支援 animal_type。
這件事反而變成最需要小心的地方。
把前端相關邏輯整理完之後,還有一個很容易讓人產生直覺的問題。
專案裡原本就有:
animal_types
hospital_animal_types
其中 hospital_animal_types 是用來表示醫院和診療動物種類之間的關聯。
既然現在前端已經不提供「診療動物」篩選了,那是不是連這些相關資料結構也可以一起刪掉?
讓我很有感的一件事就是:
不能看到名字和這個功能有關,就直接刪。
因為真正確認之後,發現後端的醫院查詢和相關規格仍然依賴這些資料結構。
所以最後的結果不是:
前端不用
↓
全部刪掉
而是:
前端篩選不再使用
↓
確認其他地方是否還有依賴
↓
後端仍然有依賴
↓
保留相關資料結構
這也是我第一次很明顯地感覺到:
刪除功能之前,真的要先確認依賴。
不能因為自己現在看不到它的用途,就直接認定它已經沒有用了。
以前看到 dead code 這個詞,我當時很直覺地把它理解成:
已經沒有用途的程式碼。
但真正自己整理過一個功能之後,我才比較有感。
當前端篩選入口已經不存在,如果相關的 state、action、query 和常數還繼續留著,下一個看到程式的人可能就會開始疑惑:
這些東西現在到底還有沒有在用?
程式不一定會因此馬上出錯。
但這些已經失去原本用途的邏輯如果一直留著,之後就會越來越難判斷哪些才是真正還在使用的東西。
不過我也學到另一半:
不是跟被移除功能有關的程式,就全部都是 dead code。
像後端的 animal_type 支援,以及 animal_types、hospital_animal_types,因為當時仍然有其他程式依賴,所以就不能因為前端篩選不見了而直接刪除。
這兩件事情看起來很像,但其實差很多。
移除功能後,相關測試也一起做了調整。
不是只確認 SearchBar 裡已經看不到「診療動物」。
還要確認原本屬於這個前端篩選的 state、action 和 query 等邏輯也真的沒有繼續留著。
其中有些測試是直接檢查程式碼結構,不是模擬使用者真的在瀏覽器裡操作。
但它讓我注意到一件事:
移除功能的驗收,不只是畫面上看不到了就算完成。
還要確認這次應該拿掉的東西真的拿掉了,同時原本其他醫院搜尋功能沒有被破壞。
以前我會覺得「新增功能」才是比較大的工作。
因為新增功能要寫新的畫面、新的邏輯,還要把很多東西串在一起。
相較之下,刪東西好像簡單很多:
找到
↓
刪掉
↓
結束
但真的做完,我反而覺得:
刪功能有時候比加功能更需要小心。
新增功能時,我是在思考:
我要讓哪些東西連起來?
但移除功能時,反而要一直確認:
這條關聯真的可以拿掉嗎?
還有沒有其他地方依賴它?
這段現在真的沒用了,還是只是我目前看不到它的用途?
尤其碰到後端和資料庫時,更不能只因為前端已經沒有入口,就直接認定後面的東西全部都不需要了。
一開始,我真的只覺得:
把「診療動物」那排按鈕刪掉就好了。
真正做下去之後才發現:
畫面消失,只代表使用者看不到了,不代表這個功能留下來的程式也全部消失了。
有些前端的 state、action、query 和常數已經沒有用途,可以一起清掉。
但再往後看,又會發現有些相關的後端與資料結構仍然被其他地方使用,所以不能因為名稱和這個功能有關就全部刪掉。
原本我只是想刪掉一個篩選條件,最後才發現,真正要學的不是「怎麼刪程式碼」,而是:
怎麼確認哪些可以安全地刪、哪些還不能動。
這也是我第一次真的感受到:
移除一個功能,從來不只是把畫面刪掉而已。
這次移除功能時,我自己去確認哪些程式可以刪、哪些東西還需要保留。
但在團隊開發裡,除了自己檢查之外,也會有其他人一起幫忙看程式。
下一篇,我想接著聊另一個我後來才慢慢理解的開發環節:Code Review。
下一篇:
Day 21|Code Review:不是挑毛病,而是讓我們一起變強。