P PasteCode
规则

PostgreSQL应用的AI编码规则

AGENTS.md规则,适用于PostgreSQL后端应用,涵盖查询安全、迁移规范、索引约定,以及防止代理编写N+1查询。

CursorClaude CodeCodexWindsurf PostgreSQLTypeScriptNext.js
.md .json 更新于 2026年6月8日

将此文件作为 AGENTS.md 放置在仓库根目录。它适用于任何以 PostgreSQL 作为主要数据存储的项目,无论通过 Prisma、Drizzle、postgres.js 还是原生 SQL 访问。

AGENTS.md

AGENTS.md
# Project Rules — PostgreSQL Apps
## Hard rules — data safety
- NEVER generate SQL that concatenates user input into a query string. Always use
parameterised queries (`$1`, `$2` placeholders) or the ORM's parameter binding.
SQL injection via agent-generated queries is a real attack vector.
- NEVER run `DROP TABLE`, `TRUNCATE`, or `DELETE FROM <table>` without a `WHERE`
clause in application code. These must be wrapped in a transaction that can be
rolled back, and must require explicit developer confirmation before execution.
- NEVER write a migration that removes a column without first deploying a version of
the application that stops reading that column. Column removal is a two-deploy
operation: (1) stop reading; (2) drop.
- All schema changes must be applied via migration files (Drizzle Kit, Flyway,
`migrate`, or Prisma Migrate). Never apply schema changes by running `ALTER TABLE`
directly against production.
## Query conventions
- Every query that returns a list MUST have a `LIMIT`. There is no acceptable reason
for a user-facing endpoint to return an unbounded result set.
- Use `EXPLAIN ANALYZE` to check query plans for new queries on tables with more than
10,000 rows before merging. Agents should output the plan in a comment block when
writing a non-trivial query.
- Avoid `SELECT *`. Name every column you need. This makes queries self-documenting
and prevents accidental exposure of sensitive columns (hashed passwords, tokens).
- N+1 patterns are forbidden. If you load a list of records and then query related
data per row in a loop, refactor to a single JOIN query or a batched `IN` query.
- Use `JOIN` instead of correlated subqueries for relation lookups — correlated
subqueries run once per row and are almost always slower.
## Indexing rules
- Every foreign key column MUST have an index. PostgreSQL does not create these
automatically (unlike MySQL). Missing FK indexes cause sequential scans on join.
- Columns used in `WHERE` clauses on high-read tables should have indexes. When
adding a query that filters by a new column, check whether an index exists and
add one in the same migration if it does not.
- Use partial indexes (`WHERE deleted_at IS NULL`) for soft-delete patterns — they
are far smaller and faster than full-table indexes when most rows are soft-deleted.
- Never create an index on a column with very low cardinality (e.g. a boolean
`is_active` with 90% `true`). PostgreSQL will ignore the index and scan anyway.
## Connection and pooling
- Import the database client from `src/lib/db.ts` (the singleton or pool). Never
instantiate a new client per request — this exhausts connections instantly under load.
- In serverless environments (Next.js API routes, Edge Functions), use a connection
pooler (PgBouncer, Neon connection pooling, Supabase pgbouncer mode) between the
application and PostgreSQL. Direct connections from serverless are not sustainable.
- Set `statement_timeout` and `lock_timeout` on the connection or session for
long-running operations to prevent them from blocking the entire application.
## Transactions
- Any operation that writes to more than one table must be wrapped in a transaction.
Partial writes leave the database in an inconsistent state.
- Keep transactions as short as possible. Do not make network requests (HTTP calls,
external APIs) inside a transaction — this holds locks for the duration of the
network call.
## Definition of done
- No string-interpolated SQL in the codebase (`grep -r "query\`" src/` should return
only parameterised template tag usage, not concatenation).
- All new tables have primary key and foreign key indexes.
- All `findMany` / `SELECT` queries have `LIMIT`.
- Migration files are committed and named descriptively.
- `tsc --noEmit` passes (typed query results match schema types).

为什么需要这些规则

  • 永远不要字符串拼接 SQL 是基础规则。参数化查询不仅是最佳实践,更是防御 SQL 注入的唯一手段——代理在时间压力下,如果不知道如何用特定 ORM 的参数语法表达查询,就会进行拼接。
  • 删除列需要两次部署 避免了最常见的零停机迁移错误。如果迁移删除列时应用程序仍在读取该列,那么迁移时刻所有正在处理的请求都会失败。代理在生成模式变更时,不会自然地考虑到部署窗口。

适用场景

  • 任何使用 PostgreSQL 的生产应用程序,且数据完整性和查询性能至关重要:SaaS 应用、内容平台、电商后端、分析管道。

不适用场景

  • 基于 SQLite 的应用或数据库真正临时的项目(测试夹具、本地开发种子数据)——迁移规范和索引规则是不必要的开销。