A vendor bundle costs 380 KB on every page load, the Coverage panel reports that four per cent of it executed, and nobody in the room can say what the tag is for.

Triage: Record Coverage per Vendor

  1. Open DevTools → More tools → Coverage, click the reload-and-record button, and let the page settle.
  2. Sort by Unused Bytes descending and note the third-party entries.
  3. Interact with the page — scroll, open a menu, submit a filter — and watch the unused figure fall for scripts that respond to interaction.
  4. Record the residual unused share per vendor after that exercise, which is closer to the real figure than the load-time number.
Coverage after a realistic session Four vendor bundles with their unused share after a page load and a realistic interaction sequence. A tag manager shows about thirty per cent unused, which is normal for a container. An analytics SDK shows about fifty per cent. A personalisation bundle shows about eighty per cent. A legacy campaign pixel shows ninety-six per cent unused, which usually means it is doing almost nothing at all. tag container 30% unused analytics SDK 50% unused personalisation 80% unused legacy pixel 96% unused High unused share is a question, not a verdict. The bottom row is the one worth asking about first. A bundle can legitimately ship code for paths this session did not take — but not ninety-six per cent of it.

Root Cause: Vendors Ship One Bundle for Every Customer

A third-party bundle is built to serve every configuration its vendor supports. It contains the code for features you enabled and the code for features you did not, because shipping one artefact is cheaper for the vendor than building per-customer bundles — and the cost of the unused portion falls on your visitors rather than on them.

That makes a high unused share normal rather than damning. A tag container legitimately carries the machinery for tag types you do not use; an analytics SDK carries collectors for events you never fire. What the number identifies is not waste in itself but disproportion: a bundle where the executed fraction is tiny and the total is large is one where the cost you pay bears almost no relationship to the value you receive.

The genuinely interesting cases are the ones where the answer is that nobody knows. A tag with a very high unused share, no owner in the inventory, and no measurable effect on anything is the archetypal removal candidate — and it is common, because tags are added for campaigns and experiments that end without anyone removing the tag.

Three explanations for a high unused share A high unused share has three explanations with different responses. The bundle may carry features you have not configured, in which case a lighter build may be available on request. The code may be reached only on paths this session did not take, in which case the figure is an artefact of the measurement. Or the tag may genuinely be doing almost nothing, in which case it is a removal candidate. unconfigured features the vendor ships one build ask for a lighter one sometimes available paths not taken the session missed them exercise more of the page re-measure before concluding genuinely idle no owner, no effect a removal candidate the common case Distinguish the three before acting. Only the third justifies removal on its own.

Resolution: Turn Coverage Into a Removal Shortlist

Coverage on its own is a measurement; combined with two other facts it becomes a decision. Join it against the inventory’s owner column and against whatever the tag is supposed to produce:

// coverage-shortlist.mjs — join coverage with ownership and observed output.
export function shortlist(coverage, inventory, vendorActivity) {
  return coverage
    .filter((c) => c.unusedShare > 0.7)                 // disproportionate, not merely large
    .map((c) => {
      const inv = inventory.find((i) => i.host === c.host) ?? {};
      return {
        host: c.host,
        bytes: c.totalBytes,
        unusedShare: c.unusedShare,
        owner: inv.owner ?? null,                        // null is the strongest signal
        justification: inv.justification ?? null,
        // Does anything actually come out of it? Events, requests, storage writes.
        observedOutput: vendorActivity[c.host] ?? 'none',
      };
    })
    // No owner and no output: the clearest candidates go first.
    .sort((a, b) => (a.owner ? 1 : 0) - (b.owner ? 1 : 0) || b.bytes - a.bytes);
}

The observedOutput field is what separates a genuinely idle tag from a lightly used one. A vendor that downloads 380 KB, executes four per cent, and makes no network requests and writes no storage during a full session is doing nothing observable at all — which is a much easier case to make than an argument from coverage alone.

Present the shortlist as a question rather than a proposal. “This tag costs 380 KB per page load, executes four per cent of itself, produces no observable output, and has no recorded owner — does anyone need it?” is a message that gets answered; “we are removing these tags” is one that gets escalated.

Verification: Remove, Watch, Keep or Restore

Removing a tag is reversible, which makes a time-boxed removal a better test than any amount of analysis. Remove it behind a flag, announce the window, and watch for a complaint. The absence of one after a fortnight is the strongest evidence available that nobody depended on it.

A removal that is safe to attempt A three-step removal process. First, announce the window and remove the tag behind a flag so it can be restored immediately. Second, wait a full business cycle including a reporting period, because most dependencies on a tag surface when someone opens a dashboard. Third, delete the flag and the configuration if nothing surfaced, or restore and record the dependency if something did. 1 · remove behind a flag announce the window restorable in seconds 2 · wait a full cycle include a reporting period dashboards are where it surfaces 3 · delete or restore nothing surfaced → delete something did → record it Waiting through a reporting cycle is the step people skip, and the one that finds the dependency.

Why the Reporting Cycle Matters

Most dependencies on a marketing or analytics tag are invisible day to day and become visible at a boundary: a weekly report, a monthly attribution model, a quarterly board pack. A tag removed on a Monday will produce no complaint all week and a sharp one when someone opens their dashboard on the first of the month.

That timing is worth planning for rather than being surprised by. Choose a removal window that spans at least one full reporting period for whoever might depend on the tag, and tell those people specifically rather than announcing it in an engineering channel they do not read. The point is not ceremony — it is that the person who will notice is not the person watching the deploy.

It also affects how you restore. Keeping the tag’s configuration intact behind a flag, rather than deleting it outright, means restoration is a toggle rather than a reconstruction — and a reconstruction after a month is genuinely difficult, because the original configuration was in a console nobody exported.

Common Pitfalls

  • Measuring coverage at load only. Code reached on interaction counts as unused until the interaction happens, so a load-only figure overstates waste on every interactive vendor.

  • Treating a high share as proof of waste. Vendors ship one bundle for all customers; the share alone does not distinguish “unconfigured” from “idle”.

  • Removing without a restoration path. A tag deleted from a console cannot be restored from a repository, so the flag matters more than the removal.

  • Skipping the ownership join. Coverage tells you what is heavy; the owner column tells you whether anyone will defend it, which decides how the conversation goes.

  • Comparing coverage across vendors as if it were a ranking. A container with thirty per cent unused and a pixel with ninety-six per cent are not on the same scale, because their totals differ by an order of magnitude. Rank by unused bytes, not by unused share, when deciding what to look at first.


Frequently Asked Questions

Does coverage measure bytes or execution time?

Bytes of code that were parsed but never executed. It is a proxy for waste rather than a direct measure of cost, and the relationship is loose: a small function executed in a tight loop costs far more main-thread time than a large unused block costs anything at all.

Use it to find candidates and use main-thread attribution to size the cost. The two answer different questions — coverage asks “is this bundle proportionate”, attribution asks “what is it costing” — and a removal argument is much stronger with both.

Can we get coverage data from real users?

Not directly; the Coverage panel is a DevTools instrument and there is no field API equivalent. What you can collect in the field is the observable output — requests made, storage written, events sent — which is the more decision-relevant half of the argument anyway.

Combining a synthetic coverage figure with field output data gives you most of what a field coverage API would. A tag that looks idle in the Coverage panel and produces nothing in the field across a week of real sessions is idle in every sense that matters.

What if the vendor says the unused code is required?

Ask specifically what it does and under what conditions it runs. The honest answer is usually that the bundle serves every customer and your configuration uses a fraction of it, which is a reasonable engineering decision on their side and a cost on yours — and it opens the useful question of whether a lighter build exists.

Several vendors do offer trimmed or modular builds and do not advertise them prominently, because most customers never ask. A specific request naming the features you use is more likely to succeed than a general complaint about size, and it occasionally halves the payload for the cost of an email.

Is a high unused share ever worth accepting permanently?

Regularly, and saying so keeps the metric credible. A tag container legitimately carries machinery for tag types you have not configured, and a payment SDK carries handling for methods you do not offer — in both cases the alternative is a per-customer build the vendor does not produce.

What should not be permanent is accepting it without a recorded decision. Note in the inventory that the share is known, understood, and judged proportionate, with the date. That converts a recurring rediscovery into a settled question, and it means the next person running a coverage audit spends their attention on the entries that have not been examined.


Up: Auditing and Inventorying Third-Party Scripts