Implementing Robust A/B Testing and Feature Flags in Python
Overview of A/B Testing in Software Design
Building software without user feedback is like flying blind. You might assume a specific button color or menu layout works best, but until you measure real-world interactions, you are guessing. —also known as split testing—solves this by comparing two versions of a variable to see which performs better.

To implement this effectively, you need two core components: a mechanism to toggle between variants (Feature Flags) and a system to measure the results (KPI tracking). Integrating these into your code requires a clean design to ensure you aren't hard-coding logic that becomes a maintenance nightmare later.
Prerequisites
To follow this tutorial, you should be comfortable with:
- basics (classes, methods, and decorators).
- Working with JSON and environment variables.
- Basic GUI concepts (the examples use ).
- Familiarity with and the
requestslibrary.
Key Libraries & Tools
- : An open-source feature flagging and A/B testing platform that provides a remote API to manage feature states.
- : A product analytics tool used to track user events and calculate conversion rates.
- : Manages sensitive API keys via a
.envfile. - : Handles the HTTP communication with feature flag services.
Code Walkthrough: From Local Config to Remote Flags
Step 1: Local Feature Flags
Start by decoupling your UI from the logic. Instead of hard-coding a button, use a boolean flag. We can read this from a local config.json to avoid changing source code for every test.
@dataclass
class Config:
show_save_button: bool = True
def read_config_file() -> Config:
config_path = Path.cwd() / "config.json"
data = json.loads(config_path.read_text())
return Config(**data)
Step 2: Integrating GrowthBook
Local files don't scale when you have thousands of users. By using , you can toggle features remotely via an API. The logic remains the same in your GUI, but the source of truth moves to the cloud.
from growthbook import GrowthBook
import requests
def read_remote_config() -> Config:
api_key = os.getenv("GROWTHBOOK_KEY")
resp = requests.get(f"https://cdn.growthbook.io/api/features/{api_key}")
features = resp.json()["features"]
gb = GrowthBook(features=features)
return Config(
show_save_button=gb.is_on("show_save_button")
)
Step 3: Tracking User Interaction with Mixpanel
A/B testing is useless without data. When a user clicks a button in "Variant A," you must report that event. We use to capture these interactions. To keep the design clean, we pass a post_event callable to our UI class.
from mixpanel import Mixpanel
def post_event(event_type: str):
mp = Mixpanel(os.getenv("MIXPANEL_TOKEN"))
mp.track("user_id_123", event_type)
# In the GUI Class
def on_button_save(self):
self.save_logic()
self.post_event("save_button_clicked")
Syntax Notes and Patterns
- Dependency Injection: Notice how we pass the
post_eventfunction into the GUI class. This makes the code testable; you can pass a dummy lambda function during unit tests to avoid hitting the actual API. - Unpacking Dictionaries: Using
Config(**data)is a concise way to map JSON keys directly to data class attributes. - Lambdas for Defaults: If a user opts out of tracking, we replace the
post_eventfunction with alambda _: None. This preventsAttributeErrorswithout needing complexif/elsechecks throughout the UI code.
Practical Examples
- Phased Rollouts: Use a feature flag to enable a new search algorithm for only 10% of users to monitor server load before a full release.
- UI Simplification: Test if removing a secondary "Save" button increases the usage of the main menu, potentially decluttering the interface.
- Paywall Testing: Toggle different pricing tiers or trial lengths for different user segments to optimize revenue.
Tips & Gotchas
- Privacy First: Always ask for user consent before tracking data. If you operate in the EU, compliance is mandatory. Implement a simple opt-in dialog at the first launch.
- Clean Up: Feature flags are technical debt. Once an experiment concludes and a winner is chosen, remove the conditional logic and the flag from your code to keep the codebase maintainable.
- Timeout Management: When fetching remote flags, always set a timeout in your
requests.get()call. You don't want your application to hang indefinitely because a third-party service is down.
- 17%· products
- 11%· products
- 6%· technology
- 6%· technology
- 6%· products
- Other topics
- 56%

How to Support A/B Testing in Your Python Code
WatchArjanCodes // 26:21
On this channel, I post videos about programming and software design to help you take your coding skills to the next level. I'm an entrepreneur and a university lecturer in computer science, with more than 20 years of experience in software development and design. If you're a software developer and you want to improve your development skills, and learn more about programming in general, make sure to subscribe for helpful videos. I post a video here every Friday. If you have any suggestion for a topic you'd like me to cover, just leave a comment on any of my videos and I'll take it under consideration. Thanks for watching!