← Back to the garden

From First Visit to Experiment

10 min read

You launch a landing page. People visit. Some create accounts. Somebody asks which campaign worked, where everyone else disappeared, and whether changing the signup button helped.

You have a page view count and a strong personal preference for the new button.

We can do better. Here is the architecture I would start with, from the first anonymous visit to an experiment whose result means something.

One Event, Two Destinations

Use Segment as the collection and routing layer. The application sends events there, and configured connections forward them to the tools that need them.

CODE
Browser and backend events
            ↓
         Segment
          ├→ Mixpanel: funnels and dashboards
          └→ BigQuery: history and SQL analysis

Mixpanel is where the product team investigates behaviour: how visitors become users, where a funnel loses people, and which groups return. Boards keep related reports together. For a business product, organization level analysis also needs an explicit account ID and group configuration, not just a collection of user profiles. Mixpanel's Segment integration.

BigQuery is the data warehouse: keep event history and join it with things such as billing records and campaign spend. Set retention deliberately. “Long term storage” is a configuration decision, not a promise that no table will ever expire.

The two destinations do not receive data in one shared transaction. Segment's standard BigQuery warehouse connector syncs on a schedule, and its raw tables can contain duplicates. Use appropriate deduplicated views or your own validated models, and check their date coverage when querying older history. Segment's BigQuery documentation.

This post describes an architecture, not a requirement to install three tools before launching your first page. Start with the questions you actually need to answer.

Meet the Anonymous Visitor

A visitor has not signed up yet, but we still need to connect their page visit with their next action.

Segment's Analytics.js generates an anonymousId and attaches it to events. I would let the SDK own that ID. With its normal persistence enabled, it uses browser storage, including cookies and local storage, to recognize later visits in the same browser. We do not need to invent a second cookie with a competing idea of who this visitor is. Segment identity documentation.

It is an identifier, not an API key, login credential, or proof that two events came from the same human.

Cleared storage, a different browser, or a new private browsing session can produce a new identity. One person can therefore have several anonymous IDs. Report anonymous visitor conversion with that limitation in mind; do not quietly rename it “the exact percentage of humans who signed up.”

Connect the Signup

Once signup succeeds, identify the visitor with the stable user ID from your database.

For the browser snippet API, the intent is this:

JAVASCRIPT
// After the backend confirms signup:
analytics.identify('user_482');

Use an immutable internal ID rather than an email address that can change. The identify call carries the connection between the existing anonymous identity and the known user. Segment's anonymous activity guide.

The downstream setup matters. Configure the Mixpanel destination for the project's identity model and test the journey from anonymous page view to authenticated event. In Simplified ID Merge, an event containing both the device ID and user ID establishes their relationship. BigQuery also needs an identity mapping in your reporting model; its historical anonymous rows do not all rewrite themselves when someone signs up. Mixpanel identity documentation.

For the actual Signup Completed business event, I would use the backend after the account transaction commits. Include the authenticated user ID and the browser's consented anonymous ID so the event can join to the earlier visit. Use a durable outbox if losing that event would materially affect your reporting.

Do not also count an independently emitted browser event as a second completed signup. Clicking the button and successfully creating an account are different events.

Reset analytics identity on logout, especially on shared devices. On another browser, identify after login again. That can connect the activity observed there; it cannot recover anonymous visits whose identifying storage was already lost.

Give Events a Clear Contract

Before making a dashboard, agree on a small event vocabulary:

  • Landing Page Viewed: the visitor reached the page.
  • Signup Started: the visitor began the form.
  • Signup Completed: the backend created the account successfully.
  • Project Created: the user reached an initial useful outcome.

Keep identity, event time, and a stable event identifier consistent across the pipeline. Add properties that answer a question, such as campaign, experiment variant, or account ID.

Retries should reuse the original event identity. Verify its mapping into each destination's deduplication fields; Mixpanel's Actions destination exposes an Insert ID for this purpose. Sending a new random ID for every retry defeats that protection. Segment's Mixpanel Actions reference.

This is a nice place to use the shared libraries from the monorepo post: standard event names, types, and wrappers can live together. A shared type helps prevent spelling mistakes. It does not prove that the event fires at the right moment.

Campaigns Need Context

A campaign URL might look like this:

CODE
https://example.com/?utm_source=linkedin&utm_medium=paid_social&utm_campaign=launch&utm_content=benefit_cta

The source identifies the channel, the medium describes the traffic type, the campaign names the initiative, and content can distinguish creatives.

Analytics.js reads standard UTM parameters into context.campaign on events from that landing URL. Use the actual parameter names, such as utm_source, rather than a nearly identical spelling that your future dashboard will not find. If you need a custom campaign ID, define its collection and destination mapping explicitly. Segment's UTM tracking guide.

An important detail: that campaign context is not automatically persisted forever. Once the visitor navigates to a URL without the parameters, later events may not contain them. Preserve attribution deliberately or join the original landing event to the conversion through identity.

Decide whether you are measuring the first campaign that brought someone in, the last qualifying campaign before conversion, or another agreed model. Define the attribution window too.

To calculate paid acquisition cost, bring in campaign spend for the same scope and divide it by attributed new paying customers. Dividing by signups gives cost per signup instead. Neither number tells you, by itself, how many conversions would have happened without the ad.

Ask Before You Experiment

An A/B test is a comparison between assigned experiences. It is not “ship two buttons and pick whichever line looks happier.”

Before configuring anything, answer these questions:

  • What problem are we trying to solve? Visitors reach the landing page but few create an account. What evidence suggests where they struggle?
  • What is our hypothesis? “Explaining the benefit in the signup button will increase completed signups because the next step feels more useful.”
  • What exactly are A and B? A says “Sign up.” B says “Start planning for free.” Keep unrelated changes out of the experiment.
  • Who should enter the experiment? New visitors who are signed out? Everyone? Define eligibility, traffic split, the unit of assignment, and how visitors keep their variant.
  • What metric determines success? For example, the percentage of exposed visitors who complete signup within seven days. Define the denominator and conversion window upfront.
  • How will we measure it reliably? Can we record which variant visitors actually saw and connect that exposure to a completed signup?
  • What could get worse? Watch signup errors, page speed, and whether new users start using the product. These are the guardrail metrics.
  • How much evidence do we need? Choose the smallest improvement worth shipping, required sample size, and duration. Decide beforehand what means ship B, keep A, or call the result inconclusive.

That last option is a real outcome. A small experiment is allowed to tell us that we still do not know.

Flags Choose the Experience

Mixpanel Feature Flags can assign the UI variant, while Mixpanel Experiments measures its impact. For this landing page test, choose an assignment key that stays stable for the anonymous visitor. For a test of a shared workspace feature, assigning by account may be more appropriate. Mixpanel's feature flag guide.

Next.js then uses that assignment to render the button. Segment carries the resulting measurement events; it does not choose the variant.

Keep the assignment consistent when someone signs up. If the flag suddenly switches from an anonymous identifier to a user identifier, the same visitor can land in a different bucket halfway through the journey. Also align the identity used by the flag SDK with the one used by events forwarded through Segment.

For a client rendered test, show a stable loading state until the assignment is ready. For a server rendered test, carry the same evaluated assignment into the client. Do not serve everyone cached HTML for A and then flash B after hydration. Keep personalized variants out of a shared cache entry.

If evaluation fails, render a safe fallback and record that it was a fallback. Do not pretend it was a randomized assignment. Feature flags control presentation; they do not replace server authorization.

Record What They Actually Saw

Assignment means the system chose B. Exposure means the application actually showed the B experience. Fetching flags for a page someone never visits should not count as seeing that page.

After the relevant UI is displayed, send the event through Segment with the experiment key and variant. This is an illustrative event call, not a complete React implementation:

JAVASCRIPT
analytics.track('Signup CTA Viewed', {
  experiment_key: 'signup_cta_v1',
  variant: 'benefit',
  assignment_source: 'network',
  page_path: '/',
});

The SDK attaches the current anonymous and known identity fields. Keep the same experiment context available when the backend records the conversion, or join the conversion to its earlier exposure within the defined window.

There is a small SDK trap here: Mixpanel's web flag getters automatically emit $experiment_started. If you also send a Segment event, keep it a distinct UI visibility event, as above. Do not accidentally create two copies of the same experiment exposure. Configure reports to use the intended exposure event, and evaluate the flag only where the experience will be used. Mixpanel's web flag implementation.

A React rerender is not another person seeing the experiment. Define whether you count the first exposure per visitor and experiment, or another explicit unit, then deduplicate accordingly. Test navigation, refreshes, signup, and logout before trusting the funnel.

Check the Measurement First

Before celebrating B, verify that the experiment received roughly the intended traffic split, both variants emitted events correctly, and conversions had time to mature through the full window.

Keep bot and internal traffic rules consistent. Monitor event failures and warehouse sync delays. Investigate differences between Mixpanel and BigQuery using the same event definitions, identity joins, time zone, and deduplication rules.

Do not stop a conventional fixed duration test simply because the chart briefly turns green. Follow the decision rule you chose, or use an analysis method designed for repeated monitoring. If there is not enough traffic, report the uncertainty rather than turning a handful of signups into a universal law of button copy.

Finally, collect only what you need, respect analytics consent, and scrub sensitive fields and URL parameters. Passwords, auth tokens, and private message content do not belong in a funnel. Browser analytics will also miss some activity because of blockers and storage restrictions. A server event does not make that measurement gap disappear, and it should not be used to bypass a user's choice.

The result should be a traceable story: someone arrived from a campaign, saw an experience, created an account, and reached something useful. If we can explain how each connection was measured, the dashboard becomes useful evidence instead of very colourful confidence.