Craft
9 min read

Thomas is a Tech Lead and frontend engineer at November Five. He's spent over five years building web and mobile products, about ten of them on DatoCMS, which is where the opinions in this post come from.
A CMS is a database: models are tables, records are rows, link fields are foreign keys.
Every noun your business talks about (FAQs, people, vacancies) gets its own model. Pages reference content; they don't contain it.
Sections that repeat across pages, like a closing CTA, are one record linked from many pages, not copies pasted into each.
Categories and other editor-managed values are models, not fixed dropdown options. Rows change with a click; schema changes need a deploy.
Blocks can't be referenced, queried on their own, or outlive their parent record, and they're capped at 300 KB, 500 blocks, and five nesting levels per record. If content needs reuse or querying, it's a model.
DatoCMS is the default CMS at November Five: ten-plus builds and an official partnership to show for it. Across those builds we keep correcting the same four modelling mistakes. All four are database mistakes, and the rules that prevent them are as old as databases.
A CMS is a database with a friendlier UI
In DatoCMS, models are tables, records are rows, and link fields are foreign keys. Editors run INSERTs through a form. Your frontend runs SELECTs through GraphQL.
Treat it that way and most modelling decisions stop being matters of taste. "Does this deserve its own model?" is the same question as "Does this deserve its own table?", and that one has had a stable answer for forty years. The four mistakes below are what happens when nobody asks it.
Mistake 1: the page is the model
The legacy mindset: a page owns everything it displays. If the About page shows team members, team members get typed into the About page. It works on day one. Then the same content needs to appear on a second page, and copy-paste is the only tool the schema offers.
We see the damage most clearly on migrations. A job title updated on the team page but not on the landing page that also mentions the person. The same answer in three versions, because each copy was edited at a different moment by a different person.
The rule: every noun your business talks about gets its own model. People, FAQs, offices, testimonials, vacancies. Pages don't contain them; pages reference them. Change the answer once and every page that links it is current. The page becomes what it always was in a database: a view.
There is a second payoff. Once FAQs are records, they are queryable. You can fetch all billing questions for a support widget, export them, or hand the whole set to an AI for analysis. Content buried in a page body can do none of that.
Mistake 2: copy-pasting the sections pages share
On a healthcare project, most pages ended the same way: a call-to-action section above the footer. Same heading, same text, same button, pasted into each page separately. A few pages carried a variant, a few had none. So "change the button label" meant opening every page to check which version it had, and "which pages still have the old one?" had no answer short of clicking through the whole site.
Databases solved this decades ago and called it normalization: one fact, one row. Model the CTA as a record and let pages link to it. The pages with a variant link to a second record; the pages without one leave the field empty. Edits propagate everywhere at once, and "which pages use this CTA?" becomes a query instead of an afternoon. And because there is only one copy, the versions can't drift apart over time.

Mistake 3: hard-coding enums for things that are content
An events model gets a category field: a select with fixed options. Conference, Meetup, Webinar. It looks tidy in the schema, and it is wrong the moment the client thinks of a fourth category. Adding one is now a schema change: a ticket and a deploy. For an edit that should take ten seconds.
It never stops at the label, either. Categories eventually want a colour, an icon, a description, a landing page of their own. A string in a dropdown can't carry any of that.
The rule: if editors will ever add, rename, or extend a value, it's a row, not an enum. Make Category a model and link to it. Schema changes ship with a release; records ship with a click. Keep real enums for values only developers change, like rendering variants or sizes, where code branches on the value and a new option would be meaningless without a code change anyway.
Mistake 4: blocks where models should be
Blocks are one of DatoCMS's distinctive features, and they are good: define a hero block, a text block, a gallery block, and editors compose pages out of them. The trap is that blocks are defined with the same editor as models, so they feel interchangeable. They are not.
A block exists only inside its parent record. It can't be referenced from anywhere else: link fields accept models only. It can't be queried on its own. Delete the parent and the block dies with it. And blocks are bounded: a record tops out at 300 KB and 500 blocks in total, nesting stops at five levels, and a localized block field counts its blocks once per locale. A page-builder schema with six locales reaches those numbers faster than you'd think.
In database terms: a block is a JSON column; a model is a table. A JSON column is fine for data that only means something inside its row, and that is exactly what a hero section is. But the moment you want to point at content from elsewhere, query it, or reuse it with edits that propagate, it should have been a table all along.
Three questions settle every case. Does anything outside this record need to reference it? Should it survive its parent's deletion? Should editing it once update it everywhere? Any yes means model.

The test that catches all four
The four mistakes are one mistake: modelling what the page looks like instead of what the content is. Every schema review we run now starts from the database question: if this were SQL, would this be a table, a row, a foreign key, or a JSON column?
Content modelled like data can be queried, linked, exported, migrated, and analyzed. Content modelled like pages can only be looked at.
That leaves one choice, made early and paid for later. Model it like a database and the site stays cheap to maintain no matter how many years and how many editors touch it. Model it like a page and it drifts, quietly at first, then into the kind of inconsistency someone eventually has to clean up. Nobody picks the second one on purpose. It's just what happens when nobody asks the database question.
DatoCMS's own docs on blocks are worth the read.