2026/09/29

我是 Frontend Engineer。
過去幾年主要處理的東西一直都很明確:
最近因為 AI Agent 越來越強,我也開始接觸一些原本不屬於純 Frontend 的工作,例如 CRUD API。
老實說,我不排斥。
如果 API 的輸入輸出、資料結構、權限驗證和業務規則都很明確,我其實覺得這是一個滿自然的能力延伸。
但最近有一次,我嘗試讓 AI Agent 協助分析一個 Data 相關需求。
做到一半,我突然停下來了。
因為我發現一件事情:
AI 可以讓我很快寫出原本不熟悉的東西,但不代表我真的有足夠的背景知識替它做決策。
以前碰到陌生領域,第一個障礙通常是:
我不會寫。
但現在這個障礙正在快速消失。
把需求和 Codebase 交給 Agent,它很快就可以開始分析:
甚至可以先幫你整理成一份完整的規格文件。
看起來非常方便。
直到 Agent 開始問我:
Redis 快取多久比較合理? 外部資料來源失敗要不要有備援? 資料缺失的時候要怎麼處理? 交易日要怎麼判斷? 身分驗證要採用哪種方式? API 回傳格式要怎麼定?
而且每一題都有「建議選項」。
理論上,我可以一路:
建議選項建議選項建議選項建議選項最後得到一份看起來非常完整的規格。
接下來甚至只需要讓 Agent 開始實作。
但我突然意識到:
我真的知道自己剛剛決定了什麼嗎?
假設今天要新增:
GET /api/prices/history現在要把這支 API 寫出來,可能真的沒有以前那麼困難。
Agent 可以產生 Route、資料驗證、快取、錯誤處理,甚至連測試都一起補。
但真正麻煩的問題通常不是:
Route 怎麼寫?
而是:
資料來源是什麼?資料來源掛掉怎麼辦?資料缺失代表真的沒有資料,還是上游異常?快取多久才合理?外部資料格式改變怎麼辦?資料正確性由誰確認?半年後 API 壞掉誰維護?這些已經不是單純的程式實作問題。
而是領域知識、架構決策和責任歸屬的問題。
AI 可以分析。
AI 可以推薦。
AI 甚至可以告訴你:
建議選擇 B。
但最後選擇 B 的人,還是應該知道:
為什麼是 B?
不然很容易變成:
AI 提出架構↓人類按下建議選項↓AI 實作↓測試通過↓部署↓半年後出問題↓???真正昂貴的,通常是最後那個「???」。
這也是我最近開始重新思考的地方。
我是 Frontend Engineer,但我並不排斥寫 Backend。
例如很典型的 CRUD:
POST /transactionsGET /transactionsPATCH /transactions/:idDELETE /transactions/:id如果資料結構、權限、資料驗證和業務規則都很明確,這種工作對我來說比較像應用程式開發的延伸。
Frontend 和 Backend 的界線本來就不是一道不能跨越的牆。
但當需求開始變成:
外部資料來源↓資料取得↓資料清理↓領域規則↓快取策略↓失敗備援↓資料正確性↓長期維護我開始覺得:
這已經不是「多寫幾支 API」這麼簡單了。
尤其對一個主要經驗仍然在 Frontend 的工程師來說。
AI 確實可以讓我跨過實作的門檻。
但它不會因為幫我把 Code 寫出來,就自動把相關的領域知識一起安裝進我的腦袋。
最近另外一個讓我重新思考責任歸屬的案例,反而發生在我最熟悉的 Frontend。
專案裡有一段比較 Legacy 的 JavaScript。
當時某份 Stock Info 被直接塞進 Zustand 裡一個很大的股票物件。
概念大概像這樣:
select_stock = {
code,
name,
country,
info,
// ...很多其他東西
}因為是 JavaScript,本身沒有 TypeScript 可以協助約束資料結構。
更麻煩的是,隨著時間過去,其他功能也開始直接從這個大物件裡拿資料。
const dailyClose = stock.info.data.daily_close表面上看起來只是:
從 Zustand 拿一個值。
但真正的依賴關係其實變成:
Stock Info API↓Zustand select_stock.info↓某個完全不同功能的 Hook↓計算邏輯↓UI這條資料流沒有被明確描述。
沒有型別。
也沒有任何東西告訴你:
「如果把 stock.info 拿掉,另外一個功能會壞。」後來我把 Stock Info 從 Zustand 搬到 TanStack Query。
const { data } = useStockInfoQuery(stockCode, country)這個方向本身很合理。
Stock Info 是 Server State,本來就更適合由 TanStack Query 管理,而不是混在 Client State 裡。
Stock Info 本身也正常運作。
但另外一個看起來完全不同的功能卻出現了 Regression。
最後一路追才發現:
某個 Legacy Hook 還偷偷從:
stock.info.data取得計算需要的資料。
當 stock.info 不再存在之後,那個值變成 null。
接下來:
dailyClose = null↓計算結果異常↓某個陣列變成 []↓UI 判斷空陣列不顯示↓整列直接消失甚至沒有 Error。
這就是很典型的隱性資料流。
我不會說:
JavaScript 一定會造成這種問題。
真正的問題還是架構。
就算全部換成 TypeScript,如果大家還是把所有資料塞進一個巨大物件,再讓不同模組隨意讀取,一樣可以做出很精彩的依賴地獄。
但在這個案例裡,Legacy JavaScript 確實讓問題更難被提早發現。
如果資料結構有明確的型別:
interface SelectedStock {
code: string
name: string
country: string
}當 info 被移除時:
stock.info至少 TypeScript 很可能會直接開始抱怨。
但原本的 JavaScript 不會。
執行時沒有直接爆炸。
只是某個值一路變成 null、[],最後 UI 安靜地少了一列。
什麼都沒有發生。
除了功能壞掉。
非常有 Legacy Code 的禪意。
這次 Regression 確實是我的修改觸發的。
所以找到根本原因之後,我修掉它。
這沒有什麼問題。
自己的修改造成 Regression,本來就應該負責。
但這件事情也讓我開始注意另外一個問題:
依賴關係不透明,責任歸屬很容易也跟著不透明。
當資料流沒有清楚邊界,很容易出現:
你之前改過 Stock Info↓這個功能也有股票資料↓那是不是 Stock Info?↓找你久了甚至可能變成:
股票資料有問題↓找你但這其實是完全不同的兩件事情。
我認為工程師應該對自己的修改負責。
如果今天是我的重構造成 Regression:
我修。
但:
對自己的修改負責,不等於對整個領域負責。
這個界線以前的我其實沒有想得那麼清楚。
以前看到 Bug,我通常比較直覺:
能修就修。
但後來慢慢發現,這種做法有一個很容易被忽略的副作用。
當一個人一直幫忙處理某類問題,久了組織很容易形成:
這個問題↓找他即使那段 Code 不是他寫的。
即使那個功能不是他負責的。
甚至即使最後的根本原因跟他原本修改的東西完全無關。
所以我現在開始覺得,除了 Debug 之外,還應該把事情講清楚:
根本原因是什麼?原本的實作在哪裡?跟這次修改有沒有關?這個模組原本由誰負責?如果我要接手,原本工作的優先順序要不要調整?這不是甩鍋。
反而是讓責任歸屬透明。
因為最糟糕的情況不是有人犯錯。
而是半年後:
沒有人知道為什麼這段 Code 是這樣,也沒有人知道到底誰負責。
最近我越來越覺得,AI Agent 很像一面照妖鏡。
它會放大工程師的能力,也會把一個團隊原本對軟體工程的態度放大到很明顯。
AI Agent 很強。
它可以幫你:
讀 Codebase↓分析需求↓產生 Code↓補測試↓找可能的 Bug↓修改 Code現在很多時候,從需求到第一版實作,工程師真正需要自己動手寫的 Code 已經變得非常少。
但有一件事情很容易被忽略:
AI Agent 可以加速實作,但它不能替產品負責。
今天 Agent 產生了一段 Code。
最後還是需要有人:
Review↓Commit↓建立 MR↓通過 Review↓Merge↓進入正式環境這段 Code 最後會進入公司的 Codebase,也會成為產品的一部分。
半年後出了問題,我們不可能在 Incident Report 裡寫:
Claude 當時推薦我這樣做。
使用者遇到錯誤時,也不會因為這段 Code 是 AI 產生的,就少算一個 Bug。
Code 一旦被 Commit、Review、Merge,工程師就已經對這個決定背書了。
其中 10% 是 AI 寫的,還是 100% 是 AI 寫的,其實沒有太大差別。
AI 可以產生 Code,工程責任仍然留在人身上。
假設 Agent 告訴我:
建議 Redis Cache TTL 設定為 24 小時。
我當然可以接受。
Agent 甚至可以直接把實作和測試全部寫完。
但在 Merge 之前,我還是應該能回答:
為什麼是 24 小時?
如果我的答案只有:
Agent 說這是 Recommended。
那其實代表我沒有真正理解這個決策。
以前碰到陌生領域時,「不會寫」本身就是一道門檻。
現在 AI 可以快速跨過這道門檻。
這也產生了一個以前比較少遇到的情況:
工程師可能還沒有完全理解問題,就已經得到一份看起來完整、測試甚至全部通過的實作。
Code 可以跑。
Test 可以綠。
Type Check 也可以過。
但這些事情沒有回答:
為什麼系統應該這樣設計?
AI 越能處理實作,工程判斷反而越重要。
一個正常的開發流程可能是:
需求↓領域討論↓架構決策↓實作↓Code Review↓測試↓Merge↓長期維護加入 AI Agent 之後,我比較期待看到:
需求↓領域討論↓架構決策↓AI 加速實作↓工程師驗證↓Code Review↓測試↓Merge↓長期維護AI Agent 很適合壓縮中間大量的實作成本。
但下面這些事情依然存在:
領域討論架構決策工程驗證Code Review產品責任長期維護如果一個團隊原本對軟體工程的理解停留在:
Code 能跑就好。
AI 出現之後,很容易進一步得到:
AI 都能寫了,那誰來做應該都差不多吧?
這就是我覺得 AI Agent 很像照妖鏡的地方。
它會把一個團隊怎麼看待工程師這件事情照得非常清楚。
工程師的價值如果只剩下:
把需求轉換成 Code。
那 AI Agent 的確會讓不同角色看起來越來越沒有差別。
Frontend 可以叫 Agent 寫 Backend。
Backend 可以叫 Agent 寫 React。
沒有 Data 經驗的人,也可以叫 Agent 生出一套資料流程。
這些東西甚至都有可能正常執行。
但真正需要有人回答的問題還在:
這段 Code 正確嗎?
這個架構適合目前的產品嗎?
這些資料可信嗎?
有哪些已知限制?
誰願意讓它被 Merge?
進入 Production 之後,誰負責維護?
AI Agent 沒有辦法替團隊承擔這些事情。
所以我現在使用 AI Agent 時,會越來越在意一件事情:
我敢不敢把這段 Code Commit 下去?
如果我看得懂它。
知道它為什麼這樣設計。
知道有哪些限制。
知道出問題時要去哪裡找。
也願意在半年後它壞掉的時候回來維護。
那我很樂意 Commit。
AI 幫我寫了多少 Code,其實沒有那麼重要。
但如果整個過程只是:
Recommended↓Accept↓Accept↓Accept↓Tests Passed最後連自己都無法解釋為什麼系統變成這樣,那我不會因為測試全部通過,就覺得這件事情已經完成。
因為 Git History 最後留下的是:
Author: 我的名字MR 被 Merge 之後,產品也不會特別標記:
這 300 行是 Claude 寫的所以出事請找 Claude最後留下來的事實很簡單:
我們 Review 過這段 Code,然後決定把它放進產品。
所以 AI Agent 越強,我反而越重視工程師自己的判斷。
以前很多時間花在:
這個東西要怎麼寫?
現在 AI 可以幫忙處理掉很大一部分。
留下來的問題開始變成:
為什麼要這樣做?
我能不能判斷這個答案是對的?
我願不願意對這個決定負責?
我覺得這才是 AI 時代很重要的工程能力。
以前我對工程師成長的理解比較像:
會 React↓會 Next.js↓會 TypeScript↓會測試↓會效能優化↓會架構↓再會一點 Backend↓變強基本上就是:
會的東西越多越好。
現在我還是覺得廣度很重要。
我也還是會繼續學 Backend、API、Database。
但我開始多了一個以前比較少思考的問題:
這件事情我能不能做?
跟:
這件事情我想不想成為長期負責人?
是兩個完全不同的問題。
AI Agent 讓第一個問題越來越容易回答:
可以。
但第二個問題,AI 沒辦法替你回答。
有時候真正需要做的不是:
再學一個 Framework。
而是知道:
哪些事情是我的責任?哪些事情只是我可以協助?哪些決策我有足夠的背景知識做?哪些領域我願意長期投入?哪些東西即使做得出來,我也不想成為它的長期負責人?這些界線,我以前其實沒有那麼重視。
現在反而覺得:
知道自己的界線,也是一種工程成熟度。
我還是很喜歡 AI Agent。
它讓我可以更快理解陌生 Codebase、更快驗證想法、更快寫測試,也讓一個 Frontend Engineer 有能力接觸以前成本很高的領域。
這些都是很棒的事情。
但最近我越來越確定:
AI 降低的是實作門檻,不是責任門檻。
Code 可以由 Agent 產生。
架構可以讓 Agent 提出建議。
甚至測試都可以讓 Agent 產生。
但最後還是需要有人回答:
為什麼這樣設計?
資料錯了怎麼辦?
正式環境出問題誰處理?
半年後誰維護?
誰對這個領域有足夠的背景知識?
而這些問題的答案,不應該只是:
因為 AI 說這是建議選項。
對我來說,最近最大的體會反而很簡單:
能解決一個問題,不代表就應該成為那個問題永久的負責人。
工程師的成長,不只是知道自己還能做多少事情。
有時候也包括知道:
哪些事情,我選擇不接。