Indie developers treat analytics like tests: important, easy to skip, painful when missing. GA4 default pageview tracking is not enough once you ship auth, billing, and feature flags. You need a small event contract - signup_completed, trial_started, feature_activated - and a way to verify events after each deploy without opening four browser tabs. This is the setup solo developers use before they hire growth. The goal is not a comprehensive analytics platform. It is enough signal to answer the two questions that matter most at the indie stage: did this launch work, and is the product actually being used?
Start with an event contract in the repo
Before writing any gtag calls, create a TRACKING.md file at the root of your repo. List every event name you plan to fire, the user action that triggers it, any required parameters (like plan_type or referral_source), and the component or route where the call lives. This file takes 20 minutes to write and saves hours of debugging later. When you rename a button component, grep for the event name before merging. When a new developer joins, they have a complete map of what is being tracked and where. Most broken GA4 setups are refactors that dropped a gtag call silently, not misconfigured admin settings.
The minimal viable event set for indie projects
For a typical indie SaaS or side project, you need five events tracked from day one: page_view (automatic, no setup required), signup_completed (fires when a user successfully creates an account), trial_started (if you have a free trial flow, fires when the trial state is set), feature_activated (fires the first time a user completes your core activation action - the thing that predicts retention), and subscription_started or purchase_completed (fires when billing succeeds). Everything else can wait. These five events answer the funnel questions investors and retrospectives will ask: how many people landed, how many signed up, how many activated, how many paid.
Use GTM only if you need marketer access
Solo devs can fire events directly from application code with gtag or the GA4 Measurement Protocol. Calling window.gtag("event", "signup_completed", { plan_type: "free" }) from your post-signup handler is simpler than a GTM setup and has fewer failure modes. GTM becomes valuable when non-developers need to add tags without deploying code. If it is just you, own the instrumentation in code and document it in TRACKING.md. Either way, test in GA4 DebugView after every deploy. See /guides/how-to-set-up-ga4-event-tracking for the GTM path if you want marketer-friendly future flexibility.
Verify tracking in CI or post-deploy checks
A Playwright or Cypress smoke test that loads your staging signup flow and asserts the expected dataLayer push costs less than an hour to write and prevents weeks of silent data loss. The test checks that window.dataLayer contains a signup_completed event after the form is submitted. If the test fails, the deploy should not go to production. For teams that do not want to maintain browser automation scripts, ClimbPast /docs/mcp scans production URLs and verifies key events still fire - useful as a post-deploy check that does not require CI infrastructure.
Reading GA4 as an indie developer
Default GA4 reports are built for e-commerce teams. The Explorations section is where indie developers get useful data. Open a Free Form exploration with landing_page as the primary dimension and your key conversion event count as a metric. This shows which pages drive signups. Add session_source as a secondary dimension to see which referrers convert. Filter by your conversion event and sort descending to find your actual drivers rather than your highest-traffic pages, which are often different.
Ask product questions in plain English
After launch week, you need answers like which landing page drove the most signups last month or whether the redesign changed engagement rate on the pricing page. Building a custom Exploration for each of these takes 15 to 20 minutes. Conversational analytics on top of GA4 - /ai-analytics-tool - lets you type the question and get an answer in seconds. This keeps you in the editor and the codebase instead of the GA4 UI. Pair with /docs/mcp when signup_completed drops after a deploy, so you are not discovering broken onboarding three days later when you happen to check the dashboard.