模組一|現況盤點與立案(Day 1–5)
昨天說我接手後的第一件事不是寫程式,是盤點。今天把方法攤開。
「我覺得這 code 很爛」是一句沒有殺傷力的話。 它聽起來像抱怨、像品味之爭。你講完,對方最多說「那你有空整理一下」,然後這件事就結束了。
盤點要做的是把同一件事翻譯成另一種語言:可驗證的數字、可比較的趨勢、可估算的成本。這種話術不需要對方懂前端,只需要他會看數字。
而且盤點還有一個對你自己的作用:它會告訴你哪裡最痛,而那通常不是你以為的地方。我原本以為該優先處理那幾個兩千行的巨型元件,後來改了想法。
我盤了六個維度,每個維度都會給實際跑的指令和跑出來的數字。指令都是 shell 內建的,不需要裝任何工具,因為要求別人先裝三個 npm 套件才能盤點,本身就是一種摩擦。
以下數字全部來自我們其中一套舊系統(Vue 2,六年歷史)。
# 元件檔數量
find src -name '*.vue' | wc -l
# 總行數
find src -name '*.vue' -exec cat {} + | wc -l
# 最長的 5 個檔案
find src -name '*.vue' -exec wc -l {} + | sort -rn | sed -n '2,6p'
我們的結果:
| 指標 | 數字 |
|---|---|
.vue 檔案數 |
342 |
| 總行數 | 103,870 |
| 平均每檔 | 303 行 |
| 最長的檔案 | 2,553 行 |
「平均每檔 303 行」這個數字比總行數更有訊息量。一個好維護的 Vue 元件大概在 100–200 行之間,平均值到 303 已經偏高。
但平均值可能是「大家都稍微胖一點」,也可能是「大部分正常、少數是怪物」,兩者的處理方式完全不同。
平均值會騙人,所以要看尾巴:
# 超過 800 行的檔案有幾個
find src -name '*.vue' -exec wc -l {} + | awk '$1 > 800 && $2 != "total"' | wc -l
結果出來:23 個檔案超過 800 行,前五名分別是 2,553 / 2,419 / 2,219 / 2,065 / 1,754 行。
所以答案是後者:大部分檔案其實還好,但有二十幾個怪物,我們不需要全面翻修。

這張圖要看的不是「有幾個大檔」,是尾巴的長度:主體集中在 300 行以下,但右邊拖出一條稀疏的長尾,一路拉到 2,553 行。平均值 303 之所以騙人,就是被這條尾巴拉上去的。
800 這個門檻是我自己抓的,理由很土:超過 800 行,你在 IDE 裡就沒辦法靠捲動掌握全貌了。你可以用任何門檻,重點是要有一個門檻,而且要說得出理由。
admin 系統最大的成本不在複雜邏輯,在同一套東西寫了幾百遍。
# 表格欄位總共出現幾次
grep -rho 'el-table-column' src --include='*.vue' | wc -l
# 有幾個檔案裡有彈窗
grep -rl 'el-dialog' src --include='*.vue' | wc -l
我們的結果:表格欄位出現 3,354 次、105 個檔案裡有彈窗(共 383 個)。
翻譯成日常工作是這樣的:
產品說:「所有表格裡沒有資料的欄位,請顯示一個橫線,不要留空白。」
這聽起來像一個五分鐘的需求。但因為每個表格欄位都是各自手寫的,你要去改 3,354 個地方。
如果這些表格共用同一個元件,這個需求就是改一個檔案。這 3,354 就是「沒有共用層」的價格標籤,而它每次有類似需求就要付一次。
提醒一個容易踩的坑:
grep -c算的是符合的行數,不是出現次數。一行裡有兩個就會少算一個。要算出現次數得用grep -o把每個結果拆成一行,再wc -l。我第一次盤的時候就是這裡算少了,數字差了將近一倍。
這個維度最重要,也最少人盤:改了 A,會不會害 B 壞掉?
Vue 元件之間溝通有兩種方式:
props 把資料傳下去,子層用 emit 把事件送上來。這條路是明的,改了會有跡可循。this.$refs.xxx.someMethod() 直接伸手進子元件裡呼叫它的方法。很方便,但繞過了所有約定。後門用多了會怎樣?數數看:
# 用後門的次數
grep -rho 'this\.\$refs' src --include='*.vue' | wc -l
# 有幾個檔案在用後門
grep -rl 'this\.\$refs' src --include='*.vue' | wc -l
我們的結果:330 次,分布在 132 個檔案。342 個元件裡,將近四成在走後門。
為什麼這個數字比行數更可怕?舉個場景:
你今天要重構一個子元件,把裡面的
refresh()改名成reload()。你存檔、跑起來、自己那頁一切正常,於是你送出。三天後客服說某個報表頁的重新整理按鈕沒反應了,因為那頁的父元件裡有一行this.$refs.table.refresh(),而沒有任何工具會告訴你這件事。
這就是差別:行數多只是難讀,走後門多是難改,而且它不會在你改的時候報錯。
迪米特法則(Law of Demeter),別名叫 「不要跟陌生人說話」:只跟你的直接鄰居說話,不要伸手穿過鄰居去碰更遠的東西。
用一個生活場景就懂:
你要跟隔壁鄰居借醬油。
正常的做法:按門鈴,說「可以借我醬油嗎」,鄰居去拿給你。
違反法則的做法:你自己開門進去,走到廚房,打開第二個櫃子的第三層,拿走醬油。第二種比較快,對吧?直到有一天鄰居重新裝潢,把醬油移到冰箱旁邊,然後你就在他家廚房裡撞牆了。
props / emit 是按門鈴:我告訴你我要什麼,你決定怎麼給我。this.$refs.table.refresh() 是自己開門進廚房:我知道你家櫃子怎麼排的,我自己拿。
而 330 次的意思是:這個專案裡有 330 個「我知道你家櫃子怎麼排」的秘密約定,沒有一個寫在文件上。 那個知識只存在於當初寫它的人腦袋裡,而那個人可能已經離職了。
我盤點的當下並不知道這個名字,只覺得「這樣改東西很怕」。但知道之後有個好處:下次看到「迪米特法則」,你就會想起有人在別人家廚房撞牆的畫面。
(JavaScript 沒有型別檢查,這種錯誤更容易漏掉,這也是後來導入 TypeScript 的理由之一,Day 10 會講。)
同一個維度還可以順手抓:全域掛載(Vue.prototype)、原型污染(Array.prototype)、全域事件匯流排。這三個 Day 4 會用一整篇拆開講。
node -e "const p=require('./package.json');\
console.log('deps:', Object.keys(p.dependencies||{}).length);\
console.log('devDeps:', Object.keys(p.devDependencies||{}).length)"
我們有 59 個執行時依賴 + 44 個開發依賴,共 103 個。
但數量不是重點。要打開 package.json 人工掃一遍,找這三種東西:
0.x 開頭,而 0.x 在慣例上代表「還沒穩定」,它已經六年沒動了latest:意思是「每次安裝都給我最新的」,聽起來很好,實際上代表兩個同事在不同時間安裝可能拿到不同版本,出問題時很難重現第 2、3 點都不是「舊」的問題,是沒人在管的問題。舊還可以說是沒空升級,沒人管則代表這個專案已經沒有主人了。
find tests -name '*.spec.js' | wc -l
我們的答案是 1。
342 個元件檔,一個測試檔。這個數字我在任何會議裡都不需要解釋:任何人改任何東西,都只能靠肉眼和運氣。
上面六個維度都是「此時此刻的照片」,真正說服我自己的是加上時間:一個檔案很長不見得是問題,沒人動它就只是躺在那裡;真正危險的是「又長、又每個月都要改」的檔案。

我原本預期兩張榜單會有三、四個重疊,畫出來才發現是六個全中。這張圖之所以值得單獨畫,是因為它把「該從哪裡下刀」這個爭論直接結束掉了,不需要投票,兩個獨立的量化維度已經指向同一批檔案。
而「每個月都要改」這件事,git 已經幫你記了六年:
git log --format='' --name-only --since='2 years ago' -- 'src/*.vue' \
| grep -v '^$' | sort | uniq -c | sort -rn | head -10
(--name-only 讓 git 只印出每次改動碰到的檔名,後面那串就是把檔名排序後數次數,再由多到少排。)
我們近兩年被改最多次的前六名,分別被改了 79、72、72、69、66、63 次。
然後把這份「最常改」的名單跟前面「最長的六個檔案」做交集:
comm -12 <(sort longest.txt) <(sort hottest.txt) | wc -l
答案是 6。最長的六個檔案,就是被改最多次的六個檔案,一個都沒漏。
這個結果殺死了一個常見的反駁:「那些大檔案反正沒人動,先放著吧。」不是沒人動,是所有人每個月都在動它們。最難讀、最容易改壞、改動最頻繁,風險三重疊加。
從那一刻起,「重構那幾個兩千行的檔案」就不再是美學主張,而是風險管理。
知道「這是問題」,跟知道「哪一個正在流血」,是兩件事。 而後者只有 git 知道。
這張交叉圖還修正了我一開始的判斷:真正該優先的不是「最長的」,是「又長又燙手的」。
盤完之後我還注意到一件事:維度三和「熱點」量到的,其實是兩種方向相反的問題。
分清楚很重要,因為處方是相反的:前者要「合起來」(抽共用元件),後者要「拆開」(把巨型檔案切分)。方向搞反,你會越改越糟。
而我們兩種都有。
我最後給出去的不是這篇文章這麼長,是一頁:
這一頁比任何架構圖都有效,因為它講的不是「程式碼多醜」,是「我們每個月都在冒什麼風險」。
盤點不是免費的,有兩個成本要先知道。
一、它會花掉一到兩天,而且交不出任何功能。 這段時間你在跑指令、讀原始碼、做表格,看起來完全沒有產出。如果你的團隊正在趕交期,這件事需要先講好,否則你會在第二天被問「你到底在忙什麼」。
二、盤點報告很容易被讀成批鬥。 那些數字背後是同事六年、在各種交期壓力下的心血,而你拿著一張「23 個超大檔案、1 個測試檔」的表走進會議室,不管你多小心,都有人會覺得你在說「以前的人做得很爛」。
我的處理是:報告裡不寫「誰寫的」、不寫「這寫得很爛」,只寫數字和風險,你要的是預算,不是道歉。 但這個風險沒辦法完全消除,只能降低。如果團隊氣氛本來就緊繃,可以先私下給直屬主管看,而不是在大會上簡報。
數字要能被重跑。 我把所有指令存成一個 shell 檔放進 repo,因為半年後一定會有人問「所以有比較好嗎」,那時候你需要的是同一把尺,不是新的印象。
明天 Day 3,講一個我在盤點時發現、但當下不敢相信的東西:兩套系統裡,有 20 個檔案的 diff 結果是 0 行。以及更可怕的:另外幾個檔案已經開始不一樣了。