Tracking gets installed the fastest way available, which is a snippet pasted into a theme template. That decision is invisible for months and then produces three separate problems, none of which announce themselves.
It disappears
Theme code does not survive a theme. An update, a switch to a child theme, a redesign, a handover, and the counter is gone. Nothing errors. The dashboard just goes quiet, and by the time anyone notices you have lost a month of data you cannot backfill.
The same applies to the second counter someone added last year, in a different template file, which nobody can find now.
The fix is a plugin with a settings screen: an empty field means no output at all, which also gives you a clean way to keep a staging copy silent without touching code.
It competes with the page
Loaded normally, an analytics script asks for the browser's attention during the exact seconds a visitor is deciding whether your site feels fast. You are spending page-load responsiveness, which is itself a ranking signal, on measuring page-load responsiveness.
Wait for the browser to be idle instead:
if ('requestIdleCallback' in window) {
requestIdleCallback(loadMetrika, { timeout: 2000 });
} else {
setTimeout(loadMetrika, 2000);
}
The timeout matters. Without it, a page that never goes idle never loads the counter, and you have quietly stopped measuring your busiest pages, the ones that matter most. The fallback branch matters for the same reason: requestIdleCallback is not everywhere, and a feature check without a fallback is a silent hole in your data rather than a graceful degradation.
It double-counts
Snippets get pasted twice. Someone adds it to the theme, someone else adds it through a tag manager, and now every session is counted twice. Traffic appears to jump, and the jump is indistinguishable from a real one until somebody checks the HTML.
Guard the insertion:
for (var j = 0; j < document.scripts.length; j++) {
if (document.scripts[j].src === r) {
return;
}
}
Five lines. Compare that against a quarter of decisions made on doubled numbers.
Escape everything, including the booleans
Counter ids and option values come from the database, and the database is edited by humans through a form. Escape the id, and normalise the booleans explicitly rather than interpolating whatever PHP had:
$clickmap_value = $clickmap ? 'true' : 'false';
PHP's false renders as an empty string inside a JavaScript object literal, which is a syntax error, which kills the whole script, including anything else that shares the file. This is a small detail that produces a total outage of measurement, and it is very hard to spot because the page still looks fine.
The free-form head block, and its cost
A settings field whose contents are printed into <head> covers site verification tags, ad platform snippets and one-off scripts. It stops routine marketing requests from being deploys, and it is genuinely useful.
Be honest about what it is: an administrator-only field that injects unescaped markup into every page. That is acceptable because administrators can already edit plugin files, so it grants no new capability. It stops being acceptable the moment a lower-privileged role can reach it, or the value comes from anywhere but a human typing into the admin.
Decide what you are collecting
Session replay records what visitors do on the page: movements, clicks, sometimes form input. Click maps and accurate bounce tracking are milder but still behavioural.
Making these toggles rather than flags buried in a snippet has a side effect worth naming: it makes the answer to "what are we collecting" a screen someone can look at, rather than an archaeology expedition through minified JavaScript. If your privacy notice has to describe this, and it does, then the configuration should be readable by the person writing it.
Related
Projects

CactusPlay - Game shop run by a vetted contractor network
Before: CIS buyers compared PlayStation and Xbox prices by hand across scattered sellers, and every order had to be filled by the founders themselves. After: a shop with a live catalogue and a CRM any vetted contractor with Turkish payment details could join to take orders and earn on them. Result: 20,000+ positions grabbed hourly, price changes reaching the shop and Yandex.Market untouched, and fulfilment no longer capped by one pair of hands.
Posts
Make delivery a consequence of payment, not a task someone performs
Digital goods delivered by hand are limited by working hours and protected only by attention. Moving delivery onto order events changes what the system guarantees, and the API is what stops a second channel becoming a second implementation.
Pricing a catalogue against a store you do not control
Reselling means your prices depend on someone else changing theirs. Fetching on page load takes your shop down with theirs; a nightly export goes stale. The middle path is a cache with a pricing strategy behind it.
A support widget that already knows who it is talking to
Pasting a chat widget into a theme template works until the theme updates, the settings need changing, or the widget adds a database query to every page. Three small decisions fix all of it.
Open for contract collaboration
I am available for contract-based collaboration. If you have an interesting project idea, schedule a call via Calendly.
Schedule a 30-min call