Git history is your project's memoir. A messy history makes debugging miserable. A clean history makes it possible to understand decisions years later.
main (protected, auto-deploys to prod)
↓
feature branches → PR → review → merge → deploy
mainalways deployable- Feature branches short-lived (hours to days, not weeks)
- PR to self for anything non-trivial (review your own code before merging)
- Hotfix branches for urgent prod fixes
mainprotected, requires PRdevelopintegration branch (optional)feature/*for new workfix/*for bugschore/*for maintenance
<type>: <short summary in imperative, lowercase, under 70 chars>
<body: what changed, why, context — wrap at 72 chars>
<footer: BREAKING CHANGE, issue refs, co-authors>
- feat: new feature
- fix: bug fix
- refactor: code change without behavior change
- perf: performance improvement
- test: adding/updating tests
- docs: documentation only
- chore: tooling, dependencies, CI
- style: formatting (rare in 2026 — Prettier handles this)
feat: add voice input to chat panel
Implements Whisper API transcription via /api/transcribe endpoint.
Reuses existing rate limiting. MIME type whitelist prevents abuse.
Fixes #142
fix: race condition between unmount flush and debounce timer
Cancel debounce timer before flushing on unmount. Previously,
rapid component remounts could fire duplicate sendMessage calls.
- ❌ "fix stuff"
- ❌ "WIP"
- ❌ "updates"
- ❌ "asdf"
- ❌ Commit message identical to branch name
- ❌ Stack trace in commit message
- Atomic commits. One logical change per commit. "Fix bug + add feature + refactor" = split.
- Commit messages explain WHY. What is visible in the diff. Why is not.
- Don't commit broken code. Every commit should build and pass tests.
- Never commit secrets. Pre-commit hooks scan for API keys, passwords.
- Never commit generated files.
node_modules,dist,.next→ gitignore. - Rebase feature branches on main before merging (linear history preferred).
- Squash trivial commits — "fix typo", "address review comment" → squash into parent.
git push --force/-f— overwrites remote, can destroy others' workgit reset --hard— destroys uncommitted changesgit clean -fd— deletes untracked files permanentlygit branch -D— force delete branch, loses unmerged workgit checkout .— discards all uncommitted changes
git push --force-with-lease— safer force push (aborts if remote changed)git reset --mixed— keeps changes in working directorygit stash— save uncommitted changes for latergit branch -d— safe delete (only merged branches)
- Lint — block commit with lint errors
- Format — auto-format before commit (Prettier)
- Secret scan — detect API keys, tokens (gitleaks, detect-secrets)
- Type check —
tsc --noEmiton staged files
- Tests — run relevant tests before push
- Build — ensure production build works
- One change — new feature, one bug fix, one refactor
- Clear title — describes what, not how
- Description explains why — context, decisions, tradeoffs
- Screenshots/videos for UI changes
- Test plan — how reviewer can verify
- Link to issue/story
- Under 400 lines — bigger = harder to review = more bugs missed
- Does it solve the stated problem?
- Are there tests for the new behavior?
- Are error cases handled?
- Is there a simpler solution?
- Does naming make sense?
- Are there security implications (auth, injection, XSS)?
- Does it respect existing patterns (SSOT, Modular)?
- ❌ Commit messages like "." or "wip"
- ❌ Branches living for months without rebasing
- ❌ Force push to shared branches
- ❌ Committing to main directly for non-trivial changes
- ❌ PR with 50 files changed across 10 unrelated concerns
- ❌
git pushwithout pulling first - ❌ Committing lock file conflicts instead of resolving
- ❌ Fixing tests by changing the assertion instead of the code
Adopt conventional-commits spec for automated changelogs:
feat(auth): add Google OAuth
fix(api): handle empty response from Supabase
BREAKING CHANGE: /api/users response shape changed
Tools: semantic-release, release-please generate changelog and version bumps automatically.
When a bug appears but you don't know when:
git bisect start
git bisect bad # current commit is bad
git bisect good <known-good-sha> # last known good
# git checks out midpoint, you test, answer good/bad, repeatMultiple branches checked out simultaneously:
git worktree add ../my-project-feature feature-branchGreat for reviewing PRs without stashing current work.
git log --oneline— can you understand project history at a glance?git blame path/to/file— can you find context for every line?git log --all --graph --oneline— is history clean or spaghetti?