AllianceProject Handbook
← Knowledge

Yêu cầu với lập trình viên

Quản lý branch và commit

Quy ước branch, commit, rebase và merge. Mục tiêu là branch main luôn release được, và lịch sử commit đọc được như một bản tường thuật.

Cập nhật 12/09/2026gitbranchcommit

Hai luật không đổi

  1. Branch main luôn ở trạng thái release được. Bất kỳ lúc nào cắt ra cũng phải build, test xanh và chạy được. Không có ngoại lệ "tạm thời hỏng vài tiếng".
  2. Không ai push thẳng lên main. Mọi thay đổi đi qua pull request, kể cả sửa một ký tự, kể cả chủ kỹ thuật. Branch main được bảo vệ ở cấu hình repo, không dựa vào trí nhớ.

Mô hình branch

Dùng mô hình đơn giản nhất còn dùng được: trunk-based, branch ngắn ngày.

main ────●────●────●────●────●────●──────>
          \        /      \      /
           ●──●──●         ●──●──●
        feature/chat-retry  fix/order-dup
Loại branchTiền tốVòng đời tối đaGhi chú
Tính năngfeature/2 ngàyDài hơn thì hạng mục quá to, chẻ nhỏ
Sửa lỗifix/1 ngày
Sửa gấp productionhotfix/Vài giờCắt từ tag release, không cắt từ main
Dọn dẹp, nợ kỹ thuậtchore/2 ngày
Thử nghiệm, không mergespike/3 ngàyXoá sau khi có kết luận

Vòng đời tối đa 2 ngày là con số quan trọng nhất trong bảng. Branch sống lâu là nguồn gốc của conflict lớn, review khổng lồ và merge đau đớn. Nếu tính năng chưa hoàn chỉnh mà đã tới hạn, merge phần đã xong vào main phía sau một feature flag đang tắt.

Đặt tên branch

<loại>/<mô-tả-ngắn-không-dấu>
feature/chat-offline-queue
fix/order-total-rounding
hotfix/login-crash-android-14

Không dùng tên riêng (branch-cua-nam), không dùng số thứ tự vô nghĩa (test2, new-fix).

Commit

Kích thước

Một commit = một thay đổi logic hoàn chỉnh. Đọc tên commit là biết nó làm gì, và nếu revert riêng commit đó thì repo vẫn ở trạng thái hợp lệ.

  • Sai: một commit khổng lồ cuối ngày tên là "update".
  • Sai: mười commit tên "fix", "fix again", "final fix".
  • Đúng: fix(order): làm tròn tổng tiền theo đơn vị đồng thay vì theo float.

Định dạng thông điệp

Dùng conventional commit:

<type>(<scope>): <mô tả ngắn, thể mệnh lệnh, không dấu chấm cuối>

<thân bài: vì sao thay đổi, không phải thay đổi cái gì>

<footer: tham chiếu issue, ghi chú breaking change>
TypeDùng khi
featThêm hành vi người dùng thấy được
fixSửa lỗi
refactorĐổi cấu trúc, không đổi hành vi
perfCải thiện hiệu năng
testThêm hoặc sửa test
docsTài liệu
buildBuild, dependency, cấu hình đóng gói
ciPipeline
choreViệc lặt vặt còn lại

scope là module hoặc vùng: chat, task, sales, auth, core.

Ví dụ đủ:

fix(sales): chặn tạo đơn trùng khi người dùng bấm nút hai lần

Nút thanh toán không bị vô hiệu hoá trong lúc chờ server trả về, người dùng
bấm lại tạo ra đơn thứ hai. Thêm idempotency key sinh từ client và vô hiệu
hoá nút trong lúc chờ.

Refs: #318

Phần thân bài giải thích vì sao là phần có giá trị nhất và cũng là phần hay bị bỏ nhất. Sáu tháng sau, git log là tài liệu duy nhất còn lại.

Cấm commit

  • Secret, token, khoá riêng, file .env, chuỗi kết nối database.
  • File build, thư mục node_modules, artifact, file IDE cá nhân — đưa vào .gitignore.
  • Dữ liệu thật của người dùng, kể cả để làm dữ liệu test.
  • File nhị phân lớn không cần thiết.

Repo có secret scanner trong pre-commit hook và trong CI. Nếu scanner chặn được thì tốt, nhưng một secret đã từng nằm trong lịch sử git coi như đã lộ — phải xoay vòng khoá đó ngay, không chỉ xoá commit.

Giữ branch cập nhật

Mỗi ngày ít nhất một lần, đồng bộ branch của bạn với main:

git fetch origin
git rebase origin/main

Dùng rebase cho branch cá nhân của mình để lịch sử thẳng và dễ đọc. Nếu branch đã được người khác lấy về, không rebase — dùng merge, vì rebase viết lại lịch sử sẽ làm hỏng bản sao của họ.

Làm hằng ngày thì conflict nhỏ và dễ. Để tới lúc mở PR thì conflict to và dễ giải sai. Đây lại là shift left: kéo lỗi tích hợp về sớm mỗi ngày thay vì dồn cục vào cuối.

Xử lý conflict

  1. Không bao giờ "chọn bên mình cho nhanh". Đọc cả hai bên và hiểu vì sao lệch.
  2. Conflict ở phần code bạn không hiểu thì gọi người viết phần đó, đừng đoán.
  3. Sau khi giải xong, chạy lại toàn bộ test trước khi push. Giải conflict là lúc dễ làm mất một thay đổi của người khác nhất, và test là thứ duy nhất phát hiện ra.

Force-push

  • Được phép force-push lên branch cá nhân của mình khi chưa ai review, và luôn dùng --force-with-lease, không dùng --force.
  • Cấm tuyệt đối force-push lên main, lên branch release, và lên branch có người khác đang làm cùng.

Merge vào main

Điều kiện đủ để merge:

  • Toàn bộ quality gate tự động xanh.
  • Đủ số lượng approve theo chuẩn pull request.
  • Đã rebase lên main mới nhất, không còn conflict.
  • Mọi comment review đã được xử lý hoặc trả lời.

Cách merge: squash thành một commit khi branch có nhiều commit vụn; giữ nguyên các commit khi từng commit đều sạch và có ý nghĩa riêng. Không dùng merge commit rỗng.

Sau khi merge: xoá branch ngay. Repo có 40 branch chết là repo không ai dám dọn.

Sau khi merge, việc chưa xong

Người merge có trách nhiệm theo dõi pipeline trên main. Nếu nó đỏ:

  1. Báo vào kênh chung của đội ngay, để người khác không merge chồng lên.
  2. Sửa trong 30 phút hoặc revert.
  3. Revert không phải thất bại. Để main đỏ qua đêm mới là thất bại — nó chặn toàn bộ đội.