Quy Trình Phát Triển Sản Phẩm

Quy trình from scratch, ~100% Claude Code. Người 5 touchpoint: bootstrap, confirm PRD, approve PR, verify URL, rollback. 9 bước có flow rõ, mermaid với loop decision, stack chuẩn, defaults, filename convention, KPI outcome + process.

Quy trình triển khai web app from scratch, ~100% bằng Claude Code. Người làm 2 việc: bootstrap môi trường một lần (CLI access, secrets), và verify đầu ra mỗi PR + URL prod. Claude làm phần còn lại — draft PRD, init repo, code, migration, security wiring, deploy verify, doc.

Khi nào áp dụng

Web app từ idea → production. Có người dùng cuối, có URL công khai, vận hành dài hạn.

Loại dự ánÁp dụngVariation chính
Internal product có UIĐầy đủ 9 bướcAuth = internal (xem Stack chuẩn)
External / customer productĐầy đủ 9 bướcAuth = external (xem Stack chuẩn)
Backend service / API-onlyĐầy đủ 9 bướcBước 5 skip frontend. Bước 9 dùng docs/API.md thay USER_GUIDE.md
POC / spike ≤ 5 ngàyBước 1-5 + 7-8Skip Bước 6 (security audit) + 9 (doc gate). Không deploy prod chính thức
Library / SDKKhôngLifecycle khác (npm publish, versioning) — cần quy trình riêng

Phân vai Claude vs Người

Claude Code làm (autonomous)

  • Draft docs/PRD.md từ user prompt.
  • Init repo, branch, stack, CLAUDE.md, .env.example, Railway link.
  • Plan mode + implementation cho mọi feature.
  • Schema, migration, API, frontend, security wiring.
  • Push lên feature branch, mở PR vào staging rồi main.
  • Verify URL prod sau deploy, smoke test golden path qua curl.
  • Viết tất cả doc (README, ARCHITECTURE, USER_GUIDE hoặc API, CHANGELOG).
  • Viết postmortem khi có incident.

Người làm (5 touchpoint)

  1. Bootstrap (1 lần per product): CLI login, tạo Railway project, set secrets. Chi tiết ở section "Bootstrap" cuối doc.
  2. Confirm PRD draft (sau Bước 1): đọc draft, OK hoặc gửi revision.
  3. Approve PR vào main (Bước 7): review code + plan trong PR, click merge. Đây là gate duy nhất chặn Claude push prod.
  4. Verify URL prod (Bước 8): mở URL sau auto-deploy, test golden path manual.
  5. Decide rollback (Bước 8): khi Claude báo critical issue trong 30 phút đầu sau ship.

Tổng quan quy trình

flowchart TD
    Boot([Bootstrap 1 lần: gh + railway + secrets])

    Boot --> B1[Bước 1: Draft PRD<br/>Claude viết → người confirm]
    B1 --> B2[Bước 2: Init project<br/>repo + stack + CLAUDE.md + Railway link]
    B2 --> B3{Bước 3: Plan feature<br/>lưu docs/plans/}
    B3 --> B4[Bước 4: Build Backend<br/>schema → API → test]
    B4 --> B5[Bước 5: Build Frontend<br/>UI → tích hợp API → test]
    B5 --> B6[Bước 6: Security audit<br/>checklist trước PR]
    B6 --> B7[Bước 7: PR + merge<br/>Claude push → người approve]
    B7 --> B8[Bước 8: Verify deploy<br/>Railway auto → curl → người mở URL]
    B8 --> Loop{Còn feature<br/>trong PRD?}
    Loop -->|yes| B3
    Loop -->|no| B9{Bước 9: Doc gate<br/>4-5 file đầy đủ?}
    B9 -->|chưa đủ| B9
    B9 -->|đủ| End([Dự án xong → adoption])

    classDef gate fill:#fef3c7,stroke:#b45309,color:#1f2937;
    classDef human fill:#fce7f3,stroke:#be185d,color:#1f2937;
    classDef phase fill:#dbeafe,stroke:#1d4ed8,color:#1f2937;
    class B3,Loop,B9 gate;
    class B1,B7,B8 human;
    class Boot,End phase;
  • Node hồng = có người touchpoint. Còn lại Claude tự chạy.
  • Node vàng = decision gate. Không qua thì không đi tiếp.
  • Loop sau Bước 8: check PRD còn feature nào chưa làm. Hết thì sang Bước 9 đóng project.

Quy trình

Bước 1. Draft PRD

Claude — draft docs/PRD.md từ user prompt. Người — đọc và confirm.

PRD là input cho mọi bước sau. Phải có:

  • Ai dùng, dùng vào điểm nào trong workflow hiện tại.
  • Baseline đo được (giờ công, chi phí, cơ hội mỗi tháng).
  • Định nghĩa "MVP đã ship" và "đã có giá trị" — hai mốc tách biệt.
  • Hành vi đích: thao tác cũ nào bị thay thế, thao tác mới nào xuất hiện.
  • Feature list theo priority order — Claude sẽ thực thi tuần tự, lặp Bước 3-8 cho từng feature.
  • Override defaults (nếu có): chỉ định stack / auth provider / theme khác mặc định.

Fail mode: PRD chỉ liệt kê feature, không spec workflow. Claude build đúng spec, sai context.

Bước 2. Init project

Claude — tự tạo repo và setup stack. Người — không có touchpoint (Bootstrap đã xử lý đầu).

  1. Tạo repo: gh repo create seongon-agency/{product-slug} --private --clone.
  2. Tạo branch: git checkout -b staging, push lên remote. Cấu trúc main = production, staging = đệm.
  3. Init stack theo Stack chuẩn (xem section dưới — version exact tại thời điểm init lock vào CLAUDE.md):
    • bunx create-next-app@latest . --typescript --tailwind --app --src-dir false
    • bunx shadcn@latest init
    • bun add drizzle-orm drizzle-kit better-sqlite3 pg
    • bun add -d @biomejs/biome
  4. Setup DB: Drizzle config — schema ở db/schema.ts, client lazy-init ở db/index.ts. Dev = SQLite (data/dev.db), prod = Postgres (DATABASE_URL từ Railway).
  5. Link Railway: railway link với project đã tạo lúc Bootstrap. Verify env injection: railway run --service Postgres bun -e "console.log(process.env.DATABASE_PUBLIC_URL?.slice(0,20))" ra prefix có nghĩa.
  6. Viết CLAUDE.md ngay với các section: Stack (version exact lock), Build commands, Env vars, Architecture overview, Naming convention, Deploy target. Reference SOP này ở đầu.
  7. Commit .env.example với key name + placeholder. .env.local trong .gitignore.
  8. Initial commit lên main (chỉ 1 lần — sau commit này, Claude không tự push main nữa). Custom domain set qua Railway dashboard.

Fail mode: Skip viết CLAUDE.md để "nhanh". Feature sau Claude đoán convention, mỗi feature một style.

Bước 3. Plan feature

Claude — viết plan vào docs/plans/{date}-{slug}.md. Người — review trong PR (Bước 7).

"Non-trivial" trigger plan mode: đụng ≥ 2 file, hoặc cross-layer (DB + API hoặc API + UI), hoặc đụng auth / security / migration / billing. Edit CSS đơn lẻ hoặc fix typo: skip plan mode.

  • Output plan = todo list, file paths cụ thể + mô tả thay đổi cụ thể.
  • Lưu vào docs/plans/{YYYY-MM-DD}-{feature-slug}.md trước khi code.
  • Plan + code đẩy chung 1 PR. Người review cả plan và diff.
  • Plan >10 bước hoặc cross 3+ layer (DB + API + UI) → tách nhiều plan + nhiều PR nối tiếp.
  • 1 plan = 1 feature. Không gộp.

Fail mode: Skip plan vì "feature đơn giản". Khi feature mở rộng, không có plan để hiểu intent → rewrite 2-3 lần.

Bước 4. Build Backend

Claude — schema + API + test. Người — review trong PR.

Backend xong trước Frontend. Thứ tự bắt buộc:

  1. Schema database: Drizzle migration trước, không sửa schema lén trong code. Test ở SQLite dev → deploy → chạy migration prod qua railway run drizzle-kit migrate. (Pattern từ incident có thật.)
  2. API routes + business logic: đặt trong lib/, expose qua app/api/. Convention response: { success, data?, error? }. Logic doanh nghiệp → API Endpoint cụ thể (vd /api/images/upload).
  3. Test API bằng curl hoặc bun test. Test case = input + expected output cụ thể, viết trong plan.
  4. Update docs/ARCHITECTURE.md trong cùng PR — schema thay đổi → doc thay đổi.

API là contract giữa Backend và Frontend. Cấu trúc API phải ổn định trước khi Frontend bắt đầu.

Fail mode: Build frontend trước, backend uốn theo UI. Schema sai → migration sửa nhiều lần → dữ liệu cũ corrupt.

Bước 5. Build Frontend

Claude — implement UI + tích hợp API. Người — verify trên staging URL (Bước 7-8).

  1. App Router default: server component, 'use client' chỉ khi cần state hoặc effect.
  2. Component primitive (button, dialog, dropdown): dùng components/ui/ có sẵn từ shadcn init. Không tự design lại.
  3. Tích hợp API qua fetch ở server component hoặc useEffect/SWR ở client.
  4. Theme: light only mặc định. Implement dark mode chỉ khi PRD chỉ định (xem Defaults).
  5. Style guide: chỉ viết style_guide.md khi đã có ≥ 2 unique UI pattern (button variant, card style, ...). Đừng viết sớm khi chưa có pattern.
  6. Test luồng end-to-end trên local bun dev trước khi push.

Fail mode: Mỗi component một style. App trông như patchwork. API work nhưng giao diện lỗi.

Bước 6. Security audit

Claude — chạy checklist trước khi mở PR. Người — verify trong PR review.

Security là cross-cutting concern — phần lớn rule phải được wire trong Bước 4 và 5. Bước 6 là audit cuối cùng trước khi PR — nếu có hạng mục chưa qua, quay lại Bước 4 hoặc 5 để fix, không skip.

CheckÁp dụng từ BướcĐánh giá
requireAuth() trên mọi API route, public route opt-out có comment lý doBước 4Grep app/api/ — mọi route export GET/POST/PATCH/DELETE phải có auth check
Per-user data isolation — mọi DB query filter theo user_idBước 4Grep schema có FK user_id → mọi query reference table đó phải có filter. (Pattern từ incident có thật.)
Secret + API key bên thứ 3 chỉ server-sideBước 4-5Grep NEXT_PUBLIC_ — chỉ được dùng cho public config, không cho secret
CORS allow domain cụ thể, không wildcardBước 4Check middleware / next.config — domain Frontend whitelist
.env* trong .gitignore, .env.example chỉ key name + placeholderBước 2git ls-files | grep .env ra chỉ .env.example
Auth provider integration đúng pattern (xem Stack chuẩn)Bước 2Internal: redirect auth.seongonai.com. External: Clerk middleware ở root

Fail mode: Bỏ qua audit, để PR review tự phát hiện. Reviewer miss → user A đọc data user B → sự cố pháp lý. (Pattern từ incident có thật.)

Bước 7. PR + merge

Claude — push branch, mở PR. Người — review code + plan trong PR, click merge khi OK.

  1. Push feature branch: git checkout -b feat/{slug}, commit, git push -u origin feat/{slug}.
  2. Mở PR vào staging qua gh pr create. Body PR include: link docs/plans/{date}-{slug}.md, tóm tắt thay đổi, security checklist result (Bước 6).
  3. Merge vào staging sau CI green. Nếu có Railway staging environment, auto-deploy lên đó để verify.
  4. Mở PR staging → main khi feature ổn định trên staging.
  5. Người approve PR vào main = trigger Railway auto-deploy prod (Bước 8 xử lý phần sau). Claude không tự merge main.

Fail mode: Claude push thẳng vào main. Hoặc người approve PR mà không đọc plan. Khi vỡ, không biết feature nào gây.

Bước 8. Verify deploy + rollback nếu cần

Claude — chạy verify sequence sau auto-deploy. Người — mở URL trên browser, test golden path. Người trigger rollback khi Claude báo critical.

Verify flow (chạy sau mỗi merge vào main)

  1. Railway báo deploy success (nếu fail → Claude phân tích log, mở incident issue, không claim ship).
  2. Claude chạy railway logs --tail 50 — check không có error / warning ngoài kỳ vọng.
  3. Claude curl URL prod golden path — verify response status + body content match expectation.
  4. Claude báo "verified" trong PR comment + tag người.
  5. Người mở URL trên browser, test golden path manual (UI thật, không chỉ curl).
  6. "Đã ship" chỉ khi cả Claude verify + người verify đều OK. Không phải khi Railway báo build pass. (Pattern từ incident có thật.)

Rollback procedure (30 phút đầu sau ship)

Critical issue = 500 trên golden path / data corruption / auth bypass. Khi Claude phát hiện hoặc người báo:

  1. Người trigger rollback: railway deployment listrailway rollback <previous-deployment-id>.
  2. Claude note vào CHANGELOG.md: rollback khi nào, vì lý do gì.
  3. Claude mở incident issue trên GitHub + viết postmortem trong 48h vào docs/postmortems/{date}-{slug}.md.
  4. Fix root cause trên branch riêng → staging → main → re-deploy. Không re-deploy thẳng main khi chưa fix root.

Fail mode: Claude báo "deploy xong" khi Railway báo build pass nhưng chưa mở URL. Endpoint 500, user là người phát hiện đầu tiên.

Bước 9. Doc gate (đóng project)

Claude — viết và update doc trong từng PR. Bước 9 là verify cuối cùng trước khi tuyên bố project xong. Người — review checklist.

Bắt buộc cho mọi product

  • README.md — overview, quick start, env vars, link sang docs/.
  • CLAUDE.md — sync với reality. Stack đổi → update trong cùng PR.
  • docs/ARCHITECTURE.md — schema DB, data flow, directory structure.
  • CHANGELOG.md — version + change theo release. Update mỗi PR vào main.

Conditional theo loại product

  • docs/USER_GUIDE.md — bắt buộc khi product có UI end-user. Có screenshot nếu UI phức tạp.
  • docs/API.md — bắt buộc khi API-only / backend service. List endpoints, request/response schema, curl examples.

Không có doc đủ = user không onboard được = adoption không xảy ra.

Fail mode: Code xong "để mai viết doc". Không bao giờ có "mai".


Bootstrap (1 lần per product)

Người làm 1 lần TRƯỚC khi Claude bắt đầu Bước 1.

  1. Anthropic API key: đăng ký key, set vào Claude Code config trên máy dev.
  2. GitHub CLI: gh auth login vào account có access org seongon-agency.
  3. Railway CLI: railway login. Tạo project mới (qua dashboard hoặc railway init). Add Postgres service.
  4. Set secrets trên Railway dashboard cho project mới:
    • DATABASE_URL reference ${{Postgres.DATABASE_URL}} (Railway syntax).
    • ANTHROPIC_API_KEY / OPENAI_API_KEY nếu dùng LLM trong runtime.
    • CLERK_SECRET_KEY + CLERK_PUBLISHABLE_KEY nếu external product dùng Clerk.
    • API key của bên thứ 3 sản phẩm cần (DataForSEO, ...).
  5. Hand-off prompt cho Claude (ví dụ):
    Build sản phẩm "[tên]" theo SOP tại
    handbook.seongonai.com/project-management/quy-trinh-phat-trien-san-pham
    
    Bootstrap đã xong:
    - Railway project: [tên project]
    - Org GitHub: seongon-agency
    - Secrets đã set: DATABASE_URL, [API keys cần]
    
    Brief: [1-2 đoạn về sản phẩm — Claude sẽ draft PRD đầy đủ ở Bước 1]
    

Từ điểm này, Claude tự chạy. Người chỉ touchpoint khi confirm PRD, approve PR, verify URL, decide rollback.

Stack chuẩn

Khi Claude init project mới, dùng exact stack này. Version exact lock vào CLAUDE.md tại thời điểm init.

LayerTechLệnh init
Framework + Language + StylingNext.js (App Router) + TypeScript + Tailwind v4bunx create-next-app@latest . --typescript --tailwind --app
RuntimeBun (latest 1.x)Đã có khi Claude làm việc trên máy dev
Component libraryshadcn/uibunx shadcn@latest init ngay sau Next init
ORM + DB driverDrizzle + better-sqlite3 (dev) + pg (prod)bun add drizzle-orm drizzle-kit better-sqlite3 pg
Dev DBSQLite (data/dev.db)via bun:sqlite hoặc better-sqlite3
Prod DBPostgreSQL trên RailwayRailway tự cấp khi add Postgres service
Lint + formatBiomebun add -d @biomejs/biome + biome.json config
DeployRailway via nixpacks.tomlRailway auto-detect Next.js, nixpacks.toml chỉ cần khi custom build

Backend variants — chỉ khi PRD yêu cầu

Use caseTech
Heavy data / AI processingPython (FastAPI) — service riêng, gọi qua HTTP
Heavy concurrency / hiệu năngGo — service riêng, gọi qua HTTP

Auth variants

Loại productProviderIntegration
Internal (cho team SEONGON)auth.seongonai.comRedirect-based. Session ở Upstash Redis, cookie httpOnly, cross-domain qua redirect
External / customerClerk (default)Clerk middleware + <ClerkProvider> ở root. Override sang Auth0 / NextAuth / Lucia chỉ khi PRD chỉ định

Defaults khi PRD không chỉ định

Claude pick option đầu, chuyển tiếp. Override khi PRD nói khác.

DecisionDefaultOverride khi
ThemeLight only, không implement dark modePRD yêu cầu dark mode
Dev DBSQLite localSchema cần feature Postgres-only (jsonb, gin index, fulltext)
style_guide.md timingViết sau khi có ≥ 2 unique UI patternPRD chỉ định design system từ đầu
Test frameworkbun test cho unit, curl scripts cho APIPRD cần E2E → Playwright
i18nTiếng Việt onlyPRD chỉ định product đa ngôn ngữ
AnalyticsKhông addPRD yêu cầu (vd PostHog, GA4)

Filename convention

FileMục đíchTạo trong
docs/PRD.mdProduct Requirements DocumentBước 1
CLAUDE.md (root)Context contract — Claude đọc mỗi sessionBước 2
.env.example (root)Template env vars (key + placeholder)Bước 2
db/schema.tsDrizzle schemaBước 2 (empty) → Bước 4 (per feature)
docs/plans/{YYYY-MM-DD}-{feature-slug}.mdPlan mode output per featureBước 3
docs/ARCHITECTURE.mdSchema DB, data flow, directory structureBước 4, update mỗi PR đụng kiến trúc
style_guide.md (root)CSS variables, class conventionKhi cần (xem Defaults)
docs/USER_GUIDE.md hoặc docs/API.mdDoc cho người dùng (xem Bước 9 conditional)Bước 9
CHANGELOG.md (root)Version + change theo releaseBước 9, update mỗi PR vào main
docs/postmortems/{date}-{slug}.mdPostmortem khi rollback / incidentBước 8 khi có incident

Triết lý nền

Hai quyết định chiến lược khiến team này khác team dev truyền thống. Vi phạm bất kỳ điều nào = quay về làm lại, không phải fix sau.

  1. Team không code bằng tay. Mọi dòng code do Claude Code viết. Vai trò của người là PM, architect, reviewer — không phải scribe gõ phím.
  2. Plan mode trước mọi thay đổi non-trivial. Claude lưu plan vào docs/plans/, người review trong PR.

Convention nền

Hai quy ước infra để triết lý trên hoạt động được.

  1. CLAUDE.md là contract. Mỗi project có CLAUDE.md ở root với stack version exact, commands, env vars, kiến trúc, naming convention. Claude đọc mỗi session — không có thì Claude đoán bừa.
  2. Claude Code có CLI access vào hạ tầng thật. Railway CLI, GitHub CLI, database CLI — đăng nhập sẵn (Bootstrap xử lý). Không có = Claude là text editor, không phải agent.

KPI đo lường

Báo cáo quarterly lên leadership.

Outcome metrics (business)

MetricTargetCách đo
Time PRD-confirmed → MVP ship≤ 4 tuầngit log first commit → first deploy success
Critical incident 7 ngày sau ship0Railway alerts + user reports
MTTR khi rollback< 30 phútIncident issue timestamps

Process metrics (operational)

MetricTargetCách đo
% PR có plan ở docs/plans/≥ 95%Audit PR history
% dự án qua gate 9 (doc đủ trước khi đóng)≥ 90%Lead audit weekly
Rollback rate (rollback / total deploy vào main)< 5%Railway deployment history
% rule violation phát hiện trong PR reviewGiảm quarter-over-quarterReviewer tag #sop-violation

Lưu ý cuối

Mọi rule trong doc này sinh ra từ incident hoặc pattern thất bại của team AI cũ. Doc này không phải triết lý trừu tượng — mỗi "Fail mode" và mỗi "(Pattern từ incident có thật)" là một sự cố ai đó đã trải qua.

Push back khi rule cản trở. Nếu thấy một rule không hợp lý với case hiện tại, push back trong PR review hoặc raise lên lead — đừng âm thầm bỏ qua. Rule sửa được; bỏ qua thì rule chết.

Cập nhật doc khi có incident mới. Rule mới muốn vào doc → phải có postmortem hoặc paragraph "vì sao". Rule không có evidence = không vào doc.