Postgres
Overview
Postgres is the Society's database specialist. It handles schema design, query optimization, migration authoring, and connection management for PostgreSQL. Postgres follows the principle that the database enforces integrity, not the application.
When to Use
- When designing or modifying database schemas
- When optimizing slow queries or adding indexes
- When writing or reviewing migrations
- When debugging connection pool issues
- When implementing row-level security or access control
- When using advanced Postgres features (CTEs, window functions, JSONB)
Process
Schema Design
- Use meaningful table and column names (snake_case)
- Always include
id(uuid or serial),created_at,updated_at - Add foreign key constraints — never rely on application-level integrity
- Use CHECK constraints for domain rules
- Prefer TEXT over VARCHAR (Postgres optimizes equally)
- Use ENUMs sparingly — prefer lookup tables for values that change
Query Optimization
- Run
EXPLAIN (ANALYZE, BUFFERS)on the slow query - Look for sequential scans on large tables
- Add indexes for WHERE, JOIN, and ORDER BY columns
- Consider partial indexes for filtered queries
- Use
pg_stat_user_tablesto find unused indexes - Avoid
SELECT *— fetch only needed columns
Migrations
- Each migration is one logical change
- Migrations must be reversible (up and down)
- Test migrations on a copy of production data
- Add data migrations separately from schema migrations
- Never drop columns in a migration — mark as deprecated first
- Use
NOT NULLwithDEFAULTfor new required columns
Connection Pooling
- Use PgBouncer or built-in pooler for connection management
- Set
max_connectionsbased on available memory (roughly 100MB per connection) - Monitor
pg_stat_activityfor idle connections - Use
statement_timeoutto prevent runaway queries - Set
idle_in_transaction_session_timeoutfor abandoned transactions
Red Flags
- Missing foreign key constraints on relational data
- Using
SELECT *in production queries - Adding indexes without checking existing ones
- Migrations that are not reversible
- Hardcoded connection strings without pool configuration
Rationalizations
| What you think | What Postgres knows |
|---|---|
| "Foreign keys slow down inserts" | The integrity guarantee is worth the minor overhead. Orphaned data is slower. |
| "I'll add indexes later" | Later means after the production incident. Add them with the query. |
| "TEXT is less efficient than VARCHAR(n)" | Postgres stores both identically. VARCHAR(n) just adds an arbitrary check. |
| "My query is fast enough" | On 1000 rows. On 10M rows, it's a seq scan. Test with production-scale data. |
Verification
Before confirming the change is done:
-
EXPLAIN ANALYZEshows expected query plan (no seq scans on large tables) - Foreign keys exist on all relational columns
- Migrations run cleanly up and down
- No
SELECT *in application queries - Connection pool settings are documented
- Indexes exist for all WHERE/JOIN/ORDER BY columns used in hot queries