Trunk-based Development: Bí kíp dẹp bỏ ‘Merge Hell’ và tăng tốc CI/CD

Git tutorial - IT technology blog
Git tutorial - IT technology blog

Nỗi ám ảnh mang tên “Merge Hell” và giới hạn của Git Flow

Hãy tưởng tượng: Bạn dành cả tuần để “múa phím” trên một nhánh feature riêng biệt. Mọi thứ chạy hoàn hảo trên máy cá nhân. Đến ngày merge vào main để chuẩn bị release thì… “bùm”! Hàng trăm dòng conflict đỏ rực hiện ra. Bạn mất cả buổi chiều chỉ để ngồi “nhặt” code. Tệ hơn, sau khi merge xong, hệ thống lăn đùng ra chết vì những thay đổi chồng chéo mà bạn không lường trước được.

Mô hình Git Flow với đủ loại nhánh feature, develop, release từng là tiêu chuẩn vàng. Tuy nhiên, để chạy CI/CD (Tích hợp và Giao hàng liên tục) thực thụ, việc duy trì các nhánh sống quá lâu chính là rào cản lớn nhất. Nó khiến tốc độ release bị kéo chậm một cách vô lý. Đó là lúc Trunk-based Development (TBD) trở thành cứu cánh.

Tại team cũ của mình, khi quân số tăng lên 10 người, Git Flow bắt đầu bộc lộ sự cồng kềnh. Mỗi đợt release là một cực hình vì phải chờ các sub-team merge chồng chéo. Sau khi chuyển sang Trunk-based, thời gian gỡ conflict giảm từ 4 tiếng mỗi tuần xuống còn chưa đầy 15 phút. Team có thể tự tin deploy lên production bất cứ lúc nào.

Trunk-based Development thực chất là gì?

Thay vì chia năm xẻ bảy, Trunk-based Development tập trung tất cả developer làm việc trên một nhánh duy nhất (thường là main hoặc master). Nhánh này được gọi là Trunk.

Có hai cách triển khai phổ biến:

  • Cho team nhỏ: Dev commit và push trực tiếp lên nhánh main.
  • Cho team lớn: Dev tạo các nhánh feature cực ngắn (short-lived branches). Những nhánh này chỉ tồn tại vài giờ đến tối đa 1 ngày, sau đó merge ngay vào main sau khi qua bước Code Review.

Triết lý cốt lõi rất đơn giản: Tích hợp sớm và thường xuyên. Đừng giữ code trong máy quá lâu. Hãy đẩy nó lên Trunk ngay khi bạn hoàn thành một mẩu tính năng nhỏ nhất.

3 trụ cột để triển khai TBD thành công

Để áp dụng TBD mà không làm “nát” source code, bạn cần một bộ quy tắc chặt chẽ hơn nhiều so với Git Flow truyền thống.

1. Chia nhỏ Task (Atomic Commits)

Đừng bao giờ đẩy một PR (Pull Request) dài 2000 dòng lên Trunk. Hãy xẻ nhỏ tính năng thành các task tí hon. Mỗi lần commit chỉ giải quyết một vấn đề duy nhất. Nếu một tính năng cần 3 ngày để code, hãy tìm cách chia nó thành 5-6 lần merge nhỏ.

# Thay vì đợi xong hết, hãy merge phần khung trước
git checkout -b feat/api-user-schema
# Code phần định nghĩa schema...
git commit -m "feat: add user schema and validation rules"
git push origin feat/api-user-schema
# Tạo PR và merge ngay vào main trong 30 phút

2. Feature Flags – “Công tắc” vạn năng

Làm sao để merge code đang dở dang lên main mà không làm hỏng trải nghiệm người dùng? Câu trả lời là Feature Flags. Bạn bọc code mới trong một điều kiện if.

# Ví dụ thực tế với logic thanh toán
from feature_flags import is_enabled

def process_payment(user):
    if is_enabled("use_new_stripe_adapter", user):
        # Logic mới đang thử nghiệm
        return stripe_v2_adapter.process(user)
    else:
        # Logic cũ vẫn đang chạy ổn định
        return paypal_legacy_adapter.process(user)

Nhờ Feature Flags, mình có thể đẩy code lên production mỗi ngày nhưng vẫn ẩn nó đi. Khi nào QA test xong trên môi trường thật, mình chỉ cần bật switch trên dashboard là xong. Không cần deploy lại code.

3. Hệ thống CI “không khoan nhượng”

Trong TBD, nhánh main luôn phải ở trạng thái sẵn sàng release. Bạn bắt buộc phải có pipeline CI (GitHub Actions, GitLab CI…) tự động chạy test cực nhanh.

Kinh nghiệm của mình: Hãy thiết lập Branch Protection. Không một ai, kể cả Tech Lead, được phép merge vào main nếu các bài test chưa pass 100%. Nếu build hỏng, việc quan trọng nhất của cả team lúc đó là vào sửa build, dừng mọi việc code mới lại.

Tại sao team bạn nên đổi sang TBD ngay?

Sau nhiều dự án áp dụng TBD, đây là những thay đổi rõ rệt nhất mà mình nhận thấy:

  • Phát hiện lỗi ngay lập tức: Code được trộn vào nhau liên tục. Nếu có xung đột logic, CI sẽ báo ngay. Bạn không phải đợi đến cuối sprint mới biết mình làm hỏng code của đồng nghiệp.
  • Tư duy thiết kế mạch lạc: Để dùng được Feature Flags, bạn buộc phải viết code theo module, tách biệt các thành phần. Điều này vô tình giúp kiến trúc hệ thống sạch hơn, dễ bảo trì hơn.
  • Tốc độ bàn giao cực nhanh: Loại bỏ quy trình merge rườm rà giúp giảm thời gian chờ đợi (lead time). Code xong là có thể lên môi trường staging hoặc production ngay.

Những cái bẫy cần tránh

TBD rất mạnh nhưng có thể phản tác dụng nếu bạn vấp phải 3 lỗi sau:

  1. Lười viết Unit Test: Đừng chơi TBD nếu dự án không có test coverage tốt. Push code liên tục lên main mà không có bộ lọc tự động chẳng khác nào tự sát.
  2. Nhánh feature “ngâm” quá lâu: Nếu một nhánh tồn tại quá 2 ngày, nó không còn là Trunk-based nữa. Hãy tìm mọi cách để xẻ nhỏ nó ra.
  3. Nợ kỹ thuật từ Feature Flags: Đừng quên xóa các đoạn if/else sau khi tính năng đã ổn định. Team mình thường dành 1 buổi sáng cuối tháng để dọn dẹp các flag cũ, tránh làm rác code.

Chốt lại: Có nên chuyển đổi không?

Trunk-based Development không chỉ là kỹ thuật Git, nó là thay đổi về tư duy làm việc. Nếu bạn đang ở trong một startup cần tốc độ hoặc một team DevOps muốn tối ưu pipeline, hãy thử áp dụng TBD.

Ban đầu, team có thể thấy phiền khi phải chia nhỏ task hay viết Feature Flags. Nhưng chỉ sau 2-3 sprint, khi nỗi lo về “Merge Hell” biến mất, bạn sẽ thấy năng suất làm việc tăng vọt. Tốc độ release không còn là rào cản, mà trở thành lợi thế cạnh tranh của team.

Share: