# No CMS does not mean no content model

> Teams that drop the CMS usually drop content modeling with it. The schema was the discipline, not the tool - here is what a strict model looks like in files.

Published: 2026-08-24
Updated: 2026-08-24
Canonical: https://augustynglowacki.com/posts/no-cms-does-not-mean-no-content-model/

## Why does editing a no-CMS site get painful?

Because the structure that made editing predictable left with the CMS. Over the past few months, several consultancy requests reached me through Bejamas, the agency I cooperate with, and they shared one shape: a company ships its site as markdown or YAML files, no CMS, and within months editing has become slower than it was before. The symptoms repeat across those requests - nobody is sure which file owns a given piece of copy, the same section exists as four slightly diverged copies, and a "content change" routinely turns into a developer task because only a developer can tell what a file's fields mean.

The teams asking for help usually frame the problem as tooling: which editor, which git UI, which preview setup. In every case I have looked at so far, the tooling question was downstream of a modeling question nobody had asked - what is a page allowed to contain, and who enforces that?

## What was the CMS actually doing for you?

Enforcing a content model, whether or not anyone called it that. A CMS makes an editor pick a content type before writing, restricts each entry to that type's fields, marks fields required, and offers a closed list of section or block types to compose a page from. Teams that leave a CMS tend to keep the files and drop all four of those constraints at once, because none of them looks like a feature - they look like friction, until they are gone.

Plain files enforce nothing by default. A YAML page definition accepts any key, a markdown file accepts any structure, and the first typo in a field name fails silently instead of loudly. The discipline has to be rebuilt in code, and the tools for that already sit in the stack most no-CMS sites are built on.

## What does a content model look like in plain files?

A schema the build refuses to pass without. On [this website](/services/ai-editable-no-cms/), every content collection validates against a Zod schema at build time, and Astro generates TypeScript types from those schemas, so component props and content files cannot drift apart ([Astro content collections docs](https://docs.astro.build/en/guides/content-collections/)). Three decisions carry most of the weight:

- A page's sections are a closed vocabulary: one `z.discriminatedUnion` over 20 section types, from hero to lead form. A page composes from that list and nothing else.
- Every object is a `z.strictObject`, which rejects unknown keys instead of ignoring them ([Zod docs](https://zod.dev/api)) - a typo like `titel` fails the build with a named error rather than shipping a section with a silently missing title.
- Every field carries a `.describe()` string saying what it is for, so the schema doubles as the documentation an editor - human or AI agent - reads before touching a file.

One more constraint closes the loop on the rendering side: the renderer's component map is typed `satisfies Record<Section["type"], unknown>`, so adding a section type to the schema without adding its component is a compile error, not a blank spot on a page.

## When should a repeated section be defined once instead of pasted?

Only when it is genuinely identical across pages - and then exactly once. The default on this website is inline: a section lives inside the page that uses it, because most sections that look similar are not the same content and will diverge. A section that is truly identical everywhere it appears - the same call-to-action block, the same trust strip - moves into a `sharedSections` collection as one YAML file, and each page replaces the pasted copy with a pointer: `{ type: "sectionRef", ref: "<id>" }`. Editing that one file updates every page that references it.

Two rules keep the mechanism from growing into its own maintenance problem. A `sectionRef` cannot point at another `sectionRef` - the resolver never recurses, because a chain of pointers buys nothing over pointing at the real target. And a broken reference fails validation at build time, so a page can never ship pointing at a shared section that was renamed or deleted. Inline versus reference is the whole decision, and it is the same decision a CMS makes you take when you choose between a one-off block and a reusable entry - dropping the CMS did not make the decision go away, it made it invisible.

## What does this not solve?

The editing interface for a non-technical team. A strict schema makes files safe to edit, and it is what makes this site editable through an AI coding agent - the agent reads the schema's descriptions, writes YAML against it, and the build rejects anything malformed. It does not give a marketing team of ten a visual editor, a preview button, or a publishing workflow, and for that team, [a CMS remains the right tool](/services/ai-editable-cms/). The honest framing for anyone choosing: no CMS is a decision about the editing interface, and it was never a permission to skip the content model. The model is the part that was mandatory all along.

## Sources

- [Astro documentation, content collections](https://docs.astro.build/en/guides/content-collections/)
- [Zod documentation, strictObject](https://zod.dev/api)

## Sources

- [Astro documentation, content collections](https://docs.astro.build/en/guides/content-collections/)
- [Zod documentation, strictObject](https://zod.dev/api)
