Video Production Plan : Video 4 — Composition, Query Performance, and Modeling in Practice
Video 4 — Composition, Query Performance, and Modeling in Practice
| Attribute | Details |
|---|---|
| Course | 2 (Content Modeling), Modules 2.2 and 2.3 |
| Covers | Lessons 2.2.1, 2.2.2, 2.2.3, 2.3.1, 2.3.2, 2.3.3, 2.3.4, 2.3.5 |
| Priority | Core |
| Length | 20-28 min |
| Format | Live model review using the Veda scenario |
| Status | Not started |
Why This Video Matters
This is where modeling theory becomes real-world decision making. Learners see how to translate business requirements into content types and how to audit and improve existing models.
Outline
- References vs Modular Blocks: when to use each
- References: link to independent entries (Product references a Category)
- Modular Blocks: composable, inline content sections (hero, feature grid, testimonial)
- Show a reference field and a modular blocks field in the editor, then compare their API output
- Taxonomy, tags, and classification: organizing content beyond references
- Query performance: payload size, include depth tradeoffs, keeping queries efficient
- Start with a Veda business requirement and translate it into content types step by step
- Auditing a messy content model: identify duplicated fields, missing references, unclear naming
- Model sprawl: what 50 content types that should be 15 looks like, how to prevent it
- Modeling for multiple channels: same content, different presentations
- Localization strategy: localized fields vs localized entries, fallback behavior
- Governance frameworks: naming conventions, review processes, documentation standards
Key Lines
"Many performance problems begin as modeling problems."
"The cleanest editor experience and the cleanest API are often connected."
"A good content model scales with your team. A bad one scales against it."
Detailed Talking Points
1. References vs Modular Blocks: when to use each
- References create pointers to independent entries. A Product references a Category. The Category lives on its own, has its own publish state, its own URL in the Management API. Update the Category once, every Product that references it picks up the change.
- Modular Blocks are inline composable sections: hero, feature grid, testimonial strip, CTA. The data lives inside the parent entry. No separate entry, no separate lifecycle.
- The decision rule: if the content is reused across multiple entries and has its own editorial lifecycle, use a reference. If the content belongs to one page and nobody would ever browse or search for it independently, use a modular block.
- Common mistake: creating a separate hero_banner content type and referencing it from a page when that hero only ever appears on one page. That is unnecessary indirection. Use a modular block instead.
- Opposite mistake: embedding author data as a modular block inside articles. Now every article carries its own copy of the author bio. Updating the bio means editing every article. References solve this.
- Extensions (custom fields) exist too, but they solve a different problem: specialized editorial UI like color pickers or third-party lookups. Higher build cost, only reach for them when native fields cannot handle the UX requirement.
2. Show a reference field and a modular blocks field in the editor, then compare their API output
- Open a Veda page entry that has both a reference field (e.g., testimonials) and a modular blocks field (e.g., page_sections). Show them side by side in the editor.
- Reference field: editor picks from existing entries using an entry picker. The referenced entries exist independently.
- Modular blocks field: editor adds, removes, and reorders blocks inline. Block data is tightly coupled to this entry.
- Fetch the entry via the Delivery API without include[]. Show that reference fields return only UIDs and _content_type_uid -- not the actual content. Modular blocks return full inline data with no extra resolution needed.
- Add ?include[]=testimonials to the query. Now the referenced entries resolve inline. Stress that this is an explicit step developers must remember.
- Show the JSON side by side: references produce separate entry payloads nested under the reference key; modular blocks produce an array of objects keyed by block type.
3. Taxonomy, tags, and classification: organizing content beyond references
- Contentstack has three classification mechanisms: Taxonomy (governed, hierarchical, cross-content-type), Tags (freeform string arrays, zero setup), and reference-based categorization (categories as full content entries).
- Taxonomy: centrally managed under the Taxonomy section in the stack. You define terms in a hierarchy. Editors pick from controlled vocabularies. Queryable across content types with taxonomies.product_line syntax.
- Tags: easy to add, impossible to govern. After six months you get "AI", "A.I.", "ai", "artificial-intelligence" all meaning the same thing. Queries miss content.
- Reference-based categorization: best when the category itself is a rich content entity with its own page, description, and metadata. But querying across content types requires separate API calls per type.
- Decision framework: use taxonomies for governed facets that power navigation and filtering, tags for informal internal labels, references for content-rich categories with their own pages.
- For Veda: product_line and category taxonomies enable cross-content-type queries like "show me everything in Digital Dawn" with a single API call.
4. Query performance: payload size, include depth tradeoffs, keeping queries efficient
- Every content model is also a query contract. The fields, references, and modular blocks you define determine the size, speed, and cost of every API response.
- Include depth: default is 0 (references return as UIDs only). Each level of include[] adds latency and payload size. One level might be 80ms. Two levels might be 200ms. Three might exceed 400ms.
- Max useful depth is usually 2-3. If your model requires more, that is a signal to flatten the model, not work around the depth limit.
- Payload budgets: aim for individual entry responses under 50KB, list queries under 200KB. Rich text fields in referenced entries, modular blocks with many instances, and multi-reference fields with large arrays are the main bloat drivers.
- Use only[BASE][] for list views to return only the fields the UI needs. A product card showing title, thumbnail, and price does not need the full rich text description.
- Normalized vs denormalized: pure normalization means many references and slow queries. Pure denormalization means duplicate data and update pain. The practical middle ground: normalize entities with independent lifecycle, denormalize display-only data that rarely changes.
- Worked example: a naive product model with 5 levels of reference depth resolves 28 entries per request. Flattened to 2 levels, it resolves 7 entries. Same data, fraction of the cost.
5. Start with a Veda business requirement and translate it into content types step by step
- Start with the brief: "Veda wants to add gift sets that bundle 2-4 products, with a name, description, hero image, and optional gift message. Gift sets appear in Digital Dawn and Charmed Revival product lines. Support English and Spanish."
- Step 1: identify entities. Read the brief, underline nouns that have their own identity and lifecycle. Gift Set, Product (already exists), Product Line (already exists). Not "Gift Set Page" -- that is presentation.
- Step 2: determine relationships. Gift Set contains Products (many-to-many reference). Gift Set appears in Product Lines (many-to-many reference).
- Step 3: define fields. Gift Set gets title (mandatory, unique), URL, description (JSON RTE), hero image, gift message, products (multi-reference, mandatory), product_line (multi-reference).
- Step 4: decide what NOT to model. Inventory, cart state, pricing -- those belong in the commerce system, not the CMS. Draw a clear boundary between editorial content and transactional data.
- Step 5: validate with real entries and API responses before committing. Create 2-3 entries with real content, fetch via the Delivery API, confirm the JSON matches what the frontend expects.
6. Auditing a messy content model: identify duplicated fields, missing references, unclear naming
- Audit when you see signals: 40+ field content types, fields named *_v2 or temp_*, editors consistently skipping fields, developers getting API responses full of unused keys.
- Red flags: content types with 40+ fields, fields empty across 90% of entries, confusing names like cta_link_2 or misc_data, reference chains 4+ levels deep, dual-purpose content types.
- Step 1: export schemas via the Management API, sort by field count. Highest field counts are your first audit targets.
- Step 2: analyze field population rates. Fields with less than 10% population are candidates for removal.
- Step 3: interview editors. "Walk me through creating an entry. Where do you pause? Which fields do you skip?"
- Step 4: map API consumers. Which frontends read which fields? Fields that no consumer reads and no editor populates are dead weight.
- The 47-field Product example: split into Product (core, 10-12 fields), Product Specs (referenced), SEO Metadata (Global Field). Inventory and pricing fields removed from the CMS entirely.
- UID changes are coordinated migrations, not casual renames. Changing a UID breaks every API consumer that references the old key.
7. Model sprawl: what 50 content types that should be 15 looks like, how to prevent it
- Model sprawl: too many content types, each too small to justify its existence, connected by deep reference chains.
- Warning signs: more content types than entries for some types (a CallToAction type with 3 entries), editors cannot find where to create content (35+ options in the dropdown), assembling one page requires touching 6+ content types.
- The "one content type per component" anti-pattern: mapping every React component to its own content type. A HeroBanner with 4 fields, a FeatureCard with 3 fields, a StatCounter with 2 fields. None of these are content entities. They are field groups masquerading as content types.
- The fix: use Modular Blocks for component-level structures, Global Fields for reusable field groups, standalone content types only for entities with independent lifecycle.
- The rule of three: do not extract a reusable pattern until you have three concrete instances. One testimonial page does not justify a Testimonial content type.
- Real example: a startup with 35 content types and 200 entries consolidated to 9 content types. Same website, same content, fraction of the complexity.
- Healthy ratios: a marketing site needs 5-10 types, a corporate site 10-20, a media platform 8-15. 35 types with 200 entries is a red flag.
8. Modeling for multiple channels: same content, different presentations
- Core principle: structure content for meaning, not for presentation. Channel-specific rendering is the frontend's job.
- Channel-neutral content: structured JSON RTE, a single high-res image (use Image Delivery API transforms for sizing), key_features as an array. Works for web, mobile app, voice assistant, email.
- Channel-coupled content (anti-pattern): web_hero_html, mobile_short_description, email_preview_text, kiosk_display_mode. Every new channel requires new fields. Every copy change requires updating multiple fields.
- Practical rules: store content as structured data not markup, use Image Delivery API for responsive images, keep field names channel-agnostic (short_description not mobile_description), use include[] strategically per consumer.
9. Localization strategy: localized fields vs localized entries, fallback behavior
- Localization operates at three levels: stack-level language configuration, field-level localization settings, and entry-level data.
- Field-level decision: mark fields as non-localizable by default. Only opt in for text that editors actually translate. Title, description, CTA labels, alt text -- localize these. Dates, SKUs, reference fields, booleans -- keep universal.
- Common mistake: making a reference field localizable. Now the French version of a product silently points to a different category than the English version. Nothing in the UI flags the inconsistency.
- Fallback hierarchy: design it before launch. fr-ca falls back to fr-fr, which falls back to en-us. This lets you launch with only base-language content and progressively translate.
- Locale-specific publishing: editors switch locale in the entry editor, translate localizable fields, and publish the localized version independently. The Delivery API returns the best available version per field based on the fallback chain.
- Prioritize translations by fallback usefulness: Japanese first (fallback to English is least useful for Japanese readers), then French, then regional variants.
10. Governance frameworks: naming conventions, review processes, documentation standards
- Naming conventions are the highest-impact, lowest-cost governance tool. Content type UIDs: snake_case (blog_post, not blogPost or bp). Field UIDs: snake_case, short but unambiguous. Display names: human-readable for editors.
- Bad UIDs: blogPost (camelCase), bp (cryptic), content_blog_post_v2 (version numbers signal migration debt), page_component_hero_banner_module (over-qualified).
- Field descriptions: every field should have a populated description. "Page title shown in browser tabs and search results. Keep under 60 characters." costs 30 seconds to write, saves hours of confusion.
- Roles and permissions: restrict content type modification to Admin roles. Give editors Content Manager roles scoped to their content types. This prevents accidental schema changes.
- Lightweight review process: maintain a decision log for structural changes. Three questions per entry: what changed, why, who decided. Not a formal approval workflow -- a log. Takes 2 minutes.
- Threshold: adding an optional field -- just do it and log. Creating a new content type, removing a field UID, changing a reference target -- discuss first, then log.
- Use built-in guardrails: mandatory fields for minimum viable entries, unique constraints for identifiers, regex validation for slugs and SKUs, Select fields instead of freeform text for known value sets.
Screen: What to Show
| Outline Item / Segment | What to Show on Screen / Instructions |
|---|---|
| Outline items 1-2 (References vs Modular Blocks) | Open the Veda stack in Contentstack. Navigate to a Page content type that has both a reference field (e.g., testimonialsreferencing the Testimonial content type) and a modular blocks field (e.g., page_sections). -- show the raw UIDs for references. Then add ?include[]=testimonials and show the resolved data. Side-by-side the two JSON responses. |
| Outline item 3 (Taxonomy, tags, classification) | Navigate to the Taxonomy section in the left nav of Contentstack. Show the product_line taxonomy with its terms (Digital Dawn, Urban Armor, etc.). -- show results spanning content types. |
| Outline item 4 (Query performance) | Show a product entry with deep references: product -> product_line -> related_products -> their product_lines. Diagram or whiteboard the reference tree. -- show the dramatically smaller response. |
| Outline item 5 (Translating requirements) | Put the Veda gift set brief on screen (text overlay or slide). Walk through underlining the nouns that become entities. Whiteboard or diagram the entity-relationship map: Gift Set -> Products, Gift Set -> Product Lines. Switch to Contentstack, create the Gift Set content type live (or show a pre-built one). Walk through each field and explain why it exists. Create a sample Gift Set entry with real Veda product names. Fetch it via the API and show the JSON. |
| Outline item 6 (Auditing) | Show a deliberately messy content type schema (pre-built for the video): 40+ fields, names like banner_v2, temp_promo, old_description. Scroll through it in the content type builder. to show field counts per content type. |
| Outline item 7 (Model sprawl) | Show a stack sidebar with 30+ content types listed. Scroll through it. Point out types with 2-3 entries. Show the "before" model: 35 types, 200 entries. Then show the "after": 9 types, same content. Use a slide or diagram with both side by side. |
| Outline items 8-9 (Channels and localization) | Show a channel-neutral Product entry: structured JSON RTE, single hero image, key_features array. to an image URL, show the resized result. |
| Outline item 10 (Governance) | Show the Roles & Permissions screen in Contentstack. Walk through Admin vs Content Manager role differences. Show a well-documented content type: populated Description field, field descriptions with clear guidance, regex validation on a slug field. Contrast with an undocumented content type: empty descriptions, cryptic field names. Show a sample decision log (Markdown file in the repo or a Notion page). |
Veda Scenario Thread
The entire video uses Veda as the connective tissue:
- References vs Modular Blocks: Veda's Product references Category and Product Line (independent entities, shared across products). Veda's landing pages use modular blocks for hero, feature grid, and CTA sections (page-specific, no reuse).
- API comparison: Fetch a Veda product page. Show testimonials as unresolved UIDs, then resolved with include[]. Show page_sections as inline modular block data.
- Taxonomy: Veda classifies products by product_line (Digital Dawn, Urban Armor) and category (Earrings, Bracelets) using taxonomies. Show a cross-content-type query: "everything in Digital Dawn" returns products, product lines, and pages in one call.
- Query performance: The Veda product detail page resolves product_line, category, and 5 related products. At two levels deep, that is 17 entries per request. Show how flattening (inline category_path, capped related_products) drops it to 7 entries.
- Translating requirements: Take the Veda gift set brief live. Walk from business paragraph to entity identification to content type creation to sample entry to API response.
- Auditing: Show a hypothetical "inherited Veda stack" with model debt: a 47-field Product type, fields named promo_banner_v2, empty legacy_tagline fields. Audit it on screen.
- Model sprawl: Show what happens if someone mapped every Veda page component to its own content type: HeroBanner, FeatureCard, TestimonialSlide, StatCounter. 30+ types, 150 entries. Consolidate to the actual Veda model with modular blocks.
- Channels: Veda serves the same product data to web (full layout) and mobile app (card view). Same entry, different only[BASE][] projections and include[] depths per consumer.
- Localization: Veda launches in English and Spanish. product_name and description are localizable. SKU, price, release_date, reference fields are not. Show the fallback: es-mx falls back to es-es, which falls back to en-us.
- Governance: Veda's naming conventions: product, product_line, gift_set (snake_case UIDs). Field descriptions populated. Content type descriptions state ownership and usage. Decision log records why gift_set was added.
Transitions
1 to 2: "Now that you know the rules, let me show you what this actually looks like in the editor and the API."
2 to 3: "References and modular blocks handle composition -- but classification is a different problem entirely."
3 to 4: "Every classification and composition choice has a cost, and that cost shows up in your API responses."
4 to 5: "Knowing the performance tradeoffs is useful, but let me show you how to apply all of this from scratch with a real business requirement."
5 to 6: "That was a greenfield model -- now let me show you what happens when you inherit someone else's model and need to fix it."
6 to 7: "Auditing catches bloated content types, but the opposite problem -- too many tiny content types -- is just as damaging."
7 to 8: "Once the model is clean, make sure it works for every channel, not just the website."
8 to 9: "Multi-channel is about delivery variation -- localization is about language variation, and the modeling decisions are just as important."
9 to 10: "A good model degrades without governance -- naming conventions, role restrictions, and a lightweight decision log keep it healthy over time."
Closing to Video 5: "You now know how to compose, query, audit, and govern content models. In Video 5, we shift to the API layer -- how to actually fetch, filter, and deliver this content to your frontends."
Common Mistakes to Call Out
- Using references for content that is never reused. A separate hero_banner content type referenced from one page adds lifecycle overhead, publish-state complexity, and an extra include[] for zero reuse benefit. Use a modular block.
- Using modular blocks for content that needs independent lifecycle. Embedding author bios as modular blocks means every article carries its own copy. Updating an author bio requires editing every article individually.
- Forgetting include[] on reference fields. The frontend receives UIDs instead of content. This is the single most common "why is my data missing" question from new Contentstack developers.
- Resolving all references on every query. Fetching articles with include[]=author&include[]=category&include[]=related_articles&include[]=tags when the list view only shows title and date. Use only[BASE][] for list views.
- Ignoring payload size until production. Development datasets are small. A 2,000-entry catalog with rich text and images exposes every over-fetching pattern. Test with realistic data volumes early.
- Modeling deep hierarchies as chained references. Category trees as category -> parent -> grandparent -> root create unbounded reference depth. Store the hierarchy as a denormalized path field and use a single reference to the leaf.
- Using freeform tags for user-facing navigation. After six months: "AI", "A.I.", "ai", "artificial-intelligence" all meaning the same thing. If classification drives navigation or filtering, use taxonomies.
- Mapping frontend components 1:1 to content types. Every React component gets its own content type. 35 types, 200 entries. Editors spend more time navigating the model than writing content.
- Localizing fields that should be universal. Making a reference field or date field localizable means the French version silently points to a different category than English. Default to non-localizable, opt in per field.
- No naming conventions. Five developers create content types with five different naming styles. Onboarding a new developer six months later requires archaeology instead of reading documentation.
- Skipping the relationship mapping step. Jumping to field definitions without mapping entity relationships produces content types that either duplicate data or miss connections.
- Treating the content model as final. A content model is a living artifact. Build with the expectation of iteration, not perfection.
Notes
Use this space for recording notes, script drafts, or post-production feedback.