Guidelines that improve reliability, performance, and security in PostgreSQL databases.
Design schemas before writing code. Group related objects into logical schemas, use snake_case names, and keep names singular (customer, order). Add primary keys to every table and use the smallest data type that fits future growth.
Favor snake_case, avoid reserved words, and make names self-describing. For example, use order_date instead of date. Prefix foreign keys with the referenced table: customer_id, product_id.
Create indexes only when they speed up frequent filters, joins, or ordering. Measure impact with EXPLAIN ANALYZE. Drop unused indexes to save RAM and write overhead.
Index columns that appear in WHERE, JOIN, or ORDER BY on tables with >10,000 rows. Composite indexes help when queries filter by multiple columns in the same order.
Use partial indexes to cover hot subsets of data, such as open orders: CREATE INDEX idx_orders_open ON orders(order_date) WHERE status='open';. They stay small and improve cache hit rates.
Always profile with EXPLAIN (ANALYZE, BUFFERS). Use parameterized queries to let PostgreSQL reuse execution plans. Avoid SELECT *; specify needed columns to reduce I/O.
It uncovers sequential scans, mis-estimated row counts, and missing indexes before users notice slowness. Capture plans in CI to detect regressions early.
Partition large tables by date or hash to keep index sizes small and enable pruning. Retain only needed history; archive old partitions to cheaper storage.
Partition when rows exceed 50 million or when nightly maintenance exceeds maintenance_work_mem. Range-partition Orders by order_date month for predictable pruning.
Schedule VACUUM and ANALYZE via autovacuum but tune thresholds for write-heavy tables. Run VACUUM FULL during low-traffic windows after mass deletes to reclaim disk.
track_io_timing lets you measure disk latency in query plans, enabling better decisions on index placement and hardware upgrades.
Use role-based access control: grant SELECT, INSERT, UPDATE separately. Disable password login for superuser; use a dedicated admin role. Always enforce SSL and rotate credentials.
Autovacuum works for most workloads, but high-churn tables may require lowered thresholds or scheduled manual VACUUM FULL after bulk deletes.
BIGSERIAL is faster and smaller. Use UUID only when global uniqueness across systems outweighs the extra storage and index size.
Let autovacuum handle routine ANALYZE. Run manual ANALYZE after major data loads so the planner has accurate statistics immediately.