Django's ORM is the centre of gravity: it is expressive enough that an agent can write something readable and correct-looking that issues hundreds of queries. An agent working here needs to know the Django version, whether the project uses function based or class based views, how settings are split across environments, and that any model edit implies a migration. Async support exists but is partial, so the boundary between sync ORM code and async views is a real source of runtime failures rather than a style question.
Save this as AGENTS.md in your project root, and symlink CLAUDE.md to it so Claude Code reads the same rules. Adapt the commands to match your repository before committing it.
# AGENTS.md
This is a subscription SaaS built as a Django monolith with a REST API, background jobs, and Stripe billing.
## Project
Stack: Python 3.12, Django 5, Django REST Framework, PostgreSQL, Celery with Redis, Stripe, pytest-django, ruff.
Layout:
- `config/settings/base.py`, `config/settings/dev.py`, `config/settings/prod.py`
- `config/urls.py` root URL configuration
- `apps/accounts/` custom user model, authentication, teams
- `apps/billing/` Stripe customers, subscriptions, webhook handling
- `apps/core/` shared abstract models, mixins, and utilities
- `apps/<feature>/` one Django app per bounded feature
- Each app: `models.py`, `views.py`, `serializers.py`, `services.py`, `tasks.py`, `urls.py`, `admin.py`, `migrations/`
- `templates/` server rendered pages, `static/` assets
Entry points: `manage.py`, `config/settings/base.py`, `config/urls.py`, `apps/billing/webhooks.py`.
## Commands
```bash
uv sync # install dependencies
uv run python manage.py runserver # dev server on :8000
uv run python manage.py makemigrations # create migrations
uv run python manage.py migrate # apply migrations
uv run python manage.py showmigrations # inspect state
uv run python manage.py shell_plus # interactive shell
uv run python manage.py createsuperuser
uv run python manage.py collectstatic --noinput
uv run pytest # test suite
uv run pytest apps/billing -k webhook # one app or test
uv run ruff check . && uv run ruff format .
uv run celery -A config worker -l info # background worker
uv run celery -A config beat -l info # scheduled jobs
```
`DJANGO_SETTINGS_MODULE` defaults to `config.settings.dev` locally.
## Code style
- Business logic lives in `services.py`, not in views and not in serializers.
- Views are thin: permission check, deserialize, call a service, return a response.
- Query in the view or service, never in a template. Use `select_related` and `prefetch_related` for every relation you render.
- Custom managers and querysets for reusable filters. No copy pasted `filter()` chains.
- Model methods that mutate state wrap their writes in `transaction.atomic`.
- Use `get_user_model()`, never import the user model directly.
- Money is a `DecimalField` with explicit precision, never a float.
- Signals are avoided. If a side effect matters, call it explicitly from a service.
- Type hints on service functions. Line length 100, enforced by ruff.
- Every `TextChoices` and `IntegerChoices` is defined on the model, not as loose constants.
## Boundaries
Do not touch without explicit instruction:
- Existing files in any `migrations/` directory. Never edit, squash, or delete a migration that has been applied.
- `config/settings/prod.py` and any environment specific credential.
- `uv.lock`, `.env`, `.github/workflows/`, deployment manifests.
- `apps/billing/webhooks.py` and Stripe price configuration.
Needs human review: data migrations, changes to permission classes, anything that touches subscription state, and any change to the custom user model.
Generate migrations when models change, show the resulting file, and stop before applying to anything but the local database.
## Testing
- `uv run pytest` runs the suite, `uv run pytest apps/<app>` runs one app.
- Required tests: permission classes, service functions, Celery tasks, and every billing state transition.
- Use `pytest.mark.django_db` and the factories in `apps/<app>/tests/factories.py`. Do not add JSON fixtures.
- API tests go through `APIClient` and assert status code and response shape.
- Celery tasks are tested by calling the function directly, with `CELERY_TASK_ALWAYS_EAGER` reserved for integration tests.
- A bug fix ships with a failing-first regression test.
## Git workflow
- Branch from `main`: `feat/short-description`, `fix/short-description`.
- Conventional commits with the app as scope: `feat(billing): handle trial expiry`.
- Before creating a migration, rebase on `main` so the revision ordering stays linear. If two migrations collide, regenerate rather than hand merging.
- Code and its migration ship in the same commit.
- PR description lists migrations, new settings, and any manual step needed at deploy.
Each of these is worth a line in your rules file, because the model will otherwise repeat it every session.
An agent writes a view that returns Order.objects.all() and a template that renders order.customer.email, which issues one additional query per order. The code reads well and passes tests against a fixture of three rows. Add to your rules file: 'Any queryset that feeds a loop or a serializer must use select_related for forward foreign keys and prefetch_related for reverse and many to many relations. Assert query counts with assertNumQueries in tests for list endpoints.'
Agents edit models.py, confirm the code looks right, and stop, leaving the database schema behind. Worse, some hand-write a migration file with an invented dependency graph rather than running makemigrations. Write: 'Every change to a models.py file is followed by python manage.py makemigrations and the generated file is committed. Never hand-author a migration except for a deliberate RunPython data migration.'
Agents call list() on a queryset or iterate it and then use a comprehension to filter, or use len(qs) where qs.count() belongs, pulling every row into memory to find a handful. Querysets are lazy and chainable specifically so this is unnecessary. Add: 'Do filtering, ordering, aggregation, and counting with queryset methods. Never load a queryset into a list to filter it, and use .count() and .exists() rather than len() and truthiness.'
When something fails locally an agent will set DEBUG to True, add a wildcard to ALLOWED_HOSTS, or paste a SECRET_KEY literal into settings.py to make the traceback go away. Those edits then get committed. Make it a hard rule: 'Settings values come from environment variables. Never edit DEBUG, ALLOWED_HOSTS, SECRET_KEY, or any SECURE_ setting to resolve an error, and never commit a literal secret to a settings module.'
Asked to send a welcome email on signup, an agent adds a post_save receiver, which fires from fixtures, from bulk loads, and from every test that creates a user, and hides the control flow from anyone reading the view. Add: 'Side effects such as email, billing, and external API calls are called explicitly from a service function, not from model signals. Signals are reserved for cache invalidation and similar cross-cutting concerns.'
Agents convert a view to async def because it feels modern, then call the regular ORM inside it, which raises SynchronousOnlyOperation, or they wrap everything in sync_to_async and gain nothing but overhead. Async is only worth it when the view is genuinely IO bound on something external. Write: 'Views are sync by default. If a view is async, ORM access uses the async queryset methods or sync_to_async explicitly, and the reason for going async is stated in a comment.'
MCP gives the agent access to systems outside your codebase. These are the ones that pay off in a Django project.
| Server | What it does | Why here |
|---|---|---|
| Filesystem MCP server | Scoped read and write access to the project tree. | Django apps are directories with conventional filenames, so the agent needs to see the real app layout before adding a model or a view. |
| Git MCP server | History, diffs, and blame. | The migrations directory history is the clearest record of how the schema actually evolved, which the models file alone does not show. |
| A Postgres MCP server | Inspects schema and runs read queries against the database. | It lets the agent confirm indexes exist before it proposes a query pattern that will table scan in production. |
| Sentry MCP server | Pulls issues and stack traces from Sentry. | Django's most useful production signal is the traceback plus the request context, and that lives in Sentry rather than the codebase. |
| Playwright MCP server | Browser automation against the running server. | Admin customisations and form flows depend on session and CSRF handling that only a real browser exercises. |
Connect
Connect Claude to any app. Send emails, create issues, post messages, update databases - take real actions across Gmail, Slack, GitHub, Notion, and 1000+ services.
great_cto
Claude Code plugin: 7 specialised subagents (tech-lead, senior-dev, qa-engineer, security-officer, devops, l3-support, project-auditor) orchestrating a full SDLC pipeline — architecture, TDD, 12-angle code review, QA, security audit, deploy. 11 project archetypes auto-detected, 13 compliance frameworks (GDPR/PCI-DSS/HIPAA/SOC2/ISO 27001), self-improving knowledge layer that learns from every incident.
iOS Simulator
Enables Claude to interact with iOS Simulator for testing and debugging iOS applications.
Playwright Browser Automation
Model-invoked Playwright automation for testing and validating web applications.
pypict-claude-skill
Design comprehensive test cases using PICT (Pairwise Independent Combinatorial Testing) for requirements or code, generating optimized test suites with pairwise coverage.
subagent-driven-development
Dispatches independent subagents for individual tasks with code review checkpoints between iterations for rapid, controlled development.
Rules files help, but they cannot fix sprawling architecture. A conventional Django codebase gets more out of an agent than a clever one does.