You can document a database column without touching the database: write the description in the model rather than in the schema. That sounds like a dodge until you price the alternative. The database’s own mechanism for column documentation, COMMENT ON COLUMN in PostgreSQL and the COMMENT attribute in MySQL, sends a sentence of prose down exactly the same path as a change to how data is stored - a migration file, a code review, an approval, a deploy window - and on MySQL it does something worse than that. Schemity keeps field descriptions in the diagram, where editing one produces no SQL at all.
This is why so many production schemas have thousands of columns and almost no comments. Not because nobody wanted to write them. Because writing one costs a deploy.
How do I document a database column without running a migration?
Keep the description in the model rather than in the storage engine. A field description is a fact about what the column means to your team; it changes no type, no constraint, no index, and nothing about what the database will accept. When it lives in the diagram, editing it is like editing a comment in a code file: you change it, review it in the same pull request as everything else, and nothing has to run against production for it to take effect.
The moment that description is a column comment, it stops being prose and becomes DDL. Now it needs a migration file, and the migration needs a reviewer, and the reviewer is looking at an ALTER TABLE against a live table. Everyone in that chain is correct to be careful, which is the problem: the care is proportionate to a schema change, and this is not one.
Why a column comment is priced like a schema change
PostgreSQL gets this closest to right. COMMENT ON COLUMN invoices.voided_at IS 'set when finance reverses an invoice' is a standalone statement that touches only the catalog. It is still a migration in every workflow where migrations own schema changes, but it is a cheap and safe one.
MySQL has no equivalent. The comment is an attribute inside the column definition, so changing it means ALTER TABLE ... MODIFY, and MySQL’s own manual states the trap plainly: “Attributes present in the original definition but not specified for the new definition are not carried forward.” The manual’s example is a column defined as INT UNSIGNED DEFAULT 1 COMMENT 'my column', modified with the intention of changing only the type:
ALTER TABLE t1 MODIFY col1 BIGINT;
| Attribute | In the original definition | After that statement |
|---|---|---|
| Data type | INT | BIGINT |
UNSIGNED | present | dropped |
DEFAULT 1 | present | dropped |
COMMENT 'my column' | present | dropped |
Three attributes vanish and the statement is perfectly valid. There is no error, no warning, and no comment-only syntax to reach for. To attach one sentence of documentation to a MySQL column, the migration has to restate the column’s entire definition correctly, which means the documentation change is now capable of altering how data is stored. That is the reverse of what anyone wanted.
Why ORMs keep declining to support column comments
The obvious escape is to let the ORM manage comments alongside everything else, and the ORMs have been declining for years. Drizzle has three separate open requests for it - issues 886, 1840 and 5203, the last opened on 1 January 2026 - and the newest one argues from an angle that did not exist when the first was filed: “In modern projects, database comments are no longer only for humans - they are increasingly important machine-readable context for AI-powered tooling.” node-db-migrate’s request, issue 558, has been open since March 2018 carrying the labels “Nice to have” and “Not Planned”. Doctrine’s migrations have their own long-running report of comments disappearing from generated migrations.
None of these maintainers are wrong. From inside a migration tool, column comments genuinely are niche: they are the only part of a column definition that no query result depends on. The pattern that emerges from eight years of open issues is not neglect, it is a category error. Documentation keeps being filed as a schema feature, gets ranked against schema features, and loses every time.
Where a field description belongs instead
In Schemity, a field carries a description of its own, the same way entities and legends already carry markdown descriptions. It is a text box in the field editor, next to the type and the default, and the sentence it holds is the one a MySQL migration would have had to restate a whole column definition to attach.

That description is the kind nobody ever writes into a column comment, because it is the reasoning behind a type choice rather than a definition of the column, and it would need a deploy. It also answers the question a reader of content TEXT actually has.
A documented field is then visible without opening anything: a bar on the leading edge of the row marks any field that has one, so which parts of a table are documented is a glance down a column rather than an audit, and the bar is drawn into SVG exports too.

One marked row out of nine is the honest picture of most schemas, and it is readable at a glance precisely because the mark is absence-shaped: you are looking for the fields that have no bar.
The important property is what does not happen. Descriptions live in the diagram’s JSON file and never reach the database, so writing one produces no migration to review and no statement to run. This was a deliberate reversal on our side rather than a design we got right first time. Schemity used to write descriptions back as column comments, and that produced exactly the migration described above - on MySQL, the worst-shaped statement in the whole product, restating an entire column definition from the diagram just to attach a sentence. It also lost work: because comments came back from the database on every re-sync, a description could be silently overwritten, invisibly on PostgreSQL and MySQL and permanently on SQL Server and SQLite.
Reading still goes one way. Importing an already documented schema arrives documented, because comments that exist in the database are read in. They are simply never written back.
When the comment really does belong in the database
There is a real case on the other side, and the Drizzle issue names it: if the reader is a program that introspects the live database - an agent connecting to a schema it has never seen, a catalog crawler, a BI tool reading the information schema - then the comment has to be in the database, because that is the only place the reader looks. A description in a diagram file it cannot open is worth nothing to it.
If that is your goal, write the comments as migrations and treat them as schema changes, deliberately. What you should not do is adopt that cost by accident for documentation that only people will ever read, which is the situation almost every team is actually in.
| Comment in the database | Description in the model | |
|---|---|---|
| Read by | Anything that introspects the catalog | People, and any export you generate |
| Cost of an edit | Migration, review, deploy | Save the file |
| MySQL cost of an edit | Restating the full column definition | The same as any other engine |
| Survives a schema refresh | Yes, it is the source | Yes, it is not overwritten |
| Reviewed in the pull request | As DDL | As a diff in the diagram JSON |
Getting the documentation to the people who need it
A description nobody can reach is not documentation, which is the fair objection to keeping it out of the database. The answer is export rather than storage: the diagram exports as a data dictionary in HTML, Markdown and Excel, covering every column with its type, key role, nullability, default and description alongside the constraints and relationships. The export follows the active view, and a context view is a saved, focused subset of the schema showing only some entities and the relationships between them, so documenting one context produces a document about that context rather than the whole database.
It also ends by counting what is not written down yet: how many entities and fields carry a description, and the names of those that do not. It states the numbers and stops there, which is the only honest way to report on documentation coverage.
That closing count is the part that changes behaviour, because the reason columns go undocumented was never that people did not care. It was that the cheapest place to write it down had a deploy attached, so the note went into a wiki page instead and drifted. Take the deploy off the description and the note goes where the schema is - and then the data dictionary is a document you generate rather than one you maintain.
Frequently asked questions
How do I add a comment to a column in PostgreSQL and MySQL?
PostgreSQL has a standalone statement, COMMENT ON COLUMN table.column IS 'text', that changes nothing about storage. MySQL has no equivalent: the comment is an attribute inside the column definition, so it is set with ALTER TABLE t MODIFY col, and the statement must restate the data type and every other attribute you want to keep.
Do column comments survive an ALTER TABLE in MySQL?
Only if the statement repeats them. MySQL's manual says attributes present in the original definition but not specified for the new definition are not carried forward, and its own example shows a MODIFY that changes INT to BIGINT silently dropping UNSIGNED, DEFAULT and COMMENT. Nothing warns you, because the statement is valid.
Should database documentation live in the database or in the ERD?
It depends on the reader. Put it in the database when something reads the catalog directly, such as an agent introspecting a live connection, and accept that each edit is a schema change. Put it in the model when the readers are people, because a description that never reaches the database can be edited, reviewed and exported without a deploy.