What is your developer-dependent CMS actually costing you? Calculate your costs and get a full report
What is your developer-dependent CMS actually costing you? Calculate your costs and get a full report
Content Architecture
Twelve content modeling mistakes we see most often in Agility, each with the symptom that gives it away and the fix: layout-driven models, catch-all models, rich text as data, deep nesting, API casing, one list per locale and more.
Most content model problems don't show up on day one. They show up six months later, when the site gets a redesign, a second locale, a mobile app or an AI agent, and the model can't take it. This page lists the mistakes we see most often in Agility content models. Each rule names the mistake, the symptom that tells you you've made it, and the fix.
Use it as a review checklist before you build, and again before you launch.
| # | Rule | The named mistake |
|---|---|---|
| 1 | Model meaning, not layout | Modeling for one page layout |
| 2 | Give each model one job | The catch-all model |
| 3 | Put data in fields, not in rich text | Rich text as a database |
| 4 | Link shared content, don't copy it | Copy-paste content |
| 5 | Keep nesting shallow | Russian-doll nesting |
| 6 | Name fields for the API, not just the editor | Field names that break API casing |
| 7 | Use locales, not one list per language | One list per locale |
| 8 | Always pass take | Trusting the default page size |
| 9 | Pick the linked content UI for the list's future size | The checkbox list that grew |
| 10 | Offer choices, not free text, for values code depends on | Typed-in values |
| 11 | Leave companion fields alone | Deleting the hidden fields you don't recognize |
| 12 | Read a model before you save it through the API | The partial model save |
The mistake: modeling for one page layout. Models and fields are named after where things sit on today's design: LeftColumnText, BlueBoxHeading, HomepageBanner3.
Symptom. A redesign means remodeling and migrating content. The same content can't be reused on another page, in an app or in an email, because its structure only makes sense in one layout.
Fix. Name models and fields for what the content is: Event, Speaker, Summary, StartDate. Keep layout decisions in Components and in the zones of your Page Models. Where editors do need to choose a look, give them a Drop-down List whose stored values your code switches on (for example light and dark), not field names tied to a position. See Content-first Approach.
The mistake: the catch-all model. One GenericContent model, or one mega-Component, with dozens of optional fields that cover every case.
Symptom. Editors can't tell which fields matter for the thing they're making. Hide/when formulas multiply until nobody can predict what the form shows. Every API response carries fields nobody uses, and every front-end component has to handle every combination.
Fix. Split by purpose: one model per kind of content, one Component per kind of block. Use hide/when formulas for small variations inside a model, not to merge several models into one. If the real need is grouping blocks on a page (tabs, accordions), use a small marker Component instead of a giant one: see Grouping Components into Tabs, Accordions, and Sections.
The mistake: rich text as a database. Prices, dates, specifications, addresses or FAQ entries typed into an HTML field.
Symptom. You can't sort events by date, filter products by size, or show the FAQ in an app, because the API returns the HTML field as one string of HTML. Changing the formatting means editing every item.
Fix. Give structured values their own fields: Date/Time for dates, Number or Decimal for quantities, Drop-down List for fixed choices, URL for links, and a nested list for repeating entries such as FAQ items. Then the API returns each value separately, and you can filter and sort on them (see GraphQL & Rest API Filtering). Keep the HTML field for genuinely free-form prose. See Field Types and What the APIs Return for what each field type returns.
The mistake: copy-paste content. The same author bio, office address, disclaimer or call to action is typed into every item or Component that shows it.
Symptom. One change means finding and editing every copy, and some copies are always missed.
Fix. Put the shared content in its own content list and point at it with a Linked Content field (for one item, a Dropdown List render type). When the shared item changes, every place that links to it shows the change. See Components with Content that is shared across your site and Nest, Link or Share?
The mistake: Russian-doll nesting. Nested lists inside nested lists inside nested lists, because the design has sections, inside cards, inside tabs.
Symptom. Editors click through several levels to change one line. In the Content Fetch API, linked content is expanded only as deep as ContentLinkDepth, which defaults to 1 for items and 2 for pages (lists allow at most 5), and each level makes responses larger. In the Management API, the publish-cascade routes publish an item's nested content one level deep (see Batches and the Batch API), so automated publishing can miss deeper levels.
Fix. Aim for one level of nesting. Flatten where you can: a card's fields can usually live on the card item itself. When a Component needs a child list, fetch that list separately by its reference name rather than relying on deep expansion. If you need deeper structure, check how it publishes and how much it returns before you commit to it.
The mistake: field names that break API casing. Field names that start with an acronym or that you plan to rename later.
Symptom. Both the Content Fetch API and GraphQL return fields keyed by the field name with the first letter lowercased. A field named Title comes back as title, and a field named URL comes back as uRL. Code that expects url gets nothing. Renaming a field later breaks every query and type that uses the old name.
Fix. Use clear PascalCase words, and avoid leading acronyms (LinkUrl, not URL). Decide names before content is entered, and treat them as part of your API. Generate or write your TypeScript types with the lowercase-first names. Also note that a Text field is limited to 128 characters, and the Fields article ties that limit to any field named title. See Field Types and What the APIs Return.
The mistake: one list per locale. BlogPostsEN, BlogPostsFR and BlogPostsDE as separate lists, or separate models per language.
Symptom. Translations aren't connected, so a language switcher can't find the matching item. The models drift apart as someone adds a field to one and forgets the others.
Fix. Use one list and add locales. When you copy an item into another locale, the copies share the same contentID, and each locale holds its own field values. Mark fields that must never differ (codes, sort orders) as Constant across all languages. See Choosing a Localization Strategy.
takeThe mistake: trusting the default page size. Code that requests a list without saying how many items it wants.
Symptom. Lists silently stop growing. The Content Fetch API returns 10 items by default, and GraphQL container lists return 50. Everything works in development with a handful of items, then breaks in production.
Fix. Pass take on every list request (the maximum is 250), page with skip when a list can be longer, and pass take on nested GraphQL lists too.
The mistake: the checkbox list that grew. A Checkbox List render type pointing at a list that starts with ten items and ends up with hundreds.
Symptom. The editor form becomes a wall of checkboxes.
Fix. Use a Checkbox List only for short lists that will stay short. Use a Search List Box for anything that can grow, such as tags, products or people. A Checkbox List can be converted to a Search List Box later. See Nest, Link or Share?
The mistake: typed-in values. Editors type a theme name, an icon name or a colour into a Text field, and the front end matches on the string.
Symptom. Dark, dark and dark all exist in production, and two of them render wrong.
Fix. Use a Drop-down List: the API returns the selected choice's value, so your code switches on values you control while editors see readable labels. For icons and colours, the Picker Fields app adds picker fields that store a plain text value. For values that need a pattern rather than a fixed list, add regular expression validation.
The mistake: deleting the hidden fields you don't recognize. Someone tidies a model and removes Category_ValueField and Category_TextField because nobody remembers adding them.
Symptom. Linked content selections stop saving, and filters on the value field return nothing.
Fix. Dropdown, checkbox and search list box linked fields store the selected IDs and display text in hidden companion fields. The Fields article is explicit: don't delete them or disable them. Name them consistently ({Field}_ValueField, {Field}_TextField) so their purpose is obvious.
The mistake: the partial model save. A script or AI agent saves a Content Model or Component Model with only the fields it meant to change.
Symptom. Fields disappear, and so does their content. When you save a model through the Management API or the MCP server, the field list you send replaces the stored one, and properties you leave undefined are treated as empty. The MCP server refuses a save that would drop fields unless you explicitly allow it, and also refuses to convert a model between a Content Model and a Component, because conversion orphans nested content.
Fix. Read the model first (get_content_model_details or get_component_model_details), change only what you mean to change, and send everything back. Read it again after saving. See MCP and Agility Gotchas to Know Before You Build.
take?