CODE PHASE (Database): Data layer implementation patterns and best practices.
Provides schema design patterns, query optimization strategies, indexing guidelines, data integrity constraints,...
Database implementation patterns and best practices for the CODE phase of PACT framework.
Is this a new table?
āā YES ā Start with 3NF normalization
ā āā High read frequency (>80% reads)?
ā ā āā Consider selective denormalization for hot paths
ā āā High write frequency (>80% writes)?
ā āā Keep normalized, optimize with indexes
āā NO ā Extending existing table?
āā Adding columns ā Check NULL handling strategy
āā Changing columns ā Plan migration strategy
āā Removing columns ā Implement soft deprecation first
Data type selection:
āā Identifiers ā BIGINT (future-proof) or UUID (distributed)
āā Timestamps ā TIMESTAMP WITH TIME ZONE (always)
āā Money ā DECIMAL(19,4) (never FLOAT)
āā Text ā VARCHAR with explicit limits (avoid TEXT unless needed)
āā Boolean ā BOOLEAN (not TINYINT or CHAR)
First Normal Form (1NF)
Second Normal Form (2NF)
Third Normal Form (3NF)
When to Denormalize
Always Index
Consider Indexing
Never Index
Composite Index Guidelines
-- Order matters! Most selective first
CREATE INDEX idx_user_activity
ON user_events(user_id, event_type, created_at);
-- This query uses the index efficiently:
WHERE user_id = ? AND event_type = ? AND created_at > ?
-- This query only uses first part:
WHERE user_id = ?
-- This query CANNOT use the index:
WHERE event_type = ? AND created_at > ?
Index Types Quick Guide
Before Writing Queries
While Writing Queries
After Writing Queries
Common Anti-Patterns to Avoid
-- ā N+1 Query Problem
SELECT * FROM users;
-- Then for each user:
SELECT * FROM orders WHERE user_id = ?;
-- ā
Use JOIN instead
SELECT u.*, o.*
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
-- ā Function on indexed column prevents index use
WHERE YEAR(created_at) = 2024
-- ā
Rewrite to preserve index
WHERE created_at >= '2024-01-01'
AND created_at < '2025-01-01'
-- ā OR on different columns prevents index use
WHERE email = ? OR username = ?
-- ā
Use UNION if both columns are indexed
SELECT * FROM users WHERE email = ?
UNION
SELECT * FROM users WHERE username = ?
-- ā Implicit type conversion
WHERE user_id = '123' -- user_id is INT
-- ā
Use correct type
WHERE user_id = 123
Constraint Hierarchy
Referential Integrity Actions
-- Prevent deletion if referenced
ON DELETE RESTRICT -- Default, explicit is better
-- Delete dependent rows automatically
ON DELETE CASCADE -- Use carefully, can cascade widely
-- Set FK to NULL when parent deleted
ON DELETE SET NULL -- Parent must allow NULL
-- Prevent orphans without cascade
ON DELETE NO ACTION -- Similar to RESTRICT
Soft Delete Pattern
-- Add columns
ALTER TABLE users ADD COLUMN deleted_at TIMESTAMP;
ALTER TABLE users ADD COLUMN deleted_by BIGINT;
-- Create partial index for active records only
CREATE INDEX idx_active_users
ON users(email)
WHERE deleted_at IS NULL;
-- Queries always filter
SELECT * FROM users WHERE deleted_at IS NULL;
-- Instead of DELETE
UPDATE users
SET deleted_at = NOW(), deleted_by = ?
WHERE id = ?;
Audit Trail Pattern
-- Audit table captures all changes
CREATE TABLE user_audit (
audit_id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
operation CHAR(1) NOT NULL, -- I, U, D
changed_at TIMESTAMP NOT NULL DEFAULT NOW(),
changed_by BIGINT,
old_values JSONB,
new_values JSONB
);
-- Trigger to populate audit table
CREATE TRIGGER user_audit_trigger
AFTER INSERT OR UPDATE OR DELETE ON users
FOR EACH ROW EXECUTE FUNCTION audit_user_changes();
Zero-Downtime Migration Strategy
Safe Column Addition
-- Step 1: Add column with default (fast, no rewrite)
ALTER TABLE users
ADD COLUMN status VARCHAR(20) DEFAULT 'active' NOT NULL;
-- Step 2: Update existing rows if needed (in batches)
UPDATE users
SET status = CASE
WHEN email_verified THEN 'active'
ELSE 'pending'
END
WHERE id >= ? AND id < ?;
-- Step 3: Remove default after backfill (allows NULLs for new rows)
ALTER TABLE users ALTER COLUMN status DROP DEFAULT;
Safe Column Removal
-- Step 1: Stop writing to column (code deploy)
-- Step 2: Wait 1+ deployment cycles
-- Step 3: Drop column (can cause table rewrite in some RDBMS)
ALTER TABLE users DROP COLUMN old_field;
Renaming Strategy
-- Don't rename! Instead:
-- 1. Add new column
-- 2. Dual write to both
-- 3. Backfill
-- 4. Switch reads
-- 5. Remove old column
-- If you must rename immediately (small table):
BEGIN;
ALTER TABLE users RENAME COLUMN old_name TO new_name;
-- Update all application code simultaneously
COMMIT;
ACID Review
Isolation Levels (from weakest to strongest)
-- Read Uncommitted: Dirty reads possible
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-- Read Committed: Most common default
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- Repeatable Read: Prevents non-repeatable reads
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- Serializable: Strongest, can cause deadlocks
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
Transaction Best Practices
Deadlock Prevention
-- ā Can deadlock if two processes reverse order
-- Process A:
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- Process B:
UPDATE accounts SET balance = balance - 50 WHERE id = 2;
UPDATE accounts SET balance = balance + 50 WHERE id = 1;
-- ā
Always lock in same order (by ID)
UPDATE accounts SET balance = balance + CASE
WHEN id = 1 THEN -100
WHEN id = 2 THEN 100
END
WHERE id IN (1, 2)
ORDER BY id; -- Consistent lock order
Access Control
-- Principle of least privilege
CREATE ROLE app_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_readonly;
CREATE ROLE app_readwrite;
GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO app_readwrite;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO app_readwrite;
-- Never grant DELETE to application roles
-- Deletions should go through admin processes or soft delete
Row-Level Security (RLS)
-- Enable RLS on table
ALTER TABLE documents ENABLE ROW LEVEL SECURITY;
-- Policy: users see only their own documents
CREATE POLICY user_documents ON documents
FOR SELECT
USING (user_id = current_user_id());
-- Policy: admins see everything
CREATE POLICY admin_documents ON documents
FOR ALL
USING (is_admin());
Data Encryption
SQL Injection Prevention
-- ā NEVER construct queries with string concatenation
query = "SELECT * FROM users WHERE email = '" + user_input + "'"
-- ā
ALWAYS use parameterized queries
query = "SELECT * FROM users WHERE email = ?"
params = [user_input]
Key Metrics to Track
Query Analysis
-- PostgreSQL: Find slow queries
SELECT
calls,
total_time,
mean_time,
query
FROM pg_stat_statements
ORDER BY mean_time DESC
LIMIT 10;
-- MySQL: Enable slow query log
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 0.1; -- 100ms
-- Check index usage
SELECT
schemaname,
tablename,
indexname,
idx_scan, -- Number of index scans
idx_tup_read -- Number of index entries returned
FROM pg_stat_user_indexes
ORDER BY idx_scan ASC;
Database design often requires deep reasoning about trade-offs. Use the mcp__sequential-thinking__sequentialthinking tool when:
Schema Design Decisions
Performance Optimization
Data Integrity Design
Transaction Design
Example Sequential Thinking Prompt
I need to design a schema for a multi-tenant SaaS application with these requirements:
- 1000+ tenants, average 10k records per tenant
- Strong data isolation between tenants required for compliance
- 90% of queries filter by tenant_id
- Need to support per-tenant schema customization in the future
Should I use:
1. Shared schema with tenant_id column
2. Separate schema per tenant
3. Separate database per tenant
Walk through the trade-offs of each approach considering:
- Query performance and indexing
- Data isolation and security
- Operational complexity
- Future extensibility
- Cost at scale
Detailed patterns and examples available in:
This skill supports the CODE phase for database implementation:
Input from ARCHITECT Phase
Output for TEST Phase
Quality Gates
Design Pitfalls
Implementation Pitfalls
Security Pitfalls
Performance Pitfalls
A well-implemented database solution demonstrates:
This skill is part of the PACT Framework for principled AI-assisted development.