Structured data is the corner of SEO where lying is easiest. The markup lives in a script tag no human reads, search engines take it largely on faith, and the web is full of pages claiming five-star ratings from reviews that do not exist. That is exactly why we treat JSON-LD as an honesty exercise first and a rankings tool second.
Our rule is simple: the structured data on a page may never say more than the page itself says. And the most reliable way to enforce that rule turned out to be architectural. Generate the markup from the same typed data that renders the visible page, and the two cannot disagree.
One source of truth, two outputs
On our builds, content lives in typed data files: products, services, FAQs, case studies, company facts. The pages render from that data. So does the JSON-LD. On Creative Maintenance Solutions, an industrial maintenance contractor, the LocalBusiness entity carries an OfferCatalog of all 11 services, generated from the same data file that drives the navigation and the service pages. Rename a service and the nav, the page, and the schema change together in one commit.
The entities link to each other with @id references, so a crawler sees one connected picture instead of scattered fragments: the business, its services, its service area, its case studies, its pages. Nothing is pasted in by hand, which means nothing drifts. The footer, the contact page, and the schema all pull the same name, address, and phone number from one company record.
Belzona Baton Rouge runs the pattern at catalog scale. Each of its 112 product pages emits three schema blocks: a Product record, an FAQPage built from the questions actually answered on that page, and a BreadcrumbList matching the visible trail. All three are generated from the product's data record.
Twenty-two schema types on one site
The deepest graph we have shipped so far belongs to IMS, an authorized Belzona distributor with a roughly 492-URL product catalog. Twenty-eight of its twenty-nine route files emit JSON-LD, and the graph spans 22 distinct schema.org types: LocalBusiness with full name, address, and phone, Product and Brand, FAQPage, VideoObject, Service, Article, CollectionPage, BreadcrumbList, AdministrativeArea for the service territory, and more.
The video markup shows how far the honesty rule reaches. The site's how-to-mix page emits VideoObject data for all 31 of its mixing videos, and the upload dates in that markup are the real dates pulled from the source videos. At one point every date across the full 88-video library was re-scraped and corrected, because approximately right is a polite phrase for wrong.
Even the serializer is engineered rather than assumed. Every block on the site flows through one shared component whose serializer escapes script-tag breakouts and the two Unicode line separators that can end a script context early. Structured data is injected markup, and injected markup that carries data-layer content deserves the same care as any other injection surface.
The hard rules
Some rules are written directly into the code as guard comments, sitting where the next person to touch the file cannot miss them:
- Never emit aggregateRating without real reviews behind it. A star rating conjured for the crawler is fabrication, and it is the most common lie in structured data.
- FAQPage markup must mirror question-and-answer content that is visibly on the page. No invented Q&A written only for machines.
- Every fact flows from a single source of truth, so the schema can never disagree with the footer.
- Unknown values stay out of the graph entirely. A missing field is omitted, never guessed.
None of this is charity toward the crawlers. Search engines have spent a decade getting burned by schema spam, and markup that misrepresents the visible page is a documented route to manual penalties and lost rich results. The safest position, this year and every year after, is markup that is boringly, verifiably true.
The same discipline extends to the sitemap. Ours are generated from the content data, never maintained by hand, with truthful last-modified dates per page, because a lastmod that always says today is noise crawlers learn to ignore. Honest metadata is a habit, not a feature you bolt on.
Why generated beats maintained
The quiet advantage of this approach is that honesty becomes free. A hand-written schema block is correct on the day it ships and decays from there: prices change, FAQs get rewritten, a product is discontinued, and the JSON-LD silently keeps describing the old site. A generated block cannot decay, because it has no independent existence. It is a projection of the same data that renders the page.
It scales in the other direction too. Add a product to the data file and the next build emits its Product schema, its breadcrumb, and its FAQ markup with zero additional work. A complete entity graph on a several-hundred-page catalog would be unmaintainable by hand. As an output of the data layer, it is just another render target.
Most local-business sites ship a bare LocalBusiness block from a plugin, if they ship anything at all. A full graph describing every product, service, FAQ, and video hands search engines, and increasingly the AI answer engines, a machine-readable version of the entire business. That is worth having. But only if it tells the truth, because the graph is a set of claims made in your client's name, and we do not publish claims in a client's name that we cannot back.
