07. Deploy, Rollback & Observability — Vibe Coding Series

Deploy an toàn cho AI-generated code: feature flag, canary deployment, rollback nhanh với git tag, và observability để biết ngay khi có sự cố.

Deploy xong 10 phút, error rate tăng 300% — và tôi không biết AI đã thay đổi gì trong đống code đó.

Không phải vì code xấu. Không phải vì thiếu test. Mà vì tôi không có observability, không có rollback plan, và không biết chính xác những gì đã lên production. AI viết code nhanh, nhưng khi có sự cố, tôi đứng nhìn dashboard mà không biết bắt đầu từ đâu.

Đó là lần cuối tôi deploy mà không có chuẩn bị.


Bối cảnh

Ở bài 06, code đã pass automated QA, qua AI security review, và được human final check trước khi merge vào main. Bài này là bước cuối: đưa code lên production một cách có kiểm soát — và biết phải làm gì khi mọi thứ không như kế hoạch.


Deploy strategy cho AI-generated code

AI-generated code có một đặc điểm: nó thường hoạt động tốt trong test environment nhưng có thể có behavior bất ngờ ở production với real traffic. Lý do không phải vì AI viết code tệ hơn — mà vì AI không có context về production environment, edge case của real user, hay load pattern thực tế.

Chiến lược deploy phù hợp nhất là rollout từng phần.

Gradual rollout với feature flag — từ 5% đến 100%, có thể rollback ở bất kỳ bước nào

Feature flag: kiểm soát rollout

Feature flag cho phép deploy code lên production nhưng chỉ bật cho một nhóm user nhỏ trước. Nếu có vấn đề, tắt flag — không cần rollback code.

Với TaskFlow, tôi dùng một implementation đơn giản:

// lib/feature-flags.ts
const FLAGS: Record<string, { enabled: boolean; rolloutPercent: number }> = {
  "auth-v2": { enabled: true, rolloutPercent: 10 }, // 10% user
  "new-dashboard": { enabled: false, rolloutPercent: 0 },
};

export function isFeatureEnabled(flagName: string, userId: string): boolean {
  const flag = FLAGS[flagName];
  if (!flag?.enabled) return false;

  // Deterministic rollout dựa trên userId
  const hash = userId.split("").reduce((acc, c) => acc + c.charCodeAt(0), 0);
  return (hash % 100) < flag.rolloutPercent;
}

Cách dùng trong route handler:

// routes/auth.ts
if (isFeatureEnabled("auth-v2", req.user.id)) {
  return authV2Handler(req, res);
}
return authV1Handler(req, res);

Khi rollout ổn định (error rate không tăng, latency bình thường), tăng rolloutPercent dần: 10% → 25% → 50% → 100%. Khi đạt 100%, xóa flag và code cũ.

Canary deployment

Nếu dùng container/Kubernetes, canary deployment là bước tiếp theo: deploy version mới cho 10% pod, giữ 90% pod chạy version cũ. Traffic được split tự động.

Với setup đơn giản hơn (ví dụ: Railway, Render), feature flag đã đủ để đạt mục tiêu tương tự.

AI-generated deployment checklist

Trước mỗi deploy, tôi dùng AI để generate checklist từ diff:

Tôi sắp deploy những thay đổi này lên production. Tạo deployment checklist:

Changes:
- Thêm auth-v2 với JWT refresh token rotation
- Thêm bảng refresh_tokens vào database
- Thêm rate limiting trên /auth/login

Checklist cần bao gồm:
1. Database migration cần chạy không? Thứ tự?
2. Environment variable mới nào cần set?
3. Service nào cần restart?
4. Rollback plan nếu có vấn đề?
5. Metric nào cần monitor trong 30 phút đầu?

Checklist này mất 2 phút để generate nhưng tiết kiệm được rất nhiều đau đầu.


Rollback nhanh

Khi có sự cố, mỗi phút downtime đều có giá. Rollback plan phải được chuẩn bị trước, không phải nghĩ ra trong lúc hoảng loạn.

Git tag strategy

Tag mỗi deploy để có điểm rollback rõ ràng:

# Trước khi deploy
git tag -a deploy-2024-01-15-v1.2.3 -m "Deploy: auth-v2 feature flag at 10%"
git push origin deploy-2024-01-15-v1.2.3

# Deploy lên production
npm run deploy:production

# Nếu cần rollback
git checkout deploy-2024-01-14-v1.2.2
npm run deploy:production

Convention tôi dùng: deploy-{date}-{semver}. Nhìn vào tag là biết ngay deploy nào, khi nào.

Với database migration, rollback phức tạp hơn — cần có migration down script và test nó trước khi deploy up.

Automated rollback trigger

Với production traffic thực, tôi setup alert để tự động rollback khi error rate vượt ngưỡng:

// monitoring/auto-rollback.ts
const ROLLBACK_THRESHOLD = {
  errorRatePercent: 5,    // > 5% request lỗi
  p99LatencyMs: 2000,     // p99 latency > 2s
  windowMinutes: 5,       // đo trong 5 phút
};

async function checkAndRollback() {
  const metrics = await getMetrics(ROLLBACK_THRESHOLD.windowMinutes);

  if (metrics.errorRate > ROLLBACK_THRESHOLD.errorRatePercent) {
    await notifySlack(`🚨 Error rate ${metrics.errorRate}% — triggering rollback`);
    await rollbackToLastStableTag();
  }
}

Automated rollback không phải lúc nào cũng đúng — đôi khi spike là do traffic bất thường, không phải bug. Nhưng nó là safety net quan trọng khi bạn không ngồi nhìn dashboard 24/7.


Observability

“AI code ít bug hơn” là một trong những suy nghĩ nguy hiểm nhất. AI code có thể ít syntax error hơn, nhưng logic bug và edge case vẫn xảy ra — và khi xảy ra ở production, bạn cần biết ngay.

Logging strategy

AI-generated code thường thiếu logging — AI viết code để chạy đúng, không phải để debug. Tôi có một rule: mỗi khi AI viết một function quan trọng, tôi yêu cầu thêm structured log.

// Thay vì để AI viết thế này:
async function loginUser(email: string, password: string) {
  const user = await db.users.findByEmail(email);
  if (!user || !verifyPassword(password, user.passwordHash)) {
    throw new Error("Invalid credentials");
  }
  return generateTokens(user.id);
}

// Yêu cầu AI thêm logging:
async function loginUser(email: string, password: string) {
  const logger = createLogger("auth.login");

  const user = await db.users.findByEmail(email);
  if (!user) {
    logger.warn("Login attempt for non-existent email", { email });
    throw new UnauthorizedError("Invalid credentials");
  }

  if (!verifyPassword(password, user.passwordHash)) {
    logger.warn("Failed login attempt", { userId: user.id, email });
    throw new UnauthorizedError("Invalid credentials");
  }

  logger.info("Successful login", { userId: user.id });
  return generateTokens(user.id);
}

Structured log (JSON format) dễ query hơn plain text khi cần debug production issue.

Tracing

Với microservice hoặc app có nhiều layer, distributed tracing giúp theo dõi một request đi qua những service nào và mất thời gian ở đâu.

Với TaskFlow (monolith nhỏ), tôi dùng OpenTelemetry để trace request từ HTTP handler xuống database:

// Wrap database call với span
const span = tracer.startSpan("db.users.findByEmail");
try {
  const user = await db.users.findByEmail(email);
  span.setStatus({ code: SpanStatusCode.OK });
  return user;
} catch (err) {
  span.recordException(err);
  span.setStatus({ code: SpanStatusCode.ERROR });
  throw err;
} finally {
  span.end();
}

Khi có latency spike, trace cho thấy ngay: vấn đề ở database query hay ở application logic.

Alert: AI-assisted anomaly detection

Thay vì set threshold thủ công cho mọi metric, tôi dùng AI để phân tích pattern và đề xuất alert:

Đây là metric của app trong 7 ngày qua:
- Error rate: trung bình 0.3%, peak 1.2%
- p99 latency: trung bình 180ms, peak 450ms
- Request/min: trung bình 120, peak 380

Đề xuất alert threshold hợp lý cho:
1. Error rate (cần alert ngay vs. cần investigate)
2. Latency (user-impacting vs. warning)
3. Traffic anomaly (spike bất thường)

AI sẽ đề xuất threshold dựa trên baseline thực tế, không phải con số arbitrary.


Retrospective

Deploy xong không phải là hết. Retrospective là bước quan trọng nhất để framework ngày càng tốt hơn.

Đánh giá framework sau mỗi project

Sau khi TaskFlow auth feature lên production ổn định, tôi dành 30 phút để trả lời:

  • Phase nào mất nhiều thời gian nhất? Tại sao?
  • AI làm tốt nhất ở đâu? Làm tệ nhất ở đâu?
  • Có bước nào tôi bỏ qua và sau đó phải trả giá không?
  • Nếu làm lại, tôi sẽ thay đổi gì?

Không cần viết dài — 5–10 bullet point là đủ. Quan trọng là làm ngay khi còn nhớ.

Cập nhật AI_CONTEXT.md

AI_CONTEXT.md là file context tôi feed cho AI agent ở đầu mỗi session. Sau mỗi project, tôi cập nhật:

## Lessons learned (updated 2024-01-15)

### What works well
- Feature flag pattern cho AI-generated feature: rollout an toàn, dễ rollback
- Structured logging từ đầu: tiết kiệm debug time đáng kể
- Git tag trước mỗi deploy: rollback trong < 5 phút

### What to improve
- AI thường bỏ qua database index — cần explicit prompt
- Cần review migration script kỹ hơn trước deploy
- Alert threshold nên set ngay sau deploy đầu tiên, không đợi có incident

File này trở thành “institutional memory” — lần sau khi bắt đầu project mới, AI agent có context về những gì đã học được.

Bài học để cải thiện

Một pattern tôi thấy hữu ích: sau mỗi incident (dù nhỏ), viết một “mini post-mortem” ngắn:

  • Incident: gì xảy ra
  • Root cause: tại sao xảy ra
  • Detection: phát hiện như thế nào (và mất bao lâu)
  • Resolution: fix như thế nào
  • Prevention: làm gì để không xảy ra lại

Không cần formal — 5 câu là đủ. Nhưng pattern này giúp tôi không lặp lại cùng một sai lầm.


Ví dụ thực chiến: Deploy auth feature của TaskFlow

Sau khi auth feature pass QA ở bài 06, đây là quy trình deploy tôi đi qua:

Bước 1: Chuẩn bị

# Tag current stable version
git tag -a deploy-2024-01-14-v1.1.0 -m "Stable before auth-v2 deploy"
git push origin deploy-2024-01-14-v1.1.0

# Chạy migration trên staging trước
npm run db:migrate:staging
npm run test:integration:staging

Bước 2: Deploy với feature flag tắt

Deploy code lên production nhưng auth-v2 flag ở rolloutPercent: 0. Không có user nào dùng code mới, nhưng code đã sẵn sàng.

Bước 3: Bật flag cho 5% user

"auth-v2": { enabled: true, rolloutPercent: 5 }

Monitor trong 30 phút: error rate, latency, login success rate. Tất cả bình thường.

Bước 4: Tăng dần

5% → 25% → 50% → 100% trong 2 ngày. Mỗi bước monitor 30–60 phút.

Kết quả: Zero downtime, zero rollback cần thiết. Nếu có vấn đề ở bất kỳ bước nào, tôi chỉ cần set rolloutPercent: 0 — không cần revert code, không cần emergency deploy.


Những điều cần tránh

Deploy không có rollback plan. “Nếu có vấn đề thì fix” không phải là plan. Rollback plan phải được chuẩn bị trước deploy, không phải sau khi incident xảy ra. Git tag mất 30 giây — không có lý do để bỏ qua.

Bỏ qua observability vì “AI code ít bug hơn”. AI code có thể ít syntax error hơn, nhưng logic bug, race condition, và edge case vẫn xảy ra. Không có observability, bạn sẽ biết có vấn đề khi user complain — không phải khi metric spike. Đó là sự khác biệt giữa 5 phút và 5 giờ downtime.


Nhìn lại toàn bộ framework

Đây là bài cuối của series. Nhìn lại 8 phase:

  • Phase 0 (Foundation): Setup môi trường, project memory, glossary
  • Phase 1 (Requirements): Phân tích yêu cầu, chốt kiến trúc với AI như sparring partner
  • Phase 2 (Specs & API): Contract first — spec và API trước khi code
  • Phase 2.5 (Design System): Design token và atomic component để AI consistent
  • Phase 3 (Task Planning): Granularity đúng, test strategy trước khi code
  • Phase 4 (Execution): Single agent, multi-agent, HITL checkpoint
  • Phase 5 (QA & Review): Automated QA, AI-assisted review, human final check
  • Phase 6 (Deploy): Feature flag, rollback plan, observability, retrospective

Framework này không phải silver bullet. Nó là một cách tiếp cận có cấu trúc để làm việc với AI agent — giảm rủi ro, tăng predictability, và giúp bạn học nhanh hơn từ mỗi project.

Lời khuyên cho từng reader profile

Solo developer: Bạn không cần áp dụng tất cả 8 phase ngay. Bắt đầu với Phase 1 (requirements rõ ràng) và Phase 4 (HITL checkpoint) — hai phase này có ROI cao nhất. Thêm dần các phase khác khi bạn thấy cần.

Team nhỏ (2–5 người): Phase 5 (QA & Review) và Phase 6 (Deploy) quan trọng nhất khi có nhiều người cùng dùng AI. Cần đồng thuận về quy trình review và deploy strategy để tránh conflict.

Senior developer: Framework này có thể cảm giác “quá nhiều process” nếu bạn đã quen làm việc nhanh. Hãy xem nó như một checklist — không phải mọi bước đều cần làm đầy đủ, nhưng biết bước nào đang bỏ qua và tại sao.

Điều quan trọng nhất tôi học được sau khi dùng AI agent cho nhiều project: AI làm tốt nhất khi bạn biết mình muốn gì. Framework này không phải để kiểm soát AI — mà để giúp bạn biết mình muốn gì trước khi bắt đầu.


← Bài 06: QA, Code Review & PR Workflow