GTM Dependency Mapping: What Breaks If You Delete That
Published

Open any GTM container that has been running for two years and you will find assets nobody can account for. A Custom JavaScript variable called JS - helper. A trigger named Trigger 4. A Custom HTML tag with no notes.
The obvious move is to delete them. The reason nobody does is that GTM will let you. There is no dependency check, no "this variable is used by 3 tags" warning, no confirmation. You delete it, you publish, and if something was quietly depending on it, that thing now resolves to undefined — and GTM does not error on undefined. The tag fires, the parameter is empty, and your reports have a hole in them starting from the publish date.
So the container accumulates. Everyone knows there is dead weight in it and nobody can prove which parts.
The graph GTM does not show you
A container is a directed graph with four kinds of edge:
Trigger → Tag. A tag has firing triggers and blocking triggers. This one GTM does show you: open a trigger and it lists the tags it fires.
Variable → Tag. A tag reads a variable in any of its fields — a Measurement ID, an event parameter value, a URL. GTM does not show you this from the variable's side. You cannot open a variable and ask what uses it.
Variable → Trigger. A trigger's conditions reference variables. Page Path contains /checkout depends on the built-in Page Path variable. Custom conditions frequently reference custom variables. Also invisible from the variable's side.
Variable → Variable. A Custom JavaScript variable can reference other variables inside its code. A Lookup Table variable's input is a variable. These chains get several levels deep and are completely opaque — the reference lives inside a code string, so even a naive search has to parse JavaScript.
Three of those four edge types are invisible in the direction you need them. That is the whole problem.
GTM's search finds text, not references. Searching the container for a variable name will find tags whose fields contain {{JS - helper}} as literal text. It will not reliably find a Custom JavaScript variable that builds the reference dynamically, and it will produce false positives from any tag whose notes mention the name. Useful as a first pass, not as a safety check.
Building the map by hand
If you have to do this without tooling, export the container and work from the JSON. GTM's export (Admin → Export Container) gives you every tag, trigger, and variable with their full configuration.
Build the variable reference index. For every tag, trigger, and variable in the export, scan every string field for {{...}} patterns. Each match is an edge. This catches the large majority of references, including those inside Custom JavaScript code, because GTM's {{...}} syntax works inside custom code too.
Build the trigger index. Each tag lists firingTriggerId and blockingTriggerId. Invert it to get, for each trigger, the tags it affects.
Find the orphans. Variables with no inbound edges. Triggers attached to no tag. These are your deletion candidates.
Find the hubs. Variables referenced by many things. These are the ones where a change is dangerous — a Lookup Table read by fifteen tags is a single point of failure for fifteen tags.
This is a genuinely useful exercise and it takes a couple of hours per container with a script, or an afternoon without one. It is also exactly the kind of thing that is worth automating once and never doing by hand again.
Reading the graph
Once you have the map, three questions become answerable.
"What breaks if I delete this?"
Follow the outbound edges. A variable with no dependents is safe. A variable read by four tags means four tags will start sending undefined.
The nuance: undefined is not always a failure. A tag that sends an optional parameter will still fire and still record the event, just without that parameter. A tag whose Measurement ID field resolves to undefined will fail entirely. Look at where in the tag the variable is used, not just that it is used.
"Why is this parameter empty?"
Walk backwards. The tag reads {{DL - user_tier}}, which is a dataLayer variable reading user.tier. Either the dataLayer push is not happening, is happening after the tag fires, or is using a different key. A live capture tells you which.
Chains make this harder. A tag reads a Lookup Table, whose input is a Custom JavaScript variable, which reads two dataLayer variables. Any of four links can be the break. Having the chain written down turns a bisection search into a direct check.
"What is my fragility profile?"
Group your variables by source type. In a healthy container most values come from dataLayer variables — a developer pushed them deliberately and they survive a redesign. DOM element and CSS-selector variables are scraped from rendered markup and break the next time someone touches the front end, silently.
Counting these gives you a number you can put in front of a stakeholder: "Fourteen of our tags depend on scraping the page. A redesign breaks all fourteen and nothing will tell us." That is a much better argument for a dataLayer investment than "we should do it properly." The naming convention that prefixes variables by source makes this a scroll of the variable list rather than an audit.
A safe deletion process
Freeze the container. One workspace, one person, for the duration.
Build the map before touching anything.
Delete in order of certainty. Orphaned variables first — nothing references them, by definition. Then triggers attached to no tag. Then paused tags older than a quarter. Then tags with zero fires in 90 days, checked against the trigger they are attached to (a tag that has not fired might have a broken trigger rather than being genuinely dead — that is a bug, not a deletion candidate).
One publish per batch, with a descriptive version name. Not one publish for the whole cleanup. If something breaks, you want a small version range to bisect.
Verify after each publish with a live capture across your key journeys. The whole point of deleting carefully is that you can tell when you got it wrong.
Keep the export. GTM version history covers you, but an exported JSON of the pre-cleanup state costs nothing and is easier to diff.
Before a migration
Dependency mapping matters most when you are moving a container between accounts, splitting one container into several, or consolidating several into one.
The export/import round-trip preserves references within the imported set. What it does not preserve is anything referencing something you did not bring: constants that lived in a different container, user permissions, environments, and any hard-coded container ID in a Custom HTML tag.
Mapping before the move tells you what your import set actually needs to be complete. Diffing the graph after the move tells you whether it arrived intact — a variable that had four dependents before and one after is a merge that went wrong.
How Tagfire helps
Building the graph by hand is the part that stops people, so this is where tooling pays for itself immediately.
GTM Container Manager reads a live container through the GTM API and renders the full dependency graph — for any tag, trigger, or variable, what it references and what references it, including chains through Custom JavaScript and Lookup Tables. That turns "what breaks if I delete this?" into a click. It also handles the migration side: cloning versions between containers, exporting JSON, and saving individual tags, triggers, and variables to reuse elsewhere. Server-side containers are covered too, including clients and transformations.
GTM Visualizer renders the same graph as an interactive map you can explore. It runs on the marketing site with no account and no Google access — upload a container export and you get the picture. This is the one to reach for on day one of an engagement, before anyone has granted you API access, or when you just want to hand a stakeholder something they can look at.
GTM Audit produces the deletion candidate list directly. Across its 50+ checks it flags orphaned variables, unattached triggers, paused tags, tags that have never fired, document.write usage, Custom HTML security risks, and missing consent gates — which is the entire "delete in order of certainty" list, generated rather than derived.
GTM Container Scanner reads any published container straight from its public JavaScript, with no authentication at all. Useful for inventorying a container you have inherited but do not yet have access to, and for checking what a container actually shipped versus what the workspace says it contains.
The container manager documentation covers the dependency view in detail.

GTM Container Naming Conventions That Scale
A naming convention for GTM tags, triggers, and variables that stays readable past 200 assets — plus how to retrofit one onto a container that already has none.

Live Tag Debugging for GA4 and GTM
How to debug GA4 and GTM tags by watching hits fire in real time — what GTM Preview and DebugView miss, a repeatable workflow, and how to turn a debugging session into an automated check.

The 2026 GA4 + GTM Setup Checklist
A complete technical checklist for Google Analytics 4 and Google Tag Manager — collection, configuration, consent, campaign tracking, and governance. Verify each item before you trust a single report.