Schema Design Best Practices
intermediateddl
2 min read
What is Schema Design?
Schema design is the process of defining your database structure: tables, columns, data types, constraints, and relationships. Good schema design leads to efficient queries, data integrity, and maintainable systems.
Key Principles
-- Use descriptive, consistent names
CREATE TABLE students (
id SERIAL PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Always define relationships explicitly
ALTER TABLE enrollments
ADD CONSTRAINT fk_student
FOREIGN KEY (student_id) REFERENCES students(id);Best Practices
- Normalize — Follow normal forms (at least 3NF) to reduce data duplication.
- Use Proper Types — Use NUMERIC for money, TIMESTAMPTZ for timestamps, TEXT for unbounded strings.
- Constraints — Add NOT NULL, CHECK, UNIQUE, and FK constraints to enforce data integrity.
- Naming — Use snake_case, singular table names, and descriptive column names.
- Primary Keys — Every table should have a primary key.
Key Points
- created_at / updated_at — Add timestamp columns to track when rows are created and modified.
- Avoid Reserved Words — Do not name columns 'user', 'order', 'group' without quoting.
- Index Foreign Keys — PostgreSQL does not auto-index FK columns.
- Use TIMESTAMPTZ — Always use TIMESTAMPTZ (with timezone) instead of TIMESTAMP.
- Soft Deletes — Consider a deleted_at column instead of hard DELETE for audit trails.
Guided Practice
Solve the challenge below. Use hints when stuck and check your answer for instant feedback.
Practice challengeGuided learning mode
Schema Design Best Practices Challenge
Write a query that solve this task: a properly structured products table.
Expected result
A products table with proper types, constraints, and timestamps.
Hidden checks
- Returned rows and values
- Output columns and result shape
- Final database state after the query runs
Lesson guidance
What is Schema Design?
Initializing database...Each run starts from fresh sample data.
More Examples
Junction table with constraints
A well-designed many-to-many relationship table.
Initializing database...Each run starts from fresh sample data.
Frequently Asked Questions
Should I use UUID or SERIAL for primary keys?
SERIAL is simpler and more efficient for most cases. Use UUID when you need globally unique IDs across distributed systems or want to hide sequence patterns.
How normalized should my schema be?
Aim for 3NF (Third Normal Form) as a starting point. Denormalize strategically for read-heavy queries, but start normalized.