From git init to first paying user.
Most “build with AI” practices stop at the prototype. This one runs all the way through. Real prompts, the guardrails layer, the test suite, the security pipeline, the deploy. Sixteen units. By Sunday you have a SaaS service running with paying-grade hygiene.
- Write a CLAUDE.md that captures your team’s actual conventions
- Wire the GitHub MCP server with a scoped token
- Run /ultrareview before every merge with 6 specialized review agents
- Ship a SaaS service with the 5-Friday post-launch ritual running
The day-zero ritual.
Six commits you make once. Get them right and the rest of the week flows. Get them wrong and you fight your own setup forever.
Run the day-zero checklist.
- No repo of your own? Run this against any project folder, e.g. the capstone
agents/folder, orgit inita fresh one. - Walk the six rows: confirm a first commit exists, deps are pinned (
pyproject.toml/package.json),.env.exampleis committed and.envis git-ignored,tests/has one passing test,CLAUDE.mdexists, and.claude/settings.jsonexists. - Create whichever rows are missing. Ask Claude to draft the
.env.exampleand the one passing test if you don’t have them.
git log --oneline | head shows the commit, ls .env.example .claude/settings.json CLAUDE.md returns three paths, and your test command exits 0.Stretch. Add to CI: a step that fails the build if CLAUDE.md is missing or empty.
CLAUDE.md, written right.
The single most important file in your repo for working with Claude Code. Write it with the discipline of a hiring manager writing a job description.
# CLAUDE.md ## What this repo is A SaaS that does X for Y customers. Built in [stack]. Production at [domain]. ## How to run - Setup: make setup - Run: make run - Test: make test (pytest with coverage gate at 80%) - Lint: make lint (ruff + mypy) ## Conventions (the ones I actually want enforced) - Use sqlalchemy 2.0 style. Never db.query() — always db.execute(select()). - All routes go through app/routes/. No new top-level routers. - Tests in tests/. Mirror the source tree. - Type hints required on every function signature. ## Do NOT - Use type-ignore casually. - Add a JavaScript framework. We're on vanilla. - Mock the database in tests. Use the test container. - Write docstrings longer than 3 lines. ## Security guardrails - Never log a token, password, or PII. - All user input is parameterized in SQL. - All external API calls use the configured retry harness. - Claude must not run git push or npm publish autonomously.
Write your CLAUDE.md.
- No repo of your own? Run this against any project folder, e.g. the capstone
agents/folder. - Copy the template above. Fill the five sections (what / run / conventions / do-NOT / security) with this repo’s real facts.
- Keep it under 100 lines.
- Start a fresh Claude session. Ask one project-specific question whose answer is only in CLAUDE.md (e.g. “what’s the test command?”).
Stretch. Add to CI: a step that fails the build if CLAUDE.md is missing or empty.
Dependencies & conventions.
Pick a small, opinionated stack. Pin everything. Let Claude know what you’ve picked.
| Layer | Pick | Why |
|---|---|---|
| API framework | FastAPI (Python) / Hono (TS) | Type-first, fast, small. |
| DB | Postgres (Neon / Supabase) | Boring tech, infinite docs. |
| ORM | SQLAlchemy 2.0 / Drizzle | Async, type-safe. |
| Auth | Clerk / Auth.js / Stytch | Don’t roll your own. |
| Payments | Stripe | One option. Stop researching. |
| Deployment | Render / Fly / Vercel | Git push to deploy. |
| AI | Anthropic SDK | Frontier capability. |
| Tests | pytest + Playwright | Unit + e2e. |
| Lint / format | ruff + mypy / biome | Fast, opinionated. |
Lock your stack and tell Claude.
- No repo of your own? Run this against any project folder, e.g. the capstone
agents/folder. - For each layer (framework, DB, ORM, auth, payments, deploy, AI, tests, lint), write down your one pick. Stop researching once a row has an answer.
- Pin every dependency to an exact version in
pyproject.toml/package.json— no^or~ranges. - Paste the nine picks into the Conventions section of CLAUDE.md so Claude stops suggesting alternatives.
grep -E '[\^~]' pyproject.toml returns nothing), and CLAUDE.md names all nine picks.Stretch. Add a lockfile (uv.lock, poetry.lock, or package-lock.json) and commit it.
GitHub MCP server.
The official GitHub MCP server gives Claude Code direct access to issues, PRs, code search, and CI status — without you copy-pasting.
# .mcp.json at repo root or ~/.claude/.mcp.json # Option A — local Docker (recommended for self-hosted setups) { "mcpServers": { "github": { "command": "docker", "args": [ "run", "-i", "--rm", "-e", "GITHUB_PERSONAL_ACCESS_TOKEN", "ghcr.io/github/github-mcp-server" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "${env:GITHUB_PAT}" } } } } # Option B — remote (OAuth, no Docker needed) # { # "mcpServers": { # "github": { # "url": "https://api.githubcopilot.com/mcp/" # } # } # } # Claude can now: list issues, read PR diffs, search code across repos, # check CI runs, comment on issues. # Note: the older `npx @modelcontextprotocol/server-github` package was # deprecated in April 2025 — use the official Docker image or remote URL.
Wire the GitHub MCP server.
- Create a GitHub PAT scoped to one repo (fine-grained token → that repo only).
- Add the Option A or Option B config above to
.mcp.jsonat your repo root; put the token in your environment asGITHUB_PAT. - Restart Claude Code. Run
/mcpand confirmgithubis listed and connected. - Ask: “list the open issues on this repo.” Confirm the titles match what you see on GitHub — real data, not invented.
/mcp shows github connected, and the issue list Claude returns matches the repo’s actual open issues.Stretch. Add a second MCP (Linear or Notion). Cross-service queries unlock workflows you can’t do with either alone.
The feature-dev loop.
The shape of every feature you ship. Six steps. Don’t skip any.
Run the loop through plan mode.
- No repo of your own? Run this against any project folder, e.g. the capstone
agents/folder — pick a small refactor (rename a concept, extract a helper). - Make a worktree or branch for it (step 2 of the loop).
- Hit Shift+Tab to enter plan mode (step 3). Describe the change. Read the plan; edit it before approving.
- Approve, let Claude build and run tests (steps 4–5), then open the PR (step 6).
Stretch. Use plan mode for anything touching > 3 files. Always.
Real prompts for real features.
The four prompts that earn their keep on every feature.
1. Plan mode kickoff
[plan mode]
Read the issue at .claude/issues/{n}.md.
Read the relevant code under app/services/.
Read CLAUDE.md.
Produce a plan with:
- Files to modify (list with one-line reason each)
- Files to create
- Tests to add
- Migration changes (if any)
- Open questions you'd want me to answer
Do not touch any files yet.
2. Implement after plan approval
Implement the plan we just agreed on. Rules: - Write the failing test first, then the code that makes it pass. - Commit after each step. - After the last step, run `make test && make lint` and fix issues. - Do not push. Do not open the PR.
3. Critique your own diff
Review the diff for this branch as a hostile reviewer. Push back hard on: - Tests that don't actually exercise the new logic - Functions over 40 lines - Magic numbers / strings - Missing error handling at boundaries - Anything that violates CLAUDE.md Quote specific lines. Don't be polite.
4. PR description
Write the PR description for this branch. Sections: - One-line summary - Why (link the issue) - What changed (bulleted, code-level) - How to test - Risks I should think about No emoji. Write as if I'll read it on the train.
Run the hostile-reviewer prompt.
- No repo of your own? Run this against any project folder, e.g. the capstone
agents/folder — make one small edit so there’s a diff to review. - Copy prompt #3 above (the “hostile reviewer” one) and run it against your current branch’s diff.
- Read every finding. Label each: real issue / false positive / style.
- Fix the real issues before you push.
file:line, and you can name one real issue from it you’d have missed on your own read.Stretch. Save the prompt as a reusable skill or slash command so every branch starts from it.
The guardrails layer.
Six rules a SaaS-shipping Claude session should never violate. Encode them as hooks; trust them.
- No
git pushwithout explicit approval. A PreToolUse hook ongit pushrequires the magic word. - No
rm -rfoutside the repo root. Pattern-match on Bash; reject anything that escapes. - No secrets in commits. Pre-commit hook scans for high-entropy strings.
- No new top-level dependencies. See check-package.sh from Practice 02.
- Tests must pass before commit. A pre-commit runs
make test; aborts on failure. - No
type-ignorewithout a comment. Lint catches it.
Encode one guardrail as a hook.
- No repo of your own? Run this against any project folder, e.g. the capstone
agents/folder. - Pick the
git pushguardrail (rule 1). In.claude/settings.json, add a PreToolUse hook that matchesgit pushand exits non-zero unless an approval phrase is present. (Reuse check-package.sh for the “no new top-level deps” rule if you prefer.) - Restart Claude Code so the hook loads.
- Ask Claude to run
git push. Watch the hook intercept it.
git log origin/<branch> shows the remote did not advance.Stretch. Encode a second rule (block rm -rf that escapes the repo root). Two hooks, two fewer ways to wreck a Friday.
Pre-commit hooks.
The line of defense between “Claude wrote good code” and “the commit on main is good code.” Run on every commit, no exceptions.
# .pre-commit-config.yaml repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.7.0 hooks: - id: ruff args: [--fix] - id: ruff-format - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.13.0 hooks: - id: mypy - repo: https://github.com/gitleaks/gitleaks rev: v8.20.0 hooks: - id: gitleaks # blocks secrets in commits - repo: local hooks: - id: pytest-fast name: pytest (fast subset) entry: pytest -m 'not slow' -q language: system pass_filenames: false stages: [commit]
Install pre-commit.
- No repo of your own? Run this against any project folder, e.g. the capstone
agents/folder. - Install:
pip install pre-commit(orbrew install pre-commit). - Save the
.pre-commit-config.yamlabove to your repo root, then runpre-commit install. - Run
pre-commit run --all-files. Fix what it flags.
pre-commit run --all-files ends with every hook reporting Passed (or Skipped), and a deliberately bad commit — e.g. one with an unused import — is rejected by the hook.Stretch. Add a custom local hook for a team rule (no console.log / console.warn in prod code).
Setting up testing.
Three layers. Each catches a different class of bug. Skip any one and the bugs come at the wrong time.
Stand up the three layers.
- No repo of your own? Run this against any project folder, e.g. the capstone
agents/folder. - Unit: write one pytest over a pure function. Ask Claude to add a second covering an edge case.
- Integration: one test that hits a real (test) DB or a mocked external API.
- e2e: one Playwright test that loads a page and asserts on visible text. (If headless, run with
--headedonce to watch it.)
playwright test each report at least 1 passing test.Stretch. Mark the slow tests (@pytest.mark.slow) so pre-commit can run only the fast subset.
The PR review pipeline.
Six specialized reviewers run in parallel against every PR. Each looks for one class of bug. The orchestrator aggregates.
| Reviewer | Looks for |
|---|---|
| security | Injection, secret exposure, auth gaps, SSRF, path traversal. |
| performance | N+1 queries, work inside loops, unbounded allocations, slow I/O. |
| tests | Coverage gaps for new behavior, missing edge and error cases. |
| types | Types that fail to enforce invariants; illegal states left representable. |
| comments | Comments drifted from code, stale TODOs, comments that lie. |
| simplify | Dead code, duplication, needless complexity, missed reuse. |
Install /ultrareview and run it.
.claude/, then run /ultrareview on a real diff.- No repo of your own? Run this against any project folder, e.g. the capstone
agents/folder — make one small edit so there’s a diff. - Right-click → Save As to download ultrareview.md and the six agent files (security / performance / tests / types / comments / simplify).
- Place them:
mkdir -p .claude/commands .claude/agents, dropultrareview.mdinto.claude/commands/and the six agent files into.claude/agents/. (Full instructions in the README.) - Restart Claude Code. With your edit unstaged, run
/ultrareview.
## Security, ## Performance, ## Tests, ## Types, ## Comments, ## Simplify — plus a ranked “Top issues” list at the top.Stretch. Copy the pack into ~/.claude/commands/ and ~/.claude/agents/ so /ultrareview works in every repo.
The security review agent.
The reviewer that pays for itself the first time it catches an injection. Run it on every PR; run it again before every deploy.
--- name: security-review description: Review the diff for security issues. Run before every merge. tools: [Bash, Read, Grep] --- You are a senior application-security engineer reviewing a pull request for a SaaS service. Check for: 1. SQL injection — raw SQL with f-strings or string concatenation 2. Command injection — subprocess calls with user input 3. Path traversal — user input in file paths without normalization 4. Secret exposure — tokens / passwords / keys logged or in errors 5. Auth gaps — endpoints without auth, or auth checked AFTER work 6. SSRF — user-supplied URLs fetched without validation 7. Unsafe deserialization (use only safe loaders) 8. CORS gaps — wildcards in production 9. Dependency vulnerabilities — recent CVEs in pinned versions 10. Logging hygiene — PII or secrets in log lines For each finding: - Severity: BLOCKER / HIGH / MEDIUM / LOW - File:line - Quote the problem line - Suggested fix No preamble. Just the table.
Run the security-review agent.
- No repo of your own? Run this against any project folder, e.g. the capstone
agents/folder — or reuse the security agent you installed in §15.03.02. - Save the prompt above to
.claude/agents/security-review.md(or use the installed one). Restart Claude Code. - On a branch with recent changes, ask Claude to run the security review over the diff.
- Read every finding. Tag each: BLOCKER / HIGH / MEDIUM / LOW. Fix BLOCKERs before merging.
file:line and a quoted line — not prose — covering the diff you gave it.Stretch. Wire it into CI as an automated review comment on every PR.
/ultrareview workflow.
One slash command dispatches the six reviewers in parallel against the current diff. Returns a ranked report. Use before every merge.
Gate a merge on /ultrareview.
/ultrareview before a real merge and act on the ranked report — the way the CI gate will.- Use the pack you installed in §15.03.02 (code-examples/ultrareview/). No repo of your own? Run against any project folder, e.g. the capstone
agents/folder. - On a branch you’re about to merge, run
/ultrareview main(review everything sincemain). - Read the “Top issues” list. Apply the merge rule: any BLOCKER or HIGH — fix before you merge.
- Re-run
/ultrareview mainafter the fix to confirm it cleared.
Stretch. Add /ultrareview to your PR checklist so the gate runs before every merge, not just this one.
Deploy pipeline.
Three environments. Two gates. One easy rollback. Boring on purpose.
# main branch → preview env → staging → production # (auto) (manual) (manual after staging soak) # CI gates (must pass before each promotion): # 1. pytest + mypy + ruff (all tests pass) # 2. /ultrareview (no BLOCKER or HIGH security findings) # 3. integration tests against staging DB # 4. Playwright e2e against preview URL # Rollback: git revert + redeploy. 90 seconds.
Write your deploy.md and wire previews.
- No repo of your own? Run this against any project folder, e.g. the capstone
agents/folder. - Write a one-page
deploy.md: for each environment, what triggers a deploy, who can promote, and the CI gates from the block above (tests,/ultrareview, integration, e2e). - Pick a host (Vercel / Netlify / Cloudflare Pages / Fly). Connect the repo; enable preview deployments per branch.
- Push a small change. Confirm the preview URL appears on the PR.
deploy.md exists and names every gate, and the open PR shows a clickable preview URL that loads the change.Stretch. Add the “90-second rollback” (git revert + redeploy) to deploy.md and test it once on staging.
Secrets & auth.
Get this right once on day three and forget it. Get it wrong and you’ll find a token in a public repo on day forty.
- Secrets live in the platform’s secret manager. Render/Fly/Vercel environment variables, not .env files committed.
- .env is gitignored. .env.example is committed with placeholder values.
- Gitleaks runs in pre-commit AND in CI. Two layers; the second catches your team.
- Rotate tokens quarterly. Especially the GitHub PAT used by the MCP server.
- Use a managed auth service. Clerk, Auth.js, Stytch. Do not write password hashing yourself.
Scan for secrets and move them.
- No repo of your own? Run this against any project folder, e.g. the capstone
agents/folder. - Run
gitleaks detectacross history. - For every finding: rotate the secret if it’s real, then move it to the platform’s secret manager or an (un-committed)
.env. - Confirm
.envis in.gitignoreand.env.exampleholds only placeholders. Add the gitleaks pre-commit hook so this can’t recur.
gitleaks detect exits 0 (no leaks), and git check-ignore .env prints .env — proof it’s ignored.Stretch. Move secrets into a managed store (1Password, Vault, AWS Secrets Manager). A local .env should never reach git.
Observability from day one.
Adding observability after the first incident is twice as much work as adding it before. Three things on day one.
- Structured logging. Every log line is JSON with
level,request_id,user_id,route. Usestructlog(Python) orpino(Node). - Error tracking. Sentry or equivalent. Hooked into your framework’s exception handler.
- One dashboard. Requests/sec, p50/p90 latency, error rate, cost per day. Practice 14 covers the agent-specific stack on top.
Add the three day-one signals.
- No repo of your own? Run this against any project folder, e.g. the capstone
agents/folder — logging applies to any process. - Structured logging: make every log line JSON with
level,request_id,user_id,route(structlog / pino). - Error tracking: hook Sentry (or equivalent) into the framework’s exception handler.
- Add 3 alerts — error rate > 1%, p99 latency > 2s, availability < 99.5% — then trigger a fake breach for each.
Stretch. Add a 4th alert: daily cost. AI bills spike silently — set a budget threshold. See Practice 14 for the agent-specific stack.
The post-launch loop.
Shipping is the start, not the end. The five Friday-afternoon rituals that keep a SaaS healthy.
- Read the dashboard. 15 minutes. Note what changed and why.
- Read three random support threads. User language is the real spec.
- Run /ultrareview against main. Surface what slipped in during the week.
- Update CLAUDE.md. Every correction you made twice this week belongs in the file.
- Write one paragraph to your team. What shipped, what’s next, what you learned.
Run the Friday loop once, now.
- No repo of your own? Run this against any project folder, e.g. the capstone
agents/folder. - Read the dashboard (or your logs) for 15 min; jot what changed. Skim three support threads (or three issues).
- Run
/ultrareview mainagainst the main branch. Note anything that slipped in. - Update CLAUDE.md with any correction you made twice this week. Write one paragraph to your team (what shipped, what’s next).
/ultrareview report, a CLAUDE.md diff (git diff CLAUDE.md is non-empty), and a sent paragraph.Stretch. Put the loop on your calendar for every Friday. The compound is in the consistency, not the heroics.