Chương 40. Production khác localhost như thế nào?

Từ chương này, ta bước sang phần vận hành, deploy và hạ tầng.

Đây là phần rất quan trọng.

Vì một hệ thống không chỉ cần:

Code chạy được.

Nó còn cần:

Chạy được khi có người dùng thật.
Chạy được khi lỗi xảy ra.
Chạy được sau deploy.
Chạy được khi server restart.
Chạy được khi database chậm.
Chạy được khi mạng chập chờn.
Không làm mất dữ liệu thật.
Không làm chi phí tăng vô lý.

Thông điệp chính của chương:

> Localhost chứng minh code có thể chạy. Production chứng minh hệ thống có thể sống.

---

40.1. Ví dụ dễ hiểu: nấu ăn ở nhà và mở nhà hàng

Nấu một món ăn ở nhà khác rất xa với mở nhà hàng.

Ở nhà:

Bạn nấu cho 1-2 người.
Sai thì nấu lại.
Thiếu nguyên liệu thì đi mua.
Chậm 10 phút không sao.

Nhà hàng:

50 khách đến cùng lúc.
Nguyên liệu phải chuẩn bị trước.
Nhân viên có thể nghỉ.
Bếp có thể hỏng.
Khách không muốn chờ.
Sai món là mất uy tín.
Thực phẩm hỏng là mất tiền.

Localhost giống nấu ở nhà.

Production giống mở nhà hàng.

Cùng là "nấu được món đó", nhưng yêu cầu hoàn toàn khác.

---

40.2. Localhost là môi trường tha thứ

Trên localhost:

  • Chỉ có bạn dùng.
  • Dữ liệu thường là giả.
  • Lỗi thì refresh.
  • Database nhỏ.
  • File có thể nằm trong máy.
  • Không ai phụ thuộc.
  • Không có traffic thật.
  • Không có hacker thật.
  • Không có hóa đơn cloud thật.

Bạn có thể:

Xóa database.
Restart server.
Sửa config.
Chạy migration lại.
In log lung tung.
Hardcode secret tạm.

Vì hậu quả nhỏ.

Production thì không tha thứ như vậy.

---

40.3. Production có người dùng thật

Trong production, có nhiều người dùng cùng lúc.

Họ có thể:

  • Login cùng lúc.
  • Nộp bài cùng lúc.
  • Upload file cùng lúc.
  • Refresh liên tục.
  • Bấm retry.
  • Mở nhiều tab.
  • Mạng yếu.
  • Đóng tab giữa chừng.
  • Gửi request trùng.
  • Làm đúng thứ bạn không ngờ.

Một flow chạy tốt với một user trên localhost chưa chắc chạy tốt với 1.000 user.

Ví dụ AI Judge:

Một học sinh nộp bài:
  ổn.

2.000 học sinh nộp bài lúc 23:55 trước deadline:
  câu chuyện khác.

Production là nơi concurrency và traffic thật lộ ra.

---

40.4. Production có dữ liệu thật

Dữ liệu production không phải thứ muốn xóa là xóa.

Nó có thể là:

  • Tài khoản người dùng.
  • Bài nộp.
  • Điểm.
  • Feedback.
  • File.
  • Thanh toán.
  • Quyền truy cập.
  • Audit log.
  • Lịch sử học tập.

Nếu migration lỗi làm mất dữ liệu, không thể nói:

Chạy lại seed là được.

Nếu file bị mất, user có thể mất bài nộp.

Nếu điểm bị ghi sai, hậu quả nghiệp vụ thật.

Vì vậy production cần:

  • Backup.
  • Migration cẩn thận.
  • Rollback plan.
  • Audit.
  • Permission.
  • Monitoring.
  • Quy trình deploy.

---

40.5. "Chạy được" chưa đủ

Một app có thể chạy được nhưng vẫn chưa production-ready.

Ví dụ:

Chạy được nếu chỉ có một user.
Chạy được nếu database luôn khỏe.
Chạy được nếu không ai upload file lớn.
Chạy được nếu external API không timeout.
Chạy được nếu server không restart.
Chạy được nếu deploy không lỗi.

Production hỏi nhiều hơn:

Nếu một phần lỗi thì sao?
Nếu traffic tăng thì sao?
Nếu deploy giữa lúc có user thì sao?
Nếu worker chết thì job có mất không?
Nếu database chậm thì request có treo không?
Nếu AI provider timeout thì user thấy gì?

Đây là khác biệt giữa code chạy và hệ thống vận hành.

---

40.6. Nhiều người dùng cùng lúc

Trên localhost, bạn thường test tuần tự:

Click.
Wait.
Click.
Wait.

Production thì nhiều request đến song song.

Vấn đề có thể xuất hiện:

  • Race condition.
  • Duplicate submission.
  • Double payment.
  • Job bị claim hai lần.
  • File upload dở.
  • Database lock.
  • Connection pool hết.
  • Worker backlog.

Ví dụ:

User bấm "Nộp bài" hai lần rất nhanh.

Nếu backend không idempotent, có thể tạo hai submission hoặc hai grading job.

Concurrency không phải lý thuyết xa.

Nó xuất hiện ngay khi có nhiều người dùng hoặc user bấm nhanh.

---

40.7. Lỗi mạng là bình thường

Trên localhost, mạng gần như hoàn hảo.

Production thì không.

Request có thể:

  • Timeout.
  • Mất kết nối giữa chừng.
  • Retry.
  • Đến server nhưng response mất.
  • Client không biết server đã xử lý hay chưa.
  • External API chậm.
  • DNS lỗi.
  • TLS handshake lỗi.

Ví dụ:

Student upload bài.
Upload xong nhưng mạng mất trước khi browser nhận response.
Student bấm upload lại.

Nếu hệ thống không xử lý trùng lặp, dữ liệu có thể rối.

Vì vậy production cần:

  • Timeout.
  • Retry có kiểm soát.
  • Idempotency.
  • Trạng thái rõ ràng.
  • UI hiển thị pending/processing.
  • Không giả định request chỉ chạy đúng một lần.

---

40.8. Server restart là chuyện bình thường

Production server có thể restart vì:

  • Deploy.
  • Crash.
  • Autoscaling.
  • Node bị thay.
  • Memory leak.
  • Host maintenance.
  • Container bị kill.
  • Health check fail.

Nếu app lưu state quan trọng trong RAM, restart là mất.

Ví dụ xấu:

Danh sách job pending nằm trong memory.
Server restart.
Job mất.

Đúng hơn:

Job nằm trong queue/database.
Worker chết thì job retry.

Production cần giả định:

> Process có thể chết bất cứ lúc nào.

State quan trọng phải nằm ở nơi bền hơn process.

---

40.9. File local có thể biến mất

Trên localhost, lưu file vào thư mục local thấy ổn.

Production thì khác.

Nếu chạy container:

Container bị replace.
File trong container mất.

Nếu có nhiều server:

User upload vào server A.
Request download vào server B.
Server B không có file.

Nếu disk đầy:

Upload lỗi.
App crash.

Vì vậy file production thường nên nằm ở:

Object storage.

Không nên để file user upload quan trọng phụ thuộc vào filesystem tạm của app server.

---

40.10. Database quá tải

Database trên localhost có vài dòng.

Production có thể có:

  • Hàng triệu row.
  • Nhiều query đồng thời.
  • Index lớn.
  • Transaction lock.
  • Connection pool.
  • Query dashboard nặng.
  • Backup đang chạy.
  • Migration đang chạy.

Một query chạy nhanh trên data nhỏ có thể rất chậm trên data thật.

Ví dụ:

SELECT * FROM grading_results ORDER BY created_at DESC

không filter, không pagination, trên hàng chục triệu row.

Production cần:

  • Index đúng.
  • Pagination.
  • Slow query monitoring.
  • Connection pool.
  • Transaction ngắn.
  • Backup/maintenance.
  • Read replica/warehouse khi cần.

Database là trái tim của nhiều hệ thống.

Đừng chỉ test với 20 row fake.

---

40.11. External service sẽ lỗi

Production thường gọi dịch vụ bên ngoài:

  • AI API.
  • Email provider.
  • Payment provider.
  • Object storage.
  • OAuth provider.
  • SMS provider.
  • Analytics tool.

Các dịch vụ này có thể:

  • Timeout.
  • Rate limit.
  • Trả lỗi 500.
  • Chậm bất thường.
  • Thay đổi quota.
  • Bảo trì.
  • Tăng latency theo vùng.

Ví dụ AI Judge:

Gemini API mất 90 giây.
Một số request timeout.
Một số request bị rate limit.

Nếu hệ thống giữ request web chờ suốt, user sẽ thấy app treo.

Đúng hơn:

Tạo job.
Worker gọi AI.
Retry có kiểm soát.
User thấy trạng thái processing.

Production cần thiết kế cho external failure.

---

40.12. Deploy có thể làm hỏng hệ thống

Deploy không chỉ là copy code mới.

Deploy có thể:

  • Làm app crash.
  • Làm migration lỗi.
  • Làm schema không khớp code.
  • Làm worker cũ và code mới xử lý job khác format.
  • Làm cache key thay đổi.
  • Làm environment variable thiếu.
  • Làm traffic bị ngắt.
  • Làm file/static asset mismatch.

Trên localhost, bạn restart app sau khi code và database cùng đổi.

Production thì có thể có:

Server cũ và server mới chạy cùng lúc.
Worker cũ đang xử lý job.
Request vẫn đến trong lúc deploy.

Vì vậy deploy cần kế hoạch.

---

40.13. Migration production phải cẩn thận

Migration database là vùng nguy hiểm.

Ví dụ:

ALTER TABLE large_table ADD COLUMN NOT NULL DEFAULT ...

trên bảng lớn có thể lock lâu tùy database/cách làm.

Hoặc:

Rename column.

Code cũ vẫn cần column cũ, code mới cần column mới.

Production-safe migration thường chia bước:

1. Add column nullable.
2. Deploy code ghi cả cũ và mới nếu cần.
3. Backfill dữ liệu.
4. Deploy code đọc cột mới.
5. Sau khi ổn, xóa cột cũ.

Không phải migration nào cũng cần nhiều bước.

Nhưng migration lớn cần nghĩ đến:

  • Lock.
  • Backward compatibility.
  • Rollback.
  • Data backfill.
  • Worker cũ.
  • Read replica lag.

---

40.14. Rollback không phải lúc nào cũng dễ

Nhiều người nghĩ:

Deploy lỗi thì rollback code.

Nhưng nếu deploy đã chạy migration làm đổi dữ liệu, rollback code có thể không đủ.

Ví dụ:

Code mới đổi format job payload.
Worker mới đã tạo job kiểu mới.
Rollback về worker cũ không đọc được job đó.

Hoặc:

Code mới ghi dữ liệu theo schema mới.
Code cũ không hiểu schema đó.

Rollback cần được thiết kế.

Các kỹ thuật:

  • Backward-compatible migration.
  • Feature flag.
  • Deploy theo từng bước.
  • Không xóa field ngay.
  • Versioned payload.
  • Backup trước migration nguy hiểm.

Production không thích những cú đổi một phát ăn ngay.

---

40.15. Config production khác config local

Localhost thường dùng:

DEBUG=true
SQLite/local PostgreSQL
local file storage
fake email
fake payment
small worker count

Production cần:

DEBUG=false
real database
real object storage
real email/payment/AI provider
HTTPS
secret manager
proper logging
monitoring
backup
rate limit

Lỗi phổ biến:

Quên set env var.
DEBUG=true trên production.
CORS mở quá rộng.
Cookie không Secure.
Log quá nhiều dữ liệu nhạy cảm.

Config là một phần của hệ thống.

Không phải chi tiết phụ.

---

40.16. Secret production không được giống local

Local có thể dùng secret giả.

Production secret phải được bảo vệ.

Ví dụ:

  • Database password.
  • JWT signing key.
  • OAuth client secret.
  • AI provider key.
  • Payment secret.
  • Webhook secret.
  • Object storage credential.

Không nên:

Commit .env production vào Git.
Share key qua chat.
Log key ra console.
Dùng cùng secret cho dev/staging/prod.

Production cần:

  • Secret manager hoặc cơ chế quản lý secret rõ.
  • Quyền đọc secret theo service.
  • Rotation plan.
  • Không log secret.
  • Tách dev/staging/prod.

---

40.17. Logging production khác print debug

Trên localhost, bạn có thể:

print("here")
print(user)
print(response)

Production cần log có cấu trúc và an toàn.

Log nên giúp trả lời:

  • Request nào lỗi?
  • User/tenant nào bị ảnh hưởng?
  • Service nào chậm?
  • Job nào fail?
  • External API trả gì?
  • Deploy nào gây lỗi?

Nhưng không nên log:

  • Password.
  • Token.
  • Secret.
  • Nội dung private không cần.
  • Dữ liệu học sinh nhạy cảm quá mức.

Log production cần:

  • Request id.
  • Timestamp.
  • Level.
  • Service name.
  • Error code.
  • Metadata vừa đủ.

Log là công cụ điều tra.

Không phải bãi rác text.

---

40.18. Monitoring: bạn không thể sửa thứ bạn không thấy

Nếu production lỗi mà không có monitoring, bạn chỉ biết khi user phàn nàn.

Cần theo dõi:

  • Uptime.
  • Error rate.
  • Latency.
  • Throughput.
  • CPU/RAM/disk.
  • Database connections.
  • Slow queries.
  • Queue backlog.
  • Worker failures.
  • External API timeout.
  • Cost.

Ví dụ AI Judge:

Queue pending tăng nhanh.
Worker active thấp.
Gemini latency tăng.
Job retry tăng.
Cost theo giờ tăng bất thường.

Nếu nhìn được sớm, bạn sửa sớm.

Nếu không, production thành hộp đen.

---

40.19. Alert không phải dashboard

Dashboard là nơi nhìn số.

Alert là thứ gọi bạn khi cần hành động.

Không nên alert mọi thứ.

Nếu alert quá nhiều, team sẽ bỏ qua.

Alert tốt nên gắn với triệu chứng user hoặc rủi ro thật:

  • Error rate cao.
  • API latency vượt ngưỡng.
  • Queue backlog quá lâu.
  • Database gần hết connection.
  • Disk gần đầy.
  • Job fail liên tục.
  • Payment webhook fail.
  • Chi phí AI tăng bất thường.

Alert nên trả lời:

Có cần người xử lý không?
Nếu có, xử lý bắt đầu từ đâu?

---

40.20. Backup là thứ chỉ biết có tốt không khi restore

Nhiều hệ thống nói:

Chúng tôi có backup.

Nhưng chưa từng restore thử.

Backup chưa test restore thì chưa chắc dùng được.

Production cần:

  • Backup database.
  • Backup hoặc versioning file quan trọng.
  • Backup config/secret theo cách an toàn.
  • Retention policy.
  • Restore drill.
  • Biết RPO/RTO.

RPO:

Có thể mất tối đa bao nhiêu dữ liệu?

RTO:

Cần khôi phục trong bao lâu?

Không cần quá hàn lâm.

Chỉ cần biết:

> Backup không phải để yên tâm. Backup là để restore được.

---

40.21. File bị mất

File production có thể mất vì:

  • Lưu local trong container.
  • Bucket bị xóa nhầm.
  • Lifecycle policy sai.
  • Key overwrite.
  • Upload incomplete.
  • Metadata DB còn nhưng object mất.
  • Object còn nhưng metadata DB mất.

Chương 33 đã nói:

Database giữ metadata.
Object storage giữ bytes.

Production cần đảm bảo hai thứ này khớp.

Có thể cần:

  • Object versioning.
  • Soft delete.
  • Cleanup job.
  • Integrity check.
  • Audit delete.
  • Backup/replication phù hợp.

File không tự nhiên an toàn chỉ vì đã upload xong.

---

40.22. Production có chi phí thật

Localhost không gửi hóa đơn.

Production có:

  • Server cost.
  • Database cost.
  • Storage cost.
  • Bandwidth cost.
  • AI API cost.
  • Email/SMS cost.
  • Logging/monitoring cost.
  • Data transfer cost.

Ví dụ:

Một bug retry làm gọi AI API 10 lần cho mỗi bài.

Code vẫn "chạy".

Nhưng tiền bay.

Vì vậy production cần:

  • Quota.
  • Rate limit.
  • Cost dashboard.
  • Alert chi phí.
  • Idempotency.
  • Retry có backoff và giới hạn.

Với AI product, cost control là một phần của reliability.

---

40.23. Production có bảo mật thật

Localhost ít bị scan.

Production public endpoint có thể bị bot scan ngay.

Bot có thể thử:

  • Login brute force.
  • SQL injection payload.
  • Path lạ.
  • Admin URL.
  • File upload độc.
  • SSRF.
  • Exposed env.
  • Swagger/docs.
  • Old framework vulnerability.

Vì vậy chương bảo mật trước không phải lý thuyết.

Production cần:

  • HTTPS.
  • Auth đúng.
  • Permission đúng.
  • Rate limit.
  • Input validation.
  • Dependency updates.
  • Secret management.
  • Audit log.
  • WAF nếu phù hợp.

Đưa app ra internet là bước chuyển lớn.

---

40.24. Staging dùng để làm gì?

Staging là môi trường giống production hơn local.

Mục tiêu:

  • Test deploy.
  • Test migration.
  • Test config.
  • Test integration thật hoặc gần thật.
  • Test worker/queue.
  • Test object storage.
  • Test email/payment sandbox.

Staging không bao giờ giống production 100%.

Nhưng tốt hơn localhost nhiều.

Nên có:

dev/local
staging
production

Mỗi môi trường có config/secret/database riêng.

Không dùng production data bừa bãi ở staging nếu chứa dữ liệu nhạy cảm.

---

40.25. Feature flag

Feature flag cho phép bật/tắt tính năng mà không cần deploy lại.

Ví dụ:

new_grading_pipeline = off

Deploy code mới nhưng chưa bật cho mọi user.

Sau đó:

Bật cho internal users.
Bật cho 5%.
Bật cho một trường.
Bật toàn bộ.

Nếu lỗi:

Tắt flag.

Feature flag giúp production an toàn hơn.

Nhưng flag cũng cần quản lý.

Flag cũ không dọn sẽ làm code rối.

---

40.26. Health check

Health check là endpoint hoặc cơ chế để biết service có khỏe không.

Ví dụ:

GET /health
GET /ready

Có hai khái niệm:

Liveness:

Process còn sống không?

Readiness:

Service đã sẵn sàng nhận traffic chưa?

Ví dụ app vừa start nhưng chưa kết nối database xong:

Liveness: ok
Readiness: not ready

Load balancer/orchestrator dùng health check để quyết định gửi traffic.

Health check tốt giúp deploy/restart an toàn hơn.

---

40.27. Graceful shutdown

Khi server bị stop, nó nên tắt một cách tử tế.

Graceful shutdown nghĩa là:

Ngừng nhận request mới.
Xử lý xong request đang chạy nếu có thể.
Đóng connection.
Flush log.
Worker trả job hoặc hoàn tất job an toàn.

Nếu shutdown thô bạo:

  • Request đang xử lý bị cắt.
  • Job bị mất nếu không ack đúng.
  • File upload dở.
  • Transaction dang dở.

Trong hệ thống worker:

Đừng ack job trước khi xử lý xong nếu không muốn mất job.

Graceful shutdown là chuyện production rất thực tế.

---

40.28. Timeout production phải rõ

Không có timeout nghĩa là request có thể treo rất lâu.

Cần timeout ở nhiều lớp:

  • Browser/client.
  • Reverse proxy.
  • App server.
  • Database query.
  • External API call.
  • Worker job.
  • Queue visibility timeout.

Ví dụ:

Proxy timeout 60s.
AI API call mất 90s.

Nếu gọi AI trong request web, user sẽ timeout.

Nếu xử lý bằng job nền, UI có thể hiển thị processing.

Timeout không phải chi tiết nhỏ.

Nó định hình kiến trúc.

---

40.29. Capacity planning đơn giản

Không cần dự đoán hoàn hảo.

Nhưng cần ước lượng:

Bao nhiêu user cùng lúc?
Mỗi user tạo bao nhiêu request?
Mỗi job mất bao lâu?
Worker xử lý được bao nhiêu job/phút?
Database chịu bao nhiêu connection?
File upload/download bao nhiêu GB?
AI API giới hạn RPM/TPM bao nhiêu?

Ví dụ AI Judge:

2.000 bài nộp trong 10 phút.
Mỗi bài gọi AI 90 giây.
Gemini quota 100 RPM.
Worker concurrency hiện tại 4.

Lúc này bottleneck có thể không phải Gemini.

Bottleneck có thể là worker concurrency.

Production cần tính sơ bộ như vậy.

Không cần thần bí.

Chỉ cần biến cảm giác thành con số.

---

40.30. Load test

Load test là thử hệ thống dưới tải giả lập.

Mục tiêu:

  • Biết hệ thống chịu được khoảng bao nhiêu.
  • Tìm bottleneck.
  • Xem latency tăng thế nào.
  • Xem database/worker/queue phản ứng ra sao.
  • Kiểm tra autoscaling nếu có.

Không phải lúc nào cũng cần load test phức tạp.

Nhưng trước deadline lớn hoặc launch quan trọng, nên thử:

Nhiều user login.
Nhiều submission.
Nhiều upload.
Nhiều job AI.
Nhiều dashboard view.

Load test tốt dùng dữ liệu gần thực tế.

Test với request quá đơn giản có thể đánh lừa.

---

40.31. Production cần runbook

Runbook là hướng dẫn xử lý sự cố.

Ví dụ:

Nếu queue backlog cao:
  xem worker còn chạy không
  xem external AI latency
  tăng worker nếu quota còn
  kiểm tra job lỗi lặp
  thông báo trạng thái cho user nếu cần

Runbook không cần dài từ đầu.

Nhưng nên có cho các lỗi hay gặp:

  • Database quá tải.
  • Worker chết.
  • Queue backlog.
  • Payment webhook fail.
  • Object storage lỗi.
  • Deploy lỗi.
  • AI API rate limit.
  • Chi phí tăng bất thường.

Khi sự cố xảy ra, đầu óc rất dễ rối.

Runbook giúp team hành động bình tĩnh hơn.

---

40.32. Incident là gì?

Incident là sự cố ảnh hưởng đến người dùng hoặc hệ thống.

Ví dụ:

  • App không truy cập được.
  • Login lỗi.
  • Nộp bài không tạo job.
  • Chấm bài chậm hàng loạt.
  • Điểm hiển thị sai.
  • File không tải được.
  • Dữ liệu tenant bị lộ.

Sau incident, nên có postmortem:

Chuyện gì xảy ra?
Ảnh hưởng ai?
Phát hiện bằng gì?
Khôi phục thế nào?
Nguyên nhân gốc là gì?
Sẽ làm gì để giảm khả năng lặp lại?

Mục tiêu không phải đổ lỗi.

Mục tiêu là hệ thống tốt hơn.

---

40.33. Production và dữ liệu test

Không nên dùng production như sân chơi test.

Tránh:

Tạo user test bừa bãi.
Chạy script thử trên dữ liệu thật.
Gọi API test làm gửi email thật.
Chạy migration tay không backup.

Nếu cần test production:

  • Dùng account test rõ ràng.
  • Có tenant sandbox.
  • Đảm bảo không gửi thông báo thật bừa bãi.
  • Có flag/toggle.
  • Log/audit.
  • Dọn dữ liệu test.

Production data có giá trị.

Đừng đối xử như database local.

---

40.34. Một ngày production xấu có thể diễn ra thế nào?

Ví dụ AI Judge:

23:50:
  Học sinh bắt đầu nộp bài trước deadline.

23:55:
  2.000 submission đến trong vài phút.

23:56:
  Worker concurrency chỉ có 4.
  Queue backlog tăng mạnh.

23:57:
  Một số job gọi AI timeout.
  Retry không có backoff, queue càng nặng.

23:58:
  Teacher dashboard query live vào database.
  Database CPU tăng.

23:59:
  Học sinh refresh liên tục vì chưa thấy kết quả.
  API traffic tăng.

00:00:
  Deadline đóng.
  Một số submission bị trùng do double click.

00:05:
  Không có dashboard queue rõ.
  Team chỉ biết qua tin nhắn user.

Đây là production.

Không phải vì code "sai" một chỗ.

Mà vì nhiều giả định nhỏ cùng vỡ.

---

40.35. Cùng ví dụ đó, thiết kế tốt hơn

Thiết kế tốt hơn:

Submission API idempotent.
Upload dùng object storage.
Nộp bài tạo job trong queue.
Worker concurrency được tính theo quota AI.
Retry có backoff và giới hạn.
Timeout rõ.
Dashboard đọc aggregate/replica.
User thấy trạng thái queued/processing.
Queue backlog có monitoring.
Alert khi wait time vượt ngưỡng.
Cost AI được theo dõi.

Khi deadline đến:

Hệ thống có thể chậm.
Nhưng không rối.
Không mất job.
Không tạo trùng vô hạn.
Team nhìn thấy bottleneck.
User hiểu trạng thái.

Production-ready không có nghĩa là không bao giờ chậm.

Nó nghĩa là khi chậm, hệ thống vẫn có trật tự.

---

40.36. Localhost vẫn rất quan trọng

Nói production khác localhost không có nghĩa localhost vô dụng.

Localhost tốt để:

  • Phát triển nhanh.
  • Debug logic.
  • Viết test.
  • Thử UI.
  • Chạy unit/integration test nhỏ.

Nhưng localhost không chứng minh:

  • Scale.
  • Reliability.
  • Security production.
  • Backup/restore.
  • Deploy safety.
  • Monitoring.
  • Multi-user concurrency.

Mỗi môi trường có vai trò riêng.

Đừng bắt localhost chứng minh điều nó không thể chứng minh.

---

40.37. Staging cũng không phải production

Staging giúp nhiều, nhưng vẫn khác production.

Khác ở:

  • Traffic ít hơn.
  • Dữ liệu khác.
  • User behavior khác.
  • Integration có thể là sandbox.
  • Scale nhỏ hơn.
  • Không có áp lực thật.

Vì vậy có lỗi chỉ xuất hiện ở production.

Điều này bình thường.

Quan trọng là production phải có:

  • Monitoring.
  • Alert.
  • Rollback/mitigation.
  • Logs.
  • Audit.
  • Runbook.

Đừng tin staging tuyệt đối.

Nhưng đừng bỏ staging.

---

40.38. Tư duy production-ready

Khi thiết kế một tính năng, hỏi:

  • Nếu request chạy hai lần thì sao?
  • Nếu worker chết giữa chừng thì sao?
  • Nếu external API timeout thì sao?
  • Nếu deploy giữa lúc job đang chạy thì sao?
  • Nếu file upload dở thì sao?
  • Nếu database chậm thì sao?
  • Nếu user không có quyền thì sao?
  • Nếu traffic tăng 10 lần thì nghẽn ở đâu?
  • Nếu cần rollback thì dữ liệu có tương thích không?
  • Nếu lỗi xảy ra, mình nhìn thấy bằng gì?

Đây là tư duy production.

Không phải bi quan.

Là thiết kế như người đã từng thấy hệ thống thật bị gió thổi.

---

40.39. Bảng chọn nhanh

| Khác biệt production | Cần nghĩ đến | |---|---| | Nhiều user cùng lúc | Concurrency, idempotency, lock, queue | | Mạng lỗi | Timeout, retry, trạng thái rõ | | Server restart | State bền vững, graceful shutdown | | File local mất | Object storage, metadata, backup | | Database lớn | Index, pagination, slow query, pool | | External API chậm | Job nền, retry/backoff, circuit breaker nếu cần | | Deploy có rủi ro | Migration an toàn, rollback, feature flag | | Dữ liệu thật | Backup, audit, permission | | Chi phí thật | Quota, rate limit, cost alert | | Security thật | HTTPS, secret, rate limit, dependency update | | Không thấy lỗi | Monitoring, logging, alert |

---

40.40. Tóm tắt bằng AI Judge

Trên localhost:

Một submission.
Một worker.
Một database nhỏ.
Một file local.
Một user test.

Production:

Hàng nghìn submission trước deadline.
Worker có thể chết.
AI API có thể timeout.
Queue có thể backlog.
Database có thể chậm.
File phải nằm bền vững.
Deploy có thể chạy giữa lúc job đang xử lý.
User refresh, retry, double click.
Chi phí AI tăng theo bug retry.
Teacher cần dashboard.
Admin cần audit.

Vì vậy AI Judge production cần:

Queue/worker đúng.
Object storage.
Idempotency.
Timeout/retry/backoff.
Monitoring queue/job/AI cost.
Database index/pagination.
Backup/restore.
Safe migration.
Secret management.
Rate limit/quota.
Audit log.

Chạy được trên máy mình là bước đầu.

Chạy được với người dùng thật mới là hệ thống.

---

40.41. Kết luận của chương

Production không phải localhost lớn hơn một chút.

Production là môi trường có:

  • Người dùng thật.
  • Dữ liệu thật.
  • Lỗi thật.
  • Chi phí thật.
  • Tấn công thật.
  • Deploy thật.
  • Hậu quả thật.

Thông điệp cần nhớ:

> Production-ready nghĩa là hệ thống vẫn có trật tự khi một phần của nó lỗi, chậm, restart, bị retry, hoặc bị dùng nhiều hơn dự kiến.

Không cần làm mọi thứ phức tạp từ ngày đầu.

Nhưng cần biết localhost chưa chứng minh đủ.

Ở chương tiếp theo, ta sẽ nói về reverse proxy và load balancer: request đi qua những lớp nào trước khi đến app, TLS termination là gì, health check dùng để làm gì, và khi nào một server là đủ hay cần scale ngang.