Measured, not asserted
Published
A budget is money, not media, and how much media it buys depends entirely on how fast it is spent: inventory is priced on a rising curve, so the cheap impressions in a day run out and the next ones cost more. That makes pacing the rare setting whose loss is invisible to the two numbers a buyer checks. The flight spends its budget either way and the line item reconciles to the cent. This test renamed the pacing mode against a real obfuscation engine. The failing run spent MORE of the budget than the correct one and bought 764,111 fewer impressions.
A budget is money, not media
The fixture is a fourteen-day flight on a hundred and forty thousand, against a price curve with four rungs a day: ninety thousand impressions at a 3.20 CPM, a hundred and twenty thousand at 6.40, two hundred and sixty thousand at 12.80 and the rest at 26.00. Supply thins out over one weekend, which is there so the catch-up setting has something to catch up.
Correct, the flight delivers on all fourteen days, buys 9,115,389 impressions for 138,012.11 at an effective CPM of 15.14, and leaves 1,987.89 unspent because the thin weekend could not absorb its full share.
The flight was sold as a two-week presence. The brand wants to be in market every day of it, not famous for a few days and absent for the rest. The library's default is to spend as fast as the inventory allows, which is the only assumption available about a buyer who has not said otherwise.
The failing arm spends more and buys less
^(requirePacingCheck|pacingMode)$ spends 140,000.00 -- the entire budget, with UNSPENT=0.00 -- against the correct run's 138,012.11. On every figure an invoice or a finance reconciliation carries, the failing run looks BETTER: fuller delivery, no unspent budget, nothing to credit back.
It buys 8,351,278 impressions instead of 9,115,389. That is 764,111 fewer, at an effective CPM of 16.76 instead of 15.14, and the campaign is dark for two days of the fortnight.
There is no cleverness in the mechanism and no way to argue with it. Spending a hundred and forty thousand across fourteen days means buying low on fourteen separate price curves. Spending it across twelve means buying higher on twelve. There is no way to spend the same money faster and get more of it.
The daily breakdown does show the shape -- the correct run reads 10000,10000,10000,10000,9256,9256,9256,10244.11,10000,... and the failing one front-loads and then stops -- but the daily breakdown is a report somebody has to open, and the flight total is what the invoice is written against.
The loud half and the union that silences it
^(pacingMode)$ on its own fails closed. The buyer's own pacing rule refuses the flight with pacing-check(2 dark day(s) in the flight), nothing is delivered, and the failure is visible immediately.
^(requirePacingCheck)$ and ^(pacingRule)$ each move only the line that reports the policy: 9,115,389 impressions for 138,012.11 in both, exactly as in the correct run. With the pace still configured there is nothing for the review to reject.
The union is the silent overspend above. The half that would have screamed is switched off by the half that fails quietly, and the telemetry does not help: the rule is still present, still correct, still bound -- it is simply never consulted.
The wider pattern is worse than the narrow one
This area reproduces one of the least intuitive results in the series. ^(dailyCapPct)$, ^(minDailySpendPct)$ and ^(overdeliveryTolerancePct)$ each leave the delivery completely untouched on their own -- 9,115,389 impressions for 138,012.11, identical to the correct run. An evenly paced day spends 7.14 per cent of the budget, which is under the ten per cent ceiling and over the two per cent floor, so none of the three is ever consulted.
Add the daily ceiling to the pair above and ^(requirePacingCheck|pacingMode|dailyCapPct)$ delivers on eight days, goes dark for six, and buys 7,253,125 impressions at a 19.30 effective CPM. Against the correct run that is 1,862,264 impressions lost on a fully-spent budget.
So the three keys that look like the safest things in the file are the ones that decide how bad the pacing failure is, and testing the halves says nothing about the union. Had this area been tested only with a narrow, careful pattern that excluded the pace, none of it would have appeared.
The two arms that fail closed
^(flightDays)$ collapses the flight to a single day: 14,000.00 spent and 126,000.00 unspent. That is a nine-tenths under-delivery and it is on the front page of every dashboard the morning after.
^(catchUpPct)$ is the mild one and it moves in the honest direction. Without it, the shortfall from the thin weekend is simply never made up: 9,106,000 impressions for 137,768.00, against 9,115,389 for 138,012.11. The buyer is under-delivered by a quarter of a per cent and the money is returned.
The record fields on the price curve fail closed too. Renaming the CPM or the impression count on the inventory tiers makes the day unbuyable rather than mispriced. Losing a value fails closed; losing a meaning fails open. That contrast has now held for every shape this series has measured.
What to change, whether or not you obfuscate
A pacing mode is exactly the kind of setting that goes missing without any obfuscator involved: it is a string, it is optional, and every platform spells it differently. A migration between demand-side platforms will do it.
Refuse to run a flight whose pacing mode was not explicitly supplied, rather than defaulting to fastest delivery. A missing pace is a configuration error, not a preference.
Alert on effective CPM and on dark days, not on budget consumption. Budget consumption is conserved by construction here -- it is the metric that looked BEST in the worst arm -- and effective CPM is the one that moved from 15.14 to 19.30.
If you do obfuscate, keep member renaming narrow and exclude the flight configuration and the inventory records. Renaming identifiers, renaming globals and control-flow transformation are all unaffected; the base column measured every area on five presets and all twenty-five were byte-identical. Pacing logic that ships inside a server-rendered application, such as a Next.js app, needs the same exclusion on both the server and the client bundle.
How this was measured
A grounding harness drives the protection engine directly. Every sample is deterministic, the unprotected original is run twice and required to reproduce itself, and the simulated pacing library is copied in unprotected because an installed dependency is not rebuilt with your bundle.
Every option key and record field was deleted from the unprotected file one at a time first, to establish that it moves the output at all. Three of the ten option keys in this area are inert alone and were declared as such before the run, because an inert arm that was predicted is a held prediction and an inert arm that was not is a blind one.
The base column ran all five areas on five presets and all twenty-five verdicts were byte-identical to the unprotected run. Further reading on what protection buys and does not buy in bought media: obfuscating ad tech JavaScript.
Frequently asked questions
Did obfuscation on its own break budget pacing?
No. Five presets -- the ES5 default, the modern target, the emit gate's option set on both targets, and the string transforms -- all produced output byte-identical to the unprotected run across all five areas measured this pass.
What actually broke it, then?
Member renaming with a pattern that reached the names the pacer is made of: the pacing mode, the flight length, the daily ceiling and floor, the catch-up fraction, and the record fields on the inventory price curve.
How can the budget be fully spent and the campaign still be worse off?
Because inventory is priced on a rising curve. Spending the same money over twelve days instead of fourteen means buying higher up each day's curve. The failing arm spent 140,000.00 against the correct run's 138,012.11 and bought 764,111 fewer impressions.
Which metric would have caught it?
Effective CPM, which moved from 15.14 to 16.76 and to 19.30 in the widest arm, and the count of dark days. Budget consumption would not have: it is conserved by construction and it looked better in the failing run.
Why were three of the options inert on their own?
Because an evenly paced day spends 7.14 per cent of the budget, which sits inside the ten per cent ceiling and above the two per cent floor, so none of them is ever consulted. They become load-bearing the moment the pace itself is lost, and the union delivers on eight days of fourteen.
Which arm was loud?
Renaming the flight length. The flight collapsed to a single day and left 126,000.00 of 140,000.00 unspent, which is on the front page of every dashboard the next morning.
What is the safest way to use member renaming here?
Keep the pattern narrow and explicit and exclude any options object carrying pacing modes, flight lengths or delivery ceilings, along with the inventory records they are evaluated against. Renaming identifiers and control flow is unaffected by all of this.
Related reading