iT邦幫忙

2026 iThome 鐵人賽

DAY 19
0
Software Development

重新認識主幹開發(Trunk-Based Development)系列 第 19 篇

Day 19:透過抽象分支驗證與切換新實作

  • 分享至 

  • xImage
  •  

透過依賴注入讓新舊實作並存後,新寄信程式已經可以整合到主幹,正式功能仍使用原本的方式。在切換之前,團隊需要確認新實作能在實際使用的環境中正常運作。

為了讓測試結果更接近實際使用的情況,我們會盡量讓開發、測試與線上環境保持一致。例如,資料庫、佇列等服務使用相同的種類與版本,可以減少環境差異造成的未預期結果。

不過,既使工具與版本相同,還是一定會存在環境差異是很難模擬的。例如,測試資料未必涵蓋線上的資料內容與規模,或是請求量與同時操作的情況也可能不同。

從零開始開發產品時,開發者可能先直接部署到線上環境,再隨著產品成長,一點一點建立測試環境。需要重現的情況越多,準備資料、模擬流量與維護環境的成本也越高。下圖以已有穩定線上環境的產品為例,說明這些成本如何隨模擬程度提高而增加。

https://ithelp.ithome.com.tw/upload/images/20261003/20102562ydLf9PKBoo.png

團隊因此需要衡量建置與維護成本,決定測試環境要重現哪些條件。未涵蓋的環境差異,仍可能讓上線後的結果不如預期。因此,切換前可以先在線上確認新實作是否能使用實際的設定與服務完成操作;切換後,再持續觀察它處理實際資料與流量時的表現。

寄信改版的難題在於,要確認指定網域是否真的收到通知,就需要實際寄信到該網域。但收件網域不一定由團隊管理,測試信箱與收信結果的確認,也不完全由團隊決定。因此,除了驗證自己的寄信程式,還需要和收件方約定測試地址與收信確認方式。

透過管理程序驗證背景寄送

前面已說明,Mandy 可以在測試中直接取得 DomainQueueSender,驗證新實作。但確認程式會將信件排入佇列,還不能證明背景程序能透過實際的寄信服務完成寄送。一般通知功能仍使用 ImmediateSender,因此團隊需要另一個入口,在保留原本綁定的情況下,呼叫新實作來驗證完整的寄送流程。

開發者可以透過管理程序(Admin Process)執行管理操作,也可以用這個入口測試新的寄送方法。管理程序按需要啟動,執行完就結束,同一個指令仍可重複使用。

Mandy 使用 Laravel 的 Command 功能,讓開發者可以透過執行指令來呼叫待測的新實作,不需要修改原本通知功能的程式。收件地址由指令參數 $email 傳入,核心呼叫如下:

use App\Mail\DomainQueueSender;
use App\Mail\NotificationMail;

$sender = app(DomainQueueSender::class);
$sender->send($email, new NotificationMail('背景寄信測試'));

這裡直接指定具體類別,因此不需要更換 Sender 的綁定,一般通知功能仍使用原本的寄信方式。同一個指令可以傳入指定網域或其他網域的地址,分別驗證背景寄送與立即寄送。

https://ithelp.ithome.com.tw/upload/images/20261003/20102562mMH8sIC52B.png

Brent 將這個版本部署到測試環境,準備好背景程序與測試收信服務後,執行指令驗證兩種寄送方式:

php artisan mail:try-domain-queue check@example.net
php artisan mail:try-domain-queue xxx@gmail.com

第一個指令使用 example.net,新實作會將任務排入佇列,由背景程序寄送;第二個指令使用 gmail.com,則維持立即寄送。

Brent 確認背景任務完成,Mandy 則到測試收信服務確認兩封信的收件人、主旨與內容。如果結果不符合預期,兩人就先修正並重新驗證。

在線上環境確認收信結果

測試環境驗證通過後,Mandy 先和收件方約定測試地址與收信確認方式,Brent 再將同一份成品部署到正式環境,執行相同的管理指令。

這次使用正式環境的佇列與寄信服務,除了確認背景處理結果,也由收件方確認通知是否收到、內容是否正確。完成必要驗證後,團隊才切換一般通知功能使用的實作。

切換通知功能使用的實作

確認要切換的通知功能都已透過容器取得 Sender 後,Mandy 就可以在 AppServiceProvider 的 register() 方法中,將 Sender 原本綁定的 ImmediateSender 改為 DomainQueueSender,讓這些通知功能改用新實作:

use App\Mail\DomainQueueSender;
use App\Mail\Sender;

public function register(): void
{
    $this->app->bind(Sender::class, DomainQueueSender::class);
}

切換後,通知功能透過 Sender 取得新實作,原本的 ImmediateSender 則暫時保留,供需要時切回,如下圖所示:

https://ithelp.ithome.com.tw/upload/images/20261003/20102562XCRpjEhtpC.png

調整綁定後,Mandy 和 Brent 先在測試環境操作通知功能,確認寄送行為符合預期。驗證通過後,Brent 再部署新版本,重新啟動背景程序以載入新程式。

上線後,兩人持續觀察實際流量下是否出現任務堆積、寄送延遲或失敗,並確認收信結果是否符合預期。

除了直接修改程式中的綁定,也可以透過功能標誌選擇實作。例如,依環境變數的設定提供 ImmediateSender 或 DomainQueueSender,讓團隊透過調整設定控制切換時機。

還原時需要處理的影響

如果切換後發生問題,團隊可以修改綁定或調整功能標誌,切回原本實作。但新實作改過的資料不會自動復原,交給背景程序的任務也不會因為切回原本實作就取消。切回之前,團隊要確認原本實作是否能處理目前的資料,並決定尚未完成的操作要繼續還是停止。

以這個寄信案例來說,改回 ImmediateSender 的綁定後,後續通知會恢復立即寄送,但先前排入的任務仍在佇列裡,已寄出的信件也不會收回。Brent 要和 Mandy 確認任務與寄信狀態,再決定繼續處理或暫停,不能把狀態不明的通知全部再寄一次。

如果佇列裡還有通知要寄,團隊就要保留處理這些通知的程式。因此,切回原本寄信方式時,只調整綁定,先保留背景寄送的程式,讓剩下的任務能繼續處理。

清理不再需要的程式

新實作持續符合需求後,Mandy 可以檢查 ImmediateSender 是否還有通知功能在使用。若已沒有使用,也不再需要切回原本方式,就可以移除這個類別。若曾用功能標誌控制切換,也可以一併移除這次替換專用的開關與判斷,固定使用新實作。

移除 ImmediateSender 時,專門測試這個類別的測試也可以一起移除。通知功能的測試則要保留,例如收件人與內容是否正確,以及新實作是否仍對其他網域立即寄送。

管理指令如果不再需要,也可以移除;若保留為維運工具,就要繼續維護。

像 Sender 這類接近基礎設施的介面,通常會保留,讓通知功能不必直接依賴寄信實作。若介面較接近業務需求,則要依需求的性質,判斷是否仍需要保留這層抽象。

清理後,仍要經過審查與整合驗證,確認通知功能正常運作。

小結

抽象分支讓新程式可以先整合到主幹,一般通知功能仍使用原本實作。團隊再透過管理程序在線上驗證新實作,確認實際結果符合需求後,才切換通知功能使用的實作。

切換後仍要持續觀察實際運作。如果發現問題,除了切回原本實作,也要處理新實作已造成的影響。等到不再需要舊實作時,再清理相關程式與設定。這些步驟可以分開驗證、逐步整合,不必等整份改版完成才一起合併。

參考資料


上一篇
Day 18:依賴注入與新舊實作並存
下一篇
Day 20:抽象分支中的抽象
系列文
重新認識主幹開發(Trunk-Based Development) 共 20 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言