From Vibe Coder
to Professional Developer
A hands-on curriculum that takes you from zero to deploying real apps using the exact tools, workflow, and standards we use at Code Impact every day.
What you'll be able to do when you finish
Navigate like a pro
Use the Terminal and VS Code fluently — no more hunting in Finder.
Use Git without fear
Create branches, write commits, open PRs — and understand what you're doing.
Work with Claude Code
Run the Code Impact slash command workflow from start to deploy, the way the team does it.
Ship a real app
Build and deploy a full-stack TypeScript app on Railway using our stack — React + Node + Postgres.
The 7 Modules
Terminal Basics
- Mac filesystem & navigation
- Files, folders, permissions
- Running programs & scripts
- Environment variables & .env files
- Installing tools with Homebrew & npm
VS Code Setup
- Installation & essential extensions
- Keyboard shortcuts that matter
- Integrated terminal
- Explorer, search, source control panel
Git & GitHub
- How version control works
- Clone, commit, push, pull
- Branches & merge strategy
- Opening PRs & code reviews
- Conventional commits
Claude Code & The Code Impact Workflow
- CLAUDE.md — the project brain
- Plan Mode before every build
- Worktrees: the parallel dev system
- Two VS Code windows rule
- All 18 slash commands
- Hard developer rules & daily rituals
- CI/CD, /release-checklist, /client-handoff
- Incident response (P1/P2/P3)
- Impact OS & Cowork setup
The Code Impact Stack
- TypeScript fundamentals
- React components & hooks
- Node.js + Express APIs
- PostgreSQL + Prisma ORM
- Docker: Dockerfiles, Compose, multi-service stacks
- Dev vs production image patterns
Pipelines & Railway
- GitHub Actions CI — writing your first pipeline YAML
- Dev, staging, and production environments
- Railway: connect repo, provision Postgres, deploy services
- Disabling auto-deploy — deliberate releases only
- Prisma in production: migrate deploy vs migrate dev
- Reading logs and debugging failed deploys
Capstone: Build Orbit
- Personal OS — Dashboard, Calendar, Tasks, Reminders, Profile
- Product brief with AC — you design the schema, architecture, and issues
- Claude API integration for AI daily briefing
- GitHub Milestones + worktree workflow throughout
- Deploy to Dev, verify AC, then deploy to Production
Terminal Basics
The terminal is where professional developers live. It's faster, more powerful, and essential for running servers, managing Git, and using Claude Code. You'll use it every single day at Code Impact.
What Is the Terminal?
45 minThe Terminal lets you control your Mac by typing commands instead of clicking. Open it with Cmd + Space — type "Terminal". You'll see a prompt like this:
tsm@mac ~ % # This is your prompt. ~ means your home directory.
Windows Terminal lets you control your PC by typing commands instead of clicking. Install it from the Microsoft Store (search "Windows Terminal") or it may already be installed. Open it, then launch PowerShell. You'll see a prompt like this:
PS C:\Users\you> # PS = PowerShell. C:\Users\you is your home directory.
The most important commands for navigating:
| Command | What it does | Example | |
|---|---|---|---|
pwd | Print working directory (where am I?) | pwd → /Users/you | pwd → C:\Users\you |
ls | List files in current folder | ls or ls -la | |
cd | Change directory | cd Documents | |
mkdir | Make a new folder | mkdir my-project | |
touchni | Create an empty file | touch index.htmlni index.html | |
rm | Remove file (careful!) | rm old-file.txt | |
cp | Copy file | cp a.txt b.txt | |
mv | Move or rename | mv a.txt new-name.txt | |
cat | Print file contents | cat README.md | |
open . | Open current folder in Finder | open . | |
start . | Open current folder in Explorer | start . |
Tab. If there's only one match, it completes instantly. If there are multiple matches, press Tab twice to see all options. This works for file names, directory names, command names, and even branch names with git checkout. ↑ arrow: Pressing ↑ cycles through your recent commands. Press ↑ twice to go two commands back. Ctrl+R lets you search your history by typing part of a previous command.Navigate your machine
In Terminal, complete these steps:
Installing Developer Tools
90 minRun these in order. Don't skip ahead — each step may depend on the previous one.
Step 1 — Xcode Command Line Tools
xcode-select --install
Step 2 — Homebrew (Mac package manager)
brew install [name]./bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Step 3 — Node.js (via nvm)
brew install nvm # Add nvm to your shell (follow the output instructions) nvm install 20 nvm use 20 nvm alias default 20 node --version # should print v20.x.x npm --version
Step 4 — Git
brew install git
git --version
# Configure your identity
git config --global user.name "Your Name"
git config --global user.email "you@codeimpact.ai"
Step 5 — Claude Code
npm install -g @anthropic-ai/claude-code claude --version
Step 6 — Docker Desktop
Download Docker Desktop from docker.com and install it. Then verify:
docker --version docker compose version
Step 1 — Windows Terminal
Install from the Microsoft Store — search "Windows Terminal" and click Install. Then open it and select PowerShell as the default profile.
Step 2 — winget (Windows package manager)
winget install [name] installs any developer tool.# Verify winget is available: winget --version # If not found, update "App Installer" in the Microsoft Store
Step 3 — Node.js (via nvm-windows)
# Install nvm-windows via winget: winget install CoreyButler.NVMforWindows # Close and reopen Windows Terminal, then: nvm install 20 nvm use 20 node --version # should print v20.x.x npm --version
Step 4 — Git
winget install Git.Git # Close and reopen Windows Terminal, then: git --version # Configure your identity git config --global user.name "Your Name" git config --global user.email "you@codeimpact.ai"
Step 5 — Claude Code
npm install -g @anthropic-ai/claude-code claude --version
Step 6 — Docker Desktop
Download Docker Desktop from docker.com and install it. Then verify:
docker --version docker compose version
Verify your setup
Environment Variables & .env Files
60 min.env file on your local machine, and have the app read them from there. Every developer and every server has their own .env with their own values — the code never changes, only the environment does.A Code Impact project has a frontend and a backend — and each has its own set of variables. Keep them in one .env file at the repo root, divided by commented sections so it's clear which vars belong where:
# ── BACKEND ────────────────────────────── DATABASE_URL=postgresql://user:password@localhost:5432/mydb JWT_SECRET=any-local-string PORT=3000 NODE_ENV=development # ── FRONTEND ───────────────────────────── VITE_API_URL=http://localhost:3000
VITE_ — Vite only exposes vars with that prefix to the browser. Backend vars have no prefix requirement. Keeping them in clearly labelled sections avoids confusion about which service reads what..gitignore. If you commit a secret, you must rotate it immediately. This is one of the most common beginner mistakes.Every project has a .env.example that lists all required variables — both frontend and backend sections — without real values. When you clone a project, copy it to .env and fill in real values:
cp .env.example .env
# Now edit .env with your actual secrets
Create and read a .env file
Running Programs & npm Scripts
75 minpackage.json is like a recipe card at the root of every JavaScript project — it lists every library the project needs (dependencies) and every command you can run (scripts). When you clone a project and run npm install, npm reads package.json and downloads all the listed libraries into a folder called node_modules. The scripts section defines shortcuts like npm run dev so you don't have to remember the full command.Every Code Impact project uses npm scripts to run common tasks. You'll see these patterns in every repo:
# Install all dependencies npm install # Start the development server npm run dev # Build for production npm run build # Run tests npm run test # Lint the code npm run lint # TypeScript type check npm run typecheck
These are defined in package.json — always check that file when you join a new project.
npm run lint && npm run typecheck && npm run test. The && means "run the next command only if the previous one passed." So if lint fails, typecheck and tests won't even run — this is intentional. Fix failures left to right. If lint fails, fix it before worrying about types. If types fail, fix those before running tests. In Module 4, Claude Code's /verify command runs all three automatically.Terminal Power Habits
60 min| Shortcut / Trick | What it does |
|---|---|
Ctrl + C | Stop a running process (use this constantly) |
Ctrl + L | Clear the terminal screen |
↑ / ↓ | Navigate command history |
Tab | Autocomplete file/folder names |
Cmd + T | Open a new terminal tab |
Ctrl + Shift + T | Open a new terminal tab (Windows Terminal) |
cmd1 && cmd2 | Run cmd2 only if cmd1 succeeds |
cmd1 | cmd2 | Pipe output of cmd1 into cmd2 |
grep "text" file | Search for text in a file |
history | See all past commands |
which node | Find where a program is installed |
Terminal fluency check
VS Code Setup
VS Code is the editor every Code Impact developer uses. Getting it set up right from day one makes Claude Code work better and prevents a whole class of bugs before they happen.
Installation & Essential Extensions
45 minDownload VS Code at code.visualstudio.com. Once installed, open it and install these extensions (Cmd+Shift+X to open Extensions panel):
| Extension | Why you need it |
|---|---|
| Claude Code | The Code Impact AI workflow lives here — chat, slash commands, and Plan Mode all run inside VS Code via this extension |
| ESLint | Shows code errors as you type — required at Code Impact |
| Prettier — Code formatter | Auto-formats your code on save |
| TypeScript + JavaScript (built in) | Type checking and autocompletion |
| Prisma | Syntax highlighting for .prisma schema files |
| GitLens | Shows git blame, history inline |
| DotENV | Syntax highlighting for .env files |
| Error Lens | Shows errors inline on the line that has the problem |
| Tailwind CSS IntelliSense | Autocomplete for Tailwind class names |
Then add this to your VS Code settings.json (Cmd+Shift+P → "Open User Settings JSON"):
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.tabSize": 2,
"editor.fontSize": 14,
"js/ts.preferences.importModuleSpecifier": "relative",
"files.trimTrailingWhitespace": true
}
The Layout — What Everything Is
30 min| Panel / Shortcut | What it does |
|---|---|
Explorer (Cmd+Shift+E) | File tree — browse and open files |
Search (Cmd+Shift+F) | Search across all files in the project |
Source Control (Cmd+Shift+G) | Git — see changed files, stage, commit |
Terminal (Ctrl+`) | Integrated terminal — runs inside VS Code |
Command Palette (Cmd+Shift+P) | Run any command by typing its name |
Quick Open (Cmd+P) | Open any file by typing part of its name |
Problems panel (Cmd+Shift+M) | See all TypeScript / lint errors at once |
code . — not by double-clicking files in Finder. In Terminal, navigate to the project folder (cd ~/Documents/code-impact-training) then run code .. This opens the entire project so VS Code's file search (Cmd+Shift+F), the integrated terminal, and Claude Code all know which project they're working in. Opening individual files with File → Open gives you disconnected files with no project context — you lose autocomplete, go-to-definition, and project-wide search.Keyboard Shortcuts That Matter
45 min| Shortcut (Mac) | Action |
|---|---|
Cmd+P | Quick open file by name (use this instead of clicking the file tree) |
Cmd+Shift+P | Command palette — run any action |
Cmd+/ | Comment / uncomment selected lines |
Alt+Click | Add multiple cursors — edit many lines at once |
Cmd+D | Select next occurrence of selected text |
Cmd+Shift+L | Select ALL occurrences of selected text |
Option+Up/Down | Move a line up or down |
Cmd+B | Toggle sidebar |
Cmd+Shift+F | Search across entire project |
F12 | Go to definition of a function/type |
Shift+F12 | Find all references to a function/type |
Cmd+Z / Cmd+Shift+Z | Undo / Redo |
| Shortcut (Windows) | Action |
|---|---|
Ctrl+P | Quick open file by name (use this instead of clicking the file tree) |
Ctrl+Shift+P | Command palette — run any action |
Ctrl+/ | Comment / uncomment selected lines |
Alt+Click | Add multiple cursors — edit many lines at once |
Ctrl+D | Select next occurrence of selected text |
Ctrl+Shift+L | Select ALL occurrences of selected text |
Alt+Up/Down | Move a line up or down |
Ctrl+B | Toggle sidebar |
Ctrl+Shift+F | Search across entire project |
F12 | Go to definition of a function/type |
Shift+F12 | Find all references to a function/type |
Ctrl+Z / Ctrl+Y | Undo / Redo |
Open your project in VS Code
Understanding the File Tree & Project Structure
60 minEvery Code Impact project follows a consistent folder structure. Here's what you'll see:
my-project/ ├── frontend/ # React app (Vite) │ └── src/ │ ├── pages/ # Full-page components │ ├── components/ # Reusable UI pieces │ ├── hooks/ # Custom React hooks │ ├── lib/ # Utility functions │ └── types/ # TypeScript definitions ├── backend/ # Node.js/Express API │ └── src/ │ ├── controllers/ # Route handlers (thin) │ ├── services/ # Business logic (fat) │ ├── routes/ # Express route definitions │ ├── middleware/ # Auth, error handling │ └── utils/ # Shared helpers ├── backend/prisma/ # Database schema + migrations ├── docs/ # Reference documentation ├── CLAUDE.md # ★ Claude reads this every session ├── .env # ★ NEVER committed to Git ├── .env.example # ★ Always committed (no real values) └── package.json # Scripts and dependencies
CLAUDE.md at the root is the most important file in any Code Impact project. It tells Claude Code everything about the project — stack, commands, standards, architecture. Always read it when you join a project.Git & GitHub
Git is how you save your work, collaborate with teammates, and never lose code. At Code Impact, every feature lives in its own branch and nothing goes to production without a reviewed PR.
How Git Works (The Mental Model)
45 minThink of Git as a timeline of snapshots. Every time you commit, you save a snapshot of your code. You can go back to any snapshot at any time. GitHub hosts your timeline in the cloud.
Working directory
Your files right now — what you see in VS Code.
Staging area
Files you've selected to include in the next commit. Like a cart before checkout.
Commit
A saved snapshot. Every commit has a message describing what changed.
Remote (GitHub)
The backup in the cloud. Push to share; pull to get others' changes.
Core Git Commands
60 min# --- Starting work --- git clone https://github.com/org/repo.git # Download a repo git status # See what's changed git pull # Get latest from remote # --- Saving work --- git add filename.ts # Stage a specific file git add . # Stage all changes (use carefully) git commit -m "feat(auth): add login form" # Commit with a message git push # Upload to GitHub # --- Branches --- git branch # List all branches git checkout -b feat/42-user-login # Create + switch to new branch git checkout main # Switch back to main git merge feat/42-user-login # Merge a branch in # --- Inspecting --- git log --oneline # See commit history git diff # See unstaged changes git diff --staged # See staged changes
Your first Git repo
Conventional Commits
30 minAt Code Impact, every commit follows this format. It keeps the history readable and makes automated changelogs possible.
type(scope): short description feat(auth): add JWT refresh token rotation fix(api): handle null user in getProfile chore(deps): upgrade react to 19.1 docs(readme): add Railway deployment steps test(auth): add coverage for login failure case refactor(services): extract email to shared util
| Type | When to use it |
|---|---|
feat | A new feature visible to users or API consumers |
fix | A bug fix |
chore | Maintenance (deps, config, tooling) |
docs | Documentation only |
test | Adding or fixing tests |
refactor | Code change that doesn't add features or fix bugs |
perf | Performance improvement |
Write your first conventional commits
The Branch → PR Workflow
75 minThis is the workflow for every single feature at Code Impact. Nothing goes directly to main.
git checkout -b feat/42-user-loginfeat/[issue-number]-[short-title] for new features or fix/[issue-number]-[short-title] for bug fixes. Keep the title lowercase with hyphens, under 30 characters. Examples: feat/42-user-auth, fix/51-null-task-crash. If there's no GitHub issue yet, create one first — branches without issue numbers are nearly impossible to track weeks later. The issue number is what links the PR back to the original specification.The commands in full:
# Start a new branch for your issue git checkout -b feat/42-short-title # Stage and commit as you work git add . git commit -m "feat(scope): description" # Push to GitHub and open a PR git push -u origin feat/42-short-title # After the PR is merged — sync main git checkout main git pull
Setting Up GitHub & Connecting SSH
60 minCreate a GitHub account if you don't have one. Then set up SSH authentication so you don't have to type your password every time you push:
# Generate an SSH key ssh-keygen -t ed25519 -C "you@codeimpact.ai" # Press Enter for all prompts # Copy the public key to your clipboard cat ~/.ssh/id_ed25519.pub | pbcopyGet-Content ~/.ssh/id_ed25519.pub | Set-Clipboard # Then go to GitHub → Settings → SSH and GPG keys → New SSH key # Paste your key and save # Test the connection ssh -T git@github.com
Full GitHub flow
Claude Code & The Code Impact Workflow
This is the module that separates Code Impact developers from everyone else. You'll learn the full feature lifecycle — from morning ritual to merged PR — using Claude Code's 18 slash commands, git worktrees for parallel development, automated CI gates, and the release and delivery process.
What Claude Code Is (and Isn't)
30 minClaude Code is an agentic coding assistant that runs in your terminal. Unlike copy-pasting from a chat interface, Claude Code can read your files, run commands, edit code, and execute project scripts — all with your oversight. It is not autopilot. You remain the architect.
Claude Code can
Read files, write and edit code, run terminal commands, search the codebase, open PRs, run tests, follow project slash commands.
You always own
Architecture decisions, plan approval, code review, understanding what is being built and why. Never let Claude make a plan you can't explain.
# Install Claude Code globally (one-time): npm install -g @anthropic-ai/claude-code # Start Claude Code inside any project folder: claude # Run a one-shot question: claude "what does this project do?" # Run a slash command: /feature-start 42
CLAUDE.md — The Brain of Every Project
60 minCLAUDE.md sits at the root of every Code Impact project. Claude Code reads it automatically at the start of every session. It is the project's constitution — stack, commands, standards, architecture, known gotchas. Read it before writing a single line on any project.
# CLAUDE.md — code-impact-training (example) ## Project - What: Training repo — full-stack app built through the Code Impact curriculum - Stack: Node.js/TypeScript — React (Vite) frontend + Express API - Database: PostgreSQL via Prisma (Railway) - Deploy: Railway via Docker, CI/CD via GitHub Actions ## Commands npm run dev # Start dev server npm run test # Jest tests npm run lint:fix # ESLint auto-fix npm run typecheck # tsc --noEmit ## Code Standards - No any types — use unknown and narrow - Controller → Service → Prisma (never put business logic in controllers) - Never console.log in production — use the structured logger ## Architecture - Frontend: React + Vite + TypeScript + Tailwind, port 5173 - Backend: Express + TypeScript, port 3001 ## Known Gotchas - Run migrations before starting the backend: npx prisma migrate dev - .env is never committed — copy .env.example and fill in real values
npm run dev to docker compose up), update it. When you add a new service or change the architecture, update the architecture section. A stale CLAUDE.md is worse than none — Claude will act on outdated information with confidence. A simple rule: if something surprised you while working on the project, add it to Known Gotchas so the next developer (or future you) doesn't hit the same wall.Plan Mode — Think Before You Build
45 minThe single most important habit in the Code Impact workflow: never start building until you have an approved plan. Plan Mode lets Claude analyze the codebase and propose an approach — without writing any code yet. You review and approve (or push back) before a single file is touched.
# Tell Claude to use Plan Mode before writing any code:
"Before writing anything, give me a plan for adding user authentication.
Use Plan Mode — list the files you'll touch, the order of operations,
and any risks. I'll approve before you start."
A good plan includes:
| Element | Example |
|---|---|
| Problem being solved | "Add JWT-based auth to protect API routes" |
| Files created or changed | auth.service.ts, auth.middleware.ts, routes/auth.ts |
| Order of operations | 1. Schema → 2. Service → 3. Routes → 4. Middleware |
| Risks or dependencies | "Token refresh requires Redis — must be running" |
Worktrees — The Parallel Development System
75 minGit worktrees are how Code Impact works on multiple features at the same time without branches stepping on each other. Each feature gets its own directory on disk — a full checkout of the repo, on its own branch, completely isolated.
The Directory Structure
Worktrees live as siblings to the main project directory — never inside it. /feature-start creates them automatically with a consistent naming convention.
~/Documents/ ├── code-impact-training/ # ★ Main project (VS Code Window 1) ├── code-impact-training-feat-42/ # ★ Worktree for issue #42 (Window 2) └── code-impact-training-feat-51/ # ★ Worktree for issue #51 (if needed)
code-impact-training-feat-42/ are completely isolated from code-impact-training-feat-51/. Git handles the shared object store underneath — no duplication of history.The Two VS Code Windows Rule
This is a hard rule, not a suggestion. Every worktree gets its own VS Code window.
| Window | Opens | Terminal runs |
|---|---|---|
| Window 1 (always open) | Main project: code-impact-training/ | Git management, /parallel-status, PR reviews |
| Window 2 (per feature) | Worktree: code-impact-training-feat-42/ | claude — this is where you build the feature |
File → Open Folder or code ~/Documents/code-impact-training-feat-42). Mixing worktrees in one window causes Claude to work in the wrong context and Git to get confused.Terminal State Checklist
Before writing a single line of code in a worktree, run these three checks. All three must pass.
# Check 1 — Are you in the right directory? pwd # Expected: /Users/you/Documents/code-impact-training-feat-42 # NOT: /Users/you/Documents/code-impact-training — wrong! # Check 2 — Are you on the right branch? git branch # Expected: * feat/42-add-auth # NOT: * main — wrong! # Check 3 — Does .env exist? ls .env # Expected: .env # If missing: cp ../code-impact-training/.env .env
/feature-start automatically copies .env from the parent project into the worktree. If you ever create a worktree manually (rare), run cp ../code-impact-training/.env .env yourself. Never commit .env — it's in .gitignore./clean-worktree — After the PR Merges
After a PR is merged, the worktree is done. Run /clean-worktree from the main project window to remove it cleanly.
# In Window 1 (main project), after PR is merged: /clean-worktree 42 # This command: # 1. Removes the worktree from Git tracking (git worktree remove) # 2. Deletes the directory on disk # 3. Prunes the remote branch reference
rm -rf a worktree directory manually. Git tracks worktrees internally — deleting the folder without telling Git leaves orphaned references that cause errors. Always use /clean-worktree.~/.claude/commands/. That's it — they'll be available in every Claude Code session immediately.
cp -r ~/Downloads/commands/. ~/.claude/commands/
Worktree mechanics — add CLAUDE.md
Create a real GitHub issue, work on it in a worktree, and merge it. The task: add a CLAUDE.md to the repo root.
The Full 18-Command Workflow
90 minEvery feature at Code Impact follows the same loop. Each step is a Claude Code slash command. These aren't suggestions — they're the workflow. Running them in order is what makes PRs mergeable and deployments predictable.
The Feature Loop (run in this order)
Creates a sibling worktree directory, checks out a new branch
feat/N-title, opens a draft PR immediately (so CI starts running), and copies .env. Open the new worktree in VS Code Window 2. All feature work happens here.Inside the worktree (Window 2): reads the GitHub issue, enters Plan Mode, waits for your plan approval, then builds using TDD. Claude writes tests first, then makes them pass. Calls
/verify internally — lint + typecheck + tests run as part of this step, not separately.Starts the full Docker stack (
docker compose up) and prints a manual testing checklist derived from the issue's acceptance criteria. Work through every item. This catches what automated tests miss.Run if the feature touches auth, user data, payments, or external integrations. Audits for OWASP-aligned vulnerabilities. Skip for purely UI or config changes.
Run if the feature adds DB queries, list rendering, search, or new packages. Checks for N+1 queries, bundle size, and slow renders. Skip for trivial changes.
Commits any review fixups, pushes the branch, writes the PR body, and marks the PR ready for review. Wait for CI to pass and a reviewer to approve, then squash merge.
After the PR is merged: run from Window 1 (main project). Removes the worktree directory and prunes the branch. Keeps your project directory clean.
Each step hands off to the next. /build-feature is the only command that calls another command internally (/verify) because testing is part of the build, not a separate gate. Everything else is human-initiated.
All 18 Commands — Quick Reference
| Command | When to use |
|---|---|
/project-init | Once, on a new repo. Sets up ESLint, Prettier, commitlint, lint-staged, PR templates. |
/prd-to-issues | Give it a PRD or brief. Breaks it into structured GitHub issues with acceptance criteria. |
/parallel-status | Every morning. Shows active worktrees and their PR status. Your daily starting point. |
/feature-start [N] | Starting a new feature. Creates worktree + branch + draft PR. |
/build-feature [N] | Building the feature. Plan → approve → TDD build. |
/verify | Called internally by /build-feature — not a manual step in the feature loop. |
/security-review | Before any PR touching auth, payments, or user data. |
/performance-review | Before any PR adding queries or complex UI. |
/human-test [N] | Manual QA. Starts Docker stack + checklist from ACs. |
/create-pr [N] | Finalizing PR. Commit fixups → push branch → write PR body → mark ready for review. |
/clean-worktree [N] | After PR merged. Removes worktree directory cleanly. |
/fix-issue [N] | Small bugs. Skips worktree for trivial fixes. |
/hotfix [N] | Production emergencies. Fast-path fix and deploy. |
/update-deps | Dependency updates. Safe incremental upgrade with test verification. |
/release-checklist | Before any production release. 11 checkpoints. |
/client-handoff | End of sprint. 5-step structured delivery to the client. |
/incident-response | Production incidents. P1/P2/P3 severity response workflow. |
/update-focus | Every Monday. Updates your weekly context in Impact OS. |
Parallel development — 2 worktrees, 3 windows
This is the real worktree workflow. You'll run two features simultaneously — one to scaffold the backend, one to scaffold the frontend — each in its own worktree and VS Code window, both running at the same time. This is how you'll work at Code Impact every day.
Step 1 — Create both issues first
Step 2 — Start both worktrees from Window 1
Step 3 — Build both features simultaneously
Step 4 — Verify and ship both
Developer Hard Rules & Daily Habits
45 minThese are the three rules the team never breaks. They exist because the times they were ignored cost hours of lost work. Memorize them.
The 3 Hard Rules
Rule 1 — Never work on main
Every piece of work goes on a feature branch created by /feature-start. Direct commits to main are blocked by branch protection. If you find yourself on main — stop and create a branch.
Rule 2 — Max 2 active worktrees
You can have at most 2 open worktrees at any time. Starting a third means you have too much in flight. Finish and merge something first. More open PRs = slower reviews for everyone.
Rule 3 — Reviews before new features
If 2 or more PRs are already waiting for review — yours or a teammate's — stop starting new features. Review the PRs first. Unreviewed PRs accumulate merge conflicts and block everyone.
The Morning Ritual — /parallel-status
Before touching any code, every morning starts with one command from the main project window:
# From Window 1 (main project), every morning: /parallel-status # Output shows: # Active worktrees and which issue they're tracking # PR status for each: draft / in review / approved / merged # Any PRs from teammates awaiting your review # Whether you're clear to start new work
/parallel-status shows 2+ PRs awaiting review, do not run /feature-start. Do the reviews first. This keeps the team unblocked and prevents a pile-up of merge conflicts.Keeping Your Branch Current
# Every morning, in each active worktree (Window 2): git fetch origin git merge origin/main -m "Merging main into branch" # This keeps your feature branch current. # /create-pr does this automatically — but do it daily # to catch conflicts while they're still small.
Day One Checklist
When you join Code Impact and get access to your first real project:
- Clone the project repo:
git clone [repo-url] - Install dependencies:
npm install - Read
CLAUDE.mdfully before anything else - Copy
.env.exampleto.envand fill in values the team provides - Start the app locally and confirm it runs:
docker compose up - Run
/parallel-statusto see current work in flight - Ask the team which GitHub issue to pick up first
The Code Impact Stack
TypeScript, React, Node.js, PostgreSQL, and Prisma. You don't need to master these before the capstone — you need enough to read code, understand what Claude is building, and know where things live.
TypeScript Fundamentals
75 min.ts files instead of .js files, and the TypeScript compiler (tsc) translates them to JavaScript that Node and browsers can run.TypeScript is JavaScript with types. The types catch bugs before they happen. At Code Impact: strict mode, no any, use unknown when unsure.
// Types type User = { id: string name: string email: string createdAt: Date } // Functions with typed params + return function greet(user: User): string { return `Hello, ${user.name}` } // Arrays and optionals type Task = { id: string title: string completed: boolean dueDate?: Date // ? means optional tags: string[] // array of strings } // Enums type Status = 'todo' | 'in_progress' | 'done' // Generics — a function that works with any type function first<T>(arr: T[]): T | undefined { return arr[0] }
any. If you don't know the type, use unknown and narrow it. any silently disables all type checking — that's the opposite of why we use TypeScript.Write your first TypeScript types
🌿 Use the branching workflow from Module 3 — git checkout -b feat/[description], commit your changes, push, and open a PR before moving on.
React Components & Hooks
75 minReact builds UIs from components. At Code Impact: functional components only, named exports, hooks over classes.
// A typed React component type TaskCardProps = { task: Task onComplete: (id: string) => void } export function TaskCard({ task, onComplete }: TaskCardProps) { return ( <div className="p-4 bg-white rounded-lg shadow"> <h3>{task.title}</h3> <button onClick={() => onComplete(task.id)}> Mark done </button> </div> ) } // useState — local component state import { useState } from 'react' export function Counter() { const [count, setCount] = useState(0) return <button onClick={() => setCount(c => c + 1)}>{count}</button> } // useEffect — side effects (fetching, subscriptions) import { useEffect, useState } from 'react' export function TaskList() { const [tasks, setTasks] = useState<Task[]>([]) useEffect(() => { fetch('/api/v1/tasks') .then(r => r.json()) .then(data => setTasks(data.data)) }, []) // [] = run once on mount return <ul>{tasks.map(t => <li key={t.id}>{t.title}</li>)}</ul> }
Write your first React component
🌿 Use the branching workflow from Module 3 — git checkout -b feat/[description], commit your changes, push, and open a PR before moving on.
Node.js + Express APIs
60 minThe backend is a Node.js server built with Express. It follows a strict 3-layer pattern: Controller → Service → Prisma (database).
// routes/tasks.ts — just wires up endpoints import { Router } from 'express' import { TaskController } from '../controllers/taskController' const router = Router() router.get('/', TaskController.getAll) router.post('/', TaskController.create) router.patch('/:id', TaskController.update) export { router } // controllers/taskController.ts — thin, delegates to service export const TaskController = { getAll: async (req, res) => { const tasks = await TaskService.getAll(req.user.id) res.json({ success: true, data: tasks }) } } // services/taskService.ts — all the logic lives here export const TaskService = { getAll: async (userId: string) => { return prisma.task.findMany({ where: { userId }, orderBy: { createdAt: 'desc' } }) } }
{"{ success: true, data: T }"} or {"{ success: false, error: { code, message } }"}. Consistent shapes make the frontend code much simpler.Build your first API route end-to-end
🌿 Use the branching workflow from Module 3 — git checkout -b feat/[description], commit your changes, push, and open a PR before moving on.
Build a GET /api/v1/tasks route following the 3-layer pattern — Route → Controller → Service. No database yet — the service returns a hardcoded empty array. You'll wire this to Prisma in the capstone.
Unit Testing
45 minCode Impact uses Vitest for unit tests. It has the same API as Jest (describe, it, expect) and works natively with TypeScript. Install it in the backend:
npm install -D vitest
Add a test script to backend/package.json:
{
"scripts": {
"test": "vitest run"
}
}
All tests live in a tests/ directory at the backend root — not scattered through the source tree. Create it once and all test files go there. Here's an example testing a pure helper function:
// src/services/taskService.ts export function isOverdue(dueDate: Date): boolean { return dueDate < new Date() } // tests/taskService.test.ts import { describe, it, expect } from 'vitest' import { isOverdue } from '../src/services/taskService' describe('isOverdue', () => { it('returns true when due date is in the past', () => { const past = new Date(Date.now() - 86400000) expect(isOverdue(past)).toBe(true) }) it('returns false when due date is in the future', () => { const future = new Date(Date.now() + 86400000) expect(isOverdue(future)).toBe(false) }) })
Run tests from the backend directory:
npm run test
ci.yml runs npm run test on every push — a failing test blocks the PR.Write your first unit tests
🌿 Use the branching workflow from Module 3 — git checkout -b feat/[description], commit your changes, push, and open a PR before moving on.
PostgreSQL & Prisma ORM
75 minSELECT * FROM tasks WHERE user_id = $1, you write prisma.task.findMany({ where: { userId } }). Prisma is the ORM Code Impact uses. You define your data model in a schema.prisma file, and Prisma generates fully-typed TypeScript functions for every database operation. If you change the schema, you run a migration — a versioned SQL script that updates the database structure.Prisma is the database toolkit. You define your data model in schema.prisma, run a migration, and Prisma generates fully-typed database functions.
// prisma/schema.prisma — source of truth for data
model User {
id String @id @default(cuid())
email String @unique
name String
tasks Task[]
createdAt DateTime @default(now())
}
model Task {
id String @id @default(cuid())
title String
status Status @default(TODO)
userId String
user User @relation(fields: [userId], references: [id])
dueDate DateTime?
createdAt DateTime @default(now())
}
enum Status {
TODO
IN_PROGRESS
DONE
}
# After changing the schema, always run: npx prisma migrate dev --name "add-task-due-date" npx prisma generate # Regenerate TypeScript types
// Querying with Prisma — fully typed const tasks = await prisma.task.findMany({ where: { userId, status: 'TODO' }, orderBy: { createdAt: 'desc' }, take: 20 // limit results }) const task = await prisma.task.create({ data: { title, userId, status: 'TODO' } }) await prisma.task.update({ where: { id: taskId }, data: { status: 'DONE' } })
Create the Prisma schema
🌿 Use the branching workflow from Module 3 — git checkout -b feat/[description], commit your changes, push, and open a PR before moving on.
Docker — The Full Picture
2 hoursEvery Code Impact production project runs in Docker. Both the frontend and backend are containerized so they behave identically on your Mac, in CI, and on Railway. This lesson teaches you to write Dockerfiles from scratch and wire them together with Compose.
5a — What Docker Actually Does
Docker packages your app and everything it needs (Node version, system libraries, config) into an image. When you run the image, it becomes a container — an isolated process that behaves the same on every machine. Docker Compose orchestrates multiple containers together as a stack.
Image
A snapshot: your code + the runtime + dependencies. Built once, run anywhere. Defined in a Dockerfile.
Container
A running image. Isolated — it can't see other containers' files unless you wire them together.
Docker Compose
Orchestrates multiple containers (frontend, backend) as a single local stack. One command to start everything.
5b — Writing a Backend Dockerfile
The backend Dockerfile is a production build — it compiles TypeScript to JavaScript, then starts the compiled output. This is the same image used locally and on Railway. Pin the Node version — never use latest.
# backend/Dockerfile FROM node:20-alpine WORKDIR /app # Copy package files first — Docker caches this layer # if they haven't changed (faster rebuilds) COPY package*.json ./ RUN npm ci COPY . . # Compile TypeScript → dist/ RUN npm run build EXPOSE 3000 CMD ["node", "dist/server.js"]
FROM node:latest — pin the version. Never put ENV MY_SECRET=xyz in a Dockerfile — secrets are injected at runtime via environment variables in Railway or your .env file.5c — Writing a Frontend Dockerfile
The frontend Dockerfile follows the same pattern — install deps and run the Vite dev server.
# frontend/Dockerfile FROM node:20-alpine WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . EXPOSE 5173 CMD ["npm", "run", "dev"]
5d — Docker Compose: The Full Local Stack
Compose wires the frontend, backend, and a local PostgreSQL database together. The db service runs Postgres in a container — no installation required. The backend depends on it starting first.
# docker-compose.yml services: db: image: postgres:16-alpine environment: - POSTGRES_USER=postgres - POSTGRES_PASSWORD=password - POSTGRES_DB=orbit ports: - "5432:5432" # Exposed so you can run prisma migrate dev from your terminal volumes: - db_data:/var/lib/postgresql/data # Persist data between restarts backend: build: context: ./backend dockerfile: Dockerfile ports: - "3000:3000" environment: - NODE_ENV=development - DATABASE_URL=postgresql://postgres:password@db:5432/orbit # Uses service name, not localhost - JWT_SECRET=${JWT_SECRET} depends_on: - db frontend: build: context: ./frontend dockerfile: Dockerfile args: - VITE_API_URL=http://localhost:3000 ports: - "5173:5173" depends_on: - backend volumes: db_data:
localhost. That's why the backend's DATABASE_URL uses @db:5432 (the service name), and the frontend reaches the backend at http://backend:3000.@db:5432 (the compose service name). But when you run npx prisma migrate dev from your terminal, you're outside Docker — so your .env must use @localhost:5432:DATABASE_URL=postgresql://postgres:password@localhost:5432/orbitThe port
5432 is exposed by the db service above, so your terminal can reach it while compose is running.5e — Essential Docker Commands
| Command | What it does |
|---|---|
docker compose up --build | Build images and start the full stack |
docker compose up -d | Start in detached mode (runs in background) |
docker compose down | Stop and remove all containers |
docker compose down -v | Also remove volumes (wipes Redis data etc.) |
docker compose ps | See what's running + health status |
docker compose logs -f backend | Tail logs for a specific service |
docker compose exec backend sh | Open a shell inside the running backend container |
docker compose build --no-cache | Force a clean rebuild (when deps change) |
docker images | List all images on your machine |
docker system prune | Clean up unused images and containers (frees disk) |
5f — The .dockerignore File
Like .gitignore but for Docker builds. Always have one — it stops node_modules and secrets from being accidentally copied into images.
# .dockerignore (put in both frontend/ and backend/)
node_modules
dist
build
.env
.env.*
*.test.ts
*.spec.ts
coverage
.git
.DS_Store
.dockerignore, Docker copies your entire node_modules into the build context — this makes builds dramatically slower and can cause platform mismatches (Mac vs Linux binaries). Always include it.Containerize your training repo
🌿 Use the branching workflow from Module 3 — git checkout -b feat/[description], commit your changes, push, and open a PR before moving on.
You already have frontend/ and backend/ directories in code-impact-training. Now add Docker to both and wire them together.
Pipelines & Railway
Before you deploy a real app, you need to understand CI pipelines, environment management, and the deployment platform. This module gives you hands-on experience with all three so that Module 7's deploy phase is familiar, not new.
GitHub Actions — Your First CI Pipeline
45 min.github/workflows/ in your repo. GitHub reads it automatically. When you push code, GitHub spins up a fresh virtual machine, checks out your code, and runs every step you defined. If any step fails, the workflow fails — and if you've set up branch protection, the PR can't merge. This is how you catch lint errors, type errors, and broken tests before they ever hit main.Here's a complete CI workflow for a monorepo with frontend and backend:
# .github/workflows/ci.yml name: CI on: push: branches: ['**'] # Run on every push to any branch pull_request: branches: [main] jobs: backend: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' cache-dependency-path: backend/package-lock.json - run: npm ci working-directory: backend - run: npm run lint working-directory: backend - run: npm run typecheck working-directory: backend - run: npm run test working-directory: backend - run: npm run build working-directory: backend frontend: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' cache-dependency-path: frontend/package-lock.json - run: npm ci working-directory: frontend - run: npm run lint working-directory: frontend - run: npm run typecheck working-directory: frontend - run: npm run build working-directory: frontend
main. Enable "Require status checks to pass before merging" and select both the backend and frontend jobs. Now the merge button is greyed out until CI passes — the same gate Code Impact uses on every client project.Add CI to your training repo
🌿 Use the branching workflow from Module 3 — git checkout -b feat/[description], commit your changes, push, and open a PR before moving on.
Local, Dev, Staging, and Production Environments
60 min| Environment | Where it runs | Purpose | Database |
|---|---|---|---|
| Local | Your laptop | Build and iterate fast. Verbose errors, hot reload, fake data. Nothing here is shared. | Local Docker Postgres — wipe freely |
| Development | Railway (dev project) | Shared environment for the team. Test new changes and releases in an active deployed environment before they go to staging. | Railway dev DB — can be reset, not real data |
| Staging | Railway (staging project) | User Acceptance Testing (UAT). Client reviews work here before it goes live. Mirrors production exactly. | Railway staging DB — realistic but not real user data |
| Production | Railway (production project) | Live. Real users. Real data. Zero tolerance for unvetted changes. | Production Railway DB — never wipe this |
NODE_ENV — How Apps Change Behaviour
The NODE_ENV environment variable tells your app which mode it's running in. Apps use this to change behaviour automatically:
// In production: hide error details from users if (process.env.NODE_ENV === 'production') { res.json({ error: 'Something went wrong' }) } else { res.json({ error: err.message, stack: err.stack }) // local only }
| Env var | Local | Development | Staging | Production |
|---|---|---|---|---|
NODE_ENV | development | production | production | production |
DATABASE_URL | Local Docker DB | Railway dev DB | Railway staging DB | Railway production DB |
JWT_SECRET | Any string | Random secret | Different random secret | Different random secret |
ANTHROPIC_API_KEY | Your personal key | Team key | Team key | Team key |
VITE_API_URL | http://localhost:3000 | Dev backend URL | Staging backend URL | Production backend URL |
JWT_SECRET and its own database URL. If someone compromises dev or staging, they shouldn't automatically have production access.Update your training repo's .env.example
🌿 Use the branching workflow from Module 3 — git checkout -b feat/[description], commit your changes, push, and open a PR before moving on.
Your training repo has accumulated several env vars across modules. Make sure .env.example documents all of them properly.
Railway — Connect, Configure, Deploy
90 min- Projects — the top-level container. You create one project per app — e.g.
code-impact-training. Inside that project you have multiple environments. - Environments — Railway gives you Development, Staging, and Production environments inside every project. Each environment has its own services, its own database, and its own env vars — completely isolated from each other.
- Services — what lives inside an environment. Your backend is one service, your frontend is another. Each service connects to a GitHub repo and builds from a Dockerfile.
- Databases — Railway provisions a managed PostgreSQL database inside an environment with one click. It generates a
DATABASE_URLautomatically and can inject it directly into your services. - Variables — each service has its own Variables tab per environment. This is where secrets live at runtime — never in code or Dockerfiles. Vars set in one environment don't bleed into another.
- Deployments & Logs — every deploy is versioned. Railway keeps build logs, deploy logs, and live runtime logs per service. You can roll back to any previous deploy in two clicks.
How to set up a project from scratch
You create the project once, then configure each environment inside it. Here's the setup flow:
| Step | What to do |
|---|---|
| 1. Create account | Go to railway.app, sign up, connect your GitHub account |
| 2. New project | New Project → Empty Project. Name it code-impact-training |
| 3. Select environment | Railway creates Development, Staging, and Production environments automatically. Start in Development. |
| 4. Add databases | + New → Database → select the database type your app needs (e.g. PostgreSQL). Each one is provisioned for this environment only and generates its own connection URL |
| 5. Add services | + New → GitHub Repo for each service your app has (e.g. backend, frontend). Each service connects to the same repo but can point to a different Dockerfile |
| 6. Add railway.toml | Tell Railway which Dockerfile to use and how to start the app (see below) |
| 7. Disable auto-deploy | Service → Settings → Source → turn off "Deploy on push." Deploys are deliberate, not automatic |
| 8. Set env vars | Service → Variables → add all required vars. These are scoped to this environment only |
| 9. Link databases to services | Database service → Settings → Connected Services → link each service that needs it. Railway auto-injects the connection URL so you don't have to copy it manually |
| 10. Manual deploy | Service → Deployments → Deploy. Watch build logs. Green health check = live |
railway.toml
Commit this to the repo root. It tells Railway which Dockerfile to build and how to start the server:
# railway.toml (at repo root) [build] builder = "dockerfile" dockerfilePath = "backend/Dockerfile" [deploy] healthcheckPath = "/health" healthcheckTimeout = 300 startCommand = "npx prisma migrate deploy && node dist/server.js"
Required environment variables
| Variable | Where to get it |
|---|---|
DATABASE_URL | Auto-injected if you link the Postgres service — otherwise copy from Postgres → Connect tab |
JWT_SECRET | Generate locally: openssl rand -hex 32 |
NODE_ENV | production |
PORT | 3000 |
Set up your training repo on Railway
🌿 Use the branching workflow from Module 3 — git checkout -b feat/[description], commit your changes, push, and open a PR before moving on.
You're setting up the Development environment for your training repo. Staging and Production follow the exact same steps — you'll do those in Module 7.
Prisma in Production & Environment Management
45 minmigrate dev vs migrate deploy
These two commands do different things. Using the wrong one in production can destroy data.
| Command | Use in | What it does |
|---|---|---|
npx prisma migrate dev | Development only | Creates migration files, applies them, regenerates the Prisma client. May reset the database in edge cases. |
npx prisma migrate deploy | Production & staging | Applies only pending migrations that already exist. Never resets. Never generates files. Safe for production. |
prisma migrate dev against a production database. It can prompt to reset the database if it detects drift — wiping all your data. Always use prisma migrate deploy in any non-development environment.The Railway deploy command in railway.toml runs migrations automatically before starting the server — so every deploy applies any pending migrations safely:
startCommand = "npx prisma migrate deploy && node dist/server.js" # Migrations run first, then the server starts. # If migrations fail, the server never starts — Railway marks the deploy failed. # The previous deploy stays live. No broken state.
Reading Logs & Debugging Failed Deploys
30 minA failed deploy is not an emergency — it's a normal part of shipping software. Railway keeps your previous successful deploy live while a new one is building. Nothing breaks for users. Your job is to read the logs, find the error, fix it, and redeploy.
Where to Find Logs
| Log type | Where in Railway | What it shows |
|---|---|---|
| Build logs | Service → Deployments → click a deploy → Build Logs | Docker build output — npm install, compile, etc. |
| Deploy logs | Service → Deployments → click a deploy → Deploy Logs | Container startup — migrations, server start |
| Runtime logs | Service → Logs (live) | Your app's console output while running |
Common Failure Patterns
| Error in logs | Cause | Fix |
|---|---|---|
Error: Cannot find module '...' | Build didn't complete — missing deps or compile error | Check build logs. Run npm run build locally first. |
address already in use or port error | PORT env var not set or wrong value | Set PORT=3000 in Railway Variables |
P1001: Can't reach database | DATABASE_URL missing or wrong | Check Railway Variables. Re-link Postgres service. |
| Migration error on startup | Pending migration that fails | Check migration SQL. Run npx prisma migrate status locally with the prod DB URL. |
| Health check timeout | Server never started, or started on wrong port | Check deploy logs for startup errors. |
Rolling Back a Deploy
If a bad deploy makes it through and breaks production, Railway keeps your deploy history. Roll back in two clicks:
Add a development deployment pipeline
🌿 Use the branching workflow from Module 3 — git checkout -b feat/[description], commit your changes, push, and open a PR before moving on.
Right now deploying means going into Railway and clicking Deploy. This exercise wires up a GitHub Actions workflow you can trigger manually from GitHub — same deliberate deploy, but from one place alongside your CI rather than jumping between tools.
The workflow uses workflow_dispatch — a manual trigger. It uses the Railway CLI with a RAILWAY_TOKEN secret. You generate the token in Railway and store it in GitHub — it never touches your code.
# .github/workflows/deploy-dev.yml name: Deploy to Development on: workflow_dispatch: # Triggered manually from GitHub → Actions jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Railway CLI run: npm install -g @railway/cli - name: Deploy backend to Development run: railway up --environment development --service backend env: RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }} - name: Deploy frontend to Development run: railway up --environment development --service frontend env: RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
RAILWAY_TOKEN is how the workflow authenticates to Railway without hardcoding credentials.Pre-Capstone Review
Before starting Module 7, verify your training repo is in the right state. The capstone builds directly on top of what you've built — if anything is missing or broken, go back and fix it now rather than hitting a wall mid-build.
Expected Directory Structure
This is what your repo should look like right now. Every file listed here was created in a specific exercise — if something is missing, the exercise reference tells you where to go back.
code-impact-training/ ├── .git/ ├── .github/ │ └── workflows/ │ ├── ci.yml # Exercise 6.1 │ └── deploy-dev.yml # Exercise 6.5 ├── .gitignore # Exercise 1.3 ├── .env # Never committed — local only ├── .env.example # Exercise 6.2 — backend + frontend sections ├── CLAUDE.md # Exercise 4.4 ├── docker-compose.yml # Exercise 5.6 ├── railway.toml # Exercise 6.3 ├── README.md # Exercise 3.5 │ ├── frontend/ │ ├── Dockerfile # Exercise 5.6 │ ├── .dockerignore # Exercise 5.6 │ ├── package.json # Exercise 4.5 — issue #4 │ ├── tsconfig.json # Exercise 4.5 │ ├── vite.config.ts # Exercise 4.5 │ ├── index.html # Exercise 4.5 │ └── src/ │ ├── main.tsx # Exercise 4.5 │ ├── index.css # Exercise 4.5 │ ├── types.ts # Exercise 5.2 — Task type │ └── App.tsx # Exercise 5.2 │ └── backend/ ├── Dockerfile # Exercise 5.6 ├── .dockerignore # Exercise 5.6 ├── package.json # Exercise 4.5 — issue #3 ├── tsconfig.json # Exercise 4.5 ├── .eslintrc # Exercise 4.5 ├── prisma/ │ ├── schema.prisma # Exercise 5.5 — User, Task, Status │ └── migrations/ # Exercise 5.6 — created by prisma migrate dev ├── src/ │ ├── server.ts # Exercise 4.5 — GET /health │ ├── app.ts # Exercise 5.3 — router registration │ ├── types.ts # Exercise 5.1 │ ├── routes/ │ │ └── tasks.ts # Exercise 5.3 │ ├── controllers/ │ │ └── taskController.ts # Exercise 5.3 │ └── services/ │ └── taskService.ts # Exercise 5.3 & 5.4 └── tests/ └── taskService.test.ts # Exercise 5.4
Verification Checklist
Work through each item. For anything that fails or is missing, the module reference tells you exactly where to go back.
Repo & Git
Backend
Frontend
Docker
CI & Railway
Build & Deploy Orbit
You already know the tools. You've used the terminal, set up a GitHub repo, written Dockerfiles, defined a Prisma schema, and run the worktree workflow on real issues. Now you apply all of it to Orbit — a personal operating system with a calendar, tasks, reminders, and an AI-powered daily briefing. This is a real project brief, not a guided exercise. You own the architecture.
code-impact-training, the repo you've been building throughout the course. It already has:
- A GitHub repo with SSH configured, git history, and your existing commits
frontend/andbackend/directories withsrc/structurefrontend/Dockerfile,backend/Dockerfile,docker-compose.yml, and both.dockerignorefilesbackend/prisma/schema.prismawithUserandTaskmodels already defined.gitignoreand a working.env
A personal OS with four integrated domains: a week-view calendar, a task manager, a reminders list, and a dashboard with an AI daily briefing powered by the Claude API. Stack: React (Vite + TypeScript + Tailwind) + Node.js/Express + PostgreSQL/Prisma + Railway.
Product Requirements
Dashboard
- Shows the current date prominently on load
- Displays all events scheduled for today in chronological order
- Displays tasks that are overdue or due today
- Displays reminders due today that have not been marked done
- A button triggers an AI-generated "your day ahead" summary via the Claude API, incorporating the day's events, tasks, and reminders — not auto-loaded on page visit
- All sections load fresh on each page visit
Calendar
- Supports month, week, and day views — user can switch between them
- Events can be created with at minimum: title, start time, end time
- Existing events can be viewed, edited, and deleted
- Tasks that have a due date/time appear on the calendar on their due date
- Reminders with a scheduled time appear on the calendar on their scheduled date
- Week and day views handle multiple events in the same time slot without hiding any data
Tasks
- Displays tasks in two sections: upcoming (incomplete) and completed
- Tasks can be created with: title, optional notes, optional due date/time
- Tasks can be marked complete, edited, and deleted
- A task can be associated with a calendar event to indicate it is a precursor to that event
- Tasks with no due date appear in a backlog section, separate from dated tasks
Reminders
- Displays a list of active reminders with their scheduled times
- Reminders can be created with: title, optional notes, reminder date/time, repeat frequency (none / daily / weekly)
- Marking a reminder done with no repeat moves it to a done/archived state; with a repeat, the next occurrence is automatically scheduled
- When a due reminder is detected while the user is active in the app, a browser notification fires — requires the user to have granted notification permission (Web Notifications API)
- Reminders can be edited and deleted
Profile
- Displays the user's name and email from signup
- User can update their display name
- User can upload and display a profile picture
- Sign out clears the session and redirects to the login page
Technical Requirements
These are non-negotiable. Every item must be true before the app is considered complete.
| Requirement | Standard |
|---|---|
| Stack | React + TypeScript + Tailwind (frontend) · Node.js + Express + TypeScript (backend) · PostgreSQL + Prisma (database) · Railway (deployment) |
| Auth | JWT-based · bcrypt password hashing (min 10 salt rounds) · all non-public routes require a valid token · data scoped to the authenticated user |
| API | GET /health returns 200 { ok: true } · versioned routes (/api/v1/) |
| Database | Prisma ORM · prisma migrate deploy in the Railway start command · no raw SQL |
| Tests | Unit tests required for all service-layer functions (business logic, data transformations, repeat scheduling) · tests run as part of CI and must pass before any PR is merged |
| CI | lint + typecheck + tests + build all pass on every push (ci.yml from Exercise 6.1) |
| Secrets | No secrets in code, Dockerfiles, or version control — environment variables only |
Process Requirements
How you build matters as much as what you build. These standards match what Code Impact uses on every client project.
/prd-to-issues in Claude Code. Review what Claude generates, write acceptance criteria for every issue, and create them on GitHub. You decide the issue count and how to decompose the work — there is no prescribed list./feature-start) or standard Git (git checkout -b feat/N-short-title) — your choice. What's required: commit regularly, push your branch, open a PR, and merge to main via the PR. Get comfortable with git pull, git push, and resolving conflicts on your own.deploy-dev.yml workflow from Exercise 6.5. Test the features from that milestone in a real hosted environment — not Docker on your machine. Fix any issues before starting the next milestone. When the final milestone is complete and all AC passes in Development, you're ready for Production./release-checklist first. Then set up the Production Railway environment and deploy. Share the Production URL with the team — that's your graduation checkpoint.Two ways to work a branch
# Option A — Standard Git (recommended if you want to practice the basics) git checkout -b feat/N-short-title # Create and switch to your branch # ... build the feature, commit as you go ... git add . git commit -m "feat(scope): description" git push -u origin feat/N-short-title # Push and open a PR on GitHub # After merge: git checkout main git pull # Option B — Worktree workflow (Claude Code slash commands) /feature-start [N] # Creates worktree + branch + draft PR + copies .env code ../code-impact-training-feat-[N] /build-feature [N] # Plan → approve → build → /verify (called internally) /human-test [N] # Docker stack + manual QA from acceptance criteria /security-review # If applicable: auth, user data, payments, integrations /performance-review # If applicable: DB queries, lists, search, packages /create-pr [N] # Push → PR body → ready for review → squash merge # After merge: /clean-worktree [N]
Extra Credit — Weather Integration
Optional — does not affect your graduation status. Complete it after all 5 pages pass AC in Production.
Add live weather to the dashboard and factor it into the AI briefing. Teaches one new skill: consuming a third-party REST API with an API key.
WEATHER_API_KEY and WEATHER_LOCATION to your Railway env vars and .env.example.WEATHER_API_KEY and WEATHER_LOCATION in Railway → Production → backend service → Variables. Trigger a manual deploy.Your app is complete when all of these are true
Product — All 5 pages pass AC in Production
Deployment
Process
Concepts to Understand
Training Complete
Congratulations — you built and shipped a full-stack production app from scratch. Send the team your Railway URL and a note on what was hardest and what clicked.