· 16 min read

A/B Testing in Mobile Apps: How to Avoid Costly Mistakes

An A/B test often looks like a simple task: build two versions of a screen, split the audience in half, and see which one performs better. In practice, a single mistake in experiment design or analytics leads the team to make a product decision based on data that measured something other than what they intended.

The cost of that mistake isn’t just the team’s time. If a test falsely shows a winner, a variant that is no better than the current one, and sometimes worse, gets rolled out to 100% of your audience. The promised growth never shows up in revenue, and any conversion loss shows up there too, not in the experiment report. If the test turns out to be broken after launch, it has to be restarted: that’s several weeks lost, or even months for apps with low traffic.

Two app windows compared in an A/B test: variant A wins with 72%, variant B loses with 22%
Not sure your A/B tests measure the right thing?Tell us how your experiments are set up. We’ll show you where the results can go wrong.
Get my estimate

This article isn't about any particular experimentation or analytics platform. We've collected lessons from A/B tests we ran in mobile applications across our projects: how to design an experiment, where the reliability of results is most often lost, and what to check before launch. It's written for founders, product owners and product leaders who make decisions based on test results. As engineers, our responsibility is not only to implement different variants but also to make sure that the experiment measures what the team thinks it measures.

Why A/B testing matters for your business

Let's imagine you're running an online business. You're selling products, operating in a competitive market, and there are plenty of other companies offering similar things. You have to compete for users' attention, keep them engaged, and ultimately convince them to buy from you.

In this environment, every percentage point matters. A small improvement in conversion rates or customer retention can have a significant impact on your revenue.

A simple example: 20,000 users start checkout every month, and the average order value is $60. A 4-percentage-point increase in conversion means 800 additional orders and around $48,000 in extra revenue per month. The same math works the other way: if the "winning" variant actually lowers conversion by 2 points, you lose 400 orders, or $24,000 every month.

Naturally, you want to understand how users interact with your application. Do they find it easy to use? Do they complete purchases? At what point do they drop off? Do they come back?

That's where analytics comes in. It gives you visibility into what's happening in your application: user behavior, key metrics, bottlenecks, and trends. But here's the thing: knowing what's happening doesn't necessarily tell you what to do about it.

Let's say you notice that the number of purchases has been steadily declining. You can see the numbers going down, but what is actually causing the decline? Maybe the checkout process is too complicated. Maybe users have trouble finding the products they're looking for. Maybe a particular UI change has made the experience less convenient. Or perhaps the problem has nothing to do with the interface at all. (We wrote more about finding the cause of a decline in Why Your Mobile App Is Underperforming and How to Diagnose the Cause Before Spending More.)

This is where A/B testing can help. Instead of rolling out a change to everyone and hoping for the best, we can experiment. We start with a hypothesis, implement the proposed change for a specific group of users, keep another group on the existing experience, and then analyze the results.

For the business, this means three things:

  • product decisions are based on how users actually behave, not on opinions inside the team;
  • a risky change is first seen by a limited part of the audience;
  • only changes whose effect has been tested on real users make it to production.

What makes a good experiment?

Start with a hypothesis

Before starting implementation, we need a clear hypothesis. In other words, what do we believe will happen, and why?

Let's take the example we'll follow throughout this article. We want to reduce the number of checkout steps from four to two. Our hypothesis is that simplifying the process will reduce friction, make the purchasing journey faster and more enjoyable, and encourage more users to complete their purchases. Ultimately, we expect this to increase our conversion rate.

CHECKOUT EXPERIMENT / 01 ILLUSTRATIVE EXAMPLE Same checkout. Fewer pages. Variant A collects one decision per page. Variant B brings related decisions together. A Four-step checkout First page 9:41 ‹ Cart Checkout STEP 1 OF 4 Address Shipping Payment Review Shipping address Where should we send your order? Full name Jamie Taylor Street address 24 Market Street City Springfield ZIP code 62701 Country United States 2 items Subtotal $84.00 Continue to shipping → Secure checkout Address → Shipping → Payment → Review B Two-step checkout First page 9:41 ‹ Cart Checkout STEP 1 OF 2 Delivery Payment & review Delivery details Where and how should we send your order? Full name Jamie Taylor Street address 24 Market Street City Springfield ZIP code 62701 Country United States Shipping method Standard delivery 3–5 business days Free Express delivery 1–2 business days $6.00 2 items Subtotal $84.00 Continue to payment & review → Secure checkout Delivery → Payment & review Conceptual app visual mockups. Fictional customer details. Same fields, shipping choices, pricing, and primary button text.
Variant A (Control) asks for one decision per page. Variant B (Treatment) puts the address and shipping method on one page.

Before writing UI code, we define exactly what's changing, who can participate, and how success will be measured. The definition should be specific enough that another engineer can implement the same experiment without guessing. In our projects, this is a short document that the product owner and the team agree on before work starts. It saves weeks: arguing about what counts as success after launch is too late.

ItemExample decision
HypothesisCombining address and shipping reduces checkout friction and increases completed purchases.
Unit of assignmentOne stable customer account.
Eligible populationSigned-in customers with a cart containing an item eligible for delivery and an app version that supports the experiment.
Variantsfour_step (Control) and two_step (Treatment).
Primary metricPurchase conversion: users with complete_purchase within seven days after start_checkout, divided by users assigned at start_checkout.
GuardrailsPayment errors, checkout crashes, refund rate, support contacts, and checkout latency.
ExposureThe first time the assigned checkout variant actually becomes visible.
Test durationDetermined by the sample size calculation, at least two full weeks.
Conversion windowSeven days after start_checkout. Users assigned in the last seven days of the test are counted until their window closes.

Guardrail metrics deserve a separate mention. A new checkout can raise conversion and at the same time increase payment errors or support contacts. Without guardrails, a "successful" test like that is easy to roll out to everyone.

Control and treatment groups

To evaluate an experiment, we need two groups: the control group keeps using the existing experience, and the treatment group receives the proposed change.

Let me explain this with an analogy I like to use. Imagine a hospital with 100 patients, and we want to test a new medicine. Only 40 patients give their consent to participate. We randomly divide them into two groups and give each patient a mark on their medical chart: yellow for the control group on the existing medicine, blue for the treatment group on the new one. The remaining 60 patients shouldn't have either of these marks.

The same principle applies to our application. Only users who actually belong to the experiment receive the variant attribute. Users outside the experiment should either have no such attribute or have a clearly distinct value, such as not_in_experiment. The important thing is to avoid mixing users who aren't participating in the experiment with our actual control group, otherwise, the comparison loses its meaning.

Keep eligibility, assignment, exposure, and outcome separate

These four concepts are easy to confuse:

  1. Eligibility: the user is allowed to participate.
  2. Assignment: the system selects a variant for the eligible user.
  3. Exposure: the user actually sees the tested experience.
  4. Outcome: the user completes or abandons the measured action.

For example, a user might be assigned to the treatment group but never navigate to checkout. They belong to the treatment group, but they haven't been exposed to the new checkout experience.

We should also distinguish users who couldn't be assigned because of a configuration failure from users successfully assigned to control.

When these concepts get mixed up, the effect of the change is either diluted or artificially inflated. Below we'll look at how we keep them apart in the implementation.

Experiment assignment: who gets which variant?

Random assignment and traffic share

Users should be assigned randomly to control and treatment groups to avoid selection bias. A common approach is a 50/50 distribution, which is generally the most statistically efficient for comparing two variants. If a change is risky, we can limit the share of participants: for example, we include only 10% of eligible users in the experiment and split them equally between Control and Treatment. The remaining 90% simply don't participate.

Scenario A: 10% of users enter the experiment, split 50/50 500 Control (5%) 500 Treatment (5%) 9,000 Outside the experiment ✓ Equal allocation within the experiment Scenario B: 100% of users enter the experiment, split 90/10 9,000 Control (90%) 1,000 Treatment (10%) ! Unequal allocation: slower to reach a reliable result
Limiting traffic to 10% lowers the risk. With an unequal split such as 90/10, the smaller group collects data slowly, so the test needs more time or more users to reach a reliable result.

This limits the risk, but we pay for it with time, and that cost is easy to underestimate. Let's say 1,000 users start checkout every day. At 10% of traffic, each group gets 50 users a day:

Effect to detectUsers needed per groupDuration at 10% of trafficDuration at 50% of traffic
Conversion grows from 40% to 44%≈ 2,400≈ 48 days≈ 10 days*
Conversion grows from 40% to 42%≈ 9,500≈ 190 days≈ 38 days

Calculated for a two-sided test at a 5% significance level and 80% power. * Even if the sample is collected faster, the test runs for at least two full weeks to account for weekly seasonality.

That's why we choose the traffic share together with the product owner, based on two questions: what minimum effect matters to the business, and how long the business is willing to wait for an answer.

Assignment should be stable

Once a user has been assigned to a variant, they should stay in that group throughout the experiment. Imagine a user gets assigned to Treatment today but receives Control tomorrow. This makes our results unreliable.

Most experimentation platforms handle variant assignment using a process called bucketing: they use a user identifier to consistently determine which group the user belongs to. However, we shouldn't just assume this works correctly. We verify that users stay in the same variant across sessions and that the variant doesn't change when they log in, log out, or switch accounts. If we're testing purchasing behavior, an account-level identifier is more appropriate than an installation identifier.

Analytics: designing measurement together with the experiment

The important thing is to design analytics together with the experiment, rather than adding tracking as an afterthought. If the events are set up incorrectly, there's no fixing it after launch: the data for the test period is lost, and the experiment has to be restarted.

Design events around the user journey, not screens

For our checkout experiment (four_step as Control, two_step as Treatment), we track six milestones:

EventWhen should it be triggered?
start_checkoutThe user enters the checkout flow.
complete_addressThe user successfully saves a valid address.
select_shippingThe user successfully selects a shipping method.
select_paymentThe user successfully selects a payment method.
submit_orderThe user submits the order after validation.
complete_purchaseThe backend confirms that the purchase is completed.

In addition to these funnel milestones, we record checkout_exposed when the assigned checkout variant becomes visible. This event is kept out of the purchase funnel calculation.

0k 2.5k 5k 7.5k 10k 10,000 100% 8,200 82% 7,300 73% 6,400 64% 5,300 53% 4,200 42% Start checkout start_checkout Complete address complete_address Select shipping select_shipping Select payment select_payment Submit order submit_order Complete purchase complete_purchase
The checkout funnel: unique users per milestone, in order, within seven days of start_checkout. Illustrative numbers.

You can measure conversion using just two events, start_checkout and complete_purchase. However, from experience, I can say that when something goes wrong, you'll want to understand what happened in between. Separate submit_order and complete_purchase events show the difference between "users didn't like the new checkout" and "the new checkout has technical issues." And even though address and shipping are handled on the same screen in the treatment variant, we still track complete_address and select_shipping separately: this way, the variants can be compared regardless of how the UI is structured.

Screen-view events are useful for understanding navigation behavior and investigating UI-specific problems. However, we shouldn't use them as our primary conversion funnel: the two variants have different screen structures, and the comparison would be incorrect.

Define a simple analytics event contract

Before implementing tracking, we agree on which properties each event needs:

PropertyPurpose
event_nameIdentifies the user action.
variantIdentifies the assigned experiment group (four_step, two_step, or not_assigned).
experiment_idIdentifies the experiment and its version.
user_idIdentifies the customer account.

The important experiment-specific property is variant. Don't assume that the analytics SDK automatically knows your application's account ID: the user needs to be identified explicitly.

Track the outcome, not the button tap

In our projects, events aren't triggered by button taps but by the successful completion of a business operation. Tapping Continue doesn't necessarily mean that the address was successfully saved, so complete_address is emitted only after the address has been saved. And complete_purchase is triggered only after the backend confirms the purchase, not simply when the user taps the Pay button.

Separate assignment from exposure

We assign the variant immediately after the user taps Start checkout, before rendering the checkout UI, and attach it to the start_checkout event. This helps us avoid initially rendering the default variant and then switching to another one.

We track exposure separately, when the assigned checkout variant actually becomes visible. A screen on Android can be recreated, so the exposure event is sent exactly once per user and experiment.

What happens when assignment fails?

Experiment configuration can fail to load: the user is offline, the request times out, or the configuration is missing or invalid. These failures shouldn't break the application experience:

  • Safe fallback. The user sees the existing checkout, but isn't automatically classified as Control: we mark them as not_assigned and exclude them from the experiment comparison. Otherwise, the control group gets contaminated by users who had network problems, and the result is skewed.
  • Cached assignments. If the user was successfully assigned during a previous session, we reuse the cached assignment when offline.
  • No UI flickering. The assignment is resolved before entering the tested flow, rather than switching a screen the user is already interacting with.

The main principle is that experiment infrastructure should never become a single point of failure for the application.

Mixed app versions

The minimum app version that contains the experiment code is checked during eligibility evaluation: users on older versions aren't assigned to either group.

How to read A/B test results

The funnel and the right denominator

We define the analysis rules in advance: count unique users rather than raw event occurrences, group them by their assigned variant, and require milestones to occur in the correct order.

Everyone assigned at start_checkout stays in the denominator, even if they never reach payment. Only users who complete a purchase within the conversion window go into the numerator.

0k 2.5k 5k 5,000 100% 5,000 100% 4,000 80% 4,200 84% 3,500 70% 3,600 72% 3,000 60% 3,400 68% 2,500 50% 2,800 56% 2,000 40% 2,200 44% Start checkout start_checkout Complete address complete_address Select shipping select_shipping Select payment select_payment Submit order submit_order Complete purchase complete_purchase A · four_step (Control): 40.0% conversion B · two_step (Treatment): 44.0% conversion
Both variants are measured on the same six milestones, with the assigned population at start_checkout as the denominator. Illustrative numbers.
Metricfour_step (Control)two_step (Treatment)
Assigned users at start_checkout5,0005,000
Users with complete_purchase2,0002,200
Conversion rate40%44%

Difference: +4 percentage points. With 5,000 users per group, this difference is statistically significant (p < 0.001), so it can't be explained by chance. In money terms, using the example from the beginning of the article, that's about $48,000 in extra revenue per month.

Notice that the percentages use the original assigned population as their denominator, not the users who reached the previous funnel step, and not the users who saw the variant. With the wrong denominator, a test can show growth where there is none. We use the exposure event for diagnostics: if there are noticeably fewer exposures than assignments, that signals a technical problem.

Sample size and statistical significance

Let's say 4 out of 10 users completed a purchase in the control group and 6 out of 10 in the treatment group. Looks like a great improvement, right? But with such a small number of users, a few additional purchases could completely change the results. Statistical significance helps us understand whether the difference we're seeing is likely to be a real effect of our change or just a result of random variation. For a simple explanation, see the Scribbr guide "Statistical Significance: A Simple Introduction".

Peeking

Imagine we're running an experiment for two weeks, but after the first day, we see that the treatment group is performing better and decide to stop the experiment. The problem is that repeatedly checking results and stopping as soon as we see statistical significance can increase our chances of a false positive. That's why we define the experiment duration, analysis approach, and required sample size in advance.

Running overlapping experiments

Let's say we're testing a new product details page layout on the same user journey as the new checkout. Their effects might interact, and we may not be able to attribute the observed change to one experiment alone. In such cases, we separate overlapping experiment populations or design the analysis to account for interactions.

The most common A/B testing mistakes we see

MistakeWhat it leads toWhat it means for the business
Users outside the experiment end up in the control groupThe effect is diluted, the comparison is invalid.The real effect isn't visible, and a good change gets rejected.
Assignment failures are counted as ControlThe control group is skewed by users with technical problems.The new variant looks better than it is.
Assignment isn't stable across sessions or accountsUsers see both variants, the results are invalid.The test has to be restarted.
Events are triggered by button tapsThe funnel shows intentions, not actual results.Payment errors look like "user behavior."
Conversion is calculated from screen viewsVariants with different screen structures can't be compared.The conclusion depends on the layout, not on behavior.
Assignment is treated as exposureThe analysis includes users who never saw the change.The effect is underestimated, and the decision gets delayed.
The test is stopped at the first good resultHigher risk of a false positive and a failed release.A variant that doesn't actually work is rolled out to everyone.
Parallel tests on the same user journeyThe effect can't be attributed to a specific change.Resources go into a change that had no effect.
Older app versions aren't accounted forThe groups include users for whom the experiment doesn't technically work.Part of the data is noise, and the conclusions are skewed.
Nobody checked that the groups actually split 50/50 (SRM)An imbalance points to a technical error in assignment.The decision is based on a technically broken test.

Recognize your project? If you see at least two of these mistakes, the results of your past tests shouldn't be trusted by default. As part of a mobile app audit, we check events, assignment logic and discrepancies between analytics and backend data, and then show you which conclusions from past tests you can rely on and which need to be re-checked.

Pre-launch A/B test checklist

We go through these questions with the team before an experiment starts. They also help when choosing an experimentation platform:

Ready to launch?

How we run experiments in Gilzor projects

When developing mobile apps, we build analytics and experimentation infrastructure into the feature design stage and define the hypothesis and metrics together with the product owner. Assignment scenarios (logging in and out, working offline, older app versions, configuration failures) are tested as part of QA, before users see the experiment.

If you're only planning your first tests, we'll join at the design stage: we'll set up events, assignment and failure handling so that your very first experiment gives you a result you can trust.

If your tests are already running but you don't trust the results, or the numbers in your analytics don't match your backend data, start with a mobile app audit: we'll check your instrumentation, events and assignment logic.

Built by Gilzor

Results we’ve shipped

70+products launched
98%delivered on time
85%clients come back
Art Scherbakov, Co-FounderAndrew Laminsky, CTOYuri Rudenya, Head of Mobile Development at GilzorAlena Timofeeva, Product Marketing Lead

Talk to the people who build it. Tell us about your project and get a free estimate of scope, timeline and cost.

See how we’d approach yours

Key takeaway

Reliable experiment analytics starts with a well-designed event contract. Different platforms may offer different features and implementation approaches, but the key things remain the same: stable user assignment, consistent analytics, separate exposure tracking, and a correct calculation of the result.

The goal isn't to collect as many analytics events as possible. It's to make sure that, when the experiment ends, we can confidently answer the question we started with: Did simplifying checkout actually help more users complete their purchases?

No sales pitch

Want to check whether your tests measure what they should?

Send us the plan for your next experiment, or tell us where the numbers don’t add up.

Next, a few optional questions so the first call is useful. We use your details only to reply to your request. Privacy Policy

Tatiana Bulavko
Written byTatiana Bulavko

Tatiana Bulavko, Android Developer at Gilzor.

Gilzor · Mobile App Audit partner

Need a team for your app?

95%referred by business partners
70+successful launches
85%repeat business
98%delivered on time

The team behind them

Art Scherbakov
Art ScherbakovCo-Founder
Andrew Laminsky
Andrew LaminskyCTOLinkedIn
Yuri Rudenya
Yuri RudenyaHead of Mobile Development at GilzorLinkedIn
Alena Timofeeva
Alena TimofeevaProduct Marketing LeadLinkedIn
Tell us what you’re buildingOptions, a rough cost and timeline for your project. No commitment.

More from the blog