Chương 59. Deploy an toàn
Ở các chương trước, ta đã nói về testing.
Test giúp ta tự tin hơn trước khi thay đổi hệ thống.
Nhưng có một sự thật rất quan trọng:
Không có bộ test nào bắt được mọi lỗi production.
Dữ liệu thật khác dữ liệu test.
Traffic thật khác traffic test.
User thật dùng hệ thống theo cách ta không ngờ.
Provider bên ngoài có thể chậm đúng lúc ta deploy.
Database production có kích thước và lịch sử mà staging không có.
Vì vậy câu hỏi không chỉ là:
Làm sao biết code đúng trước khi deploy?
Mà còn là:
Nếu code sai khi ra production, làm sao giảm thiệt hại?
Làm sao phát hiện sớm?
Làm sao quay lại?
Làm sao chỉ ảnh hưởng một nhóm nhỏ trước?
Đó là deploy an toàn.
Thông điệp chính của chương:
> Deploy an toàn không phải là deploy chậm. Deploy an toàn là đưa thay đổi ra production theo cách có quan sát, có giới hạn phạm vi, có đường quay lại, và không đặt toàn bộ hệ thống vào một cú nhảy duy nhất.
---
59.1. Một tình huống: đổi prompt chấm bài
Hãy quay lại AI Judge.
Team muốn đổi prompt chấm tự luận.
Prompt mới có vẻ tốt hơn:
Feedback chi tiết hơn.
Ít bỏ sót lỗi lập luận.
Điểm sát rubric hơn.
Test nội bộ pass.
Evaluation trên bộ mẫu cũng ổn.
Team deploy prompt mới cho toàn bộ hệ thống.
Sau 30 phút, vấn đề xuất hiện:
Latency tăng gấp 3.
Token cost tăng gấp 2.
Tỉ lệ needs_review tăng mạnh.
Một số bài dài bị timeout.
Queue bắt đầu backlog.
Giáo viên phàn nàn điểm ra chậm.
Prompt không hẳn là "sai".
Nhưng nó có tác dụng phụ:
Chậm hơn.
Đắt hơn.
Tạo nhiều trường hợp cần review hơn.
Nếu deploy một phát cho toàn bộ user, thiệt hại lớn.
Nếu deploy an toàn, ta có thể:
Bật prompt mới cho 1% bài.
So sánh latency/cost/quality.
Chỉ bật cho một assignment thử nghiệm.
Chạy song song prompt cũ và mới nhưng chưa dùng kết quả mới.
Tắt feature flag khi metric xấu.
Rollback nhanh về prompt cũ.
Đó là tư duy của chương này.
Không phải "đừng deploy".
Mà là:
Deploy sao cho lỗi nhỏ trước khi thành lỗi lớn.
---
59.2. Deploy và release không giống nhau
Nhiều người dùng hai từ này như nhau.
Nhưng trong hệ thống trưởng thành, nên tách:
Deploy: đưa code/config mới lên môi trường production.
Release: cho user thật bắt đầu dùng hành vi mới.
Ví dụ:
Deploy code có logic prompt mới.
Nhưng feature flag vẫn tắt.
User vẫn dùng prompt cũ.
Code đã ở production.
Nhưng tính năng chưa release.
Lợi ích rất lớn:
Ta có thể deploy trước.
Quan sát hệ thống vẫn ổn.
Bật feature cho nhóm nhỏ.
Tắt nhanh nếu lỗi.
Không cần rollback code ngay lập tức.
Nếu deploy và release dính chặt với nhau, mỗi lần đưa code lên là user lập tức bị ảnh hưởng.
Điều đó làm deploy đáng sợ.
Khi deploy đáng sợ, team deploy ít hơn.
Khi deploy ít hơn, mỗi deploy lại lớn hơn.
Mỗi deploy lớn hơn lại càng đáng sợ hơn.
Đó là vòng lặp xấu.
Tách deploy và release giúp phá vòng lặp đó.
---
59.3. Feature flag là gì?
Feature flag là một công tắc điều khiển hành vi của hệ thống.
Ví dụ:
use_new_grading_prompt = false
Khi false, hệ thống dùng prompt cũ.
Khi true, hệ thống dùng prompt mới.
Feature flag có thể bật theo:
Toàn hệ thống.
Một nhóm user.
Một trường.
Một lớp.
Một assignment.
Một phần trăm traffic.
Một loại bài.
Một môi trường.
Ví dụ AI Judge:
Bật prompt mới cho 5% bài tự luận của assignment thử nghiệm.
Hoặc:
Bật model mới chỉ cho internal users.
Feature flag biến release thành một hành động có thể điều khiển.
Không cần deploy lại chỉ để bật/tắt hành vi.
Nếu metric xấu:
Tắt flag.
Quay về hành vi cũ.
Giảm thiệt hại nhanh.
---
59.4. Feature flag không chỉ dành cho frontend
Nhiều người nghĩ feature flag là để hiện/ẩn nút trên UI.
Đúng, nhưng chưa đủ.
Feature flag còn dùng cho backend behavior:
Dùng prompt mới hay cũ.
Dùng provider AI A hay B.
Bật retry policy mới.
Bật ranking algorithm mới.
Bật cách tính điểm mới.
Bật endpoint mới cho nhóm nhỏ.
Bật queue priority mới.
Feature flag cũng dùng để hạ cấp mềm:
Tạm tắt report realtime.
Tạm tắt export lớn.
Tạm tắt regrade hàng loạt.
Tạm giảm sampling evaluation.
Trong sự cố, flag tốt có thể cứu hệ thống.
Ví dụ:
AI provider chậm.
Tắt feature feedback nâng cao.
Giữ phần chấm điểm cơ bản.
Một flag tốt là một tay nắm vận hành.
Nó giúp team điều khiển hệ thống khi production đang thay đổi.
---
59.5. Feature flag cũng có nợ kỹ thuật
Feature flag rất hữu ích.
Nhưng nếu dùng vô tội vạ, nó tạo rối.
Ví dụ:
if flag_a:
...
else:
...
if flag_b:
...
if flag_c and not flag_a:
...
Sau vài tháng, không ai biết tổ hợp flag nào đang tồn tại.
Code trở thành mê cung.
Vì vậy flag cần có quản lý:
Tên rõ.
Owner rõ.
Mục đích rõ.
Ngày tạo.
Ngày dự kiến xóa.
Trạng thái hiện tại.
Flag tạm thời hay lâu dài.
Flag dùng để rollout tính năng nên được xóa sau khi rollout xong.
Ví dụ:
use_new_grading_prompt
Sau khi 100% traffic đã dùng prompt mới ổn định, code prompt cũ và flag nên được dọn.
Nếu không, hệ thống sẽ mang theo quá nhiều nhánh cũ.
Feature flag giúp deploy an toàn.
Nhưng flag không được dọn sẽ làm code kém an toàn về lâu dài.
---
59.6. Canary deployment là gì?
Canary deployment là triển khai thay đổi cho một phần nhỏ traffic trước.
Ví dụ:
1% user dùng version mới.
99% user vẫn dùng version cũ.
Nếu metric ổn, tăng lên:
5% -> 10% -> 25% -> 50% -> 100%
Nếu metric xấu, dừng hoặc quay lại.
Canary trả lời câu hỏi:
Version mới sống thế nào với traffic thật?
Với AI Judge, canary có thể là:
1% grading jobs dùng worker version mới.
Một trường dùng prompt mới.
Một assignment dùng rubric parser mới.
Một nhóm internal dùng dashboard mới.
Canary rất hữu ích vì production thật luôn có dữ liệu và hành vi mà test không mô phỏng hết.
Nhưng canary chỉ có ý nghĩa nếu ta quan sát đúng.
Nếu bật 1% rồi không nhìn metric, đó không phải canary.
Đó chỉ là deploy nhỏ trong bóng tối.
---
59.7. Canary cần nhìn metric nào?
Canary không nên chỉ hỏi:
Service có crash không?
Cần nhìn cả kỹ thuật và nghiệp vụ.
Với AI Judge, khi canary prompt/model mới, nên nhìn:
Latency.
Timeout rate.
Error rate.
Retry rate.
Queue age.
Token cost.
Cost per submission.
Needs_review rate.
Score distribution.
Teacher override rate.
User complaint/support ticket.
Nếu chỉ nhìn 5xx, ta có thể bỏ sót:
Code không lỗi nhưng điểm lệch.
Code không lỗi nhưng cost tăng mạnh.
Code không lỗi nhưng latency làm queue backlog.
Canary tốt cần có tiêu chí dừng.
Ví dụ:
Nếu p95 grading latency tăng > 50%, dừng rollout.
Nếu cost/submission tăng > 30%, dừng để điều tra.
Nếu needs_review rate tăng gấp đôi, dừng rollout.
Nếu error rate > 2%, rollback.
Không nên đợi cảm giác.
Nên có ngưỡng trước khi bật.
---
59.8. Progressive rollout
Progressive rollout là mở rộng dần phạm vi release.
Ví dụ:
Internal users.
Một lớp thử nghiệm.
Một trường nhỏ.
5% traffic.
25% traffic.
50% traffic.
100% traffic.
Mỗi bước đều có quan sát.
Mỗi bước đều có quyền dừng.
Progressive rollout khác với deploy kiểu:
Tất cả hoặc không gì.
Nó phù hợp khi thay đổi có rủi ro:
Logic chấm điểm mới.
Provider AI mới.
Schema response mới.
Workflow queue mới.
UI giáo viên mới.
Rate limit mới.
Không phải thay đổi nào cũng cần rollout nhiều ngày.
Một typo nhỏ có thể deploy thẳng.
Nhưng thay đổi chạm vào dữ liệu, điểm số, quyền, cost, hoặc workflow chính nên được mở dần.
Deploy an toàn là biết phân biệt mức rủi ro.
Không biến mọi deploy thành nghi lễ nặng nề.
Nhưng cũng không coi mọi deploy là như nhau.
---
59.9. Blue-green deployment
Blue-green deployment là có hai môi trường production tương đương:
Blue: version đang phục vụ user.
Green: version mới đã được chuẩn bị.
Khi green sẵn sàng, traffic chuyển từ blue sang green.
Nếu green lỗi, chuyển traffic lại blue.
Ưu điểm:
Rollback traffic nhanh.
Môi trường mới được chuẩn bị trước.
Giảm downtime.
Nhược điểm:
Cần hạ tầng kép.
Tốn chi phí hơn.
Database migration vẫn khó.
Stateful systems phức tạp hơn.
Blue-green phù hợp với một số web/API stateless.
Nhưng nếu thay đổi database không backward-compatible, chuyển lại blue có thể không dễ.
Ví dụ:
Green migration đổi dữ liệu sang format mới.
Blue không đọc được format đó nữa.
Khi đó blue-green không còn rollback đơn giản.
Điểm cần nhớ:
Rollback code dễ hơn rollback dữ liệu.
---
59.10. Rolling deployment
Rolling deployment là thay version mới dần từng instance.
Ví dụ có 10 API instances:
Thay instance 1.
Chờ healthy.
Thay instance 2.
Chờ healthy.
...
Trong một khoảng thời gian, production có cả version cũ và version mới cùng chạy.
Điều này có nghĩa là:
Version cũ và mới phải tương thích với nhau.
Ví dụ:
API v1 vẫn trả score.
API v2 trả score và final_score.
Nếu v2 chỉ trả final_score còn frontend vẫn có thể gọi v1/v2 lẫn lộn, hệ thống sẽ khó đoán.
Rolling deployment rất phổ biến.
Nhưng nó yêu cầu thay đổi phải chịu được trạng thái mixed version.
Nếu code mới và cũ không thể chạy cùng nhau, rolling deploy sẽ nguy hiểm.
Đây là lý do backward compatibility quan trọng.
Không chỉ giữa service.
Mà cả giữa version cũ và version mới của cùng service.
---
59.11. Shadow traffic là gì?
Shadow traffic là gửi một bản sao traffic thật sang hệ thống mới, nhưng không dùng kết quả mới để trả cho user.
Ví dụ:
User nộp bài.
Hệ thống chính dùng prompt cũ để chấm và trả kết quả.
Đồng thời, bản sao job được gửi sang prompt mới.
Kết quả prompt mới chỉ được ghi để so sánh, không hiển thị cho user.
Shadow traffic giúp trả lời:
Hệ thống mới xử lý traffic thật thế nào?
Latency ra sao?
Cost ra sao?
Output khác gì?
Có crash với dữ liệu thật không?
Nó rất hữu ích cho:
Model AI mới.
Prompt mới.
Parser mới.
Search ranking mới.
Recommendation algorithm mới.
Fraud/risk model mới.
Nhưng shadow traffic phải cẩn thận.
Không được tạo side effect thật ngoài ý muốn.
Ví dụ:
Không gửi email hai lần.
Không ghi điểm chính thức từ kết quả shadow.
Không gọi webhook thật.
Không tính tiền user hai lần.
Shadow chỉ nên quan sát.
Không nên ảnh hưởng user.
---
59.12. Shadow traffic trong AI Judge
Với AI Judge, shadow traffic rất hợp để thử model/prompt mới.
Luồng:
Prompt cũ chấm bài và tạo official result.
Prompt mới cũng chấm cùng bài ở chế độ shadow.
Kết quả shadow lưu riêng.
Không hiển thị cho học sinh/giáo viên.
Không gửi notification.
Không cập nhật điểm chính thức.
Sau đó team so sánh:
Điểm khác bao nhiêu?
Feedback dài hơn hay ngắn hơn?
Tỉ lệ JSON parse lỗi ra sao?
Latency thế nào?
Token cost thế nào?
Tỉ lệ needs_review thế nào?
Prompt mới có bias kỳ lạ không?
Shadow giúp học từ production thật mà không đặt user vào rủi ro trực tiếp.
Nhưng có chi phí.
Nếu chấm shadow toàn bộ bài, cost có thể tăng gấp đôi.
Vì vậy có thể shadow theo sampling:
1% bài.
Một số assignment.
Một số loại bài.
Chỉ nội bộ.
Shadow traffic là công cụ học rất mạnh.
Nhưng phải có ngân sách, sampling, và guardrail rõ.
---
59.13. Parallel run là gì?
Parallel run là chạy hai hệ thống hoặc hai logic song song trong một thời gian để so sánh.
Nó giống shadow traffic, nhưng thường dùng rộng hơn cho migration hoặc thay đổi logic quan trọng.
Ví dụ:
Hệ thống tính điểm cũ vẫn là nguồn chính.
Hệ thống tính điểm mới chạy song song.
Hai kết quả được so sánh.
Nếu chênh lệch vượt ngưỡng, ghi nhận để điều tra.
Parallel run giúp ta tự tin trước khi chuyển hẳn.
Nó hữu ích khi:
Viết lại service.
Đổi database.
Đổi thuật toán tính điểm.
Đổi provider.
Đổi pipeline xử lý dữ liệu.
Nhưng parallel run cũng cần cẩn thận:
Kết quả nào là official?
Side effect nào được phép?
So sánh bằng tiêu chí nào?
Chạy song song bao lâu?
Khi nào đủ tự tin để cutover?
Nếu không định nghĩa rõ, parallel run có thể kéo dài mãi và tạo thêm complexity.
---
59.14. Parallel run không phải lúc nào cũng cần kết quả giống hệt
Với một số hệ thống, kết quả mới phải giống hệt kết quả cũ.
Ví dụ:
Migration tính tổng tiền.
Nếu lệch 1 đồng, phải điều tra.
Nhưng với AI Judge, prompt/model mới có thể cho feedback khác.
Không thể yêu cầu giống từng chữ.
Thay vào đó, ta cần tiêu chí so sánh phù hợp:
Điểm lệch trung bình trong ngưỡng cho phép.
Không có nhóm bài nào bị lệch bất thường.
Feedback không thiếu rubric quan trọng.
Needs_review rate không tăng quá cao.
Teacher override rate không xấu hơn.
Latency/cost trong giới hạn.
Điểm quan trọng:
So sánh phải theo bản chất của domain.
Không phải mọi parallel run đều dùng equality.
Với deterministic system, so sánh chính xác.
Với AI system, so sánh bằng evaluation và metric.
---
59.15. Rollback là gì?
Rollback là quay lại version hoặc hành vi cũ khi version mới có vấn đề.
Ví dụ:
Deploy API version mới.
Error rate tăng.
Rollback về API version cũ.
Rollback nghe đơn giản.
Nhưng thực tế có hai loại:
Rollback code.
Rollback data.
Rollback code thường dễ hơn.
Ví dụ:
Đưa container image cũ lên lại.
Tắt feature flag.
Chuyển traffic về blue.
Rollback data khó hơn nhiều.
Ví dụ:
Migration đã đổi format dữ liệu.
Job mới đã ghi dữ liệu theo schema mới.
User đã tạo dữ liệu mới.
Service cũ không đọc được dữ liệu mới.
Nếu không nghĩ trước, ta có thể phát hiện:
Code cũ không chạy được với database mới.
Lúc đó rollback code cũng không cứu.
---
59.16. Rollback tốt phải được chuẩn bị trước
Trước khi deploy thay đổi rủi ro, nên hỏi:
Nếu lỗi, quay lại bằng cách nào?
Quay lại mất bao lâu?
Có mất dữ liệu không?
Có cần migration ngược không?
Code cũ đọc được dữ liệu mới không?
Flag nào cần tắt?
Ai có quyền rollback?
Metric nào kích hoạt rollback?
Rollback không nên là ý tưởng xuất hiện sau khi cháy.
Nó phải là một phần của kế hoạch deploy.
Với AI Judge, rollback có thể là:
Tắt prompt mới.
Chuyển provider về provider cũ.
Giảm rollout từ 25% về 0%.
Tắt regrade pipeline mới.
Dừng worker version mới.
Chạy lại job bị fail bằng worker cũ.
Một rollback tốt cần nhanh và được luyện tập.
Nếu rollback cần 8 bước thủ công, người trực lúc căng thẳng rất dễ làm sai.
---
59.17. Roll forward là gì?
Không phải lúc nào rollback cũng là cách tốt nhất.
Roll forward là sửa lỗi bằng version mới hơn.
Ví dụ:
Deploy version 2 có bug nhỏ.
Thay vì quay về version 1, deploy nhanh version 3 sửa bug.
Roll forward phù hợp khi:
Bug đã rõ.
Fix nhỏ và ít rủi ro.
Rollback khó hơn fix.
Data đã migrate không quay lại dễ.
Rollback phù hợp khi:
Lỗi nghiêm trọng.
Chưa hiểu nguyên nhân.
Version cũ chắc chắn ổn.
Có đường quay lại nhanh.
Điểm quan trọng:
Phải có lựa chọn.
Nếu không thể rollback và cũng không thể roll forward nhanh, team bị kẹt.
Deploy an toàn là tránh đưa mình vào thế kẹt.
---
59.18. Database migration là phần nguy hiểm nhất
Rất nhiều deploy có thể rollback code nhanh.
Nhưng migration database làm mọi thứ khó hơn.
Ví dụ:
Đổi tên column score thành final_score.
Nếu code mới deploy xong, migration đổi column, rồi code mới lỗi, rollback code cũ sẽ fail vì code cũ cần score.
Đây là lỗi kinh điển.
Cách an toàn hơn là expand-contract.
Thay vì đổi một bước:
score -> final_score
ta làm nhiều bước:
1. Thêm column final_score, vẫn giữ score.
2. Code ghi cả score và final_score.
3. Backfill dữ liệu cũ sang final_score.
4. Code đọc final_score nhưng fallback score.
5. Đảm bảo không còn ai dùng score.
6. Xóa score sau.
Quá trình này dài hơn.
Nhưng cho phép version cũ và mới cùng tồn tại.
Nó làm rollback dễ hơn.
---
59.19. Expand-contract pattern
Expand-contract là pattern migration hai pha hoặc nhiều pha.
Expand:
Mở rộng schema để hỗ trợ cả cũ và mới.
Ví dụ:
Thêm column mới.
Thêm table mới.
Thêm field mới.
Cho phép cả format cũ và mới.
Contract:
Dọn phần cũ sau khi tất cả consumer đã chuyển.
Ví dụ:
Xóa column cũ.
Xóa code fallback.
Xóa feature flag.
Xóa endpoint cũ.
Điểm quan trọng là giữa hai pha, hệ thống chạy được với cả hai version.
Điều này rất hợp với rolling deploy.
Ví dụ AI Judge đổi kết quả chấm:
Từ score đơn giản sang result object có score, confidence, rubric_breakdown.
Không nên đổi một phát.
Nên thêm field mới, ghi song song, đọc fallback, migrate dần, rồi dọn.
---
59.20. Backfill là gì?
Backfill là điền dữ liệu mới cho dữ liệu cũ.
Ví dụ:
Thêm column final_score.
Dữ liệu cũ chỉ có score.
Backfill copy score sang final_score.
Backfill nghe đơn giản, nhưng có thể nguy hiểm với dữ liệu lớn.
Nếu chạy một query lớn:
UPDATE grading_results SET final_score = score;
trên bảng hàng trăm triệu row, database có thể bị lock, replication lag, hoặc quá tải.
Backfill an toàn thường:
Chạy theo batch nhỏ.
Có pause/resume.
Có progress.
Có rate limit.
Có metric.
Có thể chạy lại idempotent.
Không giữ lock quá lâu.
Chạy ngoài giờ cao điểm nếu cần.
Backfill cũng cần verify:
Bao nhiêu row đã backfill?
Bao nhiêu row lỗi?
Có dữ liệu lệch không?
Code mới đọc đúng không?
Backfill là production job.
Phải vận hành như production job.
---
59.21. Migration có quan sát
Migration có quan sát nghĩa là migration không chạy trong bóng tối.
Ta cần biết:
Migration đang ở bước nào?
Đã xử lý bao nhiêu record?
Tốc độ bao nhiêu record/phút?
Lỗi bao nhiêu?
Database latency có tăng không?
Lock wait có tăng không?
Replication lag có tăng không?
Queue/job có bị ảnh hưởng không?
Nếu migration bắt đầu làm database chậm, ta cần có thể dừng hoặc giảm tốc.
Migration tốt nên có:
Dry run.
Batching.
Progress metric.
Error logging.
Pause/resume.
Rollback hoặc compensation plan.
Verification query.
Owner trực trong lúc chạy.
Không nên xem migration là một câu lệnh chạy rồi cầu may.
Với hệ thống nhỏ, đôi khi một migration đơn giản là đủ.
Nhưng với dữ liệu lớn hoặc dữ liệu quan trọng, migration là một chiến dịch nhỏ.
---
59.22. Migration phải tương thích với nhiều version code
Trong rolling deploy, code cũ và code mới có thể chạy cùng lúc.
Vì vậy database schema phải chịu được cả hai.
Ví dụ nguy hiểm:
Deploy migration xóa column score.
Một số instance cũ vẫn đang chạy và đọc score.
Instance cũ crash.
Cách an toàn:
Không xóa column cũ cho đến khi chắc chắn không còn code cũ dùng.
Tương tự:
Không thêm NOT NULL required ngay nếu code cũ chưa ghi field đó.
Không đổi kiểu dữ liệu nếu code cũ không đọc được.
Không đổi meaning của field mà không có giai đoạn chuyển.
Một câu hỏi rất tốt trước migration:
Trong 30 phút deploy, version cũ và mới cùng chạy thì chuyện gì xảy ra?
Nếu câu trả lời là "không biết", migration chưa an toàn.
---
59.23. Không deploy lớn khi không có đường quay lại
Một trong những nguyên tắc quan trọng nhất:
Đừng gom quá nhiều thay đổi rủi ro vào một deploy không rollback được.
Ví dụ deploy nguy hiểm:
Đổi database schema.
Đổi prompt chấm.
Đổi provider AI.
Đổi worker queue.
Đổi UI giáo viên.
Chạy backfill lớn.
Tất cả trong một release.
Nếu lỗi xảy ra, ta không biết lỗi từ đâu.
Rollback cũng khó vì dữ liệu đã thay đổi.
Cách tốt hơn:
Tách schema migration tương thích trước.
Deploy code hỗ trợ cả cũ và mới.
Bật flag cho nhóm nhỏ.
Chạy backfill quan sát được.
Canary provider mới.
Sau khi ổn mới dọn phần cũ.
Một deploy nhỏ không tự động an toàn.
Nhưng deploy nhỏ dễ hiểu, dễ quan sát, dễ rollback hơn.
Deploy lớn không có đường quay lại là cách biến sai lầm nhỏ thành sự cố lớn.
---
59.24. Release checklist
Trước khi release một thay đổi quan trọng, nên có checklist ngắn.
Không phải checklist để làm màu.
Mà để tránh quên chuyện căn bản lúc vội.
Ví dụ:
- Thay đổi này ảnh hưởng luồng nào?
- Có feature flag không?
- Có canary/progressive rollout không?
- Metric nào cần theo dõi?
- Ngưỡng nào thì dừng rollout?
- Rollback bằng cách nào?
- Rollback có ảnh hưởng dữ liệu không?
- Migration có backward-compatible không?
- Code cũ và mới có chạy cùng nhau được không?
- Có alert liên quan không?
- Có dashboard không?
- Có owner trực lúc release không?
- Có thông báo cho support/ops nếu user bị ảnh hưởng không?
Checklist không cần dài.
Nhưng phải hỏi đúng rủi ro.
Checklist tốt là trí nhớ tập thể của những lần production từng đau.
---
59.25. Observability trong deploy
Deploy an toàn cần observability tốt.
Nếu không nhìn được hệ thống, ta không biết rollout có ổn không.
Khi deploy, nên nhìn:
Error rate.
Latency.
Traffic.
Saturation.
Queue age.
Retry rate.
Database latency.
Provider latency/error.
Cost.
Business metrics.
Với AI Judge, business metrics rất quan trọng:
Submission success rate.
Time to grade.
Needs_review rate.
Score distribution.
Teacher override rate.
Regrade rate.
Support ticket volume.
Nên đánh dấu deploy trên dashboard.
Ví dụ:
10:05 deploy version 2026.05.12.1
10:15 canary 5%
10:30 rollout 25%
11:00 rollout paused
Khi metric xấu, deploy marker giúp thấy liên hệ thời gian.
Không có marker, team dễ tranh luận:
Metric xấu từ trước hay sau deploy?
---
59.26. Smoke test sau deploy
Smoke test là kiểm tra nhanh sau deploy để biết hệ thống còn sống.
Không phải test toàn bộ.
Chỉ kiểm tra vài đường sống chính.
Ví dụ AI Judge:
API health check.
Login được.
Tạo submission test được.
Job chấm test chạy được với fake/internal assignment.
Dashboard đọc được trạng thái.
Worker không crash.
Queue không backlog bất thường.
Smoke test có thể tự động.
Nó nên chạy nhanh.
Nếu smoke test fail, dừng rollout.
Smoke test không thay thế unit/integration/E2E.
Nó là lớp kiểm tra ngay sau khi code chạm production.
Nhiều lỗi chỉ xuất hiện vì cấu hình production:
Secret thiếu.
Permission cloud sai.
Connection string sai.
Migration chưa chạy.
Feature flag config sai.
Smoke test bắt các lỗi kiểu này khá tốt.
---
59.27. Health check phải kiểm tra đúng thứ
Health check đơn giản kiểu:
Process còn chạy.
chưa đủ.
Service có thể còn chạy nhưng không làm được việc.
Ví dụ:
API process sống nhưng không kết nối được database.
Worker sống nhưng không đọc được queue.
Service sống nhưng secret provider lỗi.
Health check nên chia:
Liveness: process còn sống không?
Readiness: service đã sẵn sàng nhận traffic chưa?
Dependency health: dependency quan trọng có dùng được không?
Không phải health check nào cũng nên gọi tất cả dependency.
Nếu health check quá nặng hoặc phụ thuộc quá nhiều, nó có thể làm hệ thống bất ổn.
Nhưng readiness check nên đủ để tránh gửi traffic vào instance chưa sẵn sàng.
Ví dụ:
API chưa load config xong thì chưa ready.
Migration chưa tương thích thì chưa ready.
Worker chưa kết nối queue thì chưa ready.
Deploy an toàn cần health check có ý nghĩa.
---
59.28. Config deploy cũng nguy hiểm như code deploy
Không phải chỉ code mới gây sự cố.
Config cũng có thể phá production.
Ví dụ:
Tăng Celery concurrency quá cao.
Đổi timeout gọi AI quá thấp.
Bật retry quá mạnh.
Đổi rate limit sai.
Đổi prompt version.
Đổi model name.
Đổi database pool size.
Đổi feature flag nhầm nhóm user.
Config thay đổi nhanh nên càng cần kỷ luật.
Nên có:
Review cho config quan trọng.
Audit log ai đổi gì.
Rollback config.
Validation.
Canary config nếu có thể.
Monitoring sau đổi.
Với AI system, prompt cũng nên được xem như config/release artifact.
Không nên sửa prompt production âm thầm mà không version, không evaluation, không rollback.
Prompt versioning là một phần của release management.
---
59.29. Kill switch
Kill switch là công tắc tắt nhanh một phần hệ thống khi nó gây hại.
Ví dụ:
Tắt regrade hàng loạt.
Tắt export báo cáo lớn.
Tắt provider AI đang lỗi.
Tắt prompt mới.
Tắt gửi webhook sang hệ thống ngoài.
Kill switch khác feature flag rollout ở mục đích.
Feature flag thường dùng để bật dần.
Kill switch dùng để dừng nhanh khi có sự cố.
Một kill switch tốt:
Dễ tìm.
Ít quyền nhưng đủ an toàn.
Có audit log.
Tác dụng nhanh.
Được test định kỳ.
UI/ops biết trạng thái sau khi tắt.
Nếu kill switch chưa từng được test, có thể đến lúc cần mới phát hiện nó không hoạt động.
Kill switch là một phần của graceful degradation.
Nó giúp hệ thống bỏ phần phụ để giữ phần chính sống.
---
59.30. Deploy khi nào?
Thời điểm deploy cũng là quyết định kỹ thuật.
Không nên deploy thay đổi rủi ro đúng lúc:
Sắp hết hạn nộp bài.
Traffic peak.
Không có người trực.
Provider đang unstable.
Database đang chạy backfill khác.
Team support offline.
Với AI Judge, deploy logic chấm điểm mới lúc 23:50 trước deadline 23:59 là ý tưởng rất tệ.
Ngay cả khi code có vẻ ổn.
Deploy an toàn cần chọn thời điểm có khả năng quan sát và phản ứng.
Không phải lúc nào cũng phải deploy ban ngày.
Một số hệ thống cần deploy giờ ít traffic.
Nhưng điểm chính là:
Khi deploy rủi ro, phải có người đủ năng lực theo dõi và xử lý.
Deploy xong rồi đi ngủ chỉ hợp với thay đổi rất nhỏ hoặc hệ thống tự động cực kỳ tốt.
---
59.31. Change freeze
Change freeze là tạm dừng thay đổi rủi ro trong giai đoạn quan trọng.
Ví dụ:
Tuần thi cuối kỳ.
Ngày cao điểm nộp bài.
Black Friday.
Kỳ chốt sổ tài chính.
Giai đoạn migration lớn đang chạy.
Change freeze không có nghĩa là không sửa gì.
Bug nghiêm trọng vẫn phải sửa.
Nhưng các thay đổi không cần thiết nên hoãn.
Mục tiêu là giảm biến số.
Khi hệ thống đang phục vụ một thời điểm quan trọng, ta không muốn vừa xử lý traffic lớn vừa đưa vào nhiều hành vi mới.
Change freeze là một công cụ quản lý rủi ro.
Không phải dấu hiệu team yếu.
Team chuyên nghiệp biết lúc nào nên tiến nhanh và lúc nào nên giữ ổn định.
---
59.32. Incident sau deploy
Nếu sự cố xảy ra sau deploy, đừng mất thời gian tranh luận quá sớm.
Việc đầu tiên:
Bảo vệ user.
Giảm phạm vi ảnh hưởng.
Rollback/tắt flag nếu cần.
Ổn định hệ thống.
Sau đó mới điều tra sâu.
Một phản xạ tốt:
Metric xấu ngay sau deploy?
Nếu có đường rollback an toàn, rollback trước.
Điều tra sau.
Không phải lúc nào rollback cũng đúng.
Nhưng nếu user đang đau và rollback rẻ, đừng cố chứng minh mình đúng trong production.
Sau incident, cần postmortem:
Lỗi lọt qua test vì sao?
Canary có bắt được không?
Metric nào thiếu?
Rollback có chậm không?
Feature flag có giúp không?
Migration có làm khó rollback không?
Checklist cần thêm gì?
Postmortem tốt biến sự cố thành cải tiến hệ thống.
Không biến nó thành cuộc săn người có lỗi.
---
59.33. AI Judge: một kế hoạch deploy prompt mới an toàn
Giả sử ta muốn release prompt chấm tự luận mới.
Một kế hoạch thực dụng:
1. Version prompt mới rõ ràng.
2. Chạy evaluation offline trên bộ bài mẫu.
3. Deploy code hỗ trợ cả prompt cũ và mới.
4. Mặc định feature flag tắt.
5. Shadow 1% bài thật, không dùng kết quả shadow.
6. So sánh score/feedback/latency/cost/needs_review.
7. Nếu ổn, canary 1% official traffic.
8. Theo dõi SLO time_to_grade, cost/submission, override rate.
9. Tăng dần 5%, 25%, 50%, 100%.
10. Có ngưỡng rollback rõ.
11. Giữ prompt cũ đủ lâu để rollback.
12. Sau khi ổn định, dọn flag và prompt cũ nếu không cần.
Kế hoạch này có vẻ dài.
Nhưng không phải bước nào cũng nặng.
Nhiều bước có thể tự động hóa.
Điểm chính là:
Không dùng user thật làm thí nghiệm mù.
Không bật toàn bộ khi chưa biết tác động.
Không bỏ đường quay lại quá sớm.
---
59.34. AI Judge: một kế hoạch migration score an toàn
Giả sử ta muốn đổi dữ liệu điểm:
Từ:
{
"score": 8.5
}
Sang:
{
"final_score": 8.5,
"confidence": 0.82,
"rubric_breakdown": []
}
Cách an toàn:
1. Thêm column/field mới, không xóa score.
2. Code ghi cả score và final_score.
3. API vẫn trả score cho consumer cũ.
4. Backfill final_score cho dữ liệu cũ theo batch.
5. Consumer mới đọc final_score, fallback score.
6. Contract test đảm bảo consumer cũ chưa bị phá.
7. Theo dõi số request/consumer còn dùng score.
8. Sau thời gian deprecation, chuyển API version mới.
9. Khi chắc chắn không ai dùng score, mới dọn field cũ.
Nếu lỗi xảy ra ở bước 5, ta vẫn có score cũ.
Nếu backfill làm database chậm, ta pause.
Nếu consumer chưa sẵn sàng, ta chưa xóa field.
Đây là deploy dữ liệu có đường thở.
---
59.35. Những sai lầm phổ biến
Sai lầm thứ nhất:
Deploy và release cùng một lúc cho toàn bộ user.
Với thay đổi rủi ro, điều này làm lỗi lan rất nhanh.
Sai lầm thứ hai:
Không có rollback plan.
Đến lúc lỗi mới phát hiện quay lại khó hơn đi tiếp.
Sai lầm thứ ba:
Migration phá backward compatibility.
Code cũ và mới không thể chạy cùng nhau.
Sai lầm thứ tư:
Canary nhưng không có metric và ngưỡng dừng.
Đó là canary hình thức.
Sai lầm thứ năm:
Shadow traffic tạo side effect thật.
Ví dụ gửi email, ghi điểm, gọi webhook, hoặc tăng cost quá mức mà không kiểm soát.
Sai lầm thứ sáu:
Backfill lớn chạy không quan sát.
Database chậm nhưng không ai biết vì job chỉ là script "chạy một lần".
Sai lầm thứ bảy:
Feature flag không được dọn.
Codebase tích tụ nhánh cũ, làm thay đổi sau nguy hiểm hơn.
Sai lầm thứ tám:
Deploy thay đổi lớn ngay trước thời điểm nghiệp vụ quan trọng.
Đây là cách mời rủi ro vào lúc khó xử lý nhất.
---
59.36. Checklist deploy an toàn
Trước khi deploy thay đổi quan trọng, hãy hỏi:
- Thay đổi này có feature flag không?
- Deploy và release có tách nhau không?
- Có thể bật cho nhóm nhỏ không?
- Có canary/progressive rollout không?
- Metric kỹ thuật cần nhìn là gì?
- Metric nghiệp vụ cần nhìn là gì?
- Ngưỡng nào thì dừng rollout?
- Có dashboard/alert không?
- Có deploy marker không?
- Rollback code bằng cách nào?
- Rollback data có cần không?
- Code cũ đọc được dữ liệu mới không?
- Code mới đọc được dữ liệu cũ không?
- Migration có expand-contract không?
- Backfill có batch, pause, resume, metric không?
- Shadow/parallel run có side effect thật không?
- Có smoke test sau deploy không?
- Có owner theo dõi trong lúc rollout không?
- Có kill switch không?
- Feature flag sẽ được dọn khi nào?
- Có tránh thời điểm traffic/business critical không?
Nếu nhiều câu trả lời là "không biết", deploy này chưa thật sự an toàn.
---
59.37. Bảng nhìn nhanh
| Kỹ thuật | Dùng để làm gì | |---|---| | Feature flag | Bật/tắt hành vi mà không cần deploy lại | | Canary | Cho một phần nhỏ traffic dùng version mới trước | | Progressive rollout | Mở rộng release từng bước có quan sát | | Blue-green | Chuyển traffic giữa hai môi trường production | | Rolling deploy | Thay instance dần, cần version cũ/mới tương thích | | Shadow traffic | Gửi bản sao traffic thật sang hệ thống mới, không dùng kết quả | | Parallel run | Chạy cũ và mới song song để so sánh | | Rollback | Quay lại version/hành vi cũ | | Roll forward | Sửa bằng version mới hơn khi rollback không phù hợp | | Expand-contract | Migration nhiều pha để giữ tương thích | | Backfill | Điền dữ liệu mới cho dữ liệu cũ | | Kill switch | Tắt nhanh phần gây hại | | Smoke test | Kiểm tra nhanh đường sống chính sau deploy |
---
59.38. Kết luận của chương
Deploy an toàn là một phần cốt lõi của kiến trúc hệ thống hiện đại.
Không thể chỉ dựa vào test rồi hy vọng production sẽ giống staging.
Feature flag giúp tách deploy khỏi release.
Canary và progressive rollout giúp lỗi xuất hiện ở phạm vi nhỏ.
Shadow traffic và parallel run giúp học từ traffic thật mà không ảnh hưởng trực tiếp user.
Rollback và kill switch giúp giảm thiệt hại khi có lỗi.
Migration an toàn giúp code cũ và mới cùng tồn tại trong giai đoạn chuyển đổi.
Observability giúp ta biết rollout đang tốt hay xấu.
Thông điệp cần nhớ:
> Đừng deploy như nhảy khỏi vách đá rồi mong đáp xuống êm. Hãy deploy như đi qua cầu có lan can: đi từng đoạn, nhìn tín hiệu, có điểm quay lại, và không phá cây cầu phía sau khi chưa sang hẳn.
Ở chương tiếp theo, ta sẽ bước sang phần kiến trúc ứng dụng AI: AI app khác app truyền thống ở đâu, vì sao output không deterministic, cách quản lý prompt/model/context/evaluation/cost/latency, và vì sao AI cần một lớp kiến trúc riêng chứ không chỉ là gọi thêm một API.