2026年9月20日15 分鐘網頁自動化 · 整合測試 · 電子發票 · Electron · 工程實踐

猜出來的程式碼跑不動:eMobile 密碼輪換功能從「看起來有」到「真的能用」的整修全紀錄

eMobile 的密碼到期自動輪換功能骨架完整、八月就上線,但從來沒成功執行過。問題不是某個 bug——而是整個實作是「依平台慣例猜出來的」。本文拆解這種「推測式實作」的失敗機制,以及用一份實機文件讓 16 項驗證全過的修正過程。

3Q 編輯部(AI 協作)

猜出來的程式碼跑不動:eMobile 密碼輪換功能從「看起來有」到「真的能用」的整修全紀錄 — 文章主視覺

「這個功能八月就做了,為什麼不動?」

每個整合過政府平台或第三方系統的工程師,大概都聽過類似的問話。功能在程式碼裡存在、UI 顯示正確、骨架看起來完整——但一旦真正觸發,它就靜靜地失敗,然後回報「請人工處理」。

eMobile 電子發票系統的密碼輪換功能就是這種情況。它從八月就存在,設定頁顯示 A/B 兩組密碼、眼睛鈕可以切換遮蔽、還標著「使用中/備援」的狀態標籤。邏輯也完整:偵測密碼到期 → 切換至備援組 → 用新密碼重新登入 → 繼續原作業。HQ 和 B2B 兩套都有,程式碼完全一致。

但那段程式碼的檔頭有一行注釋,是整件事的核心:

⚠️ 此頁面尚未實機遇到,selector 為依平台慣例推測

這行字幾乎預告了所有問題。


推測式實作是什麼,為什麼它特別危險 #

「推測式實作」(Speculative Implementation)不是壞意圖,是工程師在缺乏完整資訊時的合理應對:先把骨架寫好、把流程接通,等到有真實環境時再驗證細節。

問題在於「再驗證細節」這一步往往沒有發生。

骨架一旦完成,它看起來就像一個正常功能。它通過了靜態分析、它在程式碼審查時沒有明顯錯誤、它在沒有真實目標的情況下無法被整合測試覆蓋。於是它就進入了程式碼庫,靜靜等待第一次真實觸發——那通常是幾個月後、在客戶生產環境裡、在最不方便的時間點。

更危險的是,這類實作的失敗不是「系統崩潰」或「拋出例外」,而是「功能靜靜地走失敗路徑」。使用者不知道系統嘗試過,工程師不知道失敗點在哪,問題就這樣沉進去。

eMobile 的案例清楚展示了這個失敗模式的完整形狀。


五個欄位,五個錯誤 #

密碼輪換的核心函式是 changePasswordOnPlatform,它要做的事很具體:打開政府電子發票平台的密碼變更頁面(ACT014W),填入新密碼,送出表單。

在有實機文件之前,這支函式依「一般後台管理系統的慣例」推測介面:有舊密碼欄(#old_password)、有新密碼欄(#new_password)、有確認密碼欄(#confirm_password),送出按鈕的文字大概是「確認」或「送出」。

拿到客戶實機操作的截圖文件之後,一對照:

項目 推測實作 實機驗證
舊密碼欄 #old_password被視為必填 此欄位不存在
新密碼 selector #new_password #input21
確認密碼 selector #confirm_password #input15
送出按鈕 確認/送出/變更/確定 儲存
系統訊息 modal 未處理 蓋在表單上,需先關閉才能操作

五個項目,五個全錯。

但最致命的不是最多的那個,而是第一個。

函式的邏輯結構如下:

const okOld = await page.waitForSelector('#old_password', { timeout: 3000 }).catch(() => null);
if (!okOld) {
  return { success: false, reason: '找不到舊密碼欄位' };
}
// 後面的填表邏輯永遠執行不到

找不到舊密碼欄就直接 return false。而 ACT014W 根本沒有舊密碼欄——這個平台的密碼變更頁面設計就是不需要輸入舊密碼。

所以這支函式的行為不是「填錯了然後失敗」,而是「永遠在第一步就放棄」。密碼一到期,系統 100% 走到「自動變更密碼失敗,請人工處理」——從第一天就是這樣,只是沒有人知道。


推測實作 vs 實機驗證的 selector 對照——每一個差異都是一個斷點
推測實作 vs 實機驗證的 selector 對照——每一個差異都是一個斷點

為什麼「一般平台的慣例」在這裡完全失準 #

寫這段推測程式碼的工程師做了一個合理但錯誤的假設:有 new_password 欄位就應該有 old_password 欄位,這是標準的後台密碼變更設計。

但政府電子發票平台不是一般後台。它的使用者認證邏輯、頁面架構、欄位命名方式都由財政部資訊中心決定,不受一般商業後台的設計慣例約束。#input21#input15 這種命名方式,是自動生成的表單 ID,跟語意完全無關——這在企業內部 ERP 和政府平台上很常見,但跟「依語意猜 selector」的做法是根本衝突的。

另一個被低估的因素是 modal 的存在。ACT014W 的密碼變更頁面有一個系統訊息 modal,在某些狀態下會蓋在表單上。如果不處理它,後續所有操作都會打到 modal 而不是表單,然後靜靜失敗。這種介面細節不可能從文件規格猜到,只能從實際操作中發現。

這就是推測式實作的根本問題:它依賴「這個平台大概跟我見過的平台差不多」這個假設。但每個平台都有它自己的奇特之處,而那些奇特之處恰恰是整合工作的真正複雜度所在。


修正過程:一份文件,十六項驗證 #

取得客戶提供的實機操作截圖文件之後,修正工作本身並不複雜——因為問題已經完全清楚了。

第一步:移除舊密碼欄的前置判斷。 這行邏輯不只是錯的,還是阻斷後續所有步驟的硬停點。移除後,函式才能繼續往下執行。

第二步:校正所有 selector。 依實機截圖,將 #new_password 改為 #input21#confirm_password 改為 #input15。同時加入 ACT014W 路徑偵測邏輯,確保函式只在正確的頁面執行。

第三步:修正送出按鈕的比對邏輯。 原本的按鈕識別方式是掃描頁面上包含「確認、送出、變更、確定」等字樣的按鈕,但這個頁面的按鈕文字是「儲存」。加入「儲存」之後,按鈕才能正確定位。

第四步:加入 modal 處理邏輯。 在執行表單填寫前,先偵測系統訊息 modal 是否存在,如果存在就先關閉它。這一步不加,其他步驟修得再準確都可能因為 modal 干擾而失敗。

第五步:處理登入成功但被卡住的 400 陷阱。 在某些狀態下,密碼變更成功後重新登入會遇到 HTTP 400 響應,但實際上登入是有效的。這個細節同樣只能從實機測試中發現,加入對這個狀態的特別處理之後,重新登入流程才完整。

HQ 與 B2B 兩套程式碼完全同步修正,16 項驗證全過。


踩過的坑:那些靜靜失敗的環節 #

整個修正過程中,最耗時的不是改程式碼,而是把「我以為知道的」和「實際上是什麼」之間的落差一條一條挖出來。

坑一:失敗訊息太過模糊。 changePasswordOnPlatform 回傳 { success: false, reason: '找不到舊密碼欄位' } 時,系統只記錄「自動變更密碼失敗」,沒有帶出 reason 字串。工程師看 log 只知道「失敗了」,不知道在哪一步失敗、為什麼失敗。如果錯誤訊息能把 reason 完整拋出來,這個問題可能在第一個月就被發現。

坑二:整合測試沒有真實目標。 這個函式的設計讓它無法在沒有真實平台的情況下被測試。開發環境裡永遠拿不到 ACT014W 的真實頁面,所以推測式的 selector 從來沒有被真實 DOM 驗證過。這不是偷懶,是一個設計缺口——整合邏輯和平台存取耦合得太緊,沒有留下可測試的縫隙。

坑三:注釋沒有被當成 TODO 追蹤。 檔頭那行 ⚠️ 此頁面尚未實機遇到 是正確的標記,但它只是注釋,沒有被轉成任何可追蹤的任務。注釋消失在程式碼庫裡,沒有人在功能上線後回來完成這件事。如果這個警告有對應的 issue 或 TODO 追蹤,它在八月就會被指派和關閉。

坑四:功能「看起來完整」讓驗收失去警覺。 設定頁面顯示 A/B 兩組密碼、HQ 和 B2B 兩套都有、流程邏輯也對——這個外觀上的完整讓所有人(包括工程師自己)產生了「這個功能做好了」的錯覺。真正的驗收應該是「觸發密碼到期流程一次,確認 A 組切到 B 組」,但這件事從來沒發生過,因為大家都預設它已經是通過的。


取捨與理由:為什麼不選「先上線再說」 #

在這個案例裡有一個值得討論的設計選擇:密碼輪換失敗時,系統選擇回報「請人工處理」而不是靜默重試或強制繼續。

這個選擇是對的,但它掩蓋了一個問題:「人工處理」對客戶來說意味著什麼?

在沒有修正之前,每次密碼到期,客戶都要手動登入平台、手動修改密碼、手動更新 eMobile 的設定。這個流程每季可能發生一次,而且客戶可能根本不知道「自動輪換有一個功能,但那個功能從來沒動過」。

另一個曾考慮的方向是:在沒有取得真實 selector 之前,就不要提供這個功能的 UI。隱藏一個無法執行的功能,比顯示它然後讓它失敗更誠實。但這個方向會增加 UI 管理的複雜度,而且當時的判斷是「等取得文件後很快就能修」——這個判斷本身也算是一種推測。

最終的結論是:推測式實作的問題不在「先做骨架」這個決策,而在「骨架完成就算功能完成」這個誤判。先做骨架是合理的時間管理,但骨架應該被明確標記為「待驗證」,而不是被歸入「完成」的狀態。


預防框架:三個問題在動手前回答 #

這個案例的教訓可以抽象成一個適用於所有外部系統整合的判斷框架。不論是對接政府平台、第三方 API,還是網頁自動化操作,在動手寫程式之前,有三個問題必須先有答案:

問題一:你有真實目標介面嗎?

這裡的「真實」是指:可以打開、可以操作、可以截圖確認的真實環境。不是規格書,不是另一個平台的截圖,不是「應該差不多」的推測。

如果沒有,動手前的第一件事是取得它——申請測試帳號、要求對方提供操作文件、或者直接問客戶能不能給一次螢幕共享。在沒有真實介面的情況下寫出來的 selector、欄位名稱、流程步驟,正確率接近零。

問題二:selector 和欄位名稱是你自己確認過的嗎?

「從語意推測」和「從實機截圖確認」是兩件完全不同的事。#new_password 是語意推測,#input21 是實機確認。後者可能醜,但它是對的。

任何沒有被真實 DOM 驗證過的 selector,都應該被標記為推測,並在驗收條件裡列為必須親眼確認的項目。這不只是技術問題,也是驗收流程的設計問題——讓「驗證 selector」成為功能上線的必要前置條件,而不是可選的補充步驟。

問題三:你有跑過完整流程一次嗎?

程式碼寫完不等於功能完成。尤其是整合流程,單元測試覆蓋的是程式碼邏輯,不是目標平台的實際行為。只有跑過一次完整的端對端流程——從觸發條件到最終確認——才能說這個功能完成了。

ACT014W 的 modal、登入後的 400 陷阱、送出按鈕的真實文字——這些都只能在完整流程中發現。任何一個環節沒跑到,它就會在某個不方便的時間點靜靜失敗。


整合外部系統前必須先回答的三道關卡
整合外部系統前必須先回答的三道關卡

這個工法還能用在哪 #

密碼輪換只是一個例子,同樣的失敗模式會在所有外部系統整合中重複出現:

  • 政府 API 串接:財政部電子發票、健保局、勞保局的 API 文件常常不完整,或者文件和實際行為有落差。在有測試環境之前就依文件硬寫,很高機率在某個邊界情況卡住。
  • 金流整合:永豐金流、國泰世華的沙盒環境和正式環境行為可能不同,沙盒測試過不代表正式環境會通。
  • 第三方平台 OAuth:各家 OAuth 的 scope 名稱、token 格式、錯誤碼都有自己的慣例,不能從 RFC 或其他平台類比。
  • 企業 ERP 整合:ERP 系統的欄位命名通常是歷史遺留,#input21 這種命名在 ERP 裡比在政府平台更常見。

共同的防預原則只有一個:在有真實目標的情況下寫第一行程式碼,而不是寫完再去對照。

這不是要求所有整合工作都先取得正式環境存取權限才能動工。在等待環境的過程中,可以設計資料結構、寫測試骨架、規劃錯誤處理——但具體的 selector、欄位名稱、API 參數,必須等到有真實目標之後才填入。把「selector 待確認」這件事明確寫進任務追蹤,比把錯誤的 selector 寫進程式碼然後等著被發現,要便宜很多。


結語 #

eMobile 密碼輪換的案例說明了一件事:功能骨架完整不代表流程走得通。八月的工程師做了正確的事情——先把骨架架起來,並且在注釋裡誠實標記了「尚未實機驗證」。問題在於那行注釋沒有變成一個被追蹤的任務,也沒有阻止這個功能被當成「完成」。

一份客戶提供的實機截圖文件,讓 16 項驗證全過。那份文件裡的資訊在整合工作開始前就可以取得——這是最值得反省的地方。

如果你的團隊正在對接政府平台、第三方服務、或任何沒有完整文件的外部系統,我們在這類整合工作上累積了不少踩坑經驗,也很樂意幫你在動手之前先把風險點找出來。

有類似的問題想解決?

先聊聊你的狀況,我們給你可執行的方向。諮詢免費,依工時報價。

3Q · 摩葳實業 / 牛寶創意科技社 / 倍新有限公司 — 企業 IT 系統,做了 23 年

打電話諮詢 LINE 諮詢