A data layer specification developers will actually build

A spreadsheet of event names is not a specification. It is a wish list with column headers.

The data layer is the contract between the site and the tag manager, and it is written by an analyst and built by a developer who has never opened Tag Manager. A specification that works names the trigger in the user's terms, gives the exact key, the type, whether it is required, a real example value, and where in the page lifecycle it must be pushed. A specification that fails lists event names and assumes the rest is obvious.

What the data layer actually is

Define it once, in the document, because half the confusion in these projects is vocabulary. Google's own description is short: the data layer is an object used by Google Tag Manager and gtag.js to pass information to tags, and triggers can be set up based on the values of variables. It is structured as JSON.

The rest of that page is the part a developer needs and an analyst usually skips. The data layer has to be established before Tag Manager loads. The documented pattern is a single line declaring the array, placed above the container snippet.

The practical translation for a spec: the data layer is not something the tag manager creates. It is something the site creates, and the tag manager reads. If the site does not push it, no amount of container configuration conjures it.

That single sentence resolves most of the arguments about who owns what. The analyst owns what should be in the object. The developer owns getting it there before the container needs it.

The ordering rule that produces most of the bugs

Tag Manager processes data layer messages first in, first out. Google states it directly: each message is processed one at a time in the order received, and if the message is an event, any tags whose trigger conditions are met fire before Tag Manager moves on to the next message.

Then comes the sentence that catches experienced people. If a push is made by code on the page, in a custom template or in a Custom HTML tag, that message is queued and processed after all other pending messages, which means updated values are not guaranteed to be available for the next event.

Google's stated fix is to add an event name to the message when you push it, and listen for that name with a Custom Event trigger. That is a design instruction, not a workaround, and it belongs in your specification rather than in a developer's head.

Put it in the spec as a rule with a reason: never set a value in one push and read it in the next event unless the reading tag is triggered by an event name pushed in the same message. A developer will follow a rule with a reason. They will quietly ignore a rule without one.

The spec format that gets built without a meeting

The spec format that gets built without a meeting
ColumnWhat goes in itWhy the developer needs it
User actionSubmits the quote form on any pageTells them where in the code to hook
Event namegenerate_leadThe exact string, no synonyms
Keyform_idSnake case, fixed, not a description
TypestringStops a number arriving as "1200"
RequiredYesDecides whether to push at all if missing
Example valuequote-hvac-ottawaRemoves the last ambiguity
TimingAfter server confirms receiptSeparates attempted from succeeded
OwnerWeb teamA name, not a department

Every column in that table exists because leaving it out has produced a defect. The type column exists because a value arrives as a string and the reporting layer sums it as text. The timing column exists because a form submission event fired on click counts abandoned forms as leads.

The example value column does more work than any other. An analyst writes "the form identifier" and a developer ships the DOM element id, the CMS node id, or a human label with a space in it, all of which are defensible readings. One real example ends that.

Name the events using the recommended vocabulary rather than inventing one. Google publishes an event reference for the Google tag listing the event names and parameters the platform already understands, and using a documented name costs nothing and buys you built in behaviour.

The same discipline applies to campaign values as to event values. A parameter that carries an inconsistent string is worth nothing downstream, which is the same problem as a campaign taxonomy nobody enforces.

Write the rules that are not per event

A specification needs a short standing rules section, because these apply everywhere and repeating them in forty rows is how a document stops being read.

Casing and naming are fixed. Pick snake case for keys and event names, write it down, and do not accept a second convention on the grounds that one team already uses it. Google's troubleshooting guidance calls out inconsistent casing and inconsistent quote marks as recurring data layer faults, alongside overwriting the data layer object itself.

Never overwrite the array. The declaration pattern uses a logical or against the existing value for a reason: a later script that assigns a fresh array throws away everything pushed before it, and the symptom is a tag that works on some pages and not others.

Say which measurement library the site is using, because the declaration differs. A container installation creates the data layer itself, while a direct Google tag installation declares it inside the snippet and pushes through a wrapper function documented in the Google tag API reference. A site running both needs an explicit decision about which one owns the object.

No personal data in the object unless it has been deliberately approved. An email address in a push is an email address in every tag that reads the data layer, on every destination, forever, which is precisely how personal data ends up somewhere nobody intended.

State what happens when a required value is missing. The honest options are to push the event without the key, or to not push at all. Both are defensible and they produce very different reports, so the document has to choose.

How to hand it over so it survives

Give the developer the document and a working example page. Not a diagram, a page: the declaration, the snippet, one push, and a comment saying what the push represents. It removes an entire class of misunderstanding at a cost of about twenty minutes.

Agree the acceptance test before any code is written. For each row, the test is that the value appears in the data layer, with the right type, at the right moment, on a real path in a real browser. That is a definition of done in the same sense as acceptance criteria for any other analytics deliverable.

Then test the paths the specification did not enumerate. The mobile variant, the returning visitor, the second form on the page. Conditional coverage is where these implementations actually fail, and it fails quietly, which is the pattern that survives launch QA every time.

Keep the document versioned with the site, not in a folder. A specification that lives beside the code gets updated when the code changes. A specification in a shared drive gets updated never, and six months later somebody is reverse engineering the data layer from the network tab.

What this buys, and what it does not

A good specification buys you a measurement layer that a second developer can extend without ringing you, and a report whose numbers can be traced back to a line of code. That traceability is the thing that makes a number defensible in a meeting.

It does not buy you correctness. A perfectly implemented data layer carrying the wrong definition of a lead produces confident, precise, wrong reporting, and no amount of engineering rigour catches a definitional error.

So the specification is downstream of a measurement plan, not a substitute for one. Deciding which business questions the site has to answer comes first, and the event list is derived from it, which is the sequence a GA4 and Tag Manager implementation is scoped around.

For a multi-location operator the payoff is concrete and immediate. If location is not a required key on every conversion event, per location reporting is impossible later without reimplementation, and that gap is exactly what had to be closed before a fifteen location clinic group could see cost per acquisition by site.

Start a conversationMore insights