Bỏ qua tới nội dung
Kiên

Một tính năng, mười một repo, và hai sự cố production

Khi một tính năng trải trên mười một repo, đơn vị release là cả cụm chứ không phải từng repo — gắn một sản phẩm bảo hiểm trễ chuyến vào luồng đặt vé đang chạy, sót đúng một repo là hệ thống thu thiếu tiền mà không văng lỗi nào.

Thời gian
2026
Vai trò
Feature owner đầu-cuối — backend, web khách hàng, admin, kế toán, mail

Công nghệ

  • NestJS
  • Express 4
  • Next.js
  • React
  • PostgreSQL
  • TypeORM
  • Sequelize
  • Kafka
  • Redis
  • Bull
  • GitLab CI

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.