I built my portfolio site with Claude Code. The whole thing. Stack decisions, component architecture, deploy config.
When Netlify confirmed the build and DNS propagated, the site was live in an afternoon.
First item on the post-launch list was analytics.
I’ve set up Google Analytics more times than I can count. Across client stacks, internal tools, side projects. And I’ve always done it the same way: Google Tag Manager as the container, GA4 configured inside it. Not the GA4 snippet dropped directly into the site.
That’s not a recent decision. It’s been my standard for years.
The reasons are practical. Every time you want to change something about your tracking later, you’re back in the code. New PR. New deploy. New context switch away from whatever you were actually working on.
GTM eliminates that. Completely.
Why GTM goes in first, every time
Google Tag Manager is a tag management layer. You drop one snippet of code into your site once, and after that every tracking decision happens in the GTM interface — not your HTML.
Want to add a LinkedIn Insight Tag next month? GTM change. Not a deploy.
A custom click event? GTM.
A Meta Pixel? GTM.
It’s worth the thirty extra minutes of setup upfront. The reasoning is the same as using a CRM instead of a spreadsheet. Not because you need all the features today, but because you’re building a foundation that doesn’t require a rebuild every time requirements change.
The tradeoff is two IDs instead of one, and about twenty minutes of extra configuration.
That’s it.
One-shot builds and the analytics gap
Here’s what I keep noticing about building with Claude Code: the launch energy is real. You describe the stack, the pages come together, Netlify connects, DNS propagates, and the site is live in an afternoon. It’s a different kind of productivity than anything I’ve worked with before.
And in that energy, analytics feels like a “later” problem.
I’ve watched this play out in client environments too, not just on personal projects. The site goes live. The tracking gets deferred. Three months later, when someone wants to add a conversion event, they’re back in the codebase making a change that has no business being a code change.
Don’t do that to yourself.
Where Claude Code came in
I’ve set up GTM before. I know what the process looks like. But I was building this site with Claude Code as my primary dev partner, and when it came time to add analytics, I described the setup I needed and let it run.
What came back wasn’t a generic tutorial. It was the specific answer for my architecture.
Claude Code knew immediately that Astro uses a single global layout file. Layout.astro. It wraps every page on the site. It didn’t tell me to “add the snippets to your layout file.” It told me which file, which location within that file, and why.
The <script> block in <head>. The noscript fallback immediately after <body>. The reason the noscript version exists: a 1x1 invisible iframe for JS-disabled browsers that captures basic page view data through a different mechanism. That context is what separates following a tutorial from understanding what you built.
It also flagged something I would have tripped over. GTM’s interface still shows old tag type names alongside the current ones. “Google Tag” is correct for GA4. “Universal Analytics” is the deprecated GA3 label. Both appear in the same menu. Claude Code called that out before I had to ask.
The whole setup took less time than it took me to finish my cortado. GTM container configured, GA4 property created, tag firing on page load, deployment pushed and live.
That’s the pattern I keep running into with Claude Code on technical setup work. It doesn’t just retrieve information. It understands the architecture you’re working in and gives you the answer that fits that context. The difference shows up most when the task is well-defined, the dependencies are specific, and the right answer requires knowing how the pieces actually connect.
The setup, without the noise
GTM gives you two code snippets when you create a container. The first goes in <head>, the second immediately after <body>. In Astro, both go in your layout file. src/layouts/Layout.astro, or whatever your global layout is called. Put them there once and they’re on every page.
Inside GTM, create a tag. Two decisions: tag type and trigger. Tag type is “Google Tag.” Not “Universal Analytics,” which is the deprecated GA3 label and still shows in the UI. The trigger is “Initialization - All Pages.”
The Measurement ID comes from GA4: Admin, then Data Streams, then your web stream, then Measurement ID. That’s your G-XXXXXXXXXX value. GA4 shows Measurement IDs in several places during setup. Grab it from the Data Streams page directly.
Publish the GTM container. Push the code to trigger your Netlify deploy. Open GA4 Realtime in one tab, load your live site in another. Watch the active users tick up.
Total cost: zero. GA4 is free. GTM is free.
What you’re actually building
The analytics part is the obvious deliverable. What you’re actually setting up is a clean separation between your site’s codebase and your tracking decisions.
That separation is invisible on day one. Nobody thinks about it when the site just went live and the list of things to do is still long.
Future You will notice.
The Future You who wants to add a conversion event six months from now without opening VSCode. Without a PR. Without a deploy. Without a context switch away from whatever you’re actually working on.
The thirty-minute decision you make today is the deploy you don’t make in six months.
Set up GTM first. Track everything else through it.
The same thinking applies across the stack — building clean separations between layers so future changes stay cheap. If you want to see how that plays out at a larger scale, the post-merger MarTech consolidation case study covers data foundations across global deployments.