前一章已經分析遺留系統的功能、資料變化與相依關係。接下來要建立獨立的重構工作環境,讓開發人員可以重現問題、修改程式,並且驗證目標系統的實作結果。
直接在正式環境修改程式,可能中斷現行功能、改變正式資料,或觸發無法立即復原的外部影響。只保留唯一一份程式碼也有類似風險:修改前後缺少可比較的基準,失敗時難以回到已知可執行的狀態。安全的重構環境需要同時做到以下三件事:
使用不同目錄只能隔離程式檔案。如果重構環境仍然載入正式設定、連接正式資料來源,或把結果送往正式使用的外部項目,執行測試時仍可能影響現行運作。建立環境前,應該逐項決定替代方式與阻擋條件。
| 項目 | 隔離方式 | 啟動前的確認 |
|---|---|---|
| 程式碼 | 從版本控制系統取得獨立工作副本,不直接修改正式環境使用的檔案 | 工作目錄、版本與修改分支均已確認 |
| 環境設定 | 使用重構環境專用設定,不共用正式環境的連接內容 | 設定中的環境名稱與目的地均指向重構環境 |
| 測試資料 | 使用自建資料或移除敏感內容的副本,並且保存在獨立位置 | 測試過程無法寫入正式資料來源 |
| 外部互動 | 改用測試替代項目,或在不需要互動時阻擋輸出 | 通知、檔案傳送及後續工作不會進入正式流程 |
| 產生結果 | 使用重構環境專用的輸出位置與識別方式 | 結果可以清除,且不會被正式流程讀取 |
啟動步驟也應該主動檢查這些邊界。例如,環境名稱未設定、目的地不在允許清單,或設定內容含有已知的正式環境識別時,就停止啟動。這種檢查可以在設定放錯位置時提早阻止後續影響。
如果目前只能取得正式環境中的資料,應該先確認可以取用的範圍,再建立受控副本並移除不必要的敏感內容。重構環境不應該因為設定方便而長期依賴正式資料來源。
Git 可以記錄程式碼的版本與修改歷程。開始修改前,先保存一個已知可建置或可啟動的版本,再從這個基準建立重構用分支。每次修改維持明確範圍,後續就能比較行為差異,也能找出哪一項變更造成問題。
版本控制仍需要配合下列做法:
.gitignore 只會讓尚未納入追蹤的檔案維持未追蹤狀態,無法自動移除已經提交的內容。如果敏感資訊曾經進入 Git 歷程,刪除目前檔案中的文字仍不足以解除風險,需要立即停用或更換該資訊,再按照專案規範處理既有歷程。
遺留系統常依賴特定版本、預設路徑或未寫入文件的啟動順序。只複製程式碼,可能得到可以閱讀卻無法執行的副本。建立環境時,至少需要確認下列內容:
| 類別 | 需要記錄的內容 |
|---|---|
| 程式基準 | 來源位置、版本識別、必要的建置檔與啟動入口 |
| 執行環境 | 作業系統、語言執行環境及必要工具的版本與設定 |
| 相依項目 | 套件名稱、版本、取得來源與安裝順序 |
| 環境設定 | 必要欄位、可用範例、預設值及各環境之間的差異 |
| 外部項目 | 啟動時需要的其他程式、檔案位置、互動方式與測試替代方式 |
| 初始內容 | 執行關鍵功能需要的測試輸入、保存狀態與建立方式 |
| 操作順序 | 建置、啟動、確認、停止及清除環境的步驟 |
無法確認版本時,不要直接改用最新版本。可以先從目前仍能執行的環境、建置檔、套件鎖定檔與執行紀錄取得資訊,再逐項測試可以接受的版本範圍。推斷的內容與已確認的內容應該分開記錄,避免把暫時可用的組合誤認為必要條件。
不同環境通常需要不同的資料位置、功能開關與外部目的地。這些差異應該由環境設定提供,程式碼只定義需要哪些欄位及如何讀取,不直接寫入正式值。
一般設定可以保存在設定範本,實際值則透過未納入版本控制的設定檔、環境變數(Environment Variable)或既有設定管理機制,在啟動時提供。設定值不得輸出到執行紀錄;錯誤訊息也應該只顯示缺少哪個欄位,不顯示欄位內容。
密鑰、密碼與權杖等敏感資訊需要另外管理。工具選擇應該配合既有環境、需要管理的範圍與維護能力,不需要為單一重構環境強制增加集中式工具。常見選項如下:
| 工具或方式 | 適用情況 | 需要注意的事項 |
|---|---|---|
| 環境專用設定檔或環境變數 | 項目少、存取範圍單純,且已有安全的提供方式 | 不納入 Git、不寫入紀錄,並且限制可讀取的範圍 |
| HashiCorp Vault | 需要集中保存、存取限制、輪替或依需求產生敏感資訊 | 需要另外維護服務、驗證方式與存取規則 |
| OpenBao | 需要自行維護的集中式敏感資訊管理與稽核功能 | 同樣需要規劃啟動、解除密封(Unseal)、備份及存取規則 |
| SOPS | 需要將加密後的設定檔納入版本控制 | 解密密鑰必須分開保管,解密後的內容不得留在工作目錄或紀錄中 |
不論採用哪一種方式,重構環境都應該使用專用且可停用的敏感資訊。即使取得正式環境的敏感資訊,也不應該複製到重構環境繼續使用。
重構環境可以使用安裝腳本、虛擬機器(Virtual Machine, VM)、容器(Container)或開發容器(Development Container, Dev Container)建立。選擇方式取決於系統對作業系統、安裝流程及外部項目的依賴程度。
| 方式 | 適用情況 | 特性與限制 |
|---|---|---|
| 安裝步驟或腳本 | 相依項目少,且執行環境差異容易控制 | 建立成本較低,但需要明確檢查既有項目造成的差異 |
| 虛擬機器 | 系統依賴特定作業系統版本、安裝方式或系統層級設定 | 可以保存較完整的執行環境,映像檔較大,重建與更新時間也較長 |
| 容器 | 程式與相依項目可以用映像檔及設定描述 | 啟動與重設較快,但與外部執行環境共用作業系統核心,不能取代所有虛擬機器情境 |
| 開發容器 | 已使用容器,並且希望統一編輯工具、擴充功能及開發指令 | 在容器定義上補充開發設定,仍需要另外描述系統本身的建置與啟動方式 |
開發容器規格可以將開發工具與設定寫入 devcontainer.json,讓支援該規格的工具建立一致的開發環境。容器本身則可以透過映像檔與啟動設定描述程式所需的相依項目。系統如果依賴無法放入容器的作業系統行為,可以改用虛擬機器,或將兩者組合使用。
環境形式不同,隔離原則不會改變。映像檔、虛擬機器範本或啟動腳本都不能包含正式環境的敏感資訊,也不能把正式目的地設為預設值。
環境可以在原開發人員的工作目錄啟動,不代表其他人也能重現。應該提供一份從乾淨初始狀態開始的操作文件,並且盡量將固定步驟寫成腳本。內容至少包含:
啟動腳本應該在缺少必要條件時停止並說明原因,不要略過錯誤後繼續執行。重複執行前,也需要能清除前一次留下的狀態,或判斷現有狀態是否符合預期。如此才能區分程式問題與環境殘留造成的差異。
文件完成後,應該從新的工作目錄重新執行一次,並請未參與環境建立的人按照文件操作。過程中需要人工補充、臨時修改或憑記憶完成的步驟,都應該回寫到文件或腳本。
可重現的環境還需要可重複的驗證方式。先選擇少量但能涵蓋主要處理路徑的功能,為每個案例準備固定輸入、必要初始狀態與預期結果。每次重建環境後執行相同案例,確認結果沒有因版本、設定或啟動順序而改變。
驗證時可以按照下列順序檢查:
如果兩次結果不同,先比較實際版本、載入設定、初始內容與相依項目,再判斷是否為程式行為差異。這些執行紀錄會成為後續修改與驗收的共同基準。
完成的重構工作環境應該可以回答三個問題:
只要其中一項仍需依靠特定人員的記憶或正式環境,環境就還不能作為穩定的重構基準。