LW IT Solutions logo LW IT Solutions logo LW IT Solutions
« Blog Overview /Digital Analytics/Tutorials / Tutorial: Agreeing a GTM Naming Convention and...
Read this article in other languages:

Tutorial: Agreeing a GTM Naming Convention and Enforcing It on an Existing Container

Tutorial: Agreeing a GTM Naming Convention and Enforcing It on an Existing ContainerTutorial: Agreeing a GTM Naming Convention and Enforcing It on an Existing Container

Management Summary

A naming convention gives Google Tag Manager components consistent, understandable names. In a large measurement setup, this helps show what each component does and makes maintenance or review easier for a team. Applying the convention to existing settings is more than tidying labels: names can be used by other components, so changes may affect the measurement. A recorded link between old and new names preserves understanding and makes the change easier to follow without losing the existing setup's meaning.

Contents
  1. A small, explicit convention
  2. An audit that checks more than the prefix
  3. References need a separate check
  4. Migration and acceptance
  5. Questions and answers
  6. Sources

A consistent naming convention makes a large Google Tag Manager container easier to scan, search and review. Names complement folders, element types and the workspace interface; the first word is not the container’s only organising mechanism. A useful migration keeps measurement behaviour unchanged while making the purpose of each element clearer.

A small, explicit convention

One possible scheme uses three segments for tags and two for triggers and user-defined variables. The convention is a project decision, not a GTM requirement. The following example uses shared English prefixes across language versions so that the same checker can validate every example.

Text
GA4 - Event - add_to_cart
Meta - Event - purchase
Ads - Linker - All pages

CE - add_to_cart
PV - checkout
Click - header_cta

DLV - ecommerce.items
JS - item_list
Const - GA4 measurement ID
LT - country_code

Here CE means Custom Event, PV means Page View, DLV means Data Layer Variable and LT means Lookup Table. JS denotes a custom JavaScript variable in this convention. These abbreviations need a documented meaning; GTM does not interpret the prefix as an element type.

The separator is consistently - . With an alphabetical name sort, shared prefixes bring related names together. Mixed separators reduce consistency but do not destroy sorting. The final segment should preserve a relevant technical identifier such as add_to_cart; an unrelated variable need not contain that event name.

Platform first is useful when work is organised by destination. A site-first convention may fit a multi-site container better, but requires corresponding checker rules. The words in a display name do not select the tag template or change the transmitted event. Legacy names such as “GA4 Config” should not be mistaken for current template names. Google’s 2026 interface changes also mean that fixed menu assumptions can become outdated.

Nine illustrative tag names before renaming and grouped by a consistent prefix convention afterwards.
Names support scanning; folders remain available. Renaming still requires a reference and behaviour check.

An audit that checks more than the prefix

A full export of the intended workspace provides a baseline. A partial export can omit dependencies and is unsuitable as proof of the complete container state. A ContainerVersion contains more than three arrays; this checker deliberately covers only tags, triggers and user-defined variables.

The Python 3 example below is saved as naming-audit.py. A permitted prefix alone is insufficient: GA4 - fails because the required segments are missing. The checker reads a UI export with containerVersion or a direct API ContainerVersion response. Input errors return exit code 2, naming findings return 1, and a completed naming check without findings returns 0.

Python
import json
import re
import sys
from pathlib import Path

# Project convention, not a Google requirement.
PREFIXES = {
    "tag": {"GA4", "Google", "Meta", "Ads", "LinkedIn", "TikTok", "Other"},
    "trigger": {"CE", "PV", "Click", "Form", "Timer", "Visible", "Other"},
    "variable": {"DLV", "JS", "Const", "LT", "URL", "Cookie", "Other"},
}
SEGMENTS = {"tag": 3, "trigger": 2, "variable": 2}
REF = re.compile(r"\{\{([^{}]+)\}\}")


def audit(document):
    if not isinstance(document, dict):
        raise ValueError("Expected a JSON object")
    version = document.get("containerVersion", document)
    if not isinstance(version, dict) or not version.get("containerId"):
        raise ValueError("Expected a container version with containerId")
    report = {"counts": {}, "violations": [], "references": []}
    for kind, allowed in PREFIXES.items():
        entities = version.get(kind, [])
        if not isinstance(entities, list):
            raise ValueError(f"{kind}: expected an array")
        report["counts"][kind] = len(entities)
        for index, entity in enumerate(entities):
            if not isinstance(entity, dict):
                raise ValueError(f"{kind}[{index}]: expected an object")
            name = entity.get("name")
            parts = name.split(" - ") if isinstance(name, str) else []
            valid = (len(parts) == SEGMENTS[kind]
                     and parts[0] in allowed
                     and all(part and part == part.strip()
                             and not any(ord(c) < 32 for c in part)
                             for part in parts))
            if not valid:
                report["violations"].append({
                    "kind": kind, "id": entity.get(kind + "Id"),
                    "index": index, "name": name,
                })

    def walk(value, path):
        if isinstance(value, dict):
            for key, child in value.items():
                walk(child, path + [key])
        elif isinstance(value, list):
            for index, child in enumerate(value):
                walk(child, path + [index])
        elif isinstance(value, str):
            for match in REF.finditer(value):
                report["references"].append({
                    "path": path, "kind": "braces", "name": match.group(1)
                })
            if path and path[-1] == "tagName":
                report["references"].append({
                    "path": path, "kind": "tagName", "name": value
                })
    walk(version, [])
    return report


def main():
    if len(sys.argv) != 2:
        print("Usage: python3 naming-audit.py export.json", file=sys.stderr)
        return 2
    try:
        document = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8-sig"))
        report = audit(document)
    except (OSError, UnicodeError, ValueError) as exc:
        print(f"Input error: {exc}", file=sys.stderr)
        return 2
    print(json.dumps(report, ensure_ascii=False, indent=2))
    return 1 if report["violations"] else 0


if __name__ == "__main__":
    sys.exit(main())
Shell
python3 naming-audit.py export.json > naming-report.json

The JSON report contains every naming finding and candidate reference with a structured path showing the exact field in the export. JSON is decoded before scanning, so escaped braces can be found. There is no top-30 cutoff. The scan records {{...}} and tagName strings; literal braces, notes and dynamically constructed names still require interpretation. This is neither a GTM parser nor proof that every dependency resolves.

The naming check also does not verify whether a “DLV” element really uses the Data Layer Variable type, whether a tag is needed, or whether a campaign works. Those questions belong to the substantive review. An old date, unfamiliar name or paused tag is a review signal, not automatic permission to delete.

References need a separate check

Trigger assignments use fields such as firingTriggerId and blockingTriggerId. That does not make every tag rename consequence-free. Sequencing references in setupTag[].tagName and teardownTag[].tagName also need inspection in the actual export; the field name alone is not a substitute for checking the represented target. Tag display names can additionally enter monitoring metadata.

Variable references use names in double braces, including Custom HTML and custom JavaScript. A UI rename, an API update and an edited JSON import must not be assumed to update all references identically. The proposed process records a list containing each element’s type, ID, old name and new name, performs the rename in an isolated workspace, then checks the resulting export. Any automatic reference updates need confirmation in that result.

A before/after reference list is useful but insufficient. An unchanged stale name can still be broken; an intentional difference can be valid. A comparison of what the configuration does must also cover trigger conditions, tag parameters, sequencing, consent settings, pause state and code. A global search-and-replace can modify transmitted values or arbitrary code and is not an appropriate rename method.

Migration and acceptance

  1. Baseline: full export, container identity and version or workspace.
  2. Convention: agreed prefixes, segment meanings, exceptions and an ID-based rename mapping.
  3. Scope: renaming separated from deletion and functional changes in a dedicated workspace where available.
  4. Synchronisation: resolved workspace conflicts and repeated comparisons after merged changes.
  5. Acceptance: full export review and Preview tests of affected tags, triggers, variables and sequencing.
  6. Publication record: accepted changes documented in the version description.

Preview executes the draft on a connected site; test tags can still send real requests. A test environment and appropriate test destinations avoid confusing the rename test with production measurements. A “fired” status alone is not proof of correct data at the destination. The relevant test includes expected parameter values and network requests.

A separate workspace makes review and rollback easier, but container versions form one history. Restoring an older version can also undo later unrelated changes. The intended rollback therefore needs the same dependency awareness as the forward change.

A harp is restrung string by string, with the old strings laid out for comparison. Likewise, a renaming is switched over step by step, and the result is compared with the old state before sign-off.
A harp is restrung string by string, with the old strings laid out for comparison. Likewise, a renaming is switched over step by step, and the result is compared with the old state before sign-off.

Questions and answers

Are built-in variables included?

No. Built-in variables such as Page URL are managed separately from user-defined variables and appear under builtInVariable in a version. The naming rules do not rename these standard entries. References to built-in variables may still appear in the report.

Can the check run automatically?

The API method accounts.containers.versions.live returns the published ContainerVersion directly and accepts the tagmanager.readonly scope. API enablement, OAuth authorisation and access to the relevant account/container are required; a service account is one supported approach. Read access avoids granting unnecessary write capabilities, but exports and credentials still need appropriate handling.

The live version does not include unfinished workspace changes. A workspace audit requires the corresponding entity-list endpoints, including all pagination pages, or a full workspace export. A monthly check is an optional cadence; a check before each release is often more useful for enforcing the convention.

Where should the convention be documented?

A maintained team document linked from container notes, plus relevant element and version notes, is easier to govern than an artificial tag that must never fire. Exceptions belong in the same documentation and in the checker configuration. Adding a note tag outside the naming rules would otherwise create a finding in every audit.

Lukas Wojcik

Lukas Wojcik

Systems architect and technology enthusiast specializing in scalable tracking solutions, GMP Stack (GA4 & GTM), and robust backend architectures. Advocate for clean code and privacy-first design.

Get in touch

A question or an idea for a web project? Tell me a little about it. This form uses reCAPTCHA and formconfirmed to protect against spam.

Write a comment

Differing figures from other accounts and questions about the setup are welcome here.

The email address is not published. Required fields are marked with an asterisk.

LUKASWOJCIK.COM · NEWSLETTER

Fresh ideas in your inbox

New articles, practical tools and updates from the projects.

Choose topics (optional)
No selection: all topics.

Subscription starts after confirmation by email. · Privacy

Privacy · Newsletter

Email address, language, selected topics and subscription/confirmation dates are stored to deliver the newsletter. Double opt-in is required. Every issue includes an immediate unsubscribe link. Formconfirmed checks the signup for spam. No email open or click tracking.

Read the full privacy policy ↗