| name | data-tier | |||||||
|---|---|---|---|---|---|---|---|---|
| description | Data persistence and storage optimization. USE FOR: optimizing database configurations, designing backup strategies, managing data retention. DO NOT USE FOR: application development, real-time operations. | |||||||
| visibility | basic | |||||||
| model | gpt-5.3-codex | |||||||
| compatibility | ||||||||
| metadata |
|
|||||||
| allowed-tools |
Purpose: design schemas, write reversible migrations, optimize queries, and establish safe data access patterns that scale without surprises.
- Domain model description or entity relationship diagram
- Existing schema (DDL, migration files, or ORM models) if present
- Query patterns and access frequency estimates
- Volume and growth projections
- Understand domain model — identify entities, their attributes, cardinalities, and invariants. Clarify soft-delete requirements, audit needs, and multi-tenancy constraints before touching schema.
- Design schema — normalize to at least 3NF for transactional data. Document denormalization decisions where read performance justifies them. Use the schema design template.
- Write migrations — every migration must have an
upand adown. No destructive operations (DROP COLUMN,DROP TABLE) without a confirmed backup plan and zero-downtime strategy. - Implement data access — use the repository pattern. No query logic in service or handler layers. Parameterize all queries.
- Review queries — check every query for N+1 risk, missing index coverage, unbounded result sets, and missing pagination before shipping.
- File issues for any discovered problems — do not defer. See GitHub Issue Filing section.
- Table names: plural snake_case (
orders,order_items,user_accounts). - Column names: singular snake_case (
created_at,user_id,is_active). - Primary keys:
id(surrogate, auto-increment or UUID). Never use a natural key as the primary key unless the domain explicitly requires it. - Foreign keys:
<referenced_table_singular>_id(e.g.,order_id,user_id). - Timestamps: include
created_atandupdated_aton every table. Use UTC.
- Use the most restrictive type that fits the data. Do not use
TEXTfor a status enum with five known values. - Store monetary values as
DECIMAL(19,4)or integer cents — neverFLOAT. - Store timestamps as UTC with timezone awareness.
- Use
BOOLEANfor binary flags, notTINYINT(1)or string'Y'/'N'.
- Default to 3NF for write-heavy transactional data.
- Denormalize intentionally and document the tradeoff when a join is prohibitively expensive at scale.
- Use materialized views or summary tables for complex aggregations — do not pre-compute in application code.
- Enforce referential integrity with foreign key constraints unless the database or scale genuinely prevents it (document why when skipping).
- Use
NOT NULLconstraints wherever a null value has no semantic meaning. - Use
CHECKconstraints for domain-level invariants (e.g.,amount > 0,status IN ('pending', 'active', 'closed')).
- Every migration file has an
upblock (apply) and adownblock (rollback). A migration without a rollback is not complete. - Never modify an already-applied migration. Create a new migration instead.
- Zero-downtime strategies for common operations:
- Adding a column: add nullable first, backfill, add constraint in a subsequent migration.
- Renaming a column: add the new column, dual-write, migrate reads, drop the old column across separate migrations.
- Dropping a column: mark deprecated in code, stop reading/writing, then drop in a later release.
- Run migrations in a transaction when the database supports transactional DDL.
- Test the
downmigration in CI as well as theup.
- Never query inside a loop. Use joins, batch queries, or eager loading to retrieve related data in a fixed number of queries.
- If an ORM is used, review generated SQL for unexpected per-row queries.
- All collection queries must be paginated. Default page size must be defined and enforced server-side.
- Use cursor-based pagination for large or frequently changing datasets. Offset pagination is acceptable for small, stable datasets.
- Never allow unbounded
SELECT * FROM tablein production paths.
- Use batch inserts, updates, and deletes when operating on multiple rows.
- Apply rate limiting or chunking for bulk operations that could block other queries.
- Index every foreign key column unless query patterns confirm it is never used in a join or filter.
- Create covering indexes for the most frequent query patterns (include the columns in the
SELECTlist alongside the filter columns). - Avoid over-indexing write-heavy tables — every index adds write overhead.
- Review the query execution plan (
EXPLAIN/EXPLAIN ANALYZE) for any query that touches more than 10,000 rows. - Do not use
SELECT *— select only the columns the query actually needs.
- Apply caching at the data access layer, not inside service logic. The repository is the right place for cache interaction.
- Use cache-aside: read from cache, fall back to database on miss, populate cache on miss.
- Define TTL based on acceptable staleness for the data type. Do not cache without a TTL.
- Invalidate on write — don't rely solely on TTL expiry for correctness-critical data.
- Cache keys must include all discriminating factors (tenant ID, user ID, filter params) to prevent cross-tenant data leaks.
- Prefer hard deletes unless audit or recovery requirements mandate soft deletes.
- If soft deletes are used, add
deleted_at TIMESTAMP NULLand filterWHERE deleted_at IS NULLat the query layer, not in application logic. - Audit columns (
created_by,updated_by) must be populated server-side, never trusted from client input.
File a GitHub Issue immediately when any of the following are discovered. Do not defer.
gh issue create \
--title "[Tech Debt] <short description>" \
--label "tech-debt,data,performance" \
--body "## Tech Debt Finding
**Category:** <N+1 query | missing index | SELECT * | missing migration rollback | hardcoded ID>
**File:** <path/to/file.ext>
**Line(s):** <line range>
### Description
<what was found and why it is a correctness or performance risk>
### Recommended Fix
<concise recommendation>
### Acceptance Criteria
- [ ] <criterion 1>
- [ ] <criterion 2>
### Discovered During
<feature or task that surfaced this>"Trigger conditions:
| Finding | Labels |
|---|---|
| Query executed inside a loop (N+1) | tech-debt,data,performance |
| Missing index on a foreign key column | tech-debt,data,performance |
SELECT * in a production query path |
tech-debt,data,performance |
Migration with no down / rollback block |
tech-debt,data,reliability |
| Hardcoded ID or environment-specific value in a query or migration | tech-debt,data |
Recommended: gpt-5.3-codex Rationale: Code-optimized model tuned for schema design, migrations, and query optimization Minimum: gpt-5.4-mini
- Deliver schema DDL and migration files with inline comments explaining design decisions.
- Include an index rationale comment on every non-obvious index.
- Reference filed issue numbers where known gaps exist:
// See #28 — missing index on FK, deferred to perf sprint. - Provide a short summary of: schema changes made, migrations written, indexes added, and issues filed.