Best Practices
Commit conventions, PR workflows, code style, and development standards for the Cookest ecosystem
Best Practices
This page defines the development standards for all Cookest repositories. Follow these conventions for consistent, reviewable, and maintainable contributions.
Commit Message Format
All commits follow Conventional Commits. The format is:
<type>(<scope>): <description>
[optional body]
[optional footer(s)]Types
| Type | When to use |
|---|---|
feat | New feature or capability |
fix | Bug fix |
docs | Documentation only changes |
style | Formatting, missing semicolons (no code logic change) |
refactor | Code restructuring without changing behavior |
perf | Performance improvement |
test | Adding or fixing tests |
build | Build system or dependency changes |
ci | CI/CD configuration changes |
chore | Maintenance tasks (deps update, config changes) |
Scopes
Use the component or module name as scope:
| Repository | Example Scopes |
|---|---|
api | auth, recipe, meal-plan, chat, store, subscription, middleware |
UI | auth, home, recipes, pantry, shopping, chat, theme, navigation |
web | hero, features, nav, i18n, seo |
cucl | button, input, card, modal, tokens, styles, storybook, build |
docs | backend, mobile, etl, i18n, architecture, contributing, ai, cucl |
etl | extract, transform, load, usda, mealdb |
Commit Granularity
Each commit must represent one logical change. A logical change is a single entity, service, handler, component, or configuration — not a batch of loosely related files.
Rules:
- One domain concept per commit — adding an ingredient entity is one commit, adding a recipe entity is another
- Don't bundle files just because they were created at the same time — "add 7 model files" is too broad; each model should be its own commit
- Tightly coupled files can share a commit — a handler and its route registration, or a service and its module re-export, are fine together
- Infrastructure changes are their own commits — Cargo.toml, Dockerfile, docker-compose, CI config each get separate commits
- The commit message must describe what changed specifically — not just the category of change
Good — specific and atomic:
feat(recipe): add recipe entity with dietary flags and slug-based lookup
feat(recipe): add ingredient allergen entity with 17 allergen types
refactor(auth): add JWT token service with access and refresh encoding
refactor(meal-plan): add meal plan service with AI-scored weekly generation
build: add multi-stage Dockerfile for food-apiBad — too broad or vague:
feat(recipe): add food-api entities # which entities? too many files
refactor(auth): add app-api services # which services? what do they do?
refactor: add request/response models # meaningless without specifics
feat: update various files # completely uselessNo AI Co-authorship Trailers
Do not add Co-authored-by trailers for AI tools (GitHub Copilot, Claude, ChatGPT, etc.) in commit messages. AI tools are assistants, not co-authors. All commits are authored by the human developer who reviews and approves the changes.
# ❌ Wrong — do not add AI co-author trailers
feat(auth): add refresh token rotation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
# ✅ Correct — just the commit message
feat(auth): add refresh token rotationExamples
feat(auth): add refresh token rotation on reuse detection
fix(recipe): correct dietary tag filtering for vegan+gluten-free
docs(backend): add PDF pipeline endpoint documentation
refactor(meal-plan): extract scoring logic into separate service
ci(api): add Rust clippy lint step to CI workflow
docs(i18n): add French translation for architecture overviewBreaking Changes
For breaking changes, add ! after the type/scope and explain in the footer:
feat(auth)!: replace cookie-based refresh with rotating tokens
BREAKING CHANGE: Existing refresh tokens are invalidated.
Clients must re-authenticate after this update.Branch Naming
<type>/<short-description>Examples:
feat/recipe-search-filtersfix/auth-token-refresh-loopdocs/add-architecture-diagramsrefactor/extract-meal-plan-scoring
Pull Request Standards
PR Title
Same format as commit messages:
feat(recipe): add cuisine-based search filteringPR Description Template
## What
Brief description of the change.
## Why
Link to issue or explain the motivation.
## How
Key implementation details or architectural decisions.
## Testing
How you verified the change works.
## Screenshots
If UI changes are involved.Review Checklist
Before requesting review:
- Code compiles/builds without warnings
- All existing tests pass
- New features have tests
- Documentation updated (if behavior changed)
- Translations updated (if docs changed)
- No secrets or credentials in code
- Breaking changes documented
Code Style by Language
Rust (API)
- Follow
rustfmtdefaults - Run
cargo clippybefore committing — no warnings allowed - Use
thiserrorfor error types, not string errors - Prefer
?over.unwrap()in production code - Group imports: std → external crates → internal modules
- Document public functions with
///doc comments - Keep handlers thin — business logic goes in
services/
Dart (Flutter / UI)
- Follow
dart formatandflutter analyze - Use
constconstructors where possible - Riverpod providers in dedicated files (one provider per file)
- Widget decomposition: max ~150 lines per widget file
- Name files in
snake_case, classes inPascalCase - Use
finalfor all local variables that don't change
TypeScript (Web / Docs)
- Follow project ESLint/Prettier config
- Prefer
constoverlet, never usevar - Use TypeScript strict mode — no
anyunless unavoidable - React components: one component per file, PascalCase filenames
- Use
interfacefor public contracts,typefor unions/intersections
TypeScript / React (CUCL)
- All styles via
var(--ck-*)CSS custom properties — no hard-coded hex values - Use
cn()(clsx + tailwind-merge) for all class composition — never string interpolation - Use
forwardReffor all components that wrap a native DOM element - Every component must have a
.test.tsxand a.stories.tsxfile - Every new component must be added to
src/index.tsand exported by name - Run
bun run format:checkbefore committing — uses Prettier withprettier-plugin-tailwindcss
Python (ETL)
- Follow PEP 8 (use
blackformatter) - Type hints for all function signatures
- Docstrings for public functions (Google style)
- Use
loggingmodule, notprint() - Environment variables via
python-dotenv
Environment Management
Required Tools
| Tool | Version | Repository |
|---|---|---|
| Rust | 1.78+ | api |
| PostgreSQL | 15+ | api, etl |
| Flutter | 3.x | UI |
| Dart | 3.x | UI |
| Node.js | 20+ | web, docs |
| Bun | 1.x | cucl, docs |
| Python | 3.10+ | etl |
Environment Variables
Every repository that requires environment variables includes a .env.example file. Copy it to .env and fill in the values:
cp .env.example .envNever commit .env files. They are gitignored.
Security Practices
- No secrets in code. Use environment variables for all credentials
- Hash passwords with Argon2id (API) — never store plaintext
- SHA-256 refresh tokens — raw tokens only exist in httpOnly cookies
- Verify admin from DB — never trust JWT claims alone for admin access
- HMAC-verify webhooks — Stripe webhook payloads are verified with SHA-256 HMAC
- Rate limit all endpoints — governor middleware on API, RPS limits on ETL
Dependency Management
- Rust: Pin major versions in
Cargo.toml, usecargo updateregularly - Flutter: Pin versions in
pubspec.yaml, runflutter pub upgrade --major-versionsquarterly - Node.js: Use lockfiles (
bun.lock), review breaking changes before upgrading - Python: Pin versions in
requirements.txt, test upgrades in a venv