Vấn đề
Một sản phẩm bảo hiểm trễ chuyến phải gắn vào luồng đặt vé đang chạy: chọn ở web khách hàng, cộng vào tổng tiền, tạo hợp đồng với bên bảo hiểm, hiện trên admin, vào kế toán, vào mail.
Sót đúng một repo là hệ thống thu thiếu tiền mà không văng một lỗi nào. Nên đơn vị release ở đây là cả cụm repo, không phải từng repo — và go-live phải được chạy đúng theo cách đó.
Cách làm
Go-live chạy như một chiến dịch cherry-pick kiểm toán được: mỗi repo cắt một nhánh release từ master, kèm một file ghi từng commit được pick — SHA, MR nguồn, conflict và cách xử lý, commit cố tình bỏ — và một bảng tổng hợp, trong đó in đậm những MR mà ghi chú viết tay ban đầu đã bỏ sót.
Sự cố 1 — thu thiếu tiền. Bốn ngày sau go-live, production thu ít hơn số hiển thị trên UI. Mình truy về đúng một repo chưa bao giờ nằm trong danh sách, chứng minh bằng cách diff cùng một biểu thức tổng giá trên master và test rồi so hai dòng order trong DB — và loại bỏ giả thuyết "prod đang chạy image cũ" bằng timestamp deploy sớm hơn order 17 tiếng.
Sự cố 2 — chết im. Cả tính năng ngừng hoạt động: không hợp đồng nào được tạo,
huỷ không chạy, admin báo chưa xác thực — mà không một log lỗi nào. Nguyên
nhân: một token dùng chung ghi vào config không có dấu nháy, nên các ký tự
đặc biệt bị nuốt. Mình chứng minh bằng một ma trận curl thật: token bị cắt xác
thực được với service này, còn token nguyên thì với service kia — đảo ngược hoàn
toàn. Caller nuốt 401 trong catch rồi trả null, nên ba tính năng cùng hỏng
im lặng.
Kết quả
11 repo, 69 commit cherry-pick, 18 conflict, 45 MR được bao phủ.
Fix ship thành ba MR và verify lại trên production: ba endpoint từ 401 lên 200, số dòng permission của service từ 0 lên 23.
Mình còn phát hiện nhánh fix chưa có trên test — lần merge test sang master kế tiếp sẽ xoá âm thầm bản vá production — nên ship thêm MR thứ tư để đồng bộ. Sau đó quét cả 36 repo trong workspace tìm cùng pattern, ghi rõ mọi hit khác đều là false positive, và đóng nguyên tắc ấy thành một lệnh kiểm tra go-live đa repo dùng lại được — công cụ coi cả cụm, chứ không phải từng repo, là thứ được release.