Why AI coding assistants produce plausible but broken CMS code
Author: Bob Means · Published · Estimated reading time: 11 min read
Last Updated
AI coding tools often write code that looks right for specialized CMS frameworks like ApostropheCMS, but subtly fails. Here's why, and how to fix it.
There is an issue with AI coding tools that's worse than getting obviously wrong output. Blatantly wrong output is fine: You see it immediately, lose thirty seconds, and move on. The costly failure is subtly incorrect output. Getting code that follows the right general shape, uses the right vocabulary, and looks completely plausible until you've spent three hours trying to figure out why a field isn't saving, or a template isn't rendering. Then you find it. One wrong property name. A missing namespace. A field type that's close but not valid.
AI coding assistants often struggle with specialized CMS frameworks because they have fewer reliable framework-specific patterns to draw from, may rely on outdated APIs, and have no automatic awareness of the conventions of your project. The fix isn't a more powerful model. The fix is better context: your CMS version, working examples from your codebase, relevant documentation, and clear constraints about module, schema, and template patterns.
The most effective way to supply all of that at once is to stop describing your project to the AI and instead run the AI inside your project, where the code itself does the explaining. That single choice matters more than almost anything else you can control.
Why AI coding assistants struggle with specialized CMS frameworks
AI coding models are trained on enormous amounts of public code, tutorials, and documentation. That training data skews heavily toward widely used ecosystems such as React, Express, Laravel, and WordPress, where there are millions of examples across repositories, tutorials, Stack Overflow answers, and documentation. More specialized CMS platforms and framework-specific APIs tend to have much thinner coverage, giving AI assistants fewer reliable patterns to work from. Some examples may also reflect older framework versions, obsolete APIs, or conventions that differ from the project in front of you.
The problem isn't that the AI is bad at coding. The problem is that it may be applying a generally reasonable pattern to a framework where that pattern is wrong.
CMS platforms are particularly susceptible to this. They tend to have:
Highly opinionated conventions that differ significantly from the underlying language defaults
Configuration-heavy APIs where exact option names matter
Inheritance and extension systems that are easy to get wrong by approximation
Templates and schemas that interlock in ways a general-purpose AI can't predict correctly
As a result, asking a general AI chat interface to write a widget or configure a content type will often produce something that looks plausible but won't run.
How this shows up in a real CMS framework
ApostropheCMS is a useful example because it is a mature Node.js CMS with a consistent API, but its conventions differ from generic Express, MongoDB, or headless CMS patterns. An AI assistant working from generic "Node.js CMS" assumptions may use the right vocabulary while still missing the details that make the code work.
A few examples show why this happens:
Framework-specific inheritance matters. In ApostropheCMS, a piece type (which is essentially Apostrophe's word for "content type") is not just a plain exported object. It extends @apostrophecms/piece-type. If an AI assistant treats it as a generic Node module, the code may look reasonable while failing to register correctly.
Editorial concepts may not map to generic frontend patterns. In ApostropheCMS, editable content regions are defined as schema fields of type area, and those areas reference allowed widget types by name. An AI that does not know this pattern may try to solve editable page regions with generic component or template logic instead of using the CMS abstraction.
Exact option names matter in configuration-heavy systems. Apostrophe has a rich, consistent schema field API with core fields like string, integer, relationship, array, object, and area. Each field type has its own valid options, and getting the exact property names right matters. A field definition that is "close" can still be invalid.
Namespaces and internal identifiers are easy to approximate incorrectly. Core Apostrophe modules are namespaced. If you ask an AI assistant to add an image field, it needs to know that the relationship should point to @apostrophecms/image, not image or Image.
None of these are obscure edge cases. They're the everyday building blocks of an ApostropheCMS project. The broader lesson is that specialized frameworks often depend on exact conventions. AI tools can work very effectively with those conventions, but only if they can see and verify them.
Which brings us to the single most important choice you'll make.
Why agentic AI tools produce better CMS code
There are two broad ways to work with AI on a codebase, and for CMS development the recommendation isn't close: use an agentic tool that runs inside your project.
Agentic tools such as Claude Code, Codex, and Cursor run in your project directory and can read the current files, inspect module structure, run project commands, and write changes directly into the codebase. Ask one to add a field to an existing module and it will first inspect that module, see how similar fields are already implemented, and make a targeted change that follows your project's conventions. It can also read node_modules — which means, in an ApostropheCMS project, it can read the Apostrophe source itself rather than relying on what it half-remembers about the framework.
That is the whole ballgame for specialized frameworks. The pattern-matching problem described above is a context problem, and an agentic tool sitting inside a correct, working project has more relevant context available to it than any prompt you could realistically write.
Chat interfaces such as Claude.ai and ChatGPT start each session with no awareness of your project. You can upload files or create persistent projects with source material attached, and that helps — but the uploaded snapshot will always be out of date unless you keep feeding it more context. That's precisely the problem in-project agentic tools were invented to solve.
Chat still has a place. If policy prevents you from giving a tool access to the full project, or you want to think through an approach without any possibility of an AI touching your code, a chat interface is a reasonable choice. But it's worth knowing that agentic tools cover most of that ground too: run one in plan mode and it will read your code, propose an approach, and wait for your approval before editing anything. Planning against the real codebase beats planning against a description of it.
One practical note: this generally requires a paid plan. Free tiers are typically limited to the chat window and produce shallower results. For professional development work, a paid tier per developer pays for itself quickly.
What AI can build in an ApostropheCMS project
ApostropheCMS may not be the framework on the tip of an AI's tongue. But with access to a full project, the results have been consistently strong — including on work we did not expect to hand off.
A non-developer built a multisite demo. Our product manager built a multisite, multitenant ApostropheCMS demo project for fitness companies using Claude Code. She isn't a developer. The project source supplied the structural knowledge she didn't have.
A template migration took under an hour. We used Claude Code to migrate our public demo starter kit from Nunjucks to JSX in less than an hour — with no CLAUDE.md in the project at the time. It read the relevant documentation and followed the existing patterns because we asked it to. That's worth emphasizing: you don't need to write a large instruction file before you start being productive.
It implemented the JSX support itself. Claude Code wrote the vast majority of the actual JSX support in Apostrophe, by tracking down how Nunjucks is used throughout the codebase and building an equivalent solution for JSX.
It made our Postgres and SQLite adapters possible at all. Same method: follow the existing patterns, then exhaustively explore every use of MongoDB in the code. A project of that size also depended on our extensive test suite — the agent could verify its own work at every step, which is what made an infrastructure change of that scope tractable.
That last point generalizes. The wins above weren't the result of a better prompt. They came from an AI that could read the real code, consult the real documentation, and check its own work against real tests.
How to prevent broken AI-generated CMS code
Agentic tools solve most of the context problem automatically, but they introduce a different one: they can act on more than you intended, apply a pattern too broadly, or chain several actions together before you notice the first assumption was wrong. These risks are manageable, and the techniques are straightforward.
Keep the git boundary
The most useful habit is also the simplest: don't let the AI commit. When Claude Code or a similar tool asks for permission to run git commands, hold that privilege back for yourself. Committing then becomes a natural checkpoint where you read git diff, look for anything unexpected, and decide what actually enters the repository. Everything before that point is recoverable.
Plan before editing
For anything beyond a small change, use plan mode. The tool reads the project, proposes an approach, and waits. You get to catch a wrong assumption before it becomes twelve edited files rather than after. Claude Code documents this workflow in its common workflows guide.
Ask for smaller changes
Smaller prompts are safer than broad instructions. "Add this one field to this existing module" is easier to verify than "build a complete content model for our case studies section." Once the first change is correct, build from there. This also limits the damage when the AI starts from a wrong assumption.
Let it verify its own work
An agent that can run your linter, your test suite, and your build has a feedback loop that catches its own mistakes before you see them. This is the largest single quality difference between projects with good test coverage and projects without.
You can extend the same idea to the browser. Encourage the tool to install Playwright and require it to view the running site directly, and it can debug issues visually and interactively — including logging in and taking actions as a user, which matters for anything involving the ApostropheCMS admin UI.
Start from a structure the tools recognize
For new projects, start with the ApostropheCMS CLI (npm create apostrophe@latest) or clone an official starter kit, unless you're forking an existing project of your own. That gives you a structure matching our documentation and examples, which helps both you and the agent find the right files and avoid inventing project structure. For Astro projects it matters even more: the generated project already includes the expected frontend and backend split, integration setup, and template and widget mappings.
Related: launch the tool from the project root, not from a subdirectory. In a hybrid Astro project, that one choice lets it see both apps at once, which is a lot of context for free.
Point it at the official documentation
Some things aren't in your project at all — a field option you've never used, an API you're adopting for the first time. Model memory is the weakest possible source for those specifics, and it's exactly where plausible-but-wrong output comes from.
So ask for the documentation explicitly. Instead of "How should this relationship field work?", try "Check the current ApostropheCMS documentation for relationship fields and use the documented pattern here." A tool with web access can find and read the relevant page before it makes the change, and most will tell you which page they used, so you can check it yourself.
This matters most in configuration-heavy frameworks, where the difference between the right property name and a plausible one determines whether the feature works at all. Documentation isn't infallible and an AI can still misread it, but it's a far stronger anchor than model memory alone.
It's also the one line worth putting in your project instruction file on day one, so you don't have to ask for it every time.
Add project instructions incrementally
You don't need a comprehensive CLAUDE.md before your first prompt. Agentic tools pick up most conventions from the project on their own. Start working, and add instructions only for the things the AI got wrong or had to be told twice — a link to the ApostropheCMS docs, naming conventions it didn't infer, the command that runs your tests, rules about which files it shouldn't modify. That saves your effort and the agent's tokens, and the resulting file is short enough that people will actually keep it current.
Compare generated code with existing patterns
Check generated code the same way you'd check code from a new teammate: Does it follow the existing module structure, naming conventions, schema patterns, and template style?
If the AI introduces a new pattern where the project already has one, treat that as a warning sign. There may be a good reason to introduce something new. But it should be able to explain why the existing pattern is insufficient before replacing it.
The broader principle: Better context produces more reliable AI-generated code
The underlying issue isn't specific to ApostropheCMS. The same problem appears in specialized frameworks, opinionated platforms, internal company systems, older applications, and any codebase whose conventions differ from the generic patterns an AI is most likely to recognize.
The developers who get the most out of AI tooling treat it like a fast, capable new teammate. They don't brief that teammate from memory. They hand over the repository, point at the documentation, and let the test suite tell both of them whether the work is right.
Model capability matters. Better reasoning, stronger instruction-following, and better tool use all improve the result. But context is the biggest variable you control, and the cheapest way to supply it is to give the tool the project itself.
Give an agentic tool the run of a real codebase, current documentation, and a test suite to check itself against, and it will extend your system rather than produce code that merely looks like it belongs there. Assume it already knows your system, and you'll spend more time debugging than you saved.
---
ApostropheCMS is a full-featured, open source Node.js CMS with in-context editing, a headless API, tight Astro integration, and a powerful module system. See the docs or try the live demo.