AllianceProject Handbook
← Goals

Nhịp release đều và dự đoán được

Đang thực hiện

Ai cũng biết bản kế tiếp ra khi nào, gồm những gì, bật cho bao nhiêu người trước, và nếu có sự cố thì tắt lại trong bao lâu.

20%
Quý nàyNgười chịu trách nhiệm: Quản lý dự án

Kết quả cần đạt

  • Release theo lịch cố định, không phụ thuộc việc tính năng có kịp hay không
  • Thời gian từ khi code xong tới khi tới tay người dùng dưới 5 ngày
  • Mọi bản release đều có ghi chú thay đổi và phương án rollback
  • Mọi tính năng rủi ro đều có công tắc tắt được trong 30 giây
  • Backend ghi lại phiên bản app trên mọi lần app gọi về, đọc được phân bố phiên bản theo người dùng trong vài phút

Ý tưởng cốt lõi

Release theo lịch, không theo tính năng. Tới ngày là cắt branch release với những gì đã hoàn thành; thứ chưa xong đi chuyến sau. Cách này biến release từ một sự kiện căng thẳng thành một việc thường ngày, và chính vì làm thường xuyên nên nó mới trở nên an toàn.

Nhịp đề xuất

  • Hằng ngày: bản nội bộ cho đội tự dùng.
  • Hằng tuần: bản thử nghiệm cho nhóm người dùng thân thiết.
  • Hai tuần một lần: bản chính thức lên cửa hàng ứng dụng.

Con số này là điểm khởi đầu, sẽ điều chỉnh sau vài sprint dựa trên thực tế đội.

Vì sao lô nhỏ lại an toàn hơn

Một bản release gom 40 thay đổi, khi có sự cố, ta phải dò trong 40 nghi phạm. Một bản gom 5 thay đổi thì nguyên nhân gần như hiện ra ngay. Chi phí rollback cũng khác nhau: bỏ một tuần công việc dễ chấp nhận hơn bỏ hai tháng.

Ở quy mô 500.000 người dùng, lô nhỏ còn có một lợi ích nữa: thời gian phơi nhiễm ngắn hơn. Một lỗi tồn tại 6 tiếng trước khi bị phát hiện chạm vào ít người hơn nhiều so với cùng lỗi đó tồn tại 3 ngày.

Bật dần, không bật hết

Một bản release không phải một sự kiện mà là một chuỗi bước: bật cho một nhóm nhỏ, xem số, rồi mới mở rộng. Tóm tắt ở đây, chi tiết ở phát hành từng phần.

Chỗ dễ nhầm nằm ở thứ thật sự điều khiển được. Không phải "tìm cách cho 100 người cập nhật trước": cửa hàng ứng dụng chỉ cho đặt tỷ lệ chứ không cho chọn ai, và người đã cập nhật thì không hạ về bản cũ được. Thứ điều khiển được là công tắc nằm trong bản đã cài.

Điều khiển lượt cập nhật

  • Chọn được tỷ lệ, không chọn được ai — rơi vào ai bật tự động cập nhật
  • Dừng chỉ dừng phần chưa cập nhật; người đã lên bản mới vẫn ở bản mới
  • Muốn sửa phải đẩy bản khác, chờ cửa hàng duyệt một tới ba ngày

Điều khiển cờ tính năng

  • Chọn đúng 100 người nào, theo module họ dùng và theo dòng máy họ cầm
  • Tắt là tắt cho tất cả, trong vài giây, không qua cửa hàng
  • Bản đã cài không phải đổi — code nằm sẵn trong đó, chỉ là đang tắt

Nên trình tự đúng là: đẩy code cho tất cả với cờ đang tắt, rồi bật cờ cho 100 người. Cập nhật và bật tính năng là hai việc tách rời, và chỉ việc thứ hai mới cần đi từ từ.

Cái gì thật sự trong tầm tay

Câu hỏi đúng ở đây là: làm sao ép được chỉ một phần nhỏ người dùng dùng bản mới, khi việc cập nhật do hệ điều hành tự làm? Tách ra ba tầng thì thấy ngay phần nào của ai:

  1. Cửa hàng quyết ai được mời cập nhật

    đặt được tỷ lệ, không chọn được người nào

  2. Máy người dùng quyết có cài không, lúc nào

    tự động cập nhật bật sẵn trên phần lớn máy, nên bản mới lan nhanh hơn ta tưởng

  3. Máy chủ của ta quyết cái gì đang bật

    chọn đúng người, bật tắt trong vài giây

Hai tầng đầu đúng là ngoài tầm tay. Nhưng chúng chỉ quyết code nằm ở đâu, không quyết người dùng thấy gì. Tầng thứ ba quyết điều đó, và nó chạy trên máy chủ của ta — nên câu "ngoài khả năng" chỉ đúng với hai tầng đầu, và hai tầng đầu không phải chỗ cần kiểm soát.

Hệ quả về thời gian: bản mới cứ lan theo nhịp của cửa hàng và của máy người dùng, còn tính năng thì nằm im vì cờ đang tắt. Không ai thấy gì khác. Khi đã đủ người có bản mới thì mới bật cờ.

  1. Nộp bản, cửa hàng duyệt

    cờ tắt, người dùng không thấy gì mới

  2. Chờ bản mới lan ra

    đo bằng phân bố phiên bản ở backend, không đọc tỷ lệ khai trên cửa hàng — hai số này khác nhau

  3. Đủ người có bản mới thì bật cờ cho 100

    đây mới là lúc bật dần thật sự bắt đầu

  4. Mở rộng theo thang bên dưới

    tắt được ở mọi nấc

Đường ống đã có sẵn

Alliance App đã có API kiểm tra cập nhật: app gọi về, máy chủ trả phiên bản mới nhất kèm cờ báo có cần cập nhật hay không. Cờ tính năng đi cùng đường ống đó, chỉ khác phần trả về — máy chủ trả thêm danh sách cờ đang bật cho đúng người đang hỏi. Đây là mở rộng một endpoint đang chạy, không phải dựng hạ tầng mới.

Chọn ra 100 người bằng một trong hai cách: ghi thẳng danh sách định danh, hoặc băm định danh thành số 0–99 rồi bật cho số nhỏ hơn ngưỡng. Cách sau cho nhóm ổn định — cùng một người luôn rơi vào cùng một phía, không nhảy qua nhảy lại mỗi lần mở app.

Ba thay đổi trên endpoint đó

Hôm nay endpoint trả về phiên bản mới nhất kèm cờ báo cần cập nhật — nghĩa là máy chủ đã biết app đang ở bản nào, vì không biết thì không tính ra được cờ đó. Đường ống đúng hướng rồi, còn thiếu ba việc:

  1. Request kèm định danh người gọi

    có định danh thì câu trả lời mới khác nhau theo từng người — đây là điều kiện của hai việc còn lại

  2. Response trả thêm cờ tính năng của riêng người đó

    thay vì một câu trả lời chung cho mọi máy cùng phiên bản

  3. Máy chủ giữ lại thứ nó nhận được

    nhận được và ghi lại là hai việc khác nhau

Việc thứ ba hay bị bỏ nhất vì nghe như đã có sẵn. Máy chủ đọc phiên bản để tính cờ cập nhật rồi bỏ đi là chuyện hoàn toàn bình thường — để làm việc đó nó không cần giữ. Nhưng không giữ thì không trả lời được câu "đã đủ người có bản mới chưa", tức là không mở được nấc nào.

Tối thiểu phải ghi lại, thiếu trường nào thì số ra cũng không dùng được:

TrườngVì sao cần
Phiên bản số dựng1.4.0 chưa đủ: bản bị từ chối rồi nộp lại mang cùng số phiên bản vẫn là hai bản khác nhau
Nền tảngiOS và Android lan theo hai nhịp khác nhau; gộp lại là che mất một bên
Định danhĐể đếm người, không đếm request — người dùng nặng làm lệch số rất mạnh
Phiên bản hệ điều hànhCùng lúc lấy luôn, dùng để đối chiếu khi lỗi chỉ xảy ra trên một dòng máy
Thời điểmĐể dựng đường cong lan, không chỉ có ảnh chụp lúc này

Gửi thành trường dữ liệu có cấu trúc, đừng moi từ User-Agent — chuỗi đó đổi theo hệ điều hành và không ai kiểm soát được. Giữ phiên bản gần nhất của mỗi người cộng một ảnh chụp phân bố mỗi ngày là đủ vẽ đường cong, không cần giữ toàn bộ log.

Số này phải đọc được trong vài phút chứ không phải chờ báo cáo tuần, vì nó là điều kiện mở nấc mà nấc chờ chỉ có 24 giờ. Và cùng một trường đó còn trả lời ba câu khác: crash đang dồn ở bản nào, còn bao nhiêu người ở bản cũ trước khi cắt tương thích ngược, và đặt ngưỡng bắt cập nhật ở đâu thì không chặn nhầm người.

Bắt cập nhật là đường tiến, không phải đường lùi

Hai cần gạt này hay bị nhầm là một:

Cờ tính năng — đường lùi

  • Tắt trong vài giây, không cần bản mới, không qua cửa hàng
  • Dùng được ngay khi thấy số xấu, chưa cần biết nguyên nhân
  • Chỉ che được thứ đã đặt sau cờ từ đầu

Bắt cập nhật — đường tiến

  • Cần bản đã sửa nằm sẵn trên cửa hàng, tức là đã chờ duyệt xong
  • Dùng để dọn nốt những người còn kẹt ở bản hỏng
  • Không gỡ bản hỏng khỏi máy, chỉ chặn họ dùng tiếp

Thứ tự khi có sự cố: tắt cờ trước (vài giây) → sửa → nộp bản → bắt cập nhật để dọn nốt. Bắt cập nhật đứng cuối vì nó phụ thuộc vòng duyệt của cửa hàng, và vì nó chỉ hữu ích khi đã có bản tốt để đẩy người dùng sang.

Khi không đặt sau cờ được

Có loại thay đổi không nằm sau cờ được: sập ngay lúc khởi động, sửa tầng mạng, nâng thư viện hệ thống, đổi cấu trúc dữ liệu lưu trên máy. Với chúng, tỷ lệ phát hành của cửa hàng là cần gạt duy nhất, và nó chỉ giới hạn thiệt hại chứ không rút lui được — chi tiết ở đưa app lên cửa hàng.

Đó là lý do "thay đổi rủi ro nào cũng nằm sau cờ ngay từ đầu" là điều kiện bắt buộc chứ không phải tiện nghi: thứ quyết định có đường lui hay không là quyết định lấy trước khi nộp bản.

Nấc ở quy mô hôm nay

Hôm nay Alliance App có khoảng 3.000 người dùng, còn đích là 500.000. Hai quy mô này cần hai cái thang khác nhau, và cái hay bị chép nhầm là cái thang tính bằng phần trăm.

Ở 3.000 người, 0,5% là 15 người. Mười lăm người gần như không phát hiện được gì. Cột số là xác suất nhóm thử nghiệm chạm được vào lỗi ít nhất một lần:

15 người
100 người
300 người
Lỗi cứ 10 người dính 1
79%
gần như chắc chắn
chắc chắn
Lỗi cứ 100 người dính 1
14%
63%
95%
Lỗi cứ 1.000 người dính 1
1%
10%
26%

Đọc hàng giữa: với 15 người, một lỗi mà cứ 100 người thì 1 người dính sẽ lọt qua 6 lần trong 7. Với 100 người thì bắt được 2 lần trong 3. Đó là lý do con số khởi đầu hợp lý ở quy mô này là 100 người, không phải 0,5%.

Quy luật tổng quát: thang đo bằng số người, phần trăm chỉ là kết quả chia ra. Nhóm thử nghiệm phải đủ lớn để lỗi cỡ ta quan tâm hiện ra — yêu cầu đó không nhỏ đi khi tệp người dùng nhỏ đi.

  1. Nội bộ đội, khoảng 15 người

    1 ngày, dùng thật chứ không phải bấm thử

  2. 100 người — 3%

    chờ 24 giờ

  3. 300 người — 10%

    chờ 24 giờ

  4. 1.000 người — 33%

    chờ 48 giờ

  5. 3.000 người — 100%

    xoá cờ trong 2 sprint

Thang đủ bốn nấc mất khoảng 5 ngày, nên không phải thay đổi nào cũng đáng đi hết. Sửa chữ, sửa màu, sửa lỗi nhỏ thì 100 người rồi 100%. Để dành đủ nấc cho thứ chạm vào tiền, phân quyền, dữ liệu người dùng, hoặc luồng chính của một module.

Khi tệp lớn lên, giữ nguyên cách nghĩ rồi đọc lại phần trăm:

Người dùngNấc đầuRa phần trăm
3.000100 người3%
50.000500 người1%
500.0002.500 người0,5%

Bảng năm nấc đầy đủ ở quy mô đích nằm trong phát hành từng phần. Tính năng chạm tới tiền, phân quyền hoặc dữ liệu cá nhân thì lùi xuống một nấc và nhân đôi thời gian chờ.

"Quan sát" ở 3.000 người nghĩa là gì

Đây là chỗ thang nhỏ khác thang lớn nhiều nhất. Với 100 người đang bật, crash-free tụt từ 99,5% xuống 99,0% nghĩa là 0,5 phiên hỏng thành 1 phiên hỏng. Không phân biệt được với ngẫu nhiên. Ở quy mô này, nhóm thử nghiệm bắt được hỏng, không bắt được kém đi.

Bắt được với 100 người

  • Màn hình trắng, app đóng ngay khi mở luồng vừa sửa
  • Đăng nhập hỏng, đơn không chốt được, tin nhắn không gửi đi
  • Lỗi mới hiện trong log mà bản trước không có
  • Một dòng máy cụ thể hỏng trong khi các máy khác bình thường

Không bắt được với 100 người

  • Tỷ lệ chốt đơn giảm 2%
  • p95 chậm thêm 150ms
  • Crash-free tụt nửa điểm phần trăm
  • Tỷ lệ quay lại sau 7 ngày xấu đi

Hệ quả: ở nấc đầu đừng chờ dashboard cho câu trả lời. Bù lại, 100 người là con số gọi điện được — ở 500.000 người thì không. Chọn 100 người đó trong nhóm thật sự dùng module vừa sửa, trải đủ dòng máy theo ma trận thiết bị, rồi hỏi thẳng. Tín hiệu định tính thay cho sức mạnh thống kê mà quy mô này chưa có.

Một điều kiện hay bị bỏ: bật cho 100 người không bằng quan sát 100 người. Ai không mở app trong cửa sổ chờ thì không đóng góp gì vào kết luận. Đếm số người đã thật sự đi qua luồng vừa sửa, đừng đếm số người được gán cờ.

Và đừng đọc "chỉ 100 người" thành nhẹ. 100 trong 3.000 người đầu tiên là 3% toàn bộ tệp, phần lớn là người dùng sớm — nhóm đắt nhất để có và dễ mất nhất, xem tăng trưởng bền vững.

Đo bằng gì

Chỉ sốMục tiêu
Số lần release chính thức mỗi tháng≥ 2
Thời gian từ merge tới tay người dùng< 5 ngày
Tỷ lệ bản release phải rollback hoặc hotfix< 15%
Thời gian từ lúc quyết rollback tới lúc xong< 1 giờ
Tỷ lệ tính năng rủi ro có feature flag100%

Hai dòng cuối quan trọng hơn ba dòng đầu. Release nhanh mà không tắt lại được là đánh cược, không phải là nhanh.

Ràng buộc riêng của di động

Khác web, ta không sửa được bản đã nằm trên máy người dùng. Do đó:

  • Bật dần bằng cờ tính năng, không bằng tỷ lệ cập nhật của cửa hàng — cửa hàng không chọn được ai, và không hạ người đã cập nhật xuống lại được.
  • Luôn có công tắc tắt tính năng từ xa cho những thay đổi rủi ro — tắt được không cần release mới, đây là điều kiện bắt buộc chứ không phải tiện nghi.
  • Giữ khả năng buộc cập nhật cho trường hợp lỗi nghiêm trọng, và giữ tương thích ngược giữa app và backend cho mọi phiên bản còn người dùng.
  • Tính trước thời gian cửa hàng ứng dụng duyệt bản, đừng hứa ngày mà không trừ phần này.

Giả định "ai cũng đã cập nhật" luôn sai, ở quy mô nào cũng vậy. 99% đã lên bản mới nghĩa là vẫn còn 30 người ở tệp hôm nay và 5.000 người ở quy mô đích — backend vẫn phải phục vụ họ đúng.