Score PostgreSQL schemas against a 5-level maturity model (Ad-Hoc through Optimizing) using diagnostic questions, a prioritized audit checklist by severity, and recommended next steps by timeframe. Use when assessing overall database quality, prioritizing technical debt remediation, or establishing a schema improvement roadmap. Triggered by: schema maturity, maturity model, maturity score, database quality, audit checklist, technical debt, maturity level, schema assessment.
5-level maturity model with diagnostic questions, audit checklists, and improvement roadmaps for PostgreSQL schemas.
Schema maturity scoring provides an objective framework for assessing where a database schema falls on the spectrum from "quick prototype" to "production-hardened." Without a maturity model, teams debate individual schema decisions in isolation — should we add comments? should we fix naming? — without a shared framework for prioritization. The maturity model sequences improvements so teams fix the highest-impact issues first.
Use this skill when conducting initial schema assessments, prioritizing technical debt remediation, reporting schema quality to stakeholders, or establishing a schema improvement roadmap for a team.
Characteristics:
serial or integer primary keysvarchar(255) as default text typetimestampDiagnostic signals: information_schema queries reveal mixed naming patterns, implicit constraints, and no pg_description entries.
Characteristics:
bigint or UUID primary keystext preferred over varchar(n)timestamptz used consistentlyNOT NULL on required columnsDiagnostic signals: Naming audit passes >80% of checks. All relationships have FK constraints. Nullable columns are intentional.
Characteristics:
pk_, fk_, unique_, check_)Diagnostic signals: Atlas lint passes with zero warnings. Documentation audit shows >90% coverage. Anti-pattern scan returns zero findings.
Characteristics:
pg_stat_user_indexesDiagnostic signals: Operational health dashboard exists. Index audit shows zero unused indexes. HOT update ratio >90% on write-heavy tables.
Characteristics:
Diagnostic signals: Load test results for schema changes exist. Quarterly audit history is documented. Team onboarding includes schema walkthrough.
Use these questions to quickly assess the current maturity level. Each "yes" answer earns the point for its level.
| Question | Level |
|---|---|
| Do you have naming conventions documented? | 2 |
| Are naming conventions enforced by tooling (lint/CI)? | 3 |
| Do you track schema quality metrics over time? | 4 |
| Do you have quarterly schema review cadence? | 5 |
| Question | Level |
|---|---|
| Do all relationships have FK constraints? | 2 |
| Do you use advisory locks or serializable isolation for TOCTOU-vulnerable operations? | 3 |
| Do you monitor lock contention and query wait times? | 4 |
| Do you load-test schema changes before deployment? | 5 |
| Question | Level |
|---|---|
| Is autovacuum enabled (not disabled)? | 2 |
| Have you tuned autovacuum for high-write tables? | 3 |
| Do you monitor dead tuple ratios and XID age? | 4 |
| Do you have automated alerts for vacuum-related thresholds? | 5 |
| Question | Level |
|---|---|
| Do you use a migration tool (not manual SQL)? | 2 |
| Do you lint migrations for destructive and data-dependent changes? | 3 |
| Do you use CONCURRENTLY for production index creation? | 4 |
| Do you have zero-downtime migration runbooks? | 5 |
| Question | Level |
|---|---|
| Are all columns explicitly typed (no implicit defaults)? | 2 |
| Do all tables have COMMENT ON documentation? | 3 |
| Do you track unused indexes and remove them? | 4 |
| Do you have automated schema drift detection? | 5 |
Count "yes" answers per level. The schema's maturity level is the highest level where ALL questions are answered "yes," with all lower levels also complete.
| Yes Count by Level | Assessment |
|---|---|
| Level 2: 5/5 | Ready to advance to Governed |
| Level 3: 5/5 | Ready to advance to Measured |
| Level 4: 5/5 | Ready to advance to Optimizing |
| Level 5: 5/5 | Fully mature |
Partial scores: If Level 3 has 3/5 "yes" answers, the schema is "Level 2 with partial Level 3 adoption." Focus on completing Level 3 before attempting Level 4.
These issues cause data corruption, security breaches, or service outages if left unaddressed.
| # | Check | Detection |
|---|---|---|
| 1 | XID age below 800M | SELECT age(datfrozenxid) FROM pg_database |
| 2 | No unindexed foreign keys | FK constraints without matching indexes |
| 3 | All PKs are bigint or UUID (not serial/integer) | information_schema.columns WHERE column_default LIKE 'nextval%' |
| 4 | timestamptz used everywhere (no bare timestamp) | information_schema.columns WHERE data_type = 'timestamp without time zone' |
| 5 | No plaintext secrets in schema (credential tables use hashes) | Columns named key, secret, token, password without _hash suffix |
| 6 | RLS enabled on tenant-scoped tables (if multi-tenant) | SELECT relname FROM pg_class WHERE relrowsecurity = false |
| 7 | No float/double precision for monetary values | information_schema.columns for money-related column names |
These issues cause performance degradation, maintenance burden, or developer confusion.
| # | Check | Detection |
|---|---|---|
| 8 | Consistent naming conventions across all tables | Naming audit script |
| 9 | All constraints explicitly named | pg_constraint WHERE conname matches auto-generated patterns |
| 10 | No soft-delete boolean columns (is_deleted) | Column name scan |
| 11 | NOT NULL on all columns without documented NULL semantics | Nullable column audit |
| 12 | Autovacuum tuned for tables with >10K writes/hour | Per-table n_tup_ins + n_tup_upd + n_tup_del vs autovacuum settings |
| 13 | Unused indexes identified and removed | pg_stat_user_indexes WHERE idx_scan = 0 |
| 14 | JSONB columns not used for structured, frequently-queried data | JSONB columns on high-scan tables |
These issues affect long-term maintainability and team velocity.
| # | Check | Detection |
|---|---|---|
| 15 | COMMENT ON for all tables | pg_description audit |
| 16 | COMMENT ON for all boolean, nullable, and JSONB columns | Column-level documentation audit |
| 17 | Covering indexes for hot-path queries | EXPLAIN ANALYZE on top-10 queries |
| 18 | Fillfactor tuned on write-heavy tables | pg_class.reloptions check |
| 19 | Keyset pagination replacing OFFSET on user-facing endpoints | Application code audit |
| 20 | Schema documentation enforced via CI/CD linting | Pipeline configuration audit |
| 21 | Quarterly schema audit cadence established | Process documentation |
Focus on data safety and correctness.
timestamp columns — plan migration to timestamptzserial/integer PKs — plan migration to bigintEstablish conventions and tooling.
NOT NULL to columns that should never be nullContinuous improvement and measurement.
Attempting Level 4 (Measured) without completing Level 2 (Consistent). Monitoring index usage is meaningless if the indexes don't follow naming conventions and half of them are redundant.
Running the maturity assessment and filing the results without creating improvement tasks. The assessment is only valuable if it drives prioritized action.
Refusing to ship until every check passes. The maturity model is a roadmap, not a gate. High-severity items block shipping; low-severity items are tracked debt.
Assessment:
serial on most tables → Level 1varchar(255) and timestamp widespread → Level 1Score: Level 1 (Ad-Hoc)
30-day plan: Fix PK types, add missing FKs with indexes, switch to timestamptz, adopt Atlas with destructive lint enabled.
Assessment:
bigint everywhere → Level 2+text, timestamptz, numeric used correctly → Level 2+Score: Level 2 (Consistent) with partial Level 3
30-day plan: Enable naming lint in Atlas, add explicit constraint names to new migrations. 90-day plan: Achieve 100% COMMENT ON coverage, tune autovacuum, set up pg_stat monitoring.
Assessment:
Score: Level 4 (Measured)
90-day plan: Add schema change load testing to CI/CD, implement drift detection, establish quarterly review cadence.