Sau một thời gian lập trình, bạn sẽ dần nhận ra một điều: Build một ứng dụng là một chuyện khá khó.
Thế nhưng, khi ứng dụng đã bắt đầu có người sử dụng (lên production), ta sẽ gặp phải nhiều vấn đề còn … khó hơn nữa:
- Làm sao thêm tính năng, sửa lỗi mà không ảnh hưởng đến ứng dụng đang chạy
- Làm sao để những tính năng mới, bug fix có thể được release nhanh chóng đến tay người dùng
- Khi lượng người dùng tăng gấp 5, gấp 10 lần, làm sao để có thể nhanh chóng scale hệ thống
- Làm sao để mấy bạn developer mới gia nhập có thể dễ dàng chạy ứng dụng ở local, test và push code
Đây là những vấn đề làm đau đầu nhiều team, vì nó đòi hỏi không chỉ kiến thức lập trình, mà còn là kiến thức về system architecture, operation, qui trình….
Do vậy, trong bài này, mình sẽ chia sẻ về Twelve-Factor App (12factor.net). Đây là 12 yếu tố cần thiết để xây dựng 1 ứng dụng “xịn xò”, ổn định, dễ mở rộng, dễ deploy nhé.
Đây là phần 1 trong series 3 phần về Twelve-Factor App:
- Lược dịch và giải thích Twelve-Factor. Giải thích Codebase và Dependencies
- Giải thích Config, Backing Service, Build -> Release -> Run, Processes, Port Binding
- Giải thích Concurrency, Disposability, Dev/Prod Parity, Logs, Admin Processes
Twelve-Factor App này có cái gì hay ho?
Đây là những kinh nghiệm được tổng hợp từ các developer đã tham gia phát triển về deploy, vận hành vài trăm app trên nền tảng Heroku. Bạn có thể xem 12 yếu tố này là cách để build 1 web app dễ mở rộng, dễ deploy, dễ tiếp cận cho developer mới gia nhập luôn.
Cá nhân mình thấy những kinh nghiệm này khá hữu ích, nó giải quyết được khá nhiều vấn đề chúng ta thực sự gặp phải khi xây dựng, vận hành 1 ứng dụng web.
Bản thân mình khuyên các bạn nên đọc bản gốc tiếng Anh trước (12factor.net). Mình sẽ trình bày lại 12 yếu tố này theo cách hiểu của mình, đưa thêm 1 số ví dụ thực tế nhé!

12 yếu tố này bao gồm:
- Codebase: Một codebase nằm trong source control, deploy lên nhiều nơi
- Dependencies: Depedencies như library/framework/extension phải được ghi rõ ràng, tách biệt từng app
- Config: Lưu trữ thiết lập vào biến môi trường (environment variable)
- Backing services: Xem các service đi kèm (database, API, …) như là resource của app
- Build, release, run: Tách riêng quá trình release, build và run
- Processes: App nên chạy dưới dạng stateless processes
- Port binding: Một service có thể được access thông qua 1 port cố định
- Concurrency: Một app nên được chia tách thành nhiều process nhỏ để tăng concurrency
- Disposability: Process của web app nên sống nhanh, chết vội, để có thể dễ dàng chạy/kill process nhanh chóng
- Dev/prod parity: Các môi trường dev/staging/production nên giống nhau hết sức có thể
- Logs: Logs nên được viết ra dạng stream ở stdout
- Admin Processes: Một số task dạng admin (tạo database, fix dữ liệu) nên được chạy trong cùng môi trường với app đang chạy
Nếu các bạn đọc qua thấy không hiểu cũng không sao. Mình sẽ giải thích kĩ hơn và đưa 1 số ví dụ cụ thể cho từng yếu tố nhé.
Có 1 số yếu tố mình không đồng tình, hoặc không phù hợp khi áp dụng vào các ngôn ngữ như C#, Java, nên mình cũng sẽ giải thích và chia sẻ thêm.
1. Codebase: Một source code trong source control, có nhiều deploy
Mỗi app chỉ nên có 1 source code (codebase) duy nhất, nằm trong source control như Git/SVN.
Mỗi khi code được build và chạy trên một môi trường nào đó, ta gọi nó là một bản deploy.
Code chạy trên môi trường Production gọi là Production deploy, chạy trên Staging gọi là Staging deploy. Deploy trên staging có thể có nhiều commit hơn, nằm ở branch khác Production, nhưng 2 code này đều nằm trong 1 source control.

Nhờ vậy, khi có developer mới gia nhập, ta có thể dễ dàng lấy và đọc source code từ source control.
Gần đây, khi kiến trúc microservice đang thịnh hành, ta có thể dùng monorepo – 1 repo chứa toàn bộ source code của các service. Hoặc có thể coi mỗi service là 1 app, lưu source code của service đó vào 1 repo riêng.
2. Dependencies: Dependencies rõ ràng, tách biệt
Cho các bạn chưa biết, depedencies tức là các package/thư viện/tool của bên thứ 3, mà app của bạn cần có để chạy được.
Một app cần phải thiết lập rõ ràng những dependency mà nó sử dụng, không lệ thuộc vào các dependency có sẵn của hệ thống. Các dependencies này phải tách biệt trong từng app.
Giả sử thế này, bạn đang viết ứng dụng nhận dạng JAV Idol, sử dụng thư viện IdolRec ver 2.1. Thằng bạn của bạn cũng viết ứng dụng nhận diện EU Idol, sử dụng IdolRec ver 1.3:
- Cách thức đúng ở đây là: app sẽ ghi rõ mình dùng thư viện IdolRec + version. Thư viện này sẽ bỏ vào thư mục của app, chạy trong app.
- Nếu ứng dụng phụ thuộc vào dependencies ngầm trong máy, sẽ dễ dẫn đến tình trạng “It works on my machine” => Chạy được trên máy của dev, nhưng lên Production thì tèo…
- Nếu không ghi rõ, tách biệt dependeices, khi 2 ứng dụng chạy trên 1 máy sẽ bị conflict vì 2 thư viện khác nhau. Hoặc có thể … không chạy được vì máy kia chưa xài IdolRec
Hiện tại, đa phần các ngôn ngữ lập trình đều có cách package manager hỗ trợ chuyện này (NodeJS thì lưu dependencies vào file package.json, Ruby thì có bundler lưu depedencies vào gemfile, C# thì lưu vào Web.config hoặc App.config ….).

Nhờ vậy, khi ta deploy ứng dụng lên server, hoặc developer mới gia nhập pull code về, ta chỉ cần chạy npm install hoặc bundle install để tải thư viện về, là ứng dụng có thể chạy bình thường, không cần cài dependencies ngầm.
Tạm kết
Bài này đã dài rồi nên mình tạm kết thúc ở đây! Mình sẽ tiếp tục giải thích các yếu tố còn lại ở những phần sau nhé.
Hay quá a 👍👍
LikeLike