Guides PostgreSQL schema design, index selection (B-tree/GIN/GiST), query optimization with EXPLAIN ANALYZE, connection pooling via pgbouncer, migration workflows (alembic/flyway/dbmate), transaction isolation levels, VACUUM tuning, table partitioning, and pg_stat_statements analysis. Use when writing schemas, optimizing queries, configuring connections, or planning migrations against PostgreSQL.
Provide concrete, actionable guidance for PostgreSQL schema design, query optimization, connection management, migration workflows, and operational tuning. This skill encodes production-tested patterns so agents produce correct DDL, efficient queries, and safe deployment procedures instead of guessing at PostgreSQL internals.
Use this skill when:
orm-patternsbigquerySELECT version() and \dx output. Version determines available features (e.g., MERGE in v15+, partitioning improvements in v12+).@>), full-text search (tsvector), JSONB path queriesCREATE INDEX CONCURRENTLY to avoid table locks. Always include a down/rollback migration.(2 * CPU cores) + effective_spindle_count for the PostgreSQL server; keep pgbouncer's default_pool_size at 2-3× that.pg_stat_user_tables.n_dead_tup and last_autovacuum. For high-write tables, lower autovacuum_vacuum_scale_factor to 0.01-0.05. Watch for transaction ID wraparound via age(datfrozenxid).pg_advisory_lock(key) for exclusive, pg_try_advisory_lock(key) for non-blocking. Always use pg_advisory_unlock or session-level locks that release on disconnect.pg_stat_statements for top queries by total_exec_time, calls, and mean_exec_time. Target the top 10 queries for optimization.pg_stat_user_indexes to find unused indexes for removal.MATERIALIZED hint only when you need optimization fences. In pre-12, prefer subqueries for performance-critical paths.bigserial for internal IDs (compact, ordered, index-friendly). Use UUIDv7 (time-sortable) for distributed systems or public-facing IDs.SELECT ... FOR UPDATE SKIP LOCKED for queue-like patterns. Avoid LOCK TABLE in application code.COPY for bulk inserts (10-100× faster than INSERT). Use INSERT ... ON CONFLICT for upserts. Batch updates with CTEs returning modified rows.WHERE on UPDATE/DELETE: Always require a WHERE clause in DML. Use BEGIN + verify row count before COMMIT.SELECT * in production queries: Enumerate columns explicitly. SELECT * breaks when columns are added and wastes I/O on unused columns.OFFSET for pagination: O(n) cost. Use keyset pagination (WHERE id > $last_seen_id ORDER BY id LIMIT $n) instead.idle_in_transaction_session_timeout.CONCURRENTLY for index creation: CREATE INDEX takes an ACCESS EXCLUSIVE lock. On production tables, always use CREATE INDEX CONCURRENTLY.pg_stat_statements: Flying blind on query performance. Enable it in shared_preload_libraries and review weekly.Schema Design — DDL with explicit types, constraints, and index definitionsMigration Plan — Ordered migration files with up/down, noting lock implicationsQuery Plan Analysis — EXPLAIN output with interpretation and optimization notesConnection Config — pgbouncer or application pool settings with rationaleValidation — Specific queries or commands to verify the change works correctlyRead these when relevant to the specific task:
references/implementation-patterns.md — Index selection, pooling config, migration workflows, query optimization, partitioningreferences/validation-checklist.md — Pre-deploy checks, index coverage, pool sizing, lock audits, EXPLAIN reviewreferences/failure-modes.md — Connection exhaustion, deadlocks, bloat, wraparound, replication lagorm-patterns — When the task involves ORM model definitions or query generation layered on PostgreSQLsqlite — When comparing embedded vs server database trade-offsdata-model — When the task is primarily about entity relationships and data modelingobservability-logging — When adding query logging, slow query tracking, or pgbadger analysispg_stat_activity for idle connections. Verify pgbouncer pool limits. Kill idle-in-transaction sessions with pg_terminate_backend().autovacuum_vacuum_scale_factor. Check if long-running transactions are blocking vacuum. Monitor n_dead_tup growth rate.DROP COLUMN), use a two-phase approach: deprecate first, remove later.ANALYZE table_name). Verify index is being used (enable_seqscan = off as diagnostic, never in production).VACUUM FREEZE on affected tables immediately. Monitor age(datfrozenxid) — emergency at 2 billion.