Lesson 8 of 14
GTM and gtag.js look like competing products, but they're two different front doors onto the same underlying mechanism — the global dataLayer array — and picking between them is really a question of who needs to change tags after launch.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
Every marketing team eventually asks for "the Google tag" — GA4, a conversion pixel, a remarketing tag, all of it. There are two legitimate ways to wire that up, and interviewers care that you can name the actual tradeoff, not just the product names:
gtag('config', ...) / gtag('event', ...) from your own code whenever something worth tracking happens. Adding a new tag or changing a config value means editing code and shipping a deploy.Both ultimately push events onto the same mechanism: window.dataLayer, a plain JavaScript array. gtag() is itself just a thin wrapper that calls dataLayer.push(arguments) — when you call gtag('event', 'purchase', {...}), that's arguments being queued onto dataLayer, and the loaded gtag.js library drains the queue. GTM's container script attaches its own listener to that same array. This is why you'll sometimes see raw dataLayer.push({...}) calls in a GTM-based site's code — a developer pushing structured data (say, ecommerce info on a purchase confirmation page) for marketers to pick up as GTM variables, without the developer needing to know or care what tags GTM ultimately fires from it.
The GTM container snippet is deliberately split into two pieces that go in two different places:
<!-- 1. In <head>, as early as possible -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXXX');</script>
<!-- End Google Tag Manager -->
<!-- 2. Immediately after the opening <body> tag -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXXX"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
The <head> half loads gtm.js and starts the timer GTM uses for page-load-timing variables — the earlier it runs, the fewer early page events it misses. The <noscript> half is a fallback <iframe> for visitors with JavaScript disabled: an <iframe> isn't valid inside <head>, so that half has to live in <body>, right after it opens, so it renders before the rest of the page.
The plain gtag.js alternative, for comparison — one script tag, no container to publish separately:
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
</script>
dataLayer.push(). Interviewers listening for "do you actually understand this or just copy-pasted it" will ask what gtag() does under the hood; "queues an event onto dataLayer" is the real answer.<iframe> fallback that's structurally invalid inside <head> — it's not arbitrary convention.dataLayer.push({...}) call in application code is a legitimate, common pattern under GTM — it's how developers expose structured data (ecommerce values, user properties) to marketers configuring tags, without either side needing to know the other's implementation.dataLayer instead of pushing to it is a real, repeatable bug — it silently detaches GTM's listeners and every downstream tag stops firing with no error anywhere."Why would a site use GTM instead of just calling gtag() directly?" is a question that separates candidates who've configured a container from those who've only ever seen the snippet — the real answer is about who gets to change tracking without a deploy, not which tool is "better." Being able to trace both back to the same dataLayer array, and explain why the noscript half of the GTM snippet can't live in <head>, signals you've actually debugged a tagging setup rather than pasted one in once and moved on.