Tách bạch chặt chẽ giữa build, release và runtime
- Theo Twelve-Factor, một codebase đi tới một deploy không-production qua 3 giai đoạn riêng: build biến source code thành một executable bundle; release lấy build đó và kết hợp với config hiện tại của deploy; còn run là giai đoạn khởi chạy các process của app từ một release đã chọn.
- Twelve-Factor yêu cầu tách bạch nghiêm ngặt ba giai đoạn này. Hệ quả rất quan trọng là bạn không được sửa code khi app đang chạy, vì mọi thay đổi hợp lệ phải quay lại build stage.
- Mỗi release nên có release ID duy nhất, phải là append-only và không được mutate sau khi tạo; nếu cần đổi gì, bạn tạo release mới. Deployment tools cũng thường hỗ trợ rollback về release trước đó.
- Twelve-Factor còn nhấn mạnh: run stage nên có càng ít moving parts càng tốt, vì runtime có thể tự khởi động lại vào lúc không có developer trực ca; ngược lại, build stage có thể phức tạp hơn vì lỗi build thường xảy ra khi developer đang chủ động deploy.
1) Mở bài: vì sao factor này cực kỳ thực chiến?
Một trong những dấu hiệu rõ nhất của một hệ thống “mong manh” là deploy kiểu thủ công: SSH vào server, pull code, sửa tay vài file, chạy migration, restart service, rồi cầu mong mọi thứ ổn. Cách làm đó khiến app rất khó truy vết, rollback khó, và production dễ trở thành nơi chứa những thay đổi “không ai nhớ đã sửa lúc nào”. Factor V sinh ra để chặn đúng kiểu hỗn loạn này: mọi deploy phải đi qua một đường ống rõ ràng gồm build → release → run.
2) Định nghĩa gốc của Factor V
Tên chính thức của factor này là:
“Build, release, run” — “Strictly separate build and run stages.”
Tài liệu gốc mô tả rất cụ thể:
- Build stage: chuyển code repo thành một executable bundle, dựa trên phiên bản code tại commit mà quy trình deploy chỉ định; ở bước này hệ thống fetch dependencies, compile binaries và assets.
- Release stage: lấy build vừa tạo và kết hợp với config hiện tại của deploy để tạo ra một release sẵn sàng chạy ngay.
- Run stage: chạy app trong execution environment bằng cách khởi động một tập các process của app từ một selected release.
Nói ngắn gọn:
Build tạo ra artifact có thể chạy.
Release gắn artifact đó với config của từng deploy.
Run chỉ tập trung vào việc khởi chạy app từ release đã có.
3) Factor này thật sự muốn giải quyết vấn đề gì?
Factor V giải quyết 4 vấn đề rất phổ biến.
a) Không truy được “thứ gì đang chạy”
Nếu không có ranh giới build/release/run, team thường không trả lời chắc được production đang chạy từ commit nào, cấu hình nào, và migration nào đã chạy. Twelve-Factor xử lý việc này bằng cách biến release thành một thực thể rõ ràng, có ID riêng, có thể rollback.
b) Chỉnh tay trên production
Tài liệu gốc nêu thẳng: với một app Twelve-Factor, bạn không thể sửa code tại runtime, vì không có cách nào đẩy thay đổi đó quay ngược lại build stage. Tức là “hotfix bằng tay trên server” không còn là đường hợp lệ.
c) Runtime ôm quá nhiều việc
Run stage có thể tự xảy ra do server reboot hoặc process manager restart process bị crash. Vì vậy Twelve-Factor yêu cầu run stage càng ít moving parts càng tốt; nếu startup phải làm quá nhiều việc phức tạp, app sẽ dễ gãy vào đúng lúc khó xử nhất.
d) Deploy không tái lập được
Blog chính thức năm 2025 của Twelve-Factor mô tả hướng phát triển hiện đại là chuẩn hóa các mẫu deployment với build, release và run nhất quán, hỗ trợ cấu hình khai báo, pipeline tự động và reproducible deployments. Đây chính là phần mở rộng hiện đại của Factor V.
4) Hiểu đúng 3 giai đoạn
Build là gì?
Build là bước biến source code thành một thứ có thể chạy được. Theo tài liệu gốc, nó lấy code tại một commit cụ thể, kéo dependencies, rồi compile binaries và assets để tạo thành build artifact.
Release là gì?
Release không chỉ là “bản build mới”. Theo Twelve-Factor, release là build + config hiện tại của deploy. Đây là chỗ nhiều bạn mới hay bỏ qua. Build artifact có thể giống nhau, nhưng khi ghép với config khác nhau, bạn có các release khác nhau cho staging, production hoặc các deploy khác.
Run là gì?
Run là lúc execution environment khởi động các process từ một release đã chọn. Ở đây app chỉ nên tập trung vào việc chạy các process cần thiết, không biến runtime thành nơi “làm nốt các bước deploy còn dang dở”.
5) Ví dụ đời thường cực dễ hiểu
Hãy tưởng tượng bạn đang mở một quán cà phê.
- Build là chuẩn bị sẵn bột cà phê rang xay, ly, menu, máy móc — tức là tạo ra “bộ vận hành” hoàn chỉnh.
- Release là đem bộ đó tới một chi nhánh cụ thể và gắn với các thông tin của chi nhánh đó: giá bán, số điện thoại, địa chỉ, chương trình khuyến mãi.
- Run là mở cửa quán và bắt đầu phục vụ khách.
Điều nguy hiểm là khi bạn đang mở quán rồi mới quay sang rang hạt, in menu, sửa máy pha, hoặc đổi công thức ngay tại quầy. Đó là kiểu lẫn lộn build/release/run mà Factor V muốn tránh. Ý này bám đúng logic tách giai đoạn của Twelve-Factor, chỉ là được diễn giải bằng ví dụ đời thường.
6) Ví dụ xuyên suốt với ứng dụng mẫu TaskFlow
Ta tiếp tục dùng ứng dụng mẫu TaskFlow, một SaaS quản lý công việc.
Build của TaskFlow
Đây là giai đoạn:
- lấy source code ở một commit cụ thể,
- cài dependencies,
- build frontend assets,
- compile code TypeScript hoặc đóng gói backend,
- tạo artifact để chạy.
Phần này khớp rất sát với định nghĩa build của Twelve-Factor: build stage lấy code ở commit đã chọn, fetch dependencies và compile binaries/assets.
Release của TaskFlow
Ở bước release, team ghép build đó với config của deploy:
DATABASE_URLREDIS_URLSMTP_URLAPP_BASE_URL- các API keys
- release metadata như release ID
Theo định nghĩa gốc, release chính là build cộng với config hiện tại của deploy.
Run của TaskFlow
Lúc run, hệ thống chỉ khởi động các process như:
webworkerscheduler
Đúng tinh thần Twelve-Factor, runtime chỉ dùng selected release để chạy app, thay vì tự build lại code hay sửa config trong lúc khởi động.
7) Release phải có ID riêng, không được “nặn lại”
Một ý rất quan trọng trong tài liệu gốc là: mọi release phải có unique release ID, ví dụ timestamp hoặc số tăng dần. Twelve-Factor còn nói release là một append-only ledger; một release đã tạo ra thì không được mutate nữa. Muốn thay đổi, bạn tạo release mới.
Ý này nghe có vẻ rất “kỷ luật”, nhưng lợi ích thực tế cực lớn:
- biết chính xác bản nào đang chạy,
- rollback được,
- không có cảnh “cùng tên version nhưng nội dung đã bị sửa tay”.
Deployment tools cũng thường hỗ trợ rollback; tài liệu gốc lấy ví dụ Capistrano lưu các release thành thư mục riêng và có lệnh rollback để quay về release trước.
8) Case thực tế đã được xác minh: Heroku release phase
Heroku là một ví dụ rất sát với tinh thần Factor V.
Tài liệu chính thức của Heroku cho biết release phase cho phép chạy một số tác vụ trước khi release mới được deploy, chẳng hạn:
- chạy database schema migrations,
- upload assets từ slug lên CDN hoặc S3,
- priming hoặc invalidating cache.
Điểm hay nhất là: nếu release phase thất bại, release mới sẽ không được deploy và release hiện tại vẫn không bị ảnh hưởng. Đây là minh họa rất rõ cho tư duy “release là một bước độc lập và có thể fail an toàn”.
Heroku còn nói rõ rằng release command chạy khi có:
- một build thành công,
- thay đổi config var,
- pipeline promotion,
- rollback,
- release qua API,
- provision add-on mới. Và app dynos chỉ boot sau khi release phase hoàn tất thành công.
Đây là một ví dụ thực tế rất tốt để junior thấy rằng release không chỉ là “ấn deploy”, mà là một giai đoạn riêng có kiểm soát.
9) Một ví dụ hiện đại: container image như build artifact
Twelve-Factor không bắt buộc dùng container, nhưng trong workflow hiện đại, nhiều team dùng container image như build artifact. Docker docs mô tả Docker Build là hệ sinh thái công cụ để build và package software để chạy ở local hoặc cloud; còn tài liệu “Build, tag, and publish an image” giải thích rõ ba việc riêng: build image, tag image và publish image lên registry.
Vì vậy, trong bối cảnh hiện đại, bạn có thể hiểu như sau:
- Build: tạo image từ source.
- Release: gắn image đó với config của deploy, release ID và các bước release cần thiết.
- Run: khởi chạy container từ release đã chọn.
Đây là cách diễn giải hiện đại rất tự nhiên của Factor V, nhưng cần phân biệt rõ: nội dung gốc của Twelve-Factor chỉ yêu cầu tách build, release và run; nó không bắt buộc image, registry hay Docker.
10) Junior thường hiểu sai ở đâu?
Hiểu sai 1: “Build xong là release”
Chưa đủ. Theo Twelve-Factor, release = build + config hiện tại của deploy. Build chỉ là artifact; release mới là thứ sẵn sàng để chạy trong một deploy cụ thể.
Hiểu sai 2: “Runtime startup có thể làm gì cũng được”
Không nên. Tài liệu gốc nói runtime nên có càng ít moving parts càng tốt, vì nó có thể tự chạy lại giữa đêm do reboot hoặc process restart.
Hiểu sai 3: “Sửa nóng trên server cho nhanh”
Đi ngược hẳn tinh thần Twelve-Factor. Tài liệu gốc nêu rõ không có chuyện thay đổi code hợp lệ ở runtime rồi coi như xong, vì thay đổi đó không quay về build stage.
Hiểu sai 4: “Migrations cứ để app boot lên rồi chạy luôn”
Đây không phải lúc nào cũng sai tuyệt đối trong mọi hệ thống, nhưng là một lựa chọn cần rất cẩn trọng. Heroku khuyến nghị dùng release phase cho schema setup và migrations; nếu release phase thất bại thì release mới không deploy. Docs của Heroku cũng lưu ý dùng transaction cho migrations và chú ý chuyện concurrent releases có thể gây va chạm migration.
11) Làm sai thì hậu quả gì?
- Không rollback an toàn được, vì không có khái niệm release rõ ràng và immutable.
- Runtime dễ gãy, vì startup phải làm quá nhiều việc ngoài chuyện khởi chạy app.
- Production drift xuất hiện, do người ta sửa tay trên server hoặc mutate artifact sau khi deploy. Điều này đi ngược trực tiếp với yêu cầu append-only release và không sửa code ở runtime.
- Release tasks lẫn với build tasks gây lỗi khó hiểu. Heroku nói rõ asset compilation nên xảy ra trong build; release phase không phù hợp cho các tác vụ cần file system persistence vì filesystem của dyno là ephemeral.
12) Áp dụng đúng trong team như thế nào?
Bước 1: Chuẩn hóa build thành một bước tái lập được
Build phải lấy code ở commit xác định, kéo dependency, compile assets/binaries và sinh ra artifact nhất quán. Đó là định nghĩa gốc của build stage.
Bước 2: Xem release là một bước riêng
Đừng gộp release vào runtime một cách mơ hồ. Release nên là lúc build được ghép với config hiện tại của deploy, gán release ID, và chạy các release tasks phù hợp nếu cần.
Bước 3: Giữ run stage thật mảnh
Run chỉ nên làm việc cần thiết để khởi động các process của app. Twelve-Factor nói rõ đây là nơi cần ít moving parts nhất.
Bước 4: Biến release thành immutable record
Mỗi release cần có ID duy nhất và không sửa lại sau khi tạo. Nếu cần đổi code hoặc config, tạo release mới.
Bước 5: Thiết kế rollback như một thao tác bình thường
Vì deployment tools thường hỗ trợ rollback, team nên coi rollback là một capability mặc định chứ không phải “kế hoạch B hiếm khi dùng”. Tư duy này bám trực tiếp vào phần release management của tài liệu gốc.
13) Diễn giải trong bối cảnh hiện đại
Blog chính thức năm 2025 của Twelve-Factor nói khá rõ rằng trong nền tảng cloud-native hiện đại, một trong những hướng quan trọng là chuẩn hóa build, release và run, hỗ trợ cấu hình khai báo, pipeline tự động, reproducible deployments, secure isolation và granular access trong toàn bộ quy trình.
Điều đó có nghĩa là ngày nay bạn có thể hiện thực hóa Factor V bằng:
- CI build ra artifact hoặc image,
- registry lưu artifact,
- CD tạo release với config cụ thể,
- runtime chỉ kéo release đã chọn và khởi động process.
Nhưng cần nhớ: đây là lớp diễn giải hiện đại. Nội dung gốc của Factor V vẫn chỉ là tách bạch build, release và run một cách nghiêm ngặt.
14) Checklist tự đánh giá
Hãy thử tự hỏi:
- Team có phân biệt rõ build artifact với release không?
- Mỗi release có ID duy nhất và immutable không?
- Có thể rollback về release trước không?
- Runtime startup có đang ôm quá nhiều việc như compile asset, sửa file, tạo code tạm, hay migration phức tạp không?
- Có ai vẫn SSH vào production để sửa tay nội dung release không?
Nếu có từ 2 câu trở lên khiến bạn thấy “hơi mơ hồ”, thì Factor V của hệ thống đó đang chưa khỏe.
15) Tóm tắt nhanh
- Build tạo artifact từ source code, dependencies và các bước compile cần thiết.
- Release lấy build đó ghép với config hiện tại của deploy.
- Run chỉ khởi chạy app từ selected release.
- Release phải có ID duy nhất, là append-only, và không mutate.
- Runtime càng đơn giản càng tốt; những việc như migration hay cache warmup nên được cân nhắc ở release phase phù hợp thay vì nhét bừa vào startup.