Legacy Code in GTM Containers: What 2,000 B2B SaaS Sites Haven't Cleaned Up
GTM containers grow but rarely shrink. Tags get added for campaigns, vendor trials, analytics experiments. When they're no longer needed, they get paused. Paused tags accumulate. Nobody has the confidence or the mandate to remove them.
The most visible example: Google sunset Universal Analytics in July 2023. Three years later, 4,432 UA tags still exist across the ~2,000 B2B SaaS containers in our dataset. They fire on every page load, send network requests to endpoints that no longer process data, and consume browser resources for zero return.
This is a companion to our State of GTM in B2B SaaS research, looking specifically at legacy code, paused tags, and how container debt accumulates over time.
The tag economy across 2,000 containers
| Category | Tags | Share |
|---|---|---|
| Active tags | 101,453 | 84% |
| Paused tags | 19,875 | 16% |
| Total | 121,328 | 100% |
1 in 6 tags across the entire dataset is paused but not removed. 121,328 total tags, 19,875 of them sitting idle in container inventories, adding clutter to every audit, every handoff, and every version comparison.
4,432 Universal Analytics tags, three years after sunset
UA tags are the clearest case of legacy code because the answer is definitive. Google stopped processing Universal Analytics data in July 2023. Every UA tag in a container is dead code. It executes JavaScript, constructs a tracking payload, sends a network request, and the data is discarded on arrival.
Only 5% of these UA tags have consent configured. They were created before GTM's consent framework existed, and nobody went back to update tags that no longer collect data.
Three generations in one container
35 containers (1.8%) still have _gaq.push() calls. This is the API for ga.js, Google's JavaScript tracking library that predated analytics.js, which itself predated GA4. Code from before 2014, still executing in production containers.
30 of those 35 containers also have GA4 tags. Three generations of Google Analytics coexist in these containers: ga.js (_gaq.push), Universal Analytics (ga('send')), and GA4 (gtag or GA4 Event tags). Each migration left code behind. Each generation added new tags without removing the old ones.
21 non-GTM sites in the dataset still run Universal Analytics code directly on the page, three years post-sunset.
Paused tags: the mild version
69% of containers (1,366 / 1,990) have paused tags. The distribution:
| Paused tags | Containers | Share |
|---|---|---|
| 0 | 624 | 31% |
| 1-5 | 612 | 31% |
| 6-10 | 267 | 13% |
| 11-20 | 211 | 11% |
| 21-50 | 184 | 9% |
| 51+ | 92 | 5% |
The mean is 10 paused tags per container. The median is 3. One container has 345. 92 containers have 51 or more paused tags, representing containers where tag accumulation has been extreme: hundreds of tags added over years, paused when no longer needed, never removed.
Paused tags are inert. They don't fire, don't affect performance, and don't impact data quality. The cost is organizational: they clutter the container workspace, make audits harder (every paused tag is a "is this still needed?" question), and complicate handoffs when a new person or agency inherits the container.
Paused tags scale with container size
| Container size | Mean paused tags |
|---|---|
| 1-25 tags | 1.2 |
| 26-50 tags | 5.0 |
| 51-100 tags | 11.5 |
| 101+ tags | 30.4 |
The scaling is linear. Containers with 101+ tags average 30 paused tags, which is nearly a third of a median-sized container just in disabled weight. The pattern reflects the lifecycle: larger containers have been around longer, have had more contributors, and have accumulated more retired tags.
Paused tags and health scores
| Paused tags | Containers | Mean score |
|---|---|---|
| 0 | 624 | 82.6 |
| 1-5 | 612 | 75.6 |
| 6-10 | 267 | 73.0 |
| 11-20 | 211 | 72.9 |
| 21+ | 276 | 71.8 |
The biggest drop is from zero paused tags to any paused tags (82.6 to 75.6, a 7-point gap). After that, the curve flattens. Having 21+ paused tags scores only 1.2 points lower than having 11-20.
This isn't because paused tags directly lower the score (they're inert and TagManifest doesn't penalize them heavily). It's because paused tags are a proxy for container age and complexity. Containers that have accumulated paused tags tend to be older, larger, and have had more contributors, all of which correlate with more active findings.
The UA mental model persists in GA4
Even where UA tags have been removed and GA4 is the only analytics platform, the UA conceptual framework lives on in how events are structured. Three UA-era parameter names appear across the dataset in GA4 event tags:
| Parameter | Instances |
|---|---|
| event_label | 1,258 |
| event_category | 1,241 |
| event_action | 976 |
GA4 doesn't use the category/action/label model. These parameters work as custom dimensions in GA4, but they don't map to GA4's native reporting structure and each one consumes a slot in the 25-parameter-per-event limit.
The persistence of UA naming in GA4 suggests that the migration was structural (tags were reconfigured to GA4 format) but not conceptual (the data model wasn't rethought). Teams carried the category/action/label structure into a system that was designed around event names and flexible parameters. It works. It also means GA4 is being used as a UA-shaped container rather than taking advantage of the event model GA4 was built for.
GTM as an additive system
The data from this research describes a consistent pattern. Tags get added for specific purposes: a campaign, a vendor integration, a testing setup, an analytics experiment. The tag works. The project moves forward. When the purpose is fulfilled, the tag stays.
Pausing is the path of least resistance. It's reversible, low-risk, and takes one click. Deleting requires confidence that the tag isn't needed, which requires understanding what the tag does, which requires reading the configuration or the Custom HTML code, which takes time nobody has allocated. So the tag gets paused, and the container gains another entry in its inventory of disabled components.
Over time this produces containers where 16% of all tags are paused, where three generations of Google Analytics code coexist, and where the person inheriting the container has to distinguish between tags that are active, tags that are paused, tags that are active but dead (UA tags sending data to dead endpoints), and tags that are paused and dead (UA tags that someone disabled but didn't remove).
The practitioner consensus on paused tag lifecycle is pragmatic: keep paused tags for up to six months after deactivation (allowing for campaign review or seasonal reactivation) and remove them after that, with documentation about what was removed and why. In practice, this rarely happens. The container grows. The debt accumulates. The next person does archaeology.
What to check
Search for UA tags. Any tag with a Universal Analytics type (ua) or a tracking ID starting with UA- is dead code. If it's active, it's consuming resources. If it's paused, it's clutter. Both can be removed.
Search Custom HTML for legacy patterns. _gaq.push, ga('send', and __utmz in Custom HTML source code indicate pre-GA4 tracking that may still be executing. Tags containing only legacy code can be removed. Tags mixing legacy code with active functionality need the legacy portions stripped.
Review paused tags. For containers with 10+ paused tags, a cleanup pass is worth the time. Check when each was last active (container version history shows this) and what it does. Tags paused for more than six months with no documentation are candidates for removal.
Check GA4 parameters for UA naming. If your GA4 events send event_category, event_label, or event_action, evaluate whether these parameters serve a purpose in your GA4 reporting or whether they're migration artifacts consuming parameter slots.
Companion to the State of GTM in B2B SaaS. 1,990 containers. 121,328 total tags analyzed. April 2026. Scanning engine: TagManifest.