- README - general information about how projects work
- Environment:
- Start dependencies necessary to run the project:
docker compose up -d - Apply Flyway migration:
make flyway-migrate
- Start dependencies necessary to run the project:
- Build/Serve:
- Start FastAPI Uvicorn server:
make serve - Start Celery worker:
make worker - Start Celery scheduler (beat):
make beat
- Start FastAPI Uvicorn server:
- Tests:
- All tests:
make test - Single test:
pytest path/to/test.py::TestClass::test_method
- All tests:
- Lint:
make lint
Use conventional commits for all commit messages and PR titles.
feat: New feature or functionality (touches production code)fix: Bug fix (touches production code)chore: Non-production changes (docs, tests, config, CI, refactoring agents instructions, etc.)
<type>(<scope>): <description>
Examples:
feat(insights): add retention graph exportfix(cohorts): handle empty cohort in query builderchore(ci): update GitHub Actions workflowchore: update AGENTS.md instructions
- Scope is optional but encouraged when the change is specific to a feature area
- Description should be lowercase and not end with a period
- Keep the first line under 72 characters
- Never use f-strings with user-controlled values in SQL queries - this creates SQL injection vulnerabilities
- Use parameterized queries for all VALUES:
cursor.execute("SELECT * FROM t WHERE id = %s", [id]) - Table/column names from Django ORM metadata (
model._meta.db_table) are trusted sources - When raw SQL is necessary with dynamic table/column names:
# Build query string separately from execution, document why identifiers are safe
table = model._meta.db_table # Trusted: from Django ORM metadata
query = f"SELECT COUNT(*) FROM {table} WHERE team_id = %s"
cursor.execute(query, [team_id]) # Values always parameterizedSanitizers (for use in placeholders):
ast.Constant(value=...)- wraps values safelyast.Tuple(exprs=...)- for lists of values
- Inside src/ exists folders by context (DDD style)
- Every context follows hexagonal architecture for responsability separation
- Python: Use type hints, follow mypy strict rules
- Error handling: Prefer explicit error handling with typed errors
- Naming: Use descriptive names and snake_case for Python
- Comments: should not duplicate the code below, don't tell me "this finds the shortest username" tell me why that is important, if it isn't important don't add a comment, almost never add a comment
- Python tests: do not add doc comments
- any tests: prefer to use parameterized tests, think carefully about what input and output look like so that the tests exercise the system and explain the code to the future traveller
- Python tests: in python use the parameterized library for parameterized tests, every time you are tempted to add more than one assertion to a test consider (really carefully) if it should be a parameterized test instead
- always remember that there is a tension between having the fewest parts to code (a simple system) and having the most understandable code (a maintainable system). structure code to balance these two things.
- Separation of concerns: Keep different responsibilities in different places (data/logic/presentation, safety checks/policies, etc.)
- Reduce nesting: Use early returns, guard clauses, and helper methods to avoid deeply nested code
- Avoid over-engineering: Don't apply design patterns just because you know them
- Start simple, iterate: Build minimal solution first, add complexity only when demanded
- Markdown: prefer semantic line breaks; no hard wrapping