LagomCMS, part 1: what a CMS actually has to do

On this page

This is the first of four posts about LagomCMS, the headless CMS I built and that runs this site. Before writing any code I wanted to know what a CMS actually is and where today's defaults came from, so I'd know which ones to keep. This is that research, boiled down.

Where it came from

  • Mid 1980s, document management. FileNet scanned the paper that banks and insurers were drowning in. Versioning, approval workflows and metadata were all argued out here first. Losing a document meant lawyers, so these systems assumed failure. Web CMSes assumed a page could just be republished, and it shows: WordPress will let you overwrite a post with no record of what was there.

  • 1995 and 1996, the word "CMS". Vignette StoryServer came out of CNET: editors fill in a form, templates turn it into files, the files get served. Interwoven treated a site as files under version control. Both separated content from presentation, and both published static files.

  • 2000 to 2003, open source. PHP-Nuke, Mambo, Movable Type and Drupal (both 2001), and WordPress (2003, forked from b2/cafelog). A CMS went from a six-figure contract to something you uploaded over FTP. The word stretched over all of it, which is why "is it a real CMS" arguments go nowhere.

The four parts

Strip WordPress down until it stops being a CMS and four things are left:

  • Storage. Somewhere content lives, with one copy that wins when two disagree.

  • A shape. A title, a body, a date. That's a model, even if only the form enforces it.

  • A way in that isn't the storage format. This is the one that defines the category. A folder of HTML isn't a CMS. Add a form that writes those files and it is.

  • A way out. Pages, an API, a feed.

By that test, Git plus a static site generator isn't a CMS, because you edit the storage directly. Put a git-backed editor in front of it and it becomes one.

Storage: database or files

Nearly every CMS keeps your writing in MySQL or Postgres, a choice made around 2003 and inherited ever since. The database buys querying, safe concurrent writes and referential integrity. It costs:

  • Content only that CMS can read. WordPress even stores serialised PHP arrays in single columns.

  • No real diff or review. Every CMS grows a revisions table, and it's always a worse Git.

  • Backups that drift from the code, plus one more process to run.

Files (Kirby, Statamic, Grav) flip that: readable with cat, diffable, backed up with tar, and readable by the AI tools I work with too. What they give up is querying at scale. The trade isn't fixed, though. Keep the files as the truth and build an index over them as a cache you can delete and rebuild. LagomCMS does exactly that.

The model: data or code

Content types arrive one requirement at a time: posts, then pages, then authors, then events with venues. WordPress answered with custom fields, a key-value table where nothing knows what's inside, which is why Advanced Custom Fields is on so many sites. Drupal made the model explicit.

The real question is where the model lives. In the database, non-developers can change it, but it's unversioned and staging drifts from production. In code, it's reviewed and deployed with everything else, but adding a field is a deploy. And a model only enforced by the form isn't enforced: I've found "required" fields empty in a third of records, in schemas I wrote myself. LagomCMS keeps the model in code and validates every write.

The body

Three ways to store it:

  • HTML (WordPress). Fastest to ship, painful to change later or reuse anywhere else.

  • Markdown. Readable and portable, but it can't express a gallery, so every system bolts on shortcodes.

  • Typed blocks (Sanity, Ghost). Right for galleries and embeds, heavy ceremony around a plain paragraph.

The compromise almost nobody commits to is Markdown for prose and structure only for what Markdown can't say. That's what LagomCMS does. Worth remembering too: what the editor shows and what ends up on disk are separate decisions.

Permissions and templates

WordPress's five roles map a small publication well, and the key one is Contributor, because writing and publishing are separate acts. Permission really depends on the document (its state, who wrote it, whether it's scheduled), so it's better written as a function than a checkbox grid. Most sites need about three workflow states and get sold seven. The feature with the best payoff is the audit log: nobody asks for it in a demo, and everyone wants it the day something breaks.

Themes are what won WordPress the web, and they're its tightest coupling. Your frontend is stuck in the CMS's language, every page view boots the CMS, and the content isn't available to anything else without scraping or the REST API bolted on in 2015. Decoupling costs two deploys and harder previews, but once static hosting became free the case for rendering inside the CMS got weak. LagomCMS doesn't render pages at all.

Next: what static site generators, flat-file CMSes and the headless crowd got right and wrong.