agentleFS
aniketshukla1aniketshukla1/sdlc-kit/.cursor/rules/20-backend-standards.mdc

sdlc-kit / rules

Cursor rule1 starsChanged 10 days agoMITView on GitHub

Backend standards for Python + FastAPI + SQLAlchemy code.

The file

We split this 20-backend-standards.mdc into the rules it gives an agent

9 rules. About 0 are common, 3 are distinctive, and 6 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈3

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 6

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    When a layer file nears 200 lines, turn it into a package: services/orders/{__init__,create,cancel}.py.

    line 20
  2. Distinctive:

    Every new route requires auth.

    line 25
  3. Very specific:

    Put constraints in Field(...): min_length, max_length (= DB column length), ge, le, regex.

    line 37
  4. Distinctive:

    Never return ORM objects directly.

    line 38
  5. Very specific:

    Do not raise HTTPException in services.

    line 42
  6. Distinctive:

    Check permissions/ownership here, not only in the UI.

    line 43
  7. Very specific:

    Never build SQL with f-strings or +.

    line 50
  8. Very specific:

    Avoid N+1: use selectinload/joinedload for relations used in the response.

    line 51
  9. Very specific:

    Every list query is paginated: default limit 20, max 100, offset ≥ 0, stable order_by.

    line 52

≈ “Common” is an estimate: at least 1 in 5 of the 6,668 Cursor rule files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
20-backend-standards.mdc
---
description: Backend standards for Python + FastAPI + SQLAlchemy code.
globs: backend/**/*.py
paths: backend/**/*.py
alwaysApply: false
---
# Backend standards (Python + FastAPI)

## Layers (follow the existing layout if it differs; never mix layers)
| Path                                     | Contains                                  | Must NOT contain        |
|------------------------------------------|-------------------------------------------|-------------------------|
| `app/api/v1/routes/<feature>.py`         | HTTP only: parse, call service, return    | SQL, business rules     |
| `app/schemas/<feature>.py`               | Pydantic v2 request/response models       | DB access               |
| `app/services/<feature>_service.py`      | Business rules, permissions, transactions | HTTP objects, raw SQL   |
| `app/repositories/<feature>_repository.py` | SQLAlchemy queries only                 | Business rules          |
| `app/models/<feature>.py`                | SQLAlchemy ORM models                     | Logic                   |
| `app/core/`                              | config, db session, errors, security      | Feature code            |
| `app/search/`                            | Elasticsearch queries (only if used)      | Mapping changes         |

When a layer file nears 200 lines, turn it into a package: `services/orders/{__init__,create,cancel}.py`.

## Routes
- Declare `response_model` and `status_code` on every endpoint.
- Get DB session and current user via `Depends(...)` using the project's existing dependencies.
- Every new route requires auth. A route is public only when the ticket says so; PLAN.md's API table says which.
- Changing an existing endpoint: only add optional fields. Never rename, remove or retype a field unless the
  ticket says so; the frontend and other callers depend on it.
- Match the existing sync/async style. Do not mix in one feature.
- Status codes: 200 read/update, 201 create, 204 delete, 401 unauthenticated, 403 forbidden,
  404 not found, 409 duplicate/conflict, 422 validation.

## Schemas
- Separate `<X>Create`, `<X>Update`, `<X>Read`. Read models: `model_config = ConfigDict(from_attributes=True)`.
- `Create`/`Update` never accept server-owned fields: `id`, owner/user ids, `role`, `is_admin`, timestamps.
  The service sets them from the current user or the database.
- `Update` schemas: every field optional; the service applies only the fields sent (`data.model_dump(exclude_unset=True)`).
- Put constraints in `Field(...)`: `min_length`, `max_length` (= DB column length), `ge`, `le`, regex.
- Never return ORM objects directly. Never expose password hashes, tokens or internal flags.

## Services
- Raise the project's domain errors (find them: `rg -n "class \w+Error" backend/app/core`).
  Do not raise `HTTPException` in services. If no error classes exist, ask before creating them.
- Check permissions/ownership here, not only in the UI.
- Multi-step writes run in ONE transaction; commit in one place; roll back on error.
- A new cache states its key (including the user or tenant; without it, data leaks between users) and how it
  goes stale.

## Repositories
- SQLAlchemy 2.x style: `select(...)`, `session.execute(...)`. No `session.query`.
- Never build SQL with f-strings or `+`. Use bound parameters only.
- Avoid N+1: use `selectinload`/`joinedload` for relations used in the response.
- Every list query is paginated: default limit 20, max 100, offset ≥ 0, stable `order_by`.

## Types, data, logging
- Full type hints, mypy clean. No `Any` without a comment explaining why.
- Datetimes are timezone-aware UTC, and Read schemas send them with the zone (`+00:00`). MySQL `DATETIME` gives
  back naive values: attach UTC in one shared validator or type. Money is `Decimal`, never `float`.
- `logger = logging.getLogger(__name__)`. Never log passwords, tokens or personal data.
- Log events, not prose: a stable event name plus fields, in the project's existing style (with its request id
  if it has one). `error` = someone must act, `warning` = handled degradation, `info` = business event.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.