Back to notes

October 7, 2026 · 5 min read

Your Data Layer Is a Contract, Treat It Like One

Teams treat the data layer as a loose convention everyone just sort of follows. It should be versioned, validated, and owned like any other API contract.

GTMEngineering

I asked a frontend team once if they knew their checkout page's data layer push was a dependency for three separate marketing tools. They didn't — to them it was just an object they'd copied from a ticket eighteen months earlier, with no sense that changing a field name during a refactor would quietly break conversion tracking for Meta, Google Ads, and an internal BI pipeline simultaneously.

That's the core problem: everyone treats the data layer as a loose, implicit convention instead of what it actually is, which is an API contract between the frontend codebase and every downstream system that consumes it. APIs get versioned, documented, and protected by tests that fail the build when a required field goes missing. Data layers almost never get any of that, and they break constantly for exactly that reason.

What treating it as a contract actually looks like

// A data layer push with an explicit, typed shape
interface PurchaseEvent {
  event: "purchase";
  ecommerce: {
    transaction_id: string;
    value: number;
    currency: string;
    items: Array<{ item_id: string; price: number; quantity: number }>;
  };
}

function pushPurchase(payload: PurchaseEvent) {
  // validated against a schema before it ever reaches dataLayer.push
  validate(payload);
  window.dataLayer.push(payload);
}
Nobody would accept an API that silently changed its response shape on every frontend deploy. We accept exactly that from data layers, every day, on nearly every site I've audited.

What I push every engineering team toward

  • A typed schema for every data layer event, checked in to the same repo as the frontend code that produces it, not living only in a GTM container's documentation tab.
  • A JSON schema validation step that runs in CI against a snapshot of the data layer output from key pages, failing the build on a breaking change.
  • Explicit versioning on major shape changes, with the measurement side given advance notice instead of finding out from a broken dashboard.
  • Ownership assigned to a specific team, not left as an orphaned convention nobody's job description mentions.

Every tracking outage I've diagnosed that traced back to a frontend deploy traces back to this same root cause: nobody treated the data layer's shape as something that could break a dependency, because nobody had framed it as a dependency in the first place.