Ghost is one of the most popular open-source blogging platforms — 30K+ GitHub stars, powering millions of blogs.
Its database has evolved over 10 years. From posts and users in 2015 to v6.0's big cleanup in 2025: 8 migrations, 12 tables, 80+ columns, plus Stripe payments, email tracking,
and a membership system.
The SQL migration files are all there in migrations/. But not a single comment explains what any field actually means.
Can you answer these questions?
Here's a migration from Ghost's codebase:
ALTER TABLE posts DROP COLUMN created_by;
ALTER TABLE posts DROP COLUMN updated_by;
ALTER TABLE users DROP COLUMN created_by;
ALTER TABLE users DROP COLUMN updated_by;
ALTER TABLE tags DROP COLUMN created_by;
ALTER TABLE tags DROP COLUMN updated_by;
ALTER TABLE settings DROP COLUMN created_by;
ALTER TABLE settings DROP COLUMN updated_by;
ALTER TABLE members DROP COLUMN created_by;
ALTER TABLE members DROP COLUMN updated_by;
Safe cleanup or dangerous destruction? Why remove created_by? What replaced it?
Or take the posts table:
`type` VARCHAR(50) DEFAULT 'post'
`status` VARCHAR(50) DEFAULT 'draft'
`visibility` VARCHAR(50) DEFAULT 'public'
`locale` VARCHAR(6)
What are the valid values for status? Is it draft/published or active/inactive? When did visibility gain a paid tier? Why is locale only 6 characters — can it store
zh-CN?
Nobody can answer these off the top of their head. The answers are scattered across source code, git history, and the fading memory of whoever wrote the migration three years ago.
One command. Thirty seconds.
npx schemalog generate
npx schemalog dict
8 migrations analyzed. Here's what comes out.
1. Migration Changelog
A human-readable summary of every migration, with risk levels:
| # | Timestamp | Change | Risk |
|---|-----------|--------|------|
| 1 | 2015-09-15 | Create posts | 🟢 safe |
| 2 | 2015-09-15 | Create users | 🟢 safe |
| 3 | 2015-09-15 | Create tags + posts_tags | 🟢 safe |
| 4 | 2016-04-22 | Create settings + seed data | 🟢 safe |
| 5 | 2019-05-22 | Create members (subscriptions) | 🟢 safe |
| 6 | 2020-06-15 | Stripe payments + subscriptions | 🟢 safe |
| 7 | 2023-05-23 | Email tracking system | 🟢 safe |
| 8 | 2025-07-22 | Drop 10 deprecated columns | 🔴 danger |
That last migration gets flagged red automatically. Ten columns dropped across five tables. In a decade-old project, that kind of change shouldn't pass silently.
2. Per-Migration Analysis
Analyzing: 20250722191500_v6_cleanup_deprecated_fields.sql
🔴 danger — Removes deprecated created_by and updated_by columns
from posts, users, tags, settings, and members,
plus deletes obsolete AMP settings.
⚠️ 10 local danger(s) detected
Not just what changed — why. The AI infers that created_by / updated_by were replaced by Ghost 6.0's Action Log system. All from reading the SQL.
3. Complete Data Dictionary
Every table, every column, explained. Here's the posts table:
| Column | Type | Description |
|--------|------|-------------|
| id | VARCHAR(24) | Primary key, unique identifier |
| uuid | VARCHAR(36) | Universally unique identifier |
| title | VARCHAR(2000) | Post title |
| slug | VARCHAR(191) | URL-friendly version of title |
| mobiledoc | TEXT | Content in Mobiledoc rich-text format |
| html | TEXT | Rendered HTML content |
| plaintext | TEXT | Plain text version of content |
| featured_image | VARCHAR(2000) | URL of the featured image |
| featured | TINYINT(1) | Whether the post is featured (0/1) |
| type | VARCHAR(50) | post or page |
| status | VARCHAR(50) | draft, published, scheduled |
| visibility | VARCHAR(50) | public, members, paid |
| locale | VARCHAR(6) | Language code: en, zh-CN |
| author_id | VARCHAR(24) | Foreign key to users.id |
| published_at | DATETIME | When the post was published |
80+ columns across 12 tables. Every single one gets an AI-inferred description, type explanation, and enum values where applicable.
This isn't Ghost's problem. It's yours.
Ghost is a well-designed project. But even the best projects have database documentation that slowly rots.
Your project is no different. Every release adds more .sql files to migrations/. Three months later:
- What's the difference between
user_statusandaccount_state? - Is
legacy_scorestill used anywhere? - Who ran that
DROP TABLEand did anyone notice?
This isn't about lacking someone to write documentation. Humans shouldn't be doing this job.
SQL migrations are already machine-readable. Translating them into human-readable docs is exactly what AI should be doing.
Schemalog — your database docs stop lying
npm install -g schemalog
cd your-project
schemalog init --dir migrations
schemalog generate # → SCHEMA.md
schemalog dict # → DATA_DICTIONARY.md
Supported formats: YYYYMMDD_description.sql or YYYYMMDDHHMMSS_description.sql
Works with Supabase, Drizzle, Flyway, raw SQL — whatever produces timestamped migration files.
AI Providers: DeepSeek or OpenAI (GPT-4o-mini). A typical analysis costs a fraction of a cent.
Incremental caching: Second run costs zero — only new or changed migrations hit the AI.
Danger detection: DROP TABLE, DROP COLUMN, ALTER TYPE flagged locally with zero latency.
GitHub Action: PR contains a .sql migration → auto-comments the changelog. No more digging through migration folders during code review.
Try it out:
npm install -g schemalog
npm: npmjs.com/package/schemalog
Questions or feedback? Drop a comment below.
First release. All feedback welcome.
Top comments (0)