{
  "id": "ai-rules-for-postgresql-apps",
  "type": "rules",
  "category": "rules",
  "locale": "fr",
  "url": "/fr/rules/ai-rules-for-postgresql-apps",
  "title": "Règles de codage IA pour les applications PostgreSQL",
  "description": "Règles AGENTS.md pour les applications basées sur PostgreSQL couvrant la sécurité des requêtes, la discipline de migration, les conventions d'indexation et la prévention des agents contre l'écriture de requêtes N+1.",
  "tools": [
    "Cursor",
    "Claude Code",
    "Codex",
    "Windsurf"
  ],
  "stack": [
    "PostgreSQL",
    "TypeScript",
    "Next.js"
  ],
  "tags": [
    "agents-md",
    "postgres",
    "typescript",
    "security",
    "conventions"
  ],
  "difficulty": null,
  "updated": "2026-06-08",
  "markdown": "Déposez ce fichier à la racine de votre dépôt sous le nom `AGENTS.md`. Il s'applique à tout projet où PostgreSQL est le stockage de données principal, qu'il soit accédé via Prisma, Drizzle, `postgres.js` ou SQL brut.\n\n## AGENTS.md\n\n```md title=\"AGENTS.md\"\n# Project Rules — PostgreSQL Apps\n\n## Hard rules — data safety\n- NEVER generate SQL that concatenates user input into a query string. Always use\n  parameterised queries (`$1`, `$2` placeholders) or the ORM's parameter binding.\n  SQL injection via agent-generated queries is a real attack vector.\n- NEVER run `DROP TABLE`, `TRUNCATE`, or `DELETE FROM <table>` without a `WHERE`\n  clause in application code. These must be wrapped in a transaction that can be\n  rolled back, and must require explicit developer confirmation before execution.\n- NEVER write a migration that removes a column without first deploying a version of\n  the application that stops reading that column. Column removal is a two-deploy\n  operation: (1) stop reading; (2) drop.\n- All schema changes must be applied via migration files (Drizzle Kit, Flyway,\n  `migrate`, or Prisma Migrate). Never apply schema changes by running `ALTER TABLE`\n  directly against production.\n\n## Query conventions\n- Every query that returns a list MUST have a `LIMIT`. There is no acceptable reason\n  for a user-facing endpoint to return an unbounded result set.\n- Use `EXPLAIN ANALYZE` to check query plans for new queries on tables with more than\n  10,000 rows before merging. Agents should output the plan in a comment block when\n  writing a non-trivial query.\n- Avoid `SELECT *`. Name every column you need. This makes queries self-documenting\n  and prevents accidental exposure of sensitive columns (hashed passwords, tokens).\n- N+1 patterns are forbidden. If you load a list of records and then query related\n  data per row in a loop, refactor to a single JOIN query or a batched `IN` query.\n- Use `JOIN` instead of correlated subqueries for relation lookups — correlated\n  subqueries run once per row and are almost always slower.\n\n## Indexing rules\n- Every foreign key column MUST have an index. PostgreSQL does not create these\n  automatically (unlike MySQL). Missing FK indexes cause sequential scans on join.\n- Columns used in `WHERE` clauses on high-read tables should have indexes. When\n  adding a query that filters by a new column, check whether an index exists and\n  add one in the same migration if it does not.\n- Use partial indexes (`WHERE deleted_at IS NULL`) for soft-delete patterns — they\n  are far smaller and faster than full-table indexes when most rows are soft-deleted.\n- Never create an index on a column with very low cardinality (e.g. a boolean\n  `is_active` with 90% `true`). PostgreSQL will ignore the index and scan anyway.\n\n## Connection and pooling\n- Import the database client from `src/lib/db.ts` (the singleton or pool). Never\n  instantiate a new client per request — this exhausts connections instantly under load.\n- In serverless environments (Next.js API routes, Edge Functions), use a connection\n  pooler (PgBouncer, Neon connection pooling, Supabase pgbouncer mode) between the\n  application and PostgreSQL. Direct connections from serverless are not sustainable.\n- Set `statement_timeout` and `lock_timeout` on the connection or session for\n  long-running operations to prevent them from blocking the entire application.\n\n## Transactions\n- Any operation that writes to more than one table must be wrapped in a transaction.\n  Partial writes leave the database in an inconsistent state.\n- Keep transactions as short as possible. Do not make network requests (HTTP calls,\n  external APIs) inside a transaction — this holds locks for the duration of the\n  network call.\n\n## Definition of done\n- No string-interpolated SQL in the codebase (`grep -r \"query\\`\" src/` should return\n  only parameterised template tag usage, not concatenation).\n- All new tables have primary key and foreign key indexes.\n- All `findMany` / `SELECT` queries have `LIMIT`.\n- Migration files are committed and named descriptively.\n- `tsc --noEmit` passes (typed query results match schema types).\n```\n\n## Pourquoi ces règles\n\n- **Pas de SQL concaténé avec des chaînes, jamais** est la règle fondamentale. Les requêtes paramétrées ne sont pas seulement une bonne pratique — elles sont la seule défense contre l'injection SQL, et les agents sous pression temporelle concatèneront lorsqu'ils ne savent pas comment exprimer une requête avec la syntaxe de paramètres d'un ORM spécifique.\n- **La suppression de colonne est une opération en deux déploiements** évite l'erreur de migration sans temps d'arrêt la plus courante. Si l'application lit encore une colonne lorsque la migration la supprime, chaque requête en cours au moment de la migration échoue. Les agents ne raisonnent pas naturellement sur les fenêtres de déploiement lorsqu'ils génèrent des modifications de schéma.\n\n## Bonne adéquation\n\n- Toute application de production utilisant PostgreSQL où l'intégrité des données et les performances des requêtes sont importantes : applications SaaS, plateformes de contenu, backends e-commerce, pipelines d'analyse.\n\n## Pas adapté\n\n- Applications basées sur SQLite ou projets où la base de données est véritablement éphémère (fixtures de test, graines de développement local) — la discipline de migration et les règles d'indexation sont une surcharge inutile."
}