database-conventions
Skill1 starsChanged 2 months agoMITView on GitHub
Use when designing a database schema, writing migrations, reviewing SQL queries, or choosing between relational and document models — covers naming, index strategy, migration safety, N+1 prevention, and transaction boundaries.
We split this skill into the rules it gives an agent
11 rules. About 0 are common, 5 are distinctive, and 6 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
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
- Very specific: line 31
Every table has a primary key.
- Very specific: line 34
Always store timestamps with timezone (TIMESTAMPTZ in Postgres).
- Very specific: line 37
Avoid TEXT for constrained values — use an ENUM or a lookup table so the DB enforces the constraint.
- Distinctive: line 58
Every foreign key column gets an index (Postgres doesn't auto-index FKs).
- Very specific: line 59
Add an index on any column used in a WHERE, ORDER BY, or JOIN in frequently-run queries.
- Distinctive: line 63
Don't index every column — indexes slow writes and consume storage.
- Very specific: line 82
Every migration is reversible — provide a down migration.
- Distinctive: line 122
Use transactions for any multi-step operation where partial completion would leave the data in an inconsistent state.
- Distinctive: line 137
Keep transactions as short as possible — hold locks for the minimum time.
- Distinctive: line 138
Don't make external HTTP calls inside a transaction.
- Very specific: line 139
Read-only queries don't need transactions (but REPEATABLE READ isolation may be appropriate for consistent multi-query reads).
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill 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.
Files
Database Conventions
Design and operational guidelines for relational databases (PostgreSQL primary dialect; MySQL/SQLite notes included where they differ). Apply these when creating schemas, reviewing migrations, or writing queries.
Naming
- Tables:
snake_case, plural (users,audit_logs,order_items) - Columns:
snake_case, singular (user_id,created_at,is_active) - Primary keys:
id(serial/UUID) - Foreign keys:
<referenced_table_singular>_id(user_id,order_id) - Booleans:
is_orhas_prefix (is_active,has_verified_email) - Timestamps:
created_at,updated_at,deleted_at(UTC, with timezone)
Schema design
- Every table has a primary key. Prefer UUID (
gen_random_uuid()) for distributed or externally-exposed IDs; serial/bigserial for internal join tables where IDs never leave the DB. - Always store timestamps with timezone (
TIMESTAMPTZin Postgres). - Soft-delete via
deleted_at IS NULLfilter, notDELETE— unless you have a clear data retention policy that requires hard deletes. - Avoid
TEXTfor constrained values — use anENUMor a lookup table so the DB enforces the constraint.
-- Good: constrained status field
CREATE TYPE order_status AS ENUM ('pending', 'paid', 'shipped', 'cancelled');
CREATE TABLE orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id),
status order_status NOT NULL DEFAULT 'pending',
total_cents INTEGER NOT NULL CHECK (total_cents >= 0),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);Indexes
- Every foreign key column gets an index (Postgres doesn't auto-index FKs).
- Add an index on any column used in a
WHERE,ORDER BY, orJOINin frequently-run queries. - Composite indexes: column order matters — put equality conditions first, range/sort columns last.
- Don't index every column — indexes slow writes and consume storage.
-- Required: FK index
CREATE INDEX idx_orders_user_id ON orders(user_id);
-- Composite: user_id first (equality), created_at second (range/sort)
CREATE INDEX idx_orders_user_created ON orders(user_id, created_at DESC);Migrations
Never edit a migration that has been applied anywhere. Migrations are immutable once run — treat them like published git commits.
Rules:
- Each migration does one logical change.
- Every migration is reversible — provide a
downmigration. - Adding a column: always
nullableor with aDEFAULTso existing rows are not blocked during migration. - Renaming a column: two-migration pattern (add new → backfill → drop old) to avoid downtime for live deploys.
- Dropping a column or table: mark as
NOT NULLremoval / unused first, deploy, then drop in a subsequent release.
-- WRONG: adds NOT NULL column without a default → fails on existing rows
ALTER TABLE users ADD COLUMN phone_number VARCHAR(20) NOT NULL;
-- RIGHT: nullable first, add default/constraint in a follow-up migration
ALTER TABLE users ADD COLUMN phone_number VARCHAR(20);N+1 queries
N+1 occurs when you load a list of N records and then execute 1 query per record to load related data.
# WRONG: N+1 — one query for users, then one per user for posts
users = db.query(User).all()
for user in users:
posts = db.query(Post).filter_by(user_id=user.id).all() # N queries
# RIGHT: eager load with a JOIN or subquery
users = db.query(User).options(joinedload(User.posts)).all()Detect N+1 in development: log slow queries, use query count assertions
in tests, or use a tool like sqlalchemy-utils's query counter.
Transactions
Use transactions for any multi-step operation where partial completion would leave the data in an inconsistent state.
# WRONG: transfer without transaction — debit can succeed while credit fails
account_a.balance -= amount
account_b.balance += amount
# RIGHT: wrap in a transaction
with db.begin():
account_a.balance -= amount
account_b.balance += amountRules:
- Keep transactions as short as possible — hold locks for the minimum time.
- Don't make external HTTP calls inside a transaction.
- Read-only queries don't need transactions (but
REPEATABLE READisolation may be appropriate for consistent multi-query reads).
Query review checklist
- All user-supplied values in parameterised queries (no string interpolation)
- Foreign keys are indexed
SELECT *replaced with explicit column list in production queries- Pagination uses
LIMIT+OFFSETor cursor-based (prefer cursor for large sets) - No N+1 pattern in loops
- Multi-step writes wrapped in a transaction
- Migrations are reversible and don't add NOT NULL columns without defaults
In this repository77 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- .github/copilot-instructions.mdCopilot instructions
- .cursor/rules/accessibility.mdcCursor rule
- .cursor/rules/agentharness-router.mdcCursor rule
- .cursor/rules/agentic-loops.mdcCursor rule
- .cursor/rules/api-design.mdcCursor rule
- .cursor/rules/audit-review-followup.mdcCursor rule
- .cursor/rules/big-work-plan-review.mdcCursor rule
- .cursor/rules/branching.mdcCursor rule
- .cursor/rules/clean-architecture.mdcCursor rule
- .cursor/rules/code-review-api.mdcCursor rule
- .cursor/rules/code-review-db.mdcCursor rule
- .cursor/rules/code-review.mdcCursor rule
- .cursor/rules/code-review-ui.mdcCursor rule
- .cursor/rules/committing.mdcCursor rule
- .cursor/rules/database-conventions.mdcCursor rule
- .cursor/rules/dependency-audit.mdcCursor rule
- .cursor/rules/dependency-injection.mdcCursor rule
- .cursor/rules/design-patterns.mdcCursor rule
- .cursor/rules/docker-conventions.mdcCursor rule
- .cursor/rules/error-handling.mdcCursor rule
- .cursor/rules/file-placement-policy.mdcCursor rule
- .cursor/rules/github-issue-triage.mdcCursor rule
- .cursor/rules/go-conventions.mdcCursor rule
- .cursor/rules/harness-feedback.mdcCursor rule
- .cursor/rules/logging.mdcCursor rule
- .cursor/rules/multi-agent-coordination.mdcCursor rule
- .cursor/rules/mutation-testing.mdcCursor rule
- .cursor/rules/performance-profiling.mdcCursor rule
- .cursor/rules/planning-with-files.mdcCursor rule
- .cursor/rules/port-agent-config.mdcCursor rule
- .cursor/rules/project-bootstrap.mdcCursor rule
- .cursor/rules/python-conventions.mdcCursor rule
- .cursor/rules/react-best-practices.mdcCursor rule
- .cursor/rules/refactoring.mdcCursor rule
- .cursor/rules/requirements-clarification.mdcCursor rule
- .cursor/rules/security-review.mdcCursor rule
- .cursor/rules/solid-principles.mdcCursor rule
- .cursor/rules/testing.mdcCursor rule
- .cursor/rules/typescript-conventions.mdcCursor rule
- accessibilitySkill
- agentic-loopsSkill
- api-designSkill
- audit-review-followupSkill
- big-work-plan-reviewSkill
- branchingSkill
- clean-architectureSkill
- code-review-apiSkill
- code-review-dbSkill
- code-reviewSkill
- code-review-uiSkill
- committingSkill
- dependency-auditSkill
- dependency-injectionSkill
- design-patternsSkill
- docker-conventionsSkill
- error-handlingSkill
- file-placement-policySkill
- github-issue-triageSkill
- The first 60 shown.
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
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.

