Database Design Best Practices
Table of Contents
Introduction: The Evolution of My Blog Schema
Schema Design Principles
1. Design for Queries, Not Just Storage
2. Stable Primary Keys
3. UUID vs BIGSERIAL
4. Soft Deletes vs Hard Deletes
Naming Conventions That Save You Pain
Tables and Columns
Indexes and Constraints
Data Types: Choosing Correctly the First Time
Text Columns
Numeric Columns
Date and Time
JSONB: When to Use It
Schema Evolution and Migrations
Safe Migration Patterns
Zero-Downtime Index Creation
Migration Tooling
PostgreSQL Advanced Features in Practice
Generated Columns
Table Partitioning
CTEs with RETURNING
RETURNINGUPSERT with ON CONFLICT
UPSERT with ON CONFLICTConnection Pooling and Resource Management
PgBouncer Configuration
Configure postgresql.conf for Your Workload
Monitoring and Observability
Key Metrics to Track
Autovacuum Tuning
Common Anti-Patterns to Avoid
1. The EAV (Entity-Attribute-Value) Trap
2. Storing Comma-Separated Values
3. SELECT * in Application Code
4. Missing Updated_at
5. Not Using Transactions for Multi-Step Operations
The Production-Ready Blog Schema
Bridging to ORMs
What I Learned Designing Databases
Your Database Journey Continues
Immediate Next Steps
Intermediate Topics
Advanced Topics
Last updated