<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Zayyad Muhammad Sani</title>
    <description>The latest articles on DEV Community by Zayyad Muhammad Sani (@zms).</description>
    <link>https://dev.to/zms</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1061401%2Fdf08a444-a875-4b55-a582-b6c46171f337.jpeg</url>
      <title>DEV Community: Zayyad Muhammad Sani</title>
      <link>https://dev.to/zms</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zms"/>
    <language>en</language>
    <item>
      <title>Beyond Environment Variables: When to Use Feature Flags (and Why)</title>
      <dc:creator>Zayyad Muhammad Sani</dc:creator>
      <pubDate>Thu, 07 Aug 2025 14:14:34 +0000</pubDate>
      <link>https://dev.to/zms/beyond-environment-variables-when-to-use-feature-flags-and-why-4eoh</link>
      <guid>https://dev.to/zms/beyond-environment-variables-when-to-use-feature-flags-and-why-4eoh</guid>
      <description>&lt;p&gt;When talking to devs in the software development community about feature flags, a question that often comes up is, “Can’t I just use environment variables for that?” It's a fair question; both can influence how an app behaves. But under the hood, they serve very different purposes. Let's break it down.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's an Environment Variable?
&lt;/h2&gt;

&lt;p&gt;Environment variables are key-value pairs used to supply configuration values to your application. They typically live in your app’s runtime environment. You can set environment variables through the command line, CI/CD pipelines, or hosting platform settings using a config file.&lt;/p&gt;

&lt;p&gt;Environment variables are commonly used in the following scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Applying environment-specific data such as port numbers and database URLs.&lt;/li&gt;
&lt;li&gt;Keeping sensitive data such as API keys and credentials out of your source code.&lt;/li&gt;
&lt;li&gt;Changing software behavior based on environment (e.g., setting API rate limits in a production environment).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Simple, dependable, and great for bootstrapping config. But they have limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's a Feature Flag?
&lt;/h2&gt;

&lt;p&gt;A feature flag or feature toggle represents a boolean-type setting value that allows development teams and product managers to toggle features on or off. Feature flags allow you to separate deployments from feature releases. They are typically used in if-else statements to make features visible to an entire user base or, with precise targeting, to specific audiences and individual users.&lt;/p&gt;

&lt;p&gt;Common feature flags use cases include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Progressive delivery (e.g., canary releases and staged rollouts)&lt;/li&gt;
&lt;li&gt;Trunk-based development&lt;/li&gt;
&lt;li&gt;Experimentation and marketing campaigns (e.g., A/B testing)&lt;/li&gt;
&lt;li&gt;Kill switches for cases where features are not working correctly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They are typically managed using a feature management platform like ConfigCat or LaunchDarkly, or via a homegrown solution if you have the time and resources.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Differences
&lt;/h2&gt;

&lt;p&gt;While feature flags and environment variables can both affect how applications behave, their similarities end there. Here are the factors that set them apart:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Redeployment:&lt;/strong&gt; This is probably the biggest difference. Changing the value of an environment variable requires you to redeploy the application code or rebuild your app to reflect the changes. In contrast, when you toggle a feature flag, your app reflects the changes almost immediately without redeployment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Targeting:&lt;/strong&gt; Feature flags enable you to roll out features to specific subsets of users or user segments based on contextual data, such as country, email address, or device type. Environment variables, on the other hand, do not support user targeting and segmentation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Values:&lt;/strong&gt; Environment variable values can only be strings. While you can pass numbers and booleans as strings, you need to parse them in your app before use. Complex values, such as objects, must be stringified first. Feature flags, on the other hand, are traditionally boolean values, but modern systems also support strings, numbers, and even JSON objects.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we’ve covered the differences, let’s look at the pros and cons of using feature flags and environment variables to modify application behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature Flags vs. Environment Variables: Quick Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Environment Variables&lt;/th&gt;
&lt;th&gt;Feature Flags&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Change requires redeploy?&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Supports targeting?&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Value types&lt;/td&gt;
&lt;td&gt;Strings only (must parse others)&lt;/td&gt;
&lt;td&gt;Booleans, strings, numbers, JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Per-user control&lt;/td&gt;
&lt;td&gt;❌ Not possible&lt;/td&gt;
&lt;td&gt;✅ Easy to segment users&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frontend-safe?&lt;/td&gt;
&lt;td&gt;❌ Risky (can expose secrets)&lt;/td&gt;
&lt;td&gt;✅ With proper flag setup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;Static config, secrets, bootstrapping&lt;/td&gt;
&lt;td&gt;Dynamic features, rollouts, A/B tests&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Pros and Cons
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Environment Variables&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;✅ Easy to use, no external tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;✅ Language-agnostic and widely supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;❌ Require redeploys&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;❌ No user segmentation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;❌ Harder to manage at scale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;❌ Only handle strings&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature Flags&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;✅ No redeploys needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;✅ Fine-grained control over rollouts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;✅ Safer feature experimentation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;❌ More complex testing (must test multiple flag states)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;❌ Can clutter code if not cleaned up&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What to Use When?
&lt;/h2&gt;

&lt;p&gt;Use environment variables when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need config only at startup&lt;/li&gt;
&lt;li&gt;You don't need to target users&lt;/li&gt;
&lt;li&gt;You're storing secrets or low-level infrastructure config&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use feature flags when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want to enable/disable features instantly&lt;/li&gt;
&lt;li&gt;You're rolling out to a subset of users&lt;/li&gt;
&lt;li&gt;You need flexibility and safety during releases&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Bonus Tips
&lt;/h2&gt;

&lt;p&gt;If you choose to use environment variables, check out &lt;a href="https://openfeature.dev/ecosystem/?instant_search%5BrefinementList%5D%5Bvendor%5D%5B0%5D=Environment%20Variable" rel="noopener noreferrer"&gt;OpenFeature's environment variable providers&lt;/a&gt;. They support booleans, numbers, strings, and objects, and provide a nice API for evaluating these values. If you'd rather use OpenFeature to manage your ConfigCat feature flags, take a look at our &lt;a href="https://configcat.com/blog/2024/10/31/configcat-openfeature/" rel="noopener noreferrer"&gt;OpenFeature guide&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have the culture and tooling in place to remove stale (zombie) flags.&lt;/li&gt;
&lt;li&gt;Give your feature flags &lt;a href="https://configcat.com/blog/2023/05/19/feature-flag-naming-conventions/" rel="noopener noreferrer"&gt;descriptive names&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Make your feature flags observable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ConfigCat can help you with all of the above (except the team culture part 😅). ConfigCat provides &lt;a href="https://configcat.com/docs/faq/#what-is-the-zombie-stale-flags-report" rel="noopener noreferrer"&gt;zombie flag alerts&lt;/a&gt;, &lt;a href="https://configcat.com/docs/integrations/overview/" rel="noopener noreferrer"&gt;integrations&lt;/a&gt; for monitoring and analytics platforms, and many other features that allow you to use feature flags and &lt;a href="https://configcat.com/docs/glossary/remote-configuration/" rel="noopener noreferrer"&gt;remote configuration&lt;/a&gt; with ease. &lt;a href="https://app.configcat.com/auth/signup" rel="noopener noreferrer"&gt;Get started&lt;/a&gt; with the generous forever free plan, or &lt;a href="https://calendly.com/configcat/demo" rel="noopener noreferrer"&gt;contact the team for a demo&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;You can also check out ConfigCat on &lt;a href="https://www.facebook.com/configcat" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt;, &lt;a href="https://x.com/configcat" rel="noopener noreferrer"&gt;X&lt;/a&gt;, &lt;a href="https://www.linkedin.com/company/configcat" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, and &lt;a href="https://github.com/configcat" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>featureflags</category>
      <category>environmentvariables</category>
      <category>comparison</category>
      <category>deployment</category>
    </item>
    <item>
      <title>Frontend Feature Flags vs Backend Feature Flags</title>
      <dc:creator>Zayyad Muhammad Sani</dc:creator>
      <pubDate>Sat, 24 May 2025 11:34:46 +0000</pubDate>
      <link>https://dev.to/zms/frontend-feature-flags-vs-backend-feature-flags-5cn2</link>
      <guid>https://dev.to/zms/frontend-feature-flags-vs-backend-feature-flags-5cn2</guid>
      <description>&lt;p&gt;Does it matter whether you evaluate feature flags on the backend or the frontend? The answer is yes. As you might have guessed, the frontend and backend are distinct components of software architecture—and the factors we consider when working with them, such as security and control, apply to feature flags as well.&lt;/p&gt;

&lt;p&gt;In this article, we'll explore the pros and cons of backend and frontend feature flags and solutions to challenges you may face while working with them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Feature Flags?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://configcat.com/featureflags/" rel="noopener noreferrer"&gt;Feature flags&lt;/a&gt;, also known as feature toggles, are powerful tools in modern software development that let you turn features on or off without redeploying your code. Feature flags could be used for &lt;a href="https://configcat.com/blog/2024/01/16/using-configcat-for-staged-rollouts-and-canary-releases/" rel="noopener noreferrer"&gt;gradual feature rollouts&lt;/a&gt;, separating deployments from releases, &lt;a href="https://configcat.com/blog/2022/05/02/what-is-ab-testing/" rel="noopener noreferrer"&gt;A/B testing&lt;/a&gt;, and pretty much any situation where you want to reduce the risks of feature releases.&lt;/p&gt;

&lt;p&gt;In practice, feature flags look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;isCoolNewFeatureEnabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getFeatureFlag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cool-new-feature&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isCoolNewFeatureEnabled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// do the new thing&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// do the old thing&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To assign a feature flag to a feature in your app, you'll need to wrap the feature in an if-statement that depends on the flag's value. When the feature flag is enabled, the feature or functionality will be available in the app.&lt;/p&gt;

&lt;p&gt;Feature flags can be managed and stored in various ways, but the simplest approach is to use a feature management platform like &lt;a href="http://app.configcat.com" rel="noopener noreferrer"&gt;ConfigCat&lt;/a&gt;. Such platforms provide a user-friendly interface to create, toggle, audit, and remove feature flags as needed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9i3b8eiyi24an2m9gpql.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9i3b8eiyi24an2m9gpql.png" alt="A feature flag in ConfigCat" width="800" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feature flags can be configured with &lt;a href="https://configcat.com/docs/targeting/targeting-rule/targeting-rule-overview/" rel="noopener noreferrer"&gt;targeting rules&lt;/a&gt; and/or &lt;a href="https://configcat.com/docs/targeting/percentage-options/" rel="noopener noreferrer"&gt;percentage options&lt;/a&gt; to deliver features to specific user segments. For example, you could use a feature flag to control the pricing tiers of a software product based on a user's location.&lt;/p&gt;

&lt;p&gt;The process of determining a feature flag's value is called evaluation. The context in which this evaluation happens is what distinguishes frontend (client-side) feature flags from backend (server-side) feature flags.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;TIP 💡: If you're looking for solutions to common challenges with frontend and backend feature flags, feel free to skip ahead to the Working Around the Challenges section of this article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Frontend Feature Flags
&lt;/h2&gt;

&lt;p&gt;Frontend feature flags are used to control the user interface and user experience of an application. Frontend feature flags can manage access to premium features, subscriptions, or other monetization strategies. Examples include toggling price tags during sales events, displaying different versions of a landing page, showing features based on a user's country, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Easier to implement because the features they control are close to the user and have a visual impact.&lt;/li&gt;
&lt;li&gt;Simpler to debug and straightforward to remove when they are no longer needed.&lt;/li&gt;
&lt;li&gt;Involve less code, as developers only need to wrap the feature's UI components in conditional statements. They also won't need to write HTTP requests to fetch the feature flags from an API.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A slow internet connection might cause browsers to download feature flags slowly, leading to a brief flash of content that should be hidden behind a feature flag.&lt;/li&gt;
&lt;li&gt;Users may be able to view sensitive information in a feature flag's targeting rule through a browser's Devtools.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Backend Feature Flags
&lt;/h2&gt;

&lt;p&gt;Backend feature flags are evaluated on the server-side. Common use cases include modifying system behavior, such as API rate limiting, blue-green deployments, database migrations, or implementing kill switches for misbehaving features. A &lt;a href="https://configcat.com/blog/2022/03/24/how-kantan-successfully-uses-configcat-in-its-ci-pipeline/#retiring-feature-flags-or-keeping-them-as-emergency-kill-switches" rel="noopener noreferrer"&gt;kill switch&lt;/a&gt; is a critical tool that allows teams to quickly disable problematic features if issues arise, thus maintaining stability and preventing major disruptions to the user experience.&lt;/p&gt;

&lt;p&gt;Backend feature flags can sometimes control client-facing features through frameworks like Rails and Laravel or by providing an API for the client side to fetch flag values. This approach is especially useful for scenarios where client-side evaluation is not feasible. Backend feature flags can also be gradually released to larger user groups incrementally, minimizing risks associated with software releases by enabling quick adjustments and ensuring that new functionalities perform reliably before reaching a broader audience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Backend feature flags keep evaluations secure on the server-side and reduce the risk of exposing information in the browser.&lt;/li&gt;
&lt;li&gt;Latency is less of a problem here since developers can easily configure fallbacks and make the backend fail more gracefully.&lt;/li&gt;
&lt;li&gt;They also allow for centralized control, making them ideal for features that depend on complex business logic or require consistency across different client platforms.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Because the code for backend flags is separate from the frontend code they affect, developers may easily forget the purpose of the flags over time.&lt;/li&gt;
&lt;li&gt;In architectures where multiple client-side frameworks rely on server-side evaluations, any errors at the server level can impact all connected clients.&lt;/li&gt;
&lt;li&gt;Developers need to write additional code to handle HTTP requests because backend feature flags require communication between the client-side and server-side. This extra layer of interaction adds complexity, as developers need to implement and maintain the necessary APIs or endpoints to support this process.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;TIP 💡: The points mentioned in this article apply to most feature management tools since they work in similar ways. However, there may be slight differences depending on the architecture of the feature management tool you use.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Working Around the Challenges
&lt;/h2&gt;

&lt;p&gt;Let's explore a few ways to handle the challenges we mentioned earlier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Frontend
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;To avoid leaking sensitive data, consider using a backend flag and access it through an API on the frontend. If you're using ConfigCat's feature management platform, you can use &lt;a href="https://configcat.com/docs/targeting/targeting-rule/user-condition/#confidential-text-comparators" rel="noopener noreferrer"&gt;confidential text comparators&lt;/a&gt; to hash your targeting rules so they're not readable in the browser. Though ConfigCat's SDK keys are visible in browsers, they're read-only and can't be used to modify feature flags.&lt;/li&gt;
&lt;li&gt;Consider using animations, loading states, and other techniques to hide features while the feature flags are downloading. This point is more important when you're running experiments because users interacting with multiple variations at once will affect the results.&lt;/li&gt;
&lt;li&gt;To avoid flashing content, consider using modern frameworks (like Next.js or SvelteKit) that support server-side rendering or backend frameworks like Django and Laravel that can generate web pages on the server by default.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Backend
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Consider using templates provided by server-side frameworks (e.g., Blade, ERB, Jinja2), as these mimic the visual simplicity of frontend feature flags by keeping the logic for displaying the feature flag closer to the UI, while also offering the advantages of backend feature flags.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Both (Frontend and Backend)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;For increased security and reliability, consider using &lt;a href="https://configcat.com/docs/advanced/proxy/proxy-overview/" rel="noopener noreferrer"&gt;ConfigCat Proxy&lt;/a&gt; to host an evaluation backend in your infrastructure. With the proxy, you can use external caching solutions like Redis and MongoDB and stream feature flag changes with Server-Sent Events (SSE). Plus, you won't have to write a custom API to serve feature flags to the frontend.&lt;/li&gt;
&lt;li&gt;Cache feature flags, but don't forget to update them so they don't become stale. ConfigCat &lt;a href="https://configcat.com/docs/advanced/caching/#caching" rel="noopener noreferrer"&gt;caches feature flags&lt;/a&gt; automatically and allows you to configure how often you want to update them.&lt;/li&gt;
&lt;li&gt;Use reasonable &lt;a href="https://configcat.com/docs/targeting/targeting-overview/#default-value" rel="noopener noreferrer"&gt;default values&lt;/a&gt; for your feature flags that your app can fall back to when facing network issues.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  General Best Practices
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Give your feature flags detailed names that describe their purpose, where they'll be used, and for how long. Check out this &lt;a href="https://configcat.com/blog/2023/05/19/feature-flag-naming-conventions/" rel="noopener noreferrer"&gt;feature flag naming guide&lt;/a&gt; for more.&lt;/li&gt;
&lt;li&gt;Have a system in place to remove unused flags. Tools like ConfigCat’s &lt;a href="https://configcat.com/docs/faq/#what-are-zombie-stale-flags" rel="noopener noreferrer"&gt;zombie flag reports&lt;/a&gt; and &lt;a href="https://configcat.com/docs/advanced/code-references/overview/" rel="noopener noreferrer"&gt;code references&lt;/a&gt; can help track and remove stale flags.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I hope you now have a well-rounded view of frontend and backend feature flags. Keep in mind that the suggestions here are not fixed rules, and ultimately, you'll have to choose what works best for you and your users.&lt;/p&gt;

&lt;p&gt;If you're in the market for a feature management platform, check out &lt;a href="https://app.configcat.com/auth/signup" rel="noopener noreferrer"&gt;ConfigCat&lt;/a&gt;. You can start with the &lt;a href="https://configcat.com/#pricing" rel="noopener noreferrer"&gt;forever free plan&lt;/a&gt;, which has all the features you'll need at a small scale.&lt;/p&gt;

&lt;p&gt;You can stay up-to-date with ConfigCat on &lt;a href="https://www.facebook.com/configcat" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt;, &lt;a href="https://x.com/configcat" rel="noopener noreferrer"&gt;X&lt;/a&gt;, &lt;a href="https://github.com/configcat" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, and &lt;a href="https://www.linkedin.com/company/configcat/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>featureflags</category>
      <category>frontend</category>
      <category>backend</category>
      <category>featuremanagement</category>
    </item>
    <item>
      <title>Announcing Official Support for OpenFeature</title>
      <dc:creator>Zayyad Muhammad Sani</dc:creator>
      <pubDate>Mon, 17 Mar 2025 14:43:10 +0000</pubDate>
      <link>https://dev.to/zms/announcing-official-support-for-openfeature-3gjb</link>
      <guid>https://dev.to/zms/announcing-official-support-for-openfeature-3gjb</guid>
      <description>&lt;p&gt;At ConfigCat, we've seen firsthand how feature flags help teams ship faster with less risk and build more resilient, user-friendly applications. We want more people to experience these benefits, so we've decided to officially support OpenFeature.&lt;/p&gt;

&lt;p&gt;Our OpenFeature providers were previously developed and maintained by open-source contributors. Moving forward, we've opted to develop and maintain some providers in-house, while continuing to collaborate with open-source contributors for others.&lt;/p&gt;

&lt;p&gt;In the upcoming sections, we'll explore how to integrate OpenFeature into a Python application that currently uses the ConfigCat Python SDK.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is OpenFeature
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://openfeature.dev/" rel="noopener noreferrer"&gt;OpenFeature&lt;/a&gt; is a project under the &lt;a href="https://www.cncf.io/" rel="noopener noreferrer"&gt;Cloud Native Computing Foundation (CNCF)&lt;/a&gt; that offers a standard, vendor-agnostic API for feature flagging. With OpenFeature, developers can utilize a single SDK to interact with multiple feature management platforms. To make this possible, feature management platforms or the open-source community develop &lt;a href="https://openfeature.dev/docs/reference/concepts/provider" rel="noopener noreferrer"&gt;providers&lt;/a&gt; that connect their SDKs to OpenFeature. These SDKs and providers are available for many programming languages and feature management platforms.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbft3n6zmgemddeytkjxl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbft3n6zmgemddeytkjxl.png" alt="An OpenFeature SDK connected to ConfigCat and a fictional vendor via providers" width="681" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why use OpenFeature with ConfigCat?
&lt;/h3&gt;

&lt;p&gt;Here are a few reasons why combining OpenFeature with ConfigCat is a game-changer for your development process:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Avoid vendor lock-in:&lt;/strong&gt; OpenFeature's APIs and SDKs let you work with any feature management platform, so you're not tied to just one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benefit from the OpenFeature ecosystem:&lt;/strong&gt; OpenFeature offers a growing set of &lt;a href="https://openfeature.dev/ecosystem" rel="noopener noreferrer"&gt;tools&lt;/a&gt; to help you integrate feature flags across your software development stack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Migrate to ConfigCat more easily:&lt;/strong&gt; If you're switching to ConfigCat from another platform, OpenFeature makes the migration process easier at the code level.&lt;/p&gt;

&lt;p&gt;Check out our &lt;a href="https://configcat.com/blog/2024/10/31/configcat-openfeature/#benefits-of-openfeature" rel="noopener noreferrer"&gt;OpenFeature guide&lt;/a&gt; for more detailed explanations of these benefits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started: Integrating ConfigCat with OpenFeature
&lt;/h2&gt;

&lt;p&gt;Let’s see how you can integrate OpenFeature with ConfigCat in a Python app. We'll use a project with ConfigCat already set up to demonstrate how easy this integration can be.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;TIP 💡&lt;br&gt;
If you’re interested in using OpenFeature with other languages, check out &lt;a href="https://configcat.com/docs/sdk-reference/openfeature/overview/" rel="noopener noreferrer"&gt;our docs&lt;/a&gt; for the list of OpenFeature providers we have.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Sample App
&lt;/h3&gt;

&lt;p&gt;The app is an online courses API with a &lt;code&gt;GET&lt;/code&gt; endpoint that returns a predefined list of courses. The endpoint is linked to a feature flag I created in ConfigCat:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyohbb3hszykc6h5ojpqf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyohbb3hszykc6h5ojpqf.png" alt="Feature flag turned on in ConfigCat" width="800" height="149"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The endpoint will return the course list if the feature flag is turned on:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fik770j32rw0jzcn3h9ae.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fik770j32rw0jzcn3h9ae.png" alt="JSON response showing a list of courses" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It will return an error if the flag is turned off:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj3vwhstrk4dtnzwhq222.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj3vwhstrk4dtnzwhq222.png" alt="JSON response showing a 404 not found error" width="800" height="133"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After adding OpenFeature to the app, it should function just as before.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Installing OpenFeature
&lt;/h3&gt;

&lt;p&gt;To run the sample app, you'll need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://app.configcat.com/" rel="noopener noreferrer"&gt;ConfigCat account&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Python v3.9+&lt;/li&gt;
&lt;li&gt;Git v2.33+&lt;/li&gt;
&lt;li&gt;A tool to make HTTP requests (I'll be using &lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Intermediate knowledge of Python and FastAPI and basic knowledge of Git&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find the &lt;a href="https://github.com/configcat-labs/openfeature-python-sample" rel="noopener noreferrer"&gt;sample app's source code&lt;/a&gt; on GitHub. The &lt;code&gt;main.py&lt;/code&gt; file contains the initial code before OpenFeature was added, while &lt;code&gt;cc-openfeature.py&lt;/code&gt; has the completed version with OpenFeature integrated.  &lt;/p&gt;

&lt;p&gt;Follow these steps to add OpenFeature to the project:&lt;/p&gt;

&lt;p&gt;1) Install the OpenFeature Python provider.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;configcat-openfeature-provider
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) Import the newly installed packages in &lt;code&gt;main.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ... other imports
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openfeature&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;configcat_openfeature_provider&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ConfigCatProvider&lt;/span&gt;
&lt;span class="c1"&gt;# ... remaining code
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using ConfigCat with OpenFeature
&lt;/h3&gt;

&lt;p&gt;As we integrate OpenFeature into the app, we'll use its API in place of ConfigCat code.&lt;/p&gt;

&lt;p&gt;1) In the &lt;code&gt;lifespan&lt;/code&gt; function, use the OpenFeature provider to initialize the ConfigCat client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# BEFORE
&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;configcatclient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CONFIGCAT_SDK_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;

&lt;span class="c1"&gt;# AFTER
&lt;/span&gt;&lt;span class="n"&gt;cc_provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ConfigCatProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CONFIGCAT_SDK_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) Enable the provider with OpenFeature's API and instantiate a new OpenFeature client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cc_provider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openfeature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The OpenFeature client accesses the provider we set up and uses it to evaluate feature flags.&lt;/p&gt;

&lt;p&gt;3) After the &lt;code&gt;yield&lt;/code&gt; statement, use OpenFeature's API to clean up the providers when the application shuts down.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;yield&lt;/span&gt;
&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shutdown&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;lifespan&lt;/code&gt; function should now look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@asynccontextmanager&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lifespan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cc_provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ConfigCatProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CONFIGCAT_SDK_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cc_provider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt;
&lt;span class="c1"&gt;# clean up all provders
&lt;/span&gt;    &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shutdown&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have one more code change to make.&lt;/p&gt;

&lt;p&gt;4) Evaluate the feature flag using the OpenFeature client in the &lt;code&gt;get_courses&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# BEFORE
&lt;/span&gt;&lt;span class="n"&gt;is_get_courses_enabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get_courses_enabled&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# AFTER
&lt;/span&gt;&lt;span class="n"&gt;is_get_courses_enabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_boolean_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get_courses_enabled&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's the full function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/courses/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_courses&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;is_get_courses_enabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_boolean_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get_courses_enabled&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;is_get_courses_enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;courses&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;courses&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're using a boolean flag that returns &lt;code&gt;True&lt;/code&gt; when the flag is on and &lt;code&gt;False&lt;/code&gt; when the flag is off. For cases like &lt;a href="https://configcat.com/blog/2022/05/02/what-is-ab-testing/" rel="noopener noreferrer"&gt;A/B tests&lt;/a&gt;, &lt;a href="https://configcat.com/docs/advanced/segments/" rel="noopener noreferrer"&gt;user segmentation&lt;/a&gt;, or &lt;a href="https://configcat.com/docs/advanced/targeting/" rel="noopener noreferrer"&gt;targeting&lt;/a&gt;, an OpenFeature context object is required to evaluate the feature flag. Check out our &lt;a href="https://configcat.com/docs/sdk-reference/openfeature/python/#evaluation-context" rel="noopener noreferrer"&gt;OpenFeature Python&lt;/a&gt; docs to see how the context object works.  &lt;/p&gt;

&lt;p&gt;5) Run the program in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fastapi dev main.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;6) Toggle the flag in your ConfigCat dashboard and visit &lt;code&gt;http://localhost:8000/courses&lt;/code&gt;. The app should work like before:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fea6v66zegg7yssl3ln6k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fea6v66zegg7yssl3ln6k.png" alt="Feature flag turned off in ConfigCat" width="800" height="149"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8c4jm0bduf5v9k5xzfww.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8c4jm0bduf5v9k5xzfww.png" alt="JSON response showing a 404 not found error" width="800" height="133"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And there you have it! The app is now OpenFeature compliant. As always, you can find the &lt;a href="https://github.com/configcat-labs/openfeature-python-sample" rel="noopener noreferrer"&gt;complete code on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Open-source software has increased collaboration among developers and accelerated advancements in the software industry like never before. Whether a company offers proprietary software, open-source software, or a mix of both, everyone benefits when projects like OpenFeature make feature flags more accessible and better integrated across the entire software development stack.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/configcat-labs/openfeature-python-sample" rel="noopener noreferrer"&gt;Sample app source code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://configcat.com/docs/sdk-reference/openfeature/overview/" rel="noopener noreferrer"&gt;ConfigCat's OpenFeature SDKs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openfeature.dev/" rel="noopener noreferrer"&gt;OpenFeature website&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more on feature flags, check out ConfigCat on &lt;a href="https://www.facebook.com/configcat" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt;, &lt;a href="https://x.com/configcat" rel="noopener noreferrer"&gt;X&lt;/a&gt;, &lt;a href="https://www.linkedin.com/company/configcat" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, and &lt;a href="https://github.com/configcat" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>openfeature</category>
      <category>opensource</category>
      <category>featureflags</category>
      <category>configcat</category>
    </item>
    <item>
      <title>Using OpenFeature with ConfigCat</title>
      <dc:creator>Zayyad Muhammad Sani</dc:creator>
      <pubDate>Tue, 12 Nov 2024 16:48:51 +0000</pubDate>
      <link>https://dev.to/zms/using-openfeature-with-configcat-5glj</link>
      <guid>https://dev.to/zms/using-openfeature-with-configcat-5glj</guid>
      <description>&lt;p&gt;Let's say you've heard about the benefits of using feature flags for &lt;a href="https://configcat.com/blog/2024/01/16/using-configcat-for-staged-rollouts-and-canary-releases/" rel="noopener noreferrer"&gt;gradual feature rollouts&lt;/a&gt;, and you're ready to adopt them in your organization. The next step would be deciding whether to "build or buy."&lt;/p&gt;

&lt;p&gt;If you choose to develop your feature flagging tool, you'll need to figure out how to make it fetch, evaluate, and manage feature flags. If you want to use a third-party tool, you might have to learn and unlearn different APIs as you search for the right one.&lt;/p&gt;

&lt;p&gt;Challenges like these inspired the creation of OpenFeature, a CNCF (Cloud Native Computing Foundation) project that aims to standardize feature flagging.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is OpenFeature?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://openfeature.dev/" rel="noopener noreferrer"&gt;OpenFeature&lt;/a&gt; is a specification that provides a unified API for using feature flags. Going back to our earlier scenario, this means you'd use the same code to evaluate (get the values of) feature flags when trying out feature flag platforms for your company.&lt;/p&gt;

&lt;p&gt;How does this work?&lt;/p&gt;

&lt;p&gt;OpenFeature provides &lt;a href="https://openfeature.dev/docs/reference/technologies/sdk-compatibility" rel="noopener noreferrer"&gt;SDKs&lt;/a&gt; for evaluating feature flags. Feature flag vendors (or open source contributors) then have to develop &lt;a href="https://openfeature.dev/docs/reference/concepts/provider" rel="noopener noreferrer"&gt;OpenFeature providers&lt;/a&gt; to connect their SDKs to OpenFeature SDKs. In short, OpenFeature providers act as a bridge between OpenFeature SDKs and feature flag platforms.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwtd8e0h4dmj1ezfwwqfw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwtd8e0h4dmj1ezfwwqfw.png" alt="OpenFeature providers connecting OpenFeature SDKs to different feature flag vendors" width="800" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 INFO&lt;/strong&gt;&lt;br&gt;
You don't use OpenFeature to create and manage feature flags; you only use it to evaluate them. You'll need a third-party or custom-built feature management tool to create and manage feature flags.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Benefits of OpenFeature
&lt;/h3&gt;

&lt;p&gt;Here are some positive changes OpenFeature brings to feature flagging:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;OpenFeature makes migration between feature flag platforms easier at the code level:&lt;/strong&gt; Many feature flag platforms are compatible with OpenFeature, so switching to another vendor will involve little change in your code. With the recent release of OpenFeature's &lt;a href="https://openfeature.dev/blog/openfeature-multi-provider-release" rel="noopener noreferrer"&gt;Multiproviders&lt;/a&gt; (currently available for JavaScript), you can use more than one feature flag platform during migration. This advantage also applies to homegrown feature flagging tools since you can create providers to make them accessible to OpenFeature.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;OpenFeature encourages community collaboration and tool-sharing:&lt;/strong&gt; The open-source community can collaborate and build vendor-agnostic tools around feature flags that everyone can benefit from. A good example is &lt;a href="https://openfeature.dev/ecosystem?instant_search%5Bquery%5D=opentelemetry&amp;amp;instant_search%5BrefinementList%5D%5Btype%5D%5B0%5D=Hook" rel="noopener noreferrer"&gt;OpenTelemetry Hooks&lt;/a&gt; for OpenFeature, which you can use to attach feature flag metadata to metrics and traces.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've said a lot about OpenFeature. Let's see how it integrates with &lt;a href="https://configcat.com/" rel="noopener noreferrer"&gt;ConfigCat&lt;/a&gt;, a feature management platform with first-class OpenFeature support.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;For this demo, I built an &lt;a href="https://github.com/configcat-labs/configcat-openfeature-sample" rel="noopener noreferrer"&gt;AI-powered PDF extractor&lt;/a&gt;. The app's users can choose between different AI models for extracting data from PDFs. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpfrdx4nl91xuoyjrzp2j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpfrdx4nl91xuoyjrzp2j.png" alt="Select box showing a list of Generative AI models" width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I want to test newer, more powerful AI models for employees at PD-EF Corp, a fictional SaaS company, so I'll use feature flags to enable the feature for users whose emails end with &lt;code&gt;@pd-efcorp.com&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Feature Flag in ConfigCat
&lt;/h2&gt;

&lt;p&gt;Let's get started.&lt;/p&gt;

&lt;p&gt;1) &lt;a href="https://app.configcat.com/" rel="noopener noreferrer"&gt;Sign in&lt;/a&gt; to ConfigCat, or &lt;a href="https://app.configcat.com/auth/signup" rel="noopener noreferrer"&gt;sign up&lt;/a&gt; if you don't have an account.&lt;/p&gt;

&lt;p&gt;2) Create a feature flag with the following details.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name: Enable pro models&lt;/li&gt;
&lt;li&gt;Key: &lt;code&gt;enable_pro_models&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Hint: Enable pro AI models for PD-EF Corp employees&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll add the targeting rule for PD-EF Corp employees next.&lt;/p&gt;

&lt;p&gt;3) Click &lt;code&gt;IF +&lt;/code&gt;, then click &lt;code&gt;Add rule with user condition&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You'll see two sections: an &lt;code&gt;IF&lt;/code&gt; section for configuring the condition and a &lt;code&gt;THEN&lt;/code&gt; section for setting the feature flag's value based on the condition.&lt;/p&gt;

&lt;p&gt;4) In the &lt;code&gt;IF&lt;/code&gt; section, select &lt;code&gt;Email&lt;/code&gt; as the comparison attribute, then select &lt;code&gt;ENDS WITH ANY OF (hashed)&lt;/code&gt; as the comparator.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi7euug483e32tq9dcsay.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi7euug483e32tq9dcsay.png" alt="Selecting the email comparator" width="800" height="108"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;5)  Enter &lt;code&gt;@pd-efcorp.com&lt;/code&gt; in the field next to the comparator, then turn on the feature flag in the &lt;code&gt;THEN&lt;/code&gt; section below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx2o392kpv08o32of4oek.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx2o392kpv08o32of4oek.png" alt="Enabling the pro models feature for users that pass the condition" width="800" height="283"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;6)  Click &lt;code&gt;SAVE &amp;amp; PUBLISH CHANGES&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now that we've configured the feature flag, let's move on to the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing ConfigCat and OpenFeature
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Node.js v18+&lt;/li&gt;
&lt;li&gt;Basic understanding of Node.js and Express&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To use ConfigCat and OpenFeature together, we'll need to install ConfigCat's &lt;a href="https://configcat.com/docs/sdk-reference/openfeature/node/" rel="noopener noreferrer"&gt;Node.js provider&lt;/a&gt; for OpenFeature. ConfigCat's Node SDK and OpenFeature's Server SDK will be automatically downloaded alongside the provider.&lt;/p&gt;

&lt;p&gt;1) Open your terminal and install the provider.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @openfeature/config-cat-provider
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) Import the provider and OpenFeature server SDK in &lt;code&gt;index.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ConfigCatProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@openfeature/config-cat-provider&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;OpenFeature&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@openfeature/server-sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3) Create a provider with your ConfigCat SDK key. Then, set the provider and create an OpenFeature client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// create a provider with your SDK key&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ConfigCatProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR-SDK-KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// set the provider&lt;/span&gt;
&lt;span class="nx"&gt;OpenFeature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProviderAndWait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Create a client.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;OpenFeature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ConfigCatProvider.create&lt;/code&gt; creates a ConfigCat client with the specific SDK key. The &lt;code&gt;setProviderAndWait&lt;/code&gt; method sets ConfigCat as the source of feature flag values and ensures the provider is ready before we begin using it. Finally, &lt;code&gt;OpenFeature.getClient&lt;/code&gt; creates a client that uses the set provider to evaluate feature flags.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using OpenFeature with ConfigCat
&lt;/h2&gt;

&lt;p&gt;I have an existing &lt;code&gt;/models&lt;/code&gt; route for serving the list of AI models to the frontend. I'll modify its callback function to accommodate the feature flag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/models&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="c1"&gt;// get the user's id from the database&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// fetch the models&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;proModels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getModels&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pro&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;standardModels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getModels&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;standard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// create the evaluation context&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;targetingKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// evaluate the feature flag with the context&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;modelsListEnabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBooleanValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;models_list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// serve the models based on the feature flag's value&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;modelsListEnabled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;proModels&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;standardModels&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I created the &lt;code&gt;context&lt;/code&gt; object to store the user's email and unique targeting key. Next, I evaluated the feature flag, passing in the key, default value, and &lt;code&gt;context&lt;/code&gt;. The provider automatically transforms &lt;code&gt;context&lt;/code&gt; into a ConfigCat &lt;a href="https://configcat.com/docs/targeting/targeting-rule/user-condition/" rel="noopener noreferrer"&gt;User object&lt;/a&gt;, which ConfigCat will use to determine whether the user's email matches the condition I set in the targeting rule.&lt;/p&gt;

&lt;p&gt;Finally, I added a condition to return the appropriate model based on the feature flag's value. That's all the code we'll need. Let's see how it looks.&lt;/p&gt;

&lt;p&gt;Once I open the website and sign in with a PD-EF Corp email address, I'll see the pro AI models.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flj02eqbbc1byztuaiui2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flj02eqbbc1byztuaiui2.gif" alt="Viewing the pro AI models with a pd-efcorp email address" width="795" height="672"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 INFO&lt;/strong&gt;&lt;br&gt;
The call to the API's &lt;code&gt;models/&lt;/code&gt; route is implemented in the frontend. You can find &lt;a href="https://github.com/configcat-labs/configcat-openfeature-sample" rel="noopener noreferrer"&gt;the complete code&lt;/a&gt; on GitHub.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why use ConfigCat with OpenFeature?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;First-class OpenFeature support:&lt;/strong&gt; ConfigCat builds and maintains its own &lt;a href="https://configcat.com/docs/sdk-reference/openfeature/overview/" rel="noopener noreferrer"&gt;OpenFeature providers&lt;/a&gt;, which are available for most languages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Powerful and easy to use:&lt;/strong&gt; Both seasoned and new feature flag users will benefit from using OpenFeature with a capable and beginner-friendly feature management platform.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Existing ConfigCat users can benefit from an open feature-flagging ecosystem:&lt;/strong&gt; ConfigCat users can leverage available tools in the &lt;a href="https://openfeature.dev/ecosystem" rel="noopener noreferrer"&gt;OpenFeature ecosystem&lt;/a&gt; and contribute by building tools and sharing knowledge with the feature-flagging community.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;Thanks for accompanying me on this short trip through OpenFeature and ConfigCat. We covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What OpenFeature is.&lt;/li&gt;
&lt;li&gt;The benefits of OpenFeature.&lt;/li&gt;
&lt;li&gt;How to create a feature flag in ConfigCat.&lt;/li&gt;
&lt;li&gt;How to use OpenFeature with ConfigCat.&lt;/li&gt;
&lt;li&gt;The benefits of using OpenFeature with ConfigCat.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Check out &lt;a href="https://openfeature.dev/" rel="noopener noreferrer"&gt;OpenFeature.dev&lt;/a&gt; to learn more about OpenFeature and its ecosystem. You can also join the &lt;a href="https://cloud-native.slack.com/archives/C0344AANLA1" rel="noopener noreferrer"&gt;OpenFeature Slack channel&lt;/a&gt; to get the latest updates.&lt;/p&gt;

&lt;p&gt;If you're not using feature flags yet, you can start with ConfigCat. ConfigCat has &lt;a href="https://configcat.com/docs/sdk-reference/overview/" rel="noopener noreferrer"&gt;SDKs&lt;/a&gt; and &lt;a href="https://configcat.com/docs/sdk-reference/openfeature/overview/" rel="noopener noreferrer"&gt;OpenFeature providers&lt;/a&gt; for many popular programming languages and a &lt;a href="https://configcat.com/pricing/" rel="noopener noreferrer"&gt;free-forever plan&lt;/a&gt; suitable for small-scale feature flagging.&lt;/p&gt;

&lt;p&gt;For more on feature flags, check out ConfigCat on &lt;a href="https://www.facebook.com/configcat" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt;, &lt;a href="https://x.com/configcat" rel="noopener noreferrer"&gt;X&lt;/a&gt;, &lt;a href="https://github.com/configcat" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, and &lt;a href="https://www.linkedin.com/company/configcat/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>featureflags</category>
      <category>openfeature</category>
    </item>
    <item>
      <title>A/B Testing with ConfigCat and Google Analytics</title>
      <dc:creator>Zayyad Muhammad Sani</dc:creator>
      <pubDate>Mon, 23 Sep 2024 16:22:10 +0000</pubDate>
      <link>https://dev.to/zms/ab-testing-with-configcat-and-google-analytics-5c6p</link>
      <guid>https://dev.to/zms/ab-testing-with-configcat-and-google-analytics-5c6p</guid>
      <description>&lt;p&gt;Suppose you come up with an idea to improve the conversion rates on your website. You could implement the idea and roll out the changes to all users. But what happens if the idea is only good on paper and fails in reality? In cases like this, conducting an A/B test is an excellent way to validate your idea.&lt;/p&gt;

&lt;p&gt;In the upcoming sections, we'll learn how to set up an A/B test using ConfigCat feature flags and visualize the test's data in Google Analytics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Feature Flags for A/B Testing
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://configcat.com/featureflags/" rel="noopener noreferrer"&gt;Feature flags&lt;/a&gt; (or feature toggles) are virtual switches you can use to turn features in your app on or off without redeploying code. Feature flags are commonly used for &lt;a href="https://configcat.com/blog/2024/01/16/using-configcat-for-staged-rollouts-and-canary-releases/" rel="noopener noreferrer"&gt;gradually rolling out features&lt;/a&gt;, running beta tests, quickly rolling back features, etc.&lt;/p&gt;

&lt;p&gt;An &lt;a href="https://configcat.com/blog/2022/05/02/what-is-ab-testing/" rel="noopener noreferrer"&gt;A/B test&lt;/a&gt; involves comparing two variations of a webpage, email, or other digital content to see which delivers better results. Using a feature flag, you can toggle variation A (the new feature you want to measure) for 50% of users while keeping variation B (the control) on for the remaining users. During the test, events are tracked and recorded on an analytics platform to help you select the best variation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Basic knowledge of &lt;a href="https://developers.google.com/analytics/devguides/collection/ga4/integration" rel="noopener noreferrer"&gt;Google Analytics&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Knowledge of &lt;a href="https://vuejs.org/" rel="noopener noreferrer"&gt;Vue.js 3&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript" rel="noopener noreferrer"&gt;JavaScript&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To demonstrate, I built an &lt;a href="https://github.com/configcat-labs/configcat-google-analytics-integration" rel="noopener noreferrer"&gt;animal quiz game&lt;/a&gt; where players listen to an animal's sound and select the correct answer from four options. Currently, the players don't see the correct answer when they choose an option. They only see their scores at the end of the game, leaving them to guess which questions they got wrong.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp2qgkth5w2arg6n1lc63.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp2qgkth5w2arg6n1lc63.gif" alt="Selecting an option in the control variation of the quiz. Answer is not highlighted" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While the current design makes the game fun and generates curiosity, I think highlighting the answers will encourage users to play longer to beat their high scores.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwgr2c8piqm9x3fq6nece.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwgr2c8piqm9x3fq6nece.gif" alt="Selecting an option in the variant with the answer highlighted" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's use an A/B test to determine which variation users like the most.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://app.configcat.com/auth/signup" rel="noopener noreferrer"&gt;Sign up for a free ConfigCat account&lt;/a&gt;, or &lt;a href="https://app.configcat.com/" rel="noopener noreferrer"&gt;log in&lt;/a&gt; if you already have one.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click the "ADD FEATURE FLAG" button, name the feature flag "Highlight answer" and give it the following hint: "Highlight the correct answer when a user selects an option."&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbh1sg22vsbo6isxkll41.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbh1sg22vsbo6isxkll41.png" alt="Naming the feature flag" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click "ADD FEATURE FLAG" to create the flag, then click the &lt;code&gt;+%&lt;/code&gt; button to &lt;a href="https://configcat.com/docs/targeting/percentage-options/#what-are-percentage-options" rel="noopener noreferrer"&gt;target a percentage&lt;/a&gt; of users.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7rxrkosqoy0iy9vqp7md.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7rxrkosqoy0iy9vqp7md.png" alt="Add percentage targeting" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should see 50% of users in both fields. Leave the values as they are since only half of the users should see the highlight feature.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7zs6b9l3l6l3v3ui2kxq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7zs6b9l3l6l3v3ui2kxq.png" alt="Feature flag is on for 50% of users, and off for the other 50%" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click "SAVE &amp;amp; PUBLISH CHANGES".&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Using the Feature Flag
&lt;/h3&gt;

&lt;p&gt;You'll need to install a ConfigCat SDK before you can use feature flags in your app. We'll use ConfigCat's Vue SDK since our sample app was built with Vue.js.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open your terminal and install ConfigCat's Vue SDK.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;configcat-vue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Import &lt;code&gt;configcat-vue&lt;/code&gt; in &lt;code&gt;main.js&lt;/code&gt; and initialize the ConfigCat client:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// other imports&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ConfigCatPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;configcat-vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;createPinia&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ConfigCatPlugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Don't forget to put in your actual SDK key below&lt;/span&gt;
  &lt;span class="na"&gt;sdkKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR-SDK-KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ConfigCatPlugin&lt;/code&gt; creates a ConfigCat client with the given SDK key and makes it available to other Vue components. ConfigCat will download and cache the latest feature flag values every 60 seconds by default. You can check out other &lt;a href="https://configcat.com/docs/sdk-reference/community/vue/#advanced-usage" rel="noopener noreferrer"&gt;configuration options&lt;/a&gt; in the documentation.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;code&gt;src/components/Question.vue&lt;/code&gt; and add this CSS class to the &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; section.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.correct__option&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;lime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Add the following code to the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; section of &lt;code&gt;Question.vue&lt;/code&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onBeforeMount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;configcat-vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useUserStore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/stores/user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Inject the ConfigCat client&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;configCatClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;configCatClient&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;isAnswerHighlightEnabled&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Add these after defining props and events&lt;/span&gt;

&lt;span class="nf"&gt;onBeforeMount&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setHighlightStatus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setHighlightStatus&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// get the value of the Highlight answer feature flag&lt;/span&gt;
  &lt;span class="nx"&gt;isAnswerHighlightEnabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;configCatClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getValueAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;highlight_answer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;useUserStore&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;userID&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the snippet above, I accessed the ConfigCat client and called &lt;code&gt;setHighlightStatus()&lt;/code&gt; to fetch the value of the feature flag. ConfigCat requires unique &lt;a href="https://configcat.com/docs/targeting/user-object/" rel="noopener noreferrer"&gt;user identification&lt;/a&gt; when using the targeting feature, so I also created a ConfigCat user object and assigned it a unique ID from the user store.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Finally, add the &lt;code&gt;correct__option&lt;/code&gt; class to the option button:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"options"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt;
        &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"(option, index) in question.options"&lt;/span&gt;
        &lt;span class="na"&gt;:id=&lt;/span&gt;&lt;span class="s"&gt;"`option${index + 1}`"&lt;/span&gt;
        &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn option"&lt;/span&gt;
        &lt;span class="na"&gt;:class=&lt;/span&gt;&lt;span class="s"&gt;"{ correct__option: (optionsDisabled &amp;amp;&amp;amp; option === question.answer &amp;amp;&amp;amp; isAnswerHighlightEnabled) }"&lt;/span&gt;
        &lt;span class="na"&gt;:disabled=&lt;/span&gt;&lt;span class="s"&gt;"optionsDisabled"&lt;/span&gt;
        &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"selectOption(option)"&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        {{option}}
      &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm using Vue's conditional classes to highlight the correct option when the feature flag is enabled. Let's now turn our attention to Google Analytics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up Google Analytics
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://developers.google.com/analytics" rel="noopener noreferrer"&gt;Google Analytics&lt;/a&gt; is a popular online analytics platform that allows individuals and organizations to gain insights into user interactions. For this demo, we'll use &lt;a href="https://configcat.com/docs/integrations/google-analytics/" rel="noopener noreferrer"&gt;ConfigCat's Google Analytics integration&lt;/a&gt; to gather and visualize event data for our A/B test.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Following the instructions on &lt;a href="https://support.google.com/analytics/answer/9304153" rel="noopener noreferrer"&gt;adding Google Analytics to a website&lt;/a&gt;, I'll add the following code after the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; tag in &lt;code&gt;index.html&lt;/code&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Google tag (gtag.js) --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script
  &lt;/span&gt;&lt;span class="na"&gt;async&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://www.googletagmanager.com/gtag/js?id=YOUR-MEASUREMENT-ID"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataLayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataLayer&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;gtag&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;dataLayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;gtag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="c1"&gt;// send the userID to Google Analytics&lt;/span&gt;
  &lt;span class="nf"&gt;gtag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR-MEASUREMENT-ID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the snippet above, I set up Google Analytics on the web page and set the user ID I generated for the player. The user ID helps Google Analytics &lt;a href="https://support.google.com/analytics/answer/9213390" rel="noopener noreferrer"&gt;track users across different devices&lt;/a&gt; and is always generated by the website owners. In this case, I'm keeping things simple by using a randomly generated ID stored in local storage. But in a real-world app, you'll probably have a database where you store your user IDs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;INFO&lt;/strong&gt;: 💡Google Analytics doesn't allow you to use emails or any other form of Personal Identifiable Information (PII) for your user IDs. Read the documentation for more about the &lt;a href="https://support.google.com/analytics/answer/9213390" rel="noopener noreferrer"&gt;rules and limitations for user IDs&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add a restart event to the top of &lt;code&gt;QuizContainer.vue&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// send the restart event to Google Analytics&lt;/span&gt;
  &lt;span class="nf"&gt;gtag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;event&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;restart&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;generateQuestions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;questionCounter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;currentQuestion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;questions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;questionCounter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;optionsDisabled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'll use the &lt;code&gt;restart&lt;/code&gt; event to track the number of times users restart the game after a session.&lt;/p&gt;

&lt;p&gt;Google Analytics will start tracking the events when users play the game, but it won't be able to identify which variation they belong to. I'll solve this issue with variation IDs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sending ConfigCat Variations to Google Analytics
&lt;/h3&gt;

&lt;p&gt;Whenever you create a targeting rule in ConfigCat, each variation gets a unique ID. I'll send the &lt;a href="https://configcat.com/blog/2022/10/14/how-to-use-variation-ids-in-configcat-for-ab-testing/#what-is-a-variation-id" rel="noopener noreferrer"&gt;variation IDs&lt;/a&gt; to Google Analytics so it can match events to their correct variation.&lt;/p&gt;

&lt;p&gt;Update &lt;code&gt;onBeforeMount&lt;/code&gt; in &lt;code&gt;Question.vue&lt;/code&gt; with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;onBeforeMount&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;configCatClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flagEvaluated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evaluationDetails&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// send feature flag information to Google Analytics&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;configcat-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;evaluationDetails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;evaluationDetails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;gtag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;event&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;experience_impression&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;exp_variant_string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;variation_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;evaluationDetails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;variationId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;setHighlightStatus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm using the &lt;a href="https://configcat.com/docs/sdk-reference/js/#hooks" rel="noopener noreferrer"&gt;&lt;code&gt;flagEvaluated&lt;/code&gt; hook&lt;/a&gt; with a special gtag event called &lt;code&gt;experience_impression&lt;/code&gt; to send data whenever the app evaluates (checks the value of) the feature flag. &lt;a href="https://developers.google.com/analytics/devguides/collection/ga4/integration#events" rel="noopener noreferrer"&gt;The &lt;code&gt;experience_impression&lt;/code&gt; event&lt;/a&gt; takes the parameters &lt;code&gt;exp_variant_string&lt;/code&gt; and &lt;code&gt;variation_id&lt;/code&gt; that we can use to segment users in Google Analytics. For example, if a user is in the control group with a variation ID of &lt;code&gt;64c323sh&lt;/code&gt;, the event's parameters will evaluate to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exp_variant_string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;configcat-highlight_answer-false&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;variation_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;64c323sh&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the final step, I'll create audiences in Google Analytics based on the variation data it receives from the game.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating Audiences in Google Analytics
&lt;/h3&gt;

&lt;p&gt;You can use &lt;a href="https://support.google.com/analytics/answer/12799087?hl=en" rel="noopener noreferrer"&gt;Audiences in Google Analytics&lt;/a&gt; to segment users based on the actions they perform or properties they share. For example, in a SaaS app, you can create an audience for customers in a particular country to analyze their behaviors compared to customers in other countries.&lt;/p&gt;

&lt;p&gt;Before creating audiences for the variations you created in ConfigCat, you may have to wait for up to 24-48 hours for the events' data to reach Google Analytics. However, you can confirm that users are firing the events by checking the Realtime reports page in your Google Analytics dashboard.&lt;/p&gt;

&lt;p&gt;Once Google Analytics has received the events data, follow these steps to create audiences:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to the Admin page on your dashboard and select "Audiences" under "Data Display".&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9k0q18uloatjv891myba.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9k0q18uloatjv891myba.png" alt="Select audiences" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select "New Audience", then "Create a custom audience".&lt;/li&gt;
&lt;li&gt;Name the audience "Answers highlighted" and give it a description.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fck7m0kidqtt8kgbmz1qh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fck7m0kidqtt8kgbmz1qh.png" alt="Name audience" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click "Add new condition".&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7c2a1x0g44t0vq8tsgm1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7c2a1x0g44t0vq8tsgm1.png" alt="Add new condition button highlighted" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click "Events", then select &lt;code&gt;experience_impression&lt;/code&gt; from the list.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftvsygupwg5xxvl0s85w2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftvsygupwg5xxvl0s85w2.png" alt="Selecting experience impression from the list of events" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click "Add parameter" and select &lt;code&gt;variation_id&lt;/code&gt; under "Other".&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0vrj4nz7h9xmjky0rf5u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0vrj4nz7h9xmjky0rf5u.png" alt="Selecting variation ID from the list of parameters" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to your ConfigCat dashboard and copy the variation ID for the feature flag that's turned on.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuurtlrjjggfhk70b5gru.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuurtlrjjggfhk70b5gru.png" alt="Arrow pointing to the variation ID button for the variant group in ConfigCat" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Back in Google Analytics, select "exactly matches(=)" under Condition and paste the variation ID you copied into the field.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe58wczab8j2p9js1qa6s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe58wczab8j2p9js1qa6s.png" alt="Add variation ID as a condition for belonging to the audience" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click "Apply" then "Save" in the top right corner.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Follow steps 2-9 above to create an audience for the other variation. But in this case, copy the variation ID for the other fifty percent of users who do not have the flag turned on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4s8euiq5y3cdmqhkeocr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4s8euiq5y3cdmqhkeocr.png" alt="The audiences, 'Answers highlighted' and 'Answers not highlighted' in the audiences list" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the audiences created, I can build reports to analyze the data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Visualizing Data from the A/B Test
&lt;/h3&gt;

&lt;p&gt;You may have to wait another 24-48 hours before Google Analytics includes users in your audiences. After that, you can follow these steps to view the data:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to the Reports page and select "Events" from the sidebar.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwbe2wsr0kj7wa2wioo2p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwbe2wsr0kj7wa2wioo2p.png" alt="Select events from the sidebar" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also access events from the events card in the Reports snapshot page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg72ysu2cdwb3tg0yn4h5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg72ysu2cdwb3tg0yn4h5.png" alt="A card containing the top events on the 'Reports snapshot' page" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click "Add comparison", then "Create new".&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe9ai1c64lfle3nb3di1f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe9ai1c64lfle3nb3di1f.png" alt="Add comparison button highlighted" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select "Audience name" under "Dimension", then select "exactly matches" under "Match type".&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1wqbbicpqirdxr6ids1b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1wqbbicpqirdxr6ids1b.png" alt="Setting up fields for comparison" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select "Answers highlighted" under value.&lt;/li&gt;
&lt;li&gt;Click "Save" in the top right corner and "Confirm" in the dialog that opens.&lt;/li&gt;
&lt;li&gt;Repeat steps 2 - 5 for the second audience, "Answers not highlighted".&lt;/li&gt;
&lt;li&gt;Remove the "All users" comparison.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftyc78m3hvveci0q1h7u9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftyc78m3hvveci0q1h7u9.png" alt="Arrow pointing to cancel icon next to 'All Users' comparison" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you've completed the steps above, your chart should look similar to this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz9ln68ef55z3gee3pg6i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz9ln68ef55z3gee3pg6i.png" alt="Line chart showing events fired from both audiences" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can scroll down to see details of each event per audience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkv6ugozjnheluvn7qbrh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkv6ugozjnheluvn7qbrh.png" alt="The 'restart' event highlighted for both audiences" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, before you pick the winning variation, you'd have to wait for at least a week so you can see how users interact with the new feature over time. Also, take note of any external factors that might affect these results. You can ask users for feedback on the feature when you end the experiment so you can understand the results better.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TIP:&lt;/strong&gt; Check out "&lt;a href="https://configcat.com/blog/2022/05/02/what-is-ab-testing/" rel="noopener noreferrer"&gt;The Role of Feature Flags in A/B Testing&lt;/a&gt;" for a complete, high-level guide to A/B testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Here's a recap of the steps I took to set up an A/B test with ConfigCat and Google Analytics. I:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Created a percentage-based feature flag with a 50% split in ConfigCat.&lt;/li&gt;
&lt;li&gt;Added ConfigCat to the app.&lt;/li&gt;
&lt;li&gt;Added Google Analytics to the app with a unique user ID to identify each user.&lt;/li&gt;
&lt;li&gt;Added the restart event to track how many times users restart.&lt;/li&gt;
&lt;li&gt;Added the &lt;code&gt;experience_impression&lt;/code&gt; event to send feature flag data to Google Analytics.&lt;/li&gt;
&lt;li&gt;Created audiences in Google Analytics.&lt;/li&gt;
&lt;li&gt;Added comparisons to analyze data for the audiences.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/configcat-labs/configcat-google-analytics-integration-sample" rel="noopener noreferrer"&gt;Demo source code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://configcat.com/docs/targeting/percentage-options/#what-are-percentage-options" rel="noopener noreferrer"&gt;ConfigCat percentage targeting docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.google.com/analytics/answer/9267572" rel="noopener noreferrer"&gt;Creating Audiences in Google Analytics&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you found this article useful. If you want to read more articles on A/B testing and other feature release strategies, check out the &lt;a href="https://configcat.com/blog/" rel="noopener noreferrer"&gt;ConfigCat blog&lt;/a&gt;. You can also stay connected to ConfigCat on &lt;a href="https://www.facebook.com/configcat" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt;, &lt;a href="https://x.com/configcat" rel="noopener noreferrer"&gt;X&lt;/a&gt;, &lt;a href="https://github.com/configcat" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, and &lt;a href="https://www.linkedin.com/company/configcat/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>googleanalytics</category>
      <category>featureflags</category>
      <category>experiment</category>
      <category>abtesting</category>
    </item>
    <item>
      <title>A quick guide to Feature Flag Naming Conventions</title>
      <dc:creator>Zayyad Muhammad Sani</dc:creator>
      <pubDate>Sun, 24 Dec 2023 10:39:07 +0000</pubDate>
      <link>https://dev.to/zms/a-quick-guide-to-feature-flag-naming-conventions-24ia</link>
      <guid>https://dev.to/zms/a-quick-guide-to-feature-flag-naming-conventions-24ia</guid>
      <description>&lt;p&gt;Can naming feature flags be hard?&lt;/p&gt;

&lt;p&gt;Yes. Just like variables in programming, naming feature flags can get tricky if you don't follow a naming standard. When feature flags don't have good names, it can be difficult for people using them to remember what they do. In this article, we'll see a few naming conventions we can use for feature flags.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Quick Refresher on Feature Flags
&lt;/h2&gt;

&lt;p&gt;Feature flags (or feature toggles) are controls that allow you to turn features in your software on or off without having to redeploy your code. Using feature flags, you can separate deployments from releases by deploying code and keeping it disabled until you're ready to turn it on. They have many use cases, such as &lt;a href="https://configcat.com/blog/2022/01/14/progressive-delivery/#canary-releases"&gt;canary releases&lt;/a&gt;, &lt;a href="https://configcat.com/blog/2022/05/02/what-is-ab-testing/"&gt;A/B testing&lt;/a&gt;, and even infrastructure migrations. With so many use cases, you can start to see how a naming convention can come in handy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conventions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  General Rule
&lt;/h3&gt;

&lt;p&gt;A feature flag's name should be descriptive enough to explain what it does; its function should be obvious to anyone who looks at it. That said, don't be afraid to give your feature flags long names. It's better for a feature flag to have a lengthy, descriptive name than a short, confusing one. Let’s take an example.&lt;/p&gt;

&lt;p&gt;Imagine that an online Markdown editor software has a new spellcheck feature in development. The developers want to hide it behind a feature flag and turn it on when it's ready for release. They name the feature flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rollout_text_editor_spellchecker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The word "rollout" tells us that when the developers turn the flag on, it will release the spellchecker feature to users. The "text_editor_spellchecker" part is self-explanatory. This feature flag's name is pretty descriptive, and anybody using it is unlikely to be confused about what it does. Let's see what other information we can add to feature flag names to make them more detailed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Team name
&lt;/h3&gt;

&lt;p&gt;Considering the size of development teams nowadays and the fact that non-development teams (like marketing and product management) can be in charge of toggling feature flags, it can be helpful to prefix a feature flag’s name with the name of the team responsible for toggling it. Here's an example using our spellchecker flag from before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;naming structure:
teamname_flagfunction

flag name:
engineering_rollout_text_editor_spellchecker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the prefix "engineering" in the flag's name indicates that the engineering team in the organization is responsible for toggling the flag. When people from other teams see this name, they'll immediately know who's in charge of the flag. Here's an example for the marketing team:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flag name:
marketing_simplified_signup_form
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the marketing team is using A/B testing on a signup form to see if the company's product gets more users. They'll be responsible for toggling the flag for the experiment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Flag Type
&lt;/h3&gt;

&lt;p&gt;There are 4 main &lt;a href="https://configcat.com/blog/2022/07/08/how-long-should-you-keep-feature-flags/#different-types-of-feature-flags-and-their-various-life-span"&gt;types of feature flags&lt;/a&gt;: release, experiment, ops, and permission. They all have defining characteristics, so it can be a good idea to include them in a feature flag’s name. Let’s apply this convention to our previous examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;naming structure:
teamname_flagtype_flagfunction

flag name:
engineering_release_rollout_text_editor_spellchecker

flag name:
marketing_experiment_simplified_signup_form
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since the spellchecker feature is deployed but won't be released until it's ready, it's hidden behind a release flag. As for the signup form, it's hidden behind an experiment flag because the marketing team is running an A/B test on it.&lt;/p&gt;

&lt;p&gt;You might have noticed that the names are significantly longer than they were before. I know I said you shouldn't be afraid to give your feature flags long names, but it's also nice to keep things balanced so that your flag names don't get too complex. Let's take a short detour to see how we can achieve this balance.&lt;/p&gt;

&lt;h3&gt;
  
  
  ConfigCat's Flag Hints and Tags
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://configcat.com/"&gt;ConfigCat&lt;/a&gt; is a cloud-based &lt;a href="https://configcat.com/blog/2023/02/28/what-is-a-feature-flag-management-system/"&gt;feature management service&lt;/a&gt; that enables you to toggle features remotely, with options for user targeting and segmentation which you can use for A/B testing, canary releases, and more.&lt;/p&gt;

&lt;p&gt;ConfigCat allows you to add hints to your feature flags, so you don’t forget what they do. You can also create tags for your feature flags so that they are easier to identify and organize. Here are our examples in ConfigCat's dashboard:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--z5mHMAG8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ibcgxemf3v0sb4b5gcvf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--z5mHMAG8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ibcgxemf3v0sb4b5gcvf.png" alt="Feature flags with descriptions and tags" width="650" height="295"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using the team names and flag types as tags, you won't need to keep them in the flag names anymore. And as a bonus, you'll be able to filter your feature flags by their tags. Though the flag names are shorter, they're still verbose, and you can add more information about your flags thanks to hints.&lt;/p&gt;

&lt;p&gt;That's all for our little detour. Let's get back to the conventions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Longevity
&lt;/h3&gt;

&lt;p&gt;If you want to indicate how long a feature flag is going to be used, you can add suffixes like "temp" and "perma" to its name. With this convention, you can easily spot a feature flag that is due for removal if you’re trying to clean up feature flags that are no longer in use. Let's see some examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;naming structure:
teamname_flagtype_flagfunction_longevity

flag name:
sales_permission_enable_plagiarism_checker_for_user_perma

flag name:
engineering_release_rollout_text_editor_spellchecker_temp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have a new example: a permission flag that the sales team can toggle for users that purchase the premium plagiarism checker feature. This flag will be there for as long as the app exists, so it gets "perma" in its name. The spellchecker feature is hidden behind a release flag, and since release flags are usually short-lived, the flag gets "temp" in its name.&lt;/p&gt;

&lt;h3&gt;
  
  
  Flag Creation Date
&lt;/h3&gt;

&lt;p&gt;You can include a flag's creation date in its name as a timestamp to make auditing flags easier. You may use dates as criteria for evaluating what flags to remove. For instance, if a feature flag is old and turned off, it's a good sign that it's not in use anymore. Here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;naming structure:
teamname_flagtype_flagfunction_longevity_creationdate

flag name:
engineering_release_rollout_text_editor_spellchecker_temp_051123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The date in the example is in the day-month-year format, meaning it was created on the 5th of November 2023. It's no secret that date formats can be confusing, but you're less likely to mix up dates if you select one date format for your projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Casing Styles
&lt;/h3&gt;

&lt;p&gt;Here's a list of popular case styles and how they look:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Camel case - myCoolFeatureFlag&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Kebab case - my-cool-feature-flag&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Snake case - my_cool_feature_flag&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pascal case - MyCoolFeatureFlag&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're using ConfigCat, you can choose camel, snake, or pascal case for your feature flag keys. As a bonus, you won't have to type out the name with the casing. Just give your feature flag a name, and ConfigCat will generate a key from it using the case style you chose.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SL6d0Zzk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j53wf78yfv79rkoe701s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SL6d0Zzk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j53wf78yfv79rkoe701s.png" alt="Feature flag name and key in ConfigCat" width="540" height="82"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Those are all the naming conventions we'll look at in this article. You probably understand their usefulness by now, but let's go over a few more points to see just how important they are.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoiding Costly Mistakes
&lt;/h2&gt;

&lt;p&gt;There are quite a few things that could go wrong when teams do not use a naming convention for their feature flags:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Someone could accidentally turn on a feature that is not ready for production. Such a mistake can negatively impact the software's users and cause the organization financial losses.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Someone could turn on a retired feature because its flag name is similar to that of a new feature they want to launch. The impact of such a mistake will be worse if the retired feature conflicts with other parts of the software.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Someone could mistake an experiment for a feature release and turn it on for all users. The results of such an experiment will be affected, and the team might have to discard it or start all over again.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These problems are more likely to occur when teams don't have good feature flag management practices. Nevertheless, if feature flags are a part of your development process, you wouldn't want anything to go wrong because you named a feature flag poorly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The conventions mentioned here are only guides to help you name your feature flags properly. You can tweak them as much as you need, but make sure that your feature flag names are clear enough so that those using them will understand what they do and the implications of toggling them.&lt;/p&gt;

&lt;p&gt;And if you’re not using a dedicated feature management platform yet, you should try out ConfigCat. ConfigCat has a free-forever plan that offers a lot of features for small feature flagging use cases.&lt;/p&gt;

&lt;p&gt;You can also check out ConfigCat on &lt;a href="https://github.com/configcat"&gt;Github&lt;/a&gt;, &lt;a href="https://www.facebook.com/configcat"&gt;Facebook&lt;/a&gt;, &lt;a href="https://twitter.com/configcat"&gt;Twitter&lt;/a&gt;, and &lt;a href="https://www.linkedin.com/company/configcat/"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>namingconventions</category>
      <category>featuremanagement</category>
      <category>shortlivedflags</category>
      <category>longlivedflags</category>
    </item>
    <item>
      <title>Building Software with No Code</title>
      <dc:creator>Zayyad Muhammad Sani</dc:creator>
      <pubDate>Sat, 23 Dec 2023 13:44:43 +0000</pubDate>
      <link>https://dev.to/zms/building-software-with-no-code-3k1n</link>
      <guid>https://dev.to/zms/building-software-with-no-code-3k1n</guid>
      <description>&lt;p&gt;As the world increasingly becomes digital, we rely on software to solve problems that pop up in everyday life. However, because traditional software development is complex and time-consuming, there aren't enough software developers to meet the high demand for software products. No-code and low-code tools offer a solution to this problem by allowing people to create software and multimedia content and perform complex tasks without writing code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Low Code vs No Code
&lt;/h2&gt;

&lt;p&gt;Low-code and no-code tools are very similar. Both tools offer users graphical interfaces to create content and perform tasks. They usually have building blocks and components users can put together and customize to fit their needs. The main difference between low-code and no-code tools lies in how much you can do without writing code.&lt;/p&gt;

&lt;p&gt;No-code tools are targeted at people with no programming experience. Without writing a single line of code, anyone can create complex, full-featured websites and apps using no-code tools. However, this doesn't mean that no-code tools don't allow users to write any code at all. Many no-code tools, especially website and app builders, allow users to write custom code to extend functionality.&lt;/p&gt;

&lt;p&gt;Low-code tools are better suited to people with programming skills. They can use graphical interfaces to create simple websites and apps, then seamlessly transition to writing code to handle complex development tasks and further customize their apps. That said, non-developers can use low-code tools, but they're likely to need the services of a developer for anything beyond simple cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Types of Low-code and No-Code Tools
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8FsLD_ht--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/44m3jo93krnstpzy11uo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8FsLD_ht--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/44m3jo93krnstpzy11uo.png" alt="Marketing, apps, automation, and database" width="615" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Website and Web App Builders
&lt;/h3&gt;

&lt;p&gt;By far the most popular type of low-code and no-code tools. These tools allow users to build websites using pre-made templates, components, and themes for different use cases and industries using drag-and-drop interfaces. They offer access to plugins and integrations for further customization and extra functionality. Some common low-code and no-code website builders are &lt;a href="https://bubble.io/"&gt;Bubble&lt;/a&gt;, &lt;a href="https://webflow.com/"&gt;Webflow&lt;/a&gt;, and &lt;a href="https://wordpress.org/"&gt;WordPress&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Marketing
&lt;/h3&gt;

&lt;p&gt;These tools allow businesses and individuals to automate their marketing processes and campaigns. They offer intuitive interfaces and pre-built components that enable marketers to create landing pages, run &lt;a href="https://configcat.com/blog/2022/05/02/what-is-ab-testing/"&gt;A/B tests&lt;/a&gt;, automate email campaigns, and track analytics. Some popular marketing tools are &lt;a href="https://mailchimp.com/"&gt;MailChimp&lt;/a&gt; and &lt;a href="https://www.hubspot.com/products/marketing"&gt;Hubspot Marketing&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customer Service
&lt;/h3&gt;

&lt;p&gt;These tools empower customer service teams to create interactive chatbots, self-service portals, and knowledge bases without coding. They often provide AI-powered assistants that help agents quickly solve customer support issues. They offer integrations with many platforms so customer service teams can resolve issues from other software they use. Popular customer service platforms include &lt;a href="https://www.zendesk.com/"&gt;Zendesk&lt;/a&gt; and &lt;a href="https://www.intercom.com/"&gt;Intercom&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Spreadsheets and Databases
&lt;/h3&gt;

&lt;p&gt;These tools allow users to create spreadsheets and databases, model relationships between data, and perform complex functions on them. Users can automate repetitive data entry tasks and easily visualize data with built-in charts. One cool thing about these tools is that their users can connect them to no-code builders and use the content in them as a data source for their apps. For example, Webflow users can use &lt;a href="https://www.airtable.com/"&gt;Airtable&lt;/a&gt; as a database or CMS for their websites. &lt;a href="https://www.appsheet.com/"&gt;Google AppSheet&lt;/a&gt; takes this a step further by allowing users to generate apps using the content of spreadsheets from &lt;a href="https://workspace.google.com/products/sheets/"&gt;Google Sheets&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automation
&lt;/h3&gt;

&lt;p&gt;These platforms allow users to automate and schedule tasks in different applications without writing programming scripts. Users can trigger actions in many applications in response to specific events in another. They offer visual workflow builders and pre-built integrations so users can easily set up their automation workflows.&lt;/p&gt;

&lt;p&gt;By leveraging &lt;a href="https://configcat.com/blog/2023/06/13/how-feature-flags-boost-software-development/#what-are-feature-flags"&gt;feature flags&lt;/a&gt;, which act as toggles for turning specific features on or off, users can gain even more control and flexibility when using no-code automation tools like &lt;a href="https://zapier.com/"&gt;Zapier&lt;/a&gt; and &lt;a href="https://www.make.com/"&gt;Make&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For example, let's say you're a software company planning to perform maintenance on a critical feature of your application. With a feature management platform like &lt;a href="https://configcat.com/"&gt;ConfigCat&lt;/a&gt;, you can temporarily turn off the feature associated with the maintenance task using a feature flag. This action then triggers an automation workflow in Zapier that sends out notification emails to your customers, informing them about the temporary unavailability of the feature and providing any necessary updates or alternatives.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use Low-Code and No-Code Builders
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w7sgd8Aw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dtnq2o97h0tt8n4hdp0c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w7sgd8Aw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dtnq2o97h0tt8n4hdp0c.png" alt="Loading bars showing different levels of code" width="669" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we look at other categories of low-code and no-code tools, like marketing and customer service, we'll see that they offer obvious benefits to their users. Software development, however, is more complex, so we'll look at the pros and cons of using low-code and no-code tools in this context.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Empowers non-developers to create software:&lt;/strong&gt; Non-developers with a solid grasp of their business objectives can create software solutions without relying on developers. They can build customized tools and applications to solve problems without coding experience. From websites for small businesses to personal blogs, anyone can bring their ideas to life with no-code tools.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Facilitates the easy creation of MVPs and prototypes:&lt;/strong&gt; A massive advantage for startups and indie founders who frequently build new products while trying to keep costs down. They allow them to close the gap between ideas and reality and increase time to market. They're also helpful for quickly creating functional prototypes in traditional software development environments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Provides cost-friendly solutions for a wide range of use cases:&lt;/strong&gt; This is true, especially for small businesses that need basic websites and apps. Many no-code platforms allow them to save money they would have used to hire developers. Their features also make it easy to avoid the additional costs of maintaining software.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Limits the ability to customize and adapt software to specific needs:&lt;/strong&gt; Though low-code and no-code tools are getting better with customization, they are not the best choice for custom software that has to look and function in very specific ways. Most low-code and no-code tools make users sacrifice control for ease-of-use. They might not be a right fit for people and organizations that need total control of their projects and visibility into all their systems. This lack of control can affect an application's ability to scale.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Raises potential security risks:&lt;/strong&gt; Low-code and no-code platforms heavily depend on plugins and extensions to perform different functions. These can be security risks if plugin developers do not take proper security measures when building them or if the platform does not properly verify plugins. It's also common for users to stick with older, vulnerable versions of plugins because they're incompatible with newer versions of the platform. Another source of security risks in low-code and no-code tools comes from their focus on collaboration and ease of sharing data. Users may grant sweeping access to other users or misconfigure applications in ways that attackers can exploit to steal valuable data.  &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;TIP 💡: The Open Web Application Security Project (OWASP) has a list of &lt;a href="https://owasp.org/www-project-top-10-low-code-no-code-security-risks/"&gt;10 low-code/no-code security risks&lt;/a&gt;, containing descriptions of these risks and how to mitigate them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Future of Software Development
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vILpKlAz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6hirnsz8ni1tb1obu3n2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vILpKlAz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6hirnsz8ni1tb1obu3n2.png" alt="Arrows pointing to the right" width="615" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Research firm Gartner predicts that organizations will build &lt;a href="https://www.gartner.com/en/newsroom/press-releases/2021-11-10-gartner-says-cloud-will-be-the-centerpiece-of-new-digital-experiences"&gt;70% of applications with low-code and no-code tools&lt;/a&gt; by 2025. As these tools become more powerful and allow people to build more complex software, people usually ask if they will replace developers or become the standard for software development. The answer, for now, is no. On the contrary, low-code and no-code tools have created more job opportunities for developers, as programming skills are still essential for creating custom functionality.&lt;/p&gt;

&lt;p&gt;I'll ask a more interesting question.&lt;/p&gt;

&lt;p&gt;Will no-code + generative AI tools, like ChatGPT, remove the need for programmers? After all, we are starting to see &lt;a href="https://cloudblogs.microsoft.com/powerplatform/2023/03/16/power-platform-is-leading-a-new-era-of-ai-generated-low-code-app-development/"&gt;no-code platforms incorporate generative AI into their systems&lt;/a&gt;, allowing users to create content through prompts. The answer is still no, as current generative AI tools don't always produce correct, high-quality code. They also don't have the insight experienced programmers use to solve complex, edge-case problems. However, these tools are improving every day, and as time goes by, they may reach that level someday.&lt;/p&gt;

&lt;h3&gt;
  
  
  While we wait for the Future...
&lt;/h3&gt;

&lt;p&gt;Have you ever needed to remotely turn on a feature in your app without redeploying code? With &lt;a href="https://configcat.com/"&gt;ConfigCat&lt;/a&gt;, you can seamlessly toggle features on or off in real time, providing personalized experiences for your users, greater control over feature releases, and much more. ConfigCat &lt;a href="https://configcat.com/integrations/"&gt;integrates&lt;/a&gt; with no-code tools like &lt;a href="https://configcat.com/docs/integrations/zapier/"&gt;Zapier&lt;/a&gt; and &lt;a href="https://configcat.com/docs/integrations/zoho-flow/"&gt;Zoho Flow&lt;/a&gt;, so you can automatically perform actions in other apps when you turn features on or off.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://app.configcat.com/auth/signup"&gt;Sign up&lt;/a&gt; for ConfigCat's free-forever plan to get up and running quickly. Or &lt;a href="https://calendly.com/configcat/demo"&gt;schedule a demo&lt;/a&gt;, and our team will give you a personalized walkthrough of ConfigCat and its features. The most lovable feature flag service... ever.&lt;/p&gt;

&lt;p&gt;You can also check out ConfigCat on &lt;a href="https://www.facebook.com/configcat"&gt;Facebook&lt;/a&gt;, &lt;a href="https://twitter.com/configcat"&gt;Twitter&lt;/a&gt;, &lt;a href="https://www.linkedin.com/company/configcat/"&gt;LinkedIn&lt;/a&gt;, and &lt;a href="https://github.com/configcat"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>lowcode</category>
      <category>nocode</category>
      <category>softwaredevelopment</category>
      <category>innovation</category>
    </item>
    <item>
      <title>Feature Flag Use Cases for Non-developers</title>
      <dc:creator>Zayyad Muhammad Sani</dc:creator>
      <pubDate>Sun, 21 May 2023 21:57:04 +0000</pubDate>
      <link>https://dev.to/zms/feature-flag-use-cases-for-non-developers-56ac</link>
      <guid>https://dev.to/zms/feature-flag-use-cases-for-non-developers-56ac</guid>
      <description>&lt;p&gt;Creating software products is a highly collaborative process nowadays. Developers, designers, product managers, marketers, and many others all work together to bring software to life and ensure a smooth experience for customers.&lt;/p&gt;

&lt;p&gt;Though developers are responsible for building new features, the control of releasing these features might be better in the hands of people that are outside of engineering teams. We can give these people control with the help of feature flags.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Q1P_Zpha--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7rimia0r0gjrxy3240vo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Q1P_Zpha--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7rimia0r0gjrxy3240vo.png" alt="Business presentation" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What are feature flags?
&lt;/h2&gt;

&lt;p&gt;Feature flags (or feature toggles) are basically on/off switches that allow you to toggle a feature in software after you've deployed it, letting you launch new features and change your software configuration without (re)deploying code.&lt;/p&gt;

&lt;p&gt;With feature flags, organizations can build features into an app, and release them when they want, separating feature deployment from feature release.&lt;/p&gt;

&lt;p&gt;Now that we know what feature flags are, we'll look at a few scenarios where non-developers can benefit from using feature flags.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running experiments
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NT0TFPfI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8fhnh0cipuh5xf3wqnut.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NT0TFPfI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8fhnh0cipuh5xf3wqnut.png" alt="People analyzing charts" width="550" height="367"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're a product manager or marketer, you might want to perform experiments like A/B testing to improve conversion rates of a website. Using feature flags, the developers can build new variants, hide them behind feature flags, and hand over control to you. You can then use the feature flags to turn on the variants for whatever percentage or segment of users you want.&lt;/p&gt;

&lt;p&gt;After running the experiments and determining what features to keep, you can use the feature flags to roll them out to everyone. If the results of the experiments are not positive, you can turn them off and go back to the drawing board.&lt;/p&gt;

&lt;h2&gt;
  
  
  Giving product demos
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mVZa1wRZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8w3p314x3mmrgmqu1qt4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mVZa1wRZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8w3p314x3mmrgmqu1qt4.png" alt="A man giving a presentation" width="550" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a product manager or salesperson, you might want to give a prospective customer a specialized demo of an app. You can show them the features in different pricing plans by toggling feature flags. You can also use feature flags to simulate how the app will behave in highly specific situations.&lt;/p&gt;

&lt;p&gt;Feature flags can also be helpful when you're trying to convince existing customers to upgrade to a higher plan. You can give the customers temporary access, so they can play with a live demo and decide if upgrading will be worth it. Once they've finished using the demo, you can leave the flag on or turn it off depending on the decisions they make.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enabling specific features for customers
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WQKqlqXI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ds7pzx8ocsi0zc1x3x5d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WQKqlqXI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ds7pzx8ocsi0zc1x3x5d.png" alt="A man interacting with a control panel" width="550" height="392"&gt;&lt;/a&gt;&lt;br&gt;
Many SaaS (Software as a Service) platforms offer custom plans that allow their customers to choose the features they want. In cases like this, it makes sense for customer support representatives to have access to feature flags, so they can turn on features for the customers after understanding their needs. They can also use feature flags to activate premium features for high-paying customers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running sales events
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NFPZcfV9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rjb4fgrf2bk99j0uzffp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NFPZcfV9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rjb4fgrf2bk99j0uzffp.png" alt="People checking out an online sale" width="550" height="384"&gt;&lt;/a&gt;&lt;br&gt;
As the head of sales of an e-commerce website, you're likely running several sales events in a year. Each sales event usually has discounts, coupons, and in-app banners. After fleshing out the details of each event, you can communicate with the developers to implement the features you need, deploy them and hide them behind feature flags. When it's time, you can launch the event without having the developers do it for you. And when you want to end the campaign, you only need to turn the flags off.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rolling back features
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--b0JMtQpr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jwec48fk7e0333a2bji9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--b0JMtQpr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jwec48fk7e0333a2bji9.png" alt="An app showing an error screen" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
Sometimes, when a new feature of an app goes live, it might not work well despite rigorous testing, causing bugs in the app and users to complain. If an organization uses feature flags as part of its development process, the product manager can roll back to a stable version of the app with a feature flag. It's even better if they use a cloud-based feature flag service like &lt;a href="https://configcat.com/"&gt;ConfigCat&lt;/a&gt;, so even if complaints come in after work hours, changes can easily be rolled back at any time. When the developers fix the bugs in the feature, the product manager can turn it back on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;At first glance, feature flags might seem like something only developers should handle. But as we've seen, there are scenarios where it makes sense for non-developers to use feature flags. So whether you're in product management, marketing, or even the CEO of an organization, feature flags can give you control over features and functionality most relevant to your role.&lt;/p&gt;

&lt;p&gt;You can check out ConfigCat on &lt;a href="https://twitter.com/configcat"&gt;Twitter&lt;/a&gt;, &lt;a href="https://www.facebook.com/configcat"&gt;Facebook&lt;/a&gt;, &lt;a href="https://github.com/configcat"&gt;Github&lt;/a&gt;, and &lt;a href="https://www.linkedin.com/company/configcat/"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>featureflags</category>
      <category>productmanager</category>
      <category>marketing</category>
      <category>customer</category>
    </item>
    <item>
      <title>How to A/B Test in Nest.js with ConfigCat and Amplitude</title>
      <dc:creator>Zayyad Muhammad Sani</dc:creator>
      <pubDate>Sun, 21 May 2023 20:59:59 +0000</pubDate>
      <link>https://dev.to/zms/how-to-ab-test-in-nestjs-with-configcat-and-amplitude-584j</link>
      <guid>https://dev.to/zms/how-to-ab-test-in-nestjs-with-configcat-and-amplitude-584j</guid>
      <description>&lt;p&gt;A/B testing answers the question: "Which of these versions will bring me better results, A or B?". It allows you to test two variations of a page to see which has a more positive impact. This could mean increased sign-ups for a landing page, more purchases on an e-commerce store, or even smoother user processes in an app. It all depends on what you want to improve. How does A/B testing work though?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OaUv95VH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pizfn5e7lvfztyt93h7j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OaUv95VH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pizfn5e7lvfztyt93h7j.png" alt="A/B Testing Cover" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The A/B Testing Process
&lt;/h2&gt;

&lt;p&gt;A/B testing starts off with some analysis. After analyzing data from your site, you notice a page or a component that you can improve. After you figure out a way to improve it, you implement the changes, but you don't immediately release it to everyone because you're not sure how they will respond to it. To limit the risk of poor reception, you release the change to a percentage of your users.&lt;/p&gt;

&lt;p&gt;One group of users will see the current version of the page, called the &lt;em&gt;control&lt;/em&gt;. The other group will see the new version, called the &lt;em&gt;variant&lt;/em&gt;. Whichever version makes the most positive impact will become the default version for all users.&lt;/p&gt;

&lt;p&gt;ConfigCat's feature flag service makes A/B testing easier with percentage-based targeting. Percentage-based targeting allows you to split your users into groups and turn features on/off for the groups you want.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Quick refresher:&lt;/strong&gt; Feature flags let you launch new features and change your software configuration without (re)deploying code.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I'll walk you through setting up an A/B test in a &lt;a href="https://github.com/configcat-labs/ab-testing-nestJS-sample"&gt;Nest.js app&lt;/a&gt; with &lt;a href="https://configcat.com/"&gt;ConfigCat&lt;/a&gt; and analyze the results in the &lt;a href="https://amplitude.com/"&gt;Amplitude analytics platform&lt;/a&gt;. Let's take a look at the app we'll be testing. &lt;/p&gt;

&lt;h2&gt;
  
  
  Sample app
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--etmHZLRG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jcdfodkts8xlqy61y2bj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--etmHZLRG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jcdfodkts8xlqy61y2bj.png" alt="Newsletter: control" width="560" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'll run this A/B test on a fictional student management SaaS(Software as a Service) platform. The platform has a newsletter that offers helpful resources for educators, but the sign-up conversion rate is low. From my analytics dashboard, I notice I receive about 1000 visitors per month, but only about 20 people sign up. So I decide to run an A/B test on the newsletter section's heading. Specifically, I want to change the heading "Subscribe to Our Newsletter" to "Get Exclusive Access to Content for Educators" to see if that will capture their interest and get them to sign up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zoDwAPN3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n43vvuvdl1bgvpz37ehk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zoDwAPN3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n43vvuvdl1bgvpz37ehk.png" alt="Newsletter: variant" width="559" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;If you want to code along, you'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js v16 or above&lt;/li&gt;
&lt;li&gt;Some knowledge of Nest.js and TypeScript or JavaScript&lt;/li&gt;
&lt;li&gt;A Nest.js app connected to ConfigCat.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you don't have a ConfigCat account, &lt;a href="https://app.configcat.com/signup"&gt;create one here&lt;/a&gt;, then follow the steps in &lt;a href="https://configcat.com/blog/2022/08/19/how-to-use-feature-flags-in-nestjs/"&gt;this tutorial&lt;/a&gt; to connect ConfigCat to Nest.js.&lt;/p&gt;

&lt;p&gt;The site runs on a Nest.js backend and a React frontend. I'll focus on the Nest.js part because that's where most of the logic will live. You can see the &lt;a href="https://github.com/configcat-labs/ab-testing-nestJS-sample"&gt;complete source code&lt;/a&gt; of the app on Github.&lt;br&gt;
Let's see how to set up.&lt;/p&gt;
&lt;h2&gt;
  
  
  Splitting users in ConfigCat
&lt;/h2&gt;

&lt;p&gt;For this A/B test, I want to do a 50-50 split. To do this, I'll log on to my dashboard and do the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click the &lt;code&gt;TARGET % OF USERS&lt;/code&gt; option on the feature flag.&lt;/li&gt;
&lt;li&gt;Type 50% into any of the boxes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MVHoB2O9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zoqdoogtjvs706u9kfqf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MVHoB2O9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zoqdoogtjvs706u9kfqf.png" alt="Splitting users in ConfigCat" width="800" height="204"&gt;&lt;/a&gt;&lt;br&gt;
3 Save.&lt;/p&gt;

&lt;p&gt;On the code side, we'll need to add a unique user ID to the &lt;code&gt;getValueAsync&lt;/code&gt; call. ConfigCat requires an ID for each user, so it can uniquely identify them during percentage-based targeting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;configcat&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;configcat-node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;ConfigcatService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;configcatClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;configcat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR-SDK-KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// I'm using a random userID to keep things simple. Use login IDs, and other unique values in production &lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;userID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;343467&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;getFlagStatus&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configcatClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getValueAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;newheading&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userID&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// I'll use this later in the controller file&lt;/span&gt;
  &lt;span class="nx"&gt;getUserID&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all we'll need for both versions to go live. Let's hook up the app to Amplitude, so we can track the number of clicks the "Subscribe" button gets and then analyze the results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up Amplitude
&lt;/h2&gt;

&lt;p&gt;To connect the app to Amplitude you'll need an Amplitude account. If you don't have an account, you can &lt;a href="https://analytics.amplitude.com/signup"&gt;create one here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When creating a new account or a new Amplitude project, you'll need to select an SDK and log a test event. Amplitude guides new users through this step while signing up. However, if you're an existing user, you'll have to create a new project to access the SDK selection screen. In your dashboard, navigate to Settings &amp;gt;&amp;gt; Projects and click the "Create Project" button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iQIelJG8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z10dyoyd0zsxkkoiwv64.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iQIelJG8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z10dyoyd0zsxkkoiwv64.png" alt="SDK selection page" width="661" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since this is a Nest.js app, we'll need the Node SDK. Select the NodeJS SDK and follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install the Amplitude Node SDK.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @amplitude/node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2 Copy the code from the instructions page and paste it in the &lt;code&gt;main.ts&lt;/code&gt; file to quickly log the test event. Remove the code from the file after Amplitude receives the event.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sending Event Data to Amplitude
&lt;/h2&gt;

&lt;p&gt;With the test event out of the way, we can create our custom event:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an Amplitude service file in your project folder.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nest generate service Amplitude
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;**INFO: **This command will create an &lt;code&gt;amplitude&lt;/code&gt; folder with an &lt;code&gt;amplitude.service.ts&lt;/code&gt; file in it. It will also add the file to the providers array in &lt;code&gt;app.module.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;2 Open &lt;code&gt;amplitude.service.ts&lt;/code&gt; and add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;amplitude&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@amplitude/node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;AmplitudeService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;amplitude&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR-API-KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;sendClickEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;flagValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; 
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;event_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Click Subscribe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// event name&lt;/span&gt;
                &lt;span class="na"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;event_properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;newHeadingEnabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;flagValue&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;   
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the snippet above, I created the event "Click Subscribe" and gave it an event property &lt;code&gt;newHeadingEnabled&lt;/code&gt;, which will hold the value of the feature flag. Whenever someone clicks the button that triggers this event, it will send the value of the feature flag to Amplitude.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; Every Amplitude project has an API key, which you can find under Settings &amp;gt;&amp;gt; Projects &amp;gt;&amp;gt; [Your Project Name]. Be careful not to use one project's API key for another.&lt;/p&gt;

&lt;p&gt;For the final step, I'll import the Amplitude service in the &lt;code&gt;app.controller.ts&lt;/code&gt; file and call the &lt;code&gt;sendClickEvent&lt;/code&gt; method when the frontend sends a &lt;code&gt;POST&lt;/code&gt; request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Post&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ConfigcatService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./configcat/configcat.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AmplitudeService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./amplitude/amplitude.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;AppController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;configcatService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ConfigcatService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;amplitudeService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AmplitudeService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/flag&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;getFlagStatus&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;flagStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configcatService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getFlagStatus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;flagStatus&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// New code&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/send&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;flagStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configcatService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getFlagStatus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configcatService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getUserId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amplitudeService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendClickEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;flagStatus&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;     
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The backend is fully set up. Let's briefly take a look at the frontend code that triggers the event so we can see how it all comes together.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// other code...&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;submitForm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// call to the '/send' route in the controller&lt;/span&gt;
      &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:3000/api/send&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// remaining code here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all the code we'll need for the A/B test. Let's create a chart in Amplitude and see the results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Analyzing the Results
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Creating an Event Segmentation Chart
&lt;/h3&gt;

&lt;p&gt;I'll use an event segmentation chart to see how many users have clicked the sign up button.&lt;br&gt;
On your Amplitude dashboard, do the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;code&gt;+ New&lt;/code&gt; on the left sidebar.&lt;/li&gt;
&lt;li&gt;Select "Analysis".&lt;/li&gt;
&lt;li&gt;Select "Segmentation".&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You should now see an area with input fields:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wn8uGWFv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yok7at0hyyi8xumltkwh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wn8uGWFv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yok7at0hyyi8xumltkwh.png" alt="Event segmentation input" width="601" height="170"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm going to add the "Click Subscribe" event and filter it by the &lt;code&gt;newHeadingEnabled&lt;/code&gt; property.&lt;br&gt;
Take the following steps to add the event to the chart:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click the "Select Event" button and pick "Click Subscribe" from the dropdown.&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;+ where&lt;/code&gt; and select &lt;code&gt;newHeadingEnabled&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Select a value there, either &lt;code&gt;True&lt;/code&gt; or &lt;code&gt;False&lt;/code&gt;.
If you see only one of the two values there, you can manually add the other one. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Y03UelSQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h5dmmha8khmu7dwq9713.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y03UelSQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h5dmmha8khmu7dwq9713.png" alt="Manual adding" width="410" height="193"&gt;&lt;/a&gt;&lt;br&gt;
4 Repeat steps 1 - 3 for event B, the second event on the list. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qwa0SPw1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vufs3xq1lboxdiae41gu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qwa0SPw1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vufs3xq1lboxdiae41gu.png" alt="Events and filters selected" width="446" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you're done setting up the events, the results will appear in the chart area below. Scroll down to the chart area to see the results.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---Tx8JB6D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9awjsks8cq7owqwlcjkv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---Tx8JB6D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9awjsks8cq7owqwlcjkv.png" alt="Bar chart showing events" width="600" height="302"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see the number of clicks for the event when the new heading is enabled and when it's disabled. Click the "Uniques" button to see the number of unique users that triggered the event. You can also change the view to bar chart and compare the results to the past.&lt;/p&gt;

&lt;h3&gt;
  
  
  Picking the winning variation
&lt;/h3&gt;

&lt;p&gt;The data just started rolling in, so I'll need more time to see how both versions perform before I make my decision. For a more high-level look at A/B testing and its best practices, check out &lt;a href="https://configcat.com/blog/2022/05/02/what-is-ab-testing/"&gt;this article&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;As the drive for growth and improvement increases, A/B testing is a handy tool to have in your belt. Whether the results of your tests are positive or not, you can easily toggle them with ConfigCat's feature flags. Iterate, experiment, and learn more about your users' preferences. ConfigCat supports simple feature toggles, user segmentation, and A/B testing and has a generous free tier for low-volume use cases or those just starting out.&lt;/p&gt;

&lt;p&gt;Here's a quick recap of how I set up the A/B test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;split users into two groups in the ConfigCat dashboard&lt;/li&gt;
&lt;li&gt;set up the Amplitude SDK&lt;/li&gt;
&lt;li&gt;set up an Amplitude event with custom properties for the feature flag&lt;/li&gt;
&lt;li&gt;created an Event Segmentation chart&lt;/li&gt;
&lt;li&gt;filtered the event with its custom properties&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/configcat-labs/ab-testing-nestJS-sample"&gt;Sample app source code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://configcat.com/docs/advanced/targeting/#targeting-a-percentage-of-users"&gt;ConfigCat Percentage-based Targeting Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.docs.developers.amplitude.com/data/sdks/node/"&gt;Amplitude Node SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://help.amplitude.com/hc/en-us/articles/360052274852"&gt;Amplitude Event Segmentation Charts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://configcat.com/blog/2022/05/02/what-is-ab-testing/"&gt;A/B Testing with Feature Flags&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://configcat.com/blog/2022/08/19/how-to-use-feature-flags-in-nestjs/"&gt;Feature Flags in Nest.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.nestjs.com/"&gt;Nest.js Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can stay up to date with ConfigCat on &lt;a href="https://twitter.com/configcat"&gt;Twitter&lt;/a&gt;, &lt;a href="https://www.facebook.com/configcat"&gt;Facebook&lt;/a&gt;, &lt;a href="https://github.com/configcat"&gt;Github&lt;/a&gt;, and &lt;a href="https://www.linkedin.com/company/configcat/"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>abtesting</category>
      <category>featureflags</category>
      <category>amplitude</category>
      <category>nestjs</category>
    </item>
    <item>
      <title>Implementing A/B Testing in Next.js with ConfigCat &amp; Amplitude</title>
      <dc:creator>Zayyad Muhammad Sani</dc:creator>
      <pubDate>Sun, 09 Apr 2023 09:34:55 +0000</pubDate>
      <link>https://dev.to/zms/implementing-ab-testing-in-nextjs-with-configcat-amplitude-24j7</link>
      <guid>https://dev.to/zms/implementing-ab-testing-in-nextjs-with-configcat-amplitude-24j7</guid>
      <description>&lt;p&gt;Scenario: You’ve thought up a small change for your app. You write and test the code, and everything looks good. As you’re about to push it into production, you stop and ask yourself, “Will the users like this?”&lt;/p&gt;

&lt;p&gt;You start having doubts, that maybe the idea isn’t as good as you previously thought. Still, you continue to have a strong feeling that it’ll make your app better.&lt;/p&gt;

&lt;p&gt;One solution to this dilemma is to gradually introduce the change to a portion of users and track its impact on them. This is called A/B testing, and it’s a simple, low-risk way of letting your users pick which variant yields better results.&lt;/p&gt;

&lt;h2&gt;
  
  
  How A/B Testing Works
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uyupVLam--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/15pkh5ry054g08h294mz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uyupVLam--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/15pkh5ry054g08h294mz.png" alt="A/B Testing example" width="742" height="648"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In an A/B test, you serve users a slightly different version of a page on your app or website. Half of your users will see this new version, called the &lt;em&gt;variant&lt;/em&gt;. The other half will see the version they’re used to, called the &lt;em&gt;control&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;You track user activity on that page, and if the variant produces better results than the control, you make it the new default page for all users.&lt;/p&gt;

&lt;p&gt;There should only be one difference between the control and variant. If there is more than one difference, it will be harder to know what change caused the improvement.&lt;/p&gt;

&lt;p&gt;A/B testing is effective because users don’t know they’re participating in a test and because the versions are randomly distributed among them, leaving little room for bias. These two properties make A/B tests natural.&lt;/p&gt;

&lt;p&gt;Let’s see how to implement A/B testing in a &lt;a href="https://github.com/configcat-labs/ab-testing-nextJS-amplitude-sample"&gt;Next.js app&lt;/a&gt; with &lt;a href="https://configcat.com/"&gt;ConfigCat&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  My A/B Testing Use Case
&lt;/h2&gt;

&lt;p&gt;You can find the sample app’s code &lt;a href="https://github.com/configcat-labs/ab-testing-nextJS-amplitude-sample"&gt;on Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--52H1iG6U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oi7o8qiknqd41zn6vq0d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--52H1iG6U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oi7o8qiknqd41zn6vq0d.png" alt="A and B versions of the sample app" width="700" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This sample website allows people to download a free e-book. The website does its job well, but I believe that there’s room for improvement. I want to know if more people will download the book if they see its size on the download button. My target audience is conscious of download sizes, so I think they’ll appreciate this change.&lt;/p&gt;

&lt;p&gt;Let’s take a look at the button’s code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;download&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;Download&lt;/span&gt; &lt;span class="nx"&gt;PDF&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;showSizeEnabled&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="nx"&gt;MB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The feature flag’s value is stored in &lt;code&gt;showSizeEnabled&lt;/code&gt;. The website currently doesn’t display the download size on the button because the feature flag is turned off for all users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I’ve already connected ConfigCat to my Next.js website. You can find out how to do that &lt;a href="https://configcat.com/blog/2022/04/22/how-to-use-feature-flags-in-nextjs/"&gt;in this tutorial&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To kick off the A/B test, I’ll segment my site’s visitors with ConfigCat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Segmenting Users in ConfigCat
&lt;/h2&gt;

&lt;p&gt;ConfigCat offers user segmentation features through &lt;a href="https://configcat.com/docs/advanced/targeting/"&gt;targetting&lt;/a&gt;. I’ll use percentage targetting because I want 50% of users to see the download size on the button.&lt;/p&gt;

&lt;p&gt;Here’s how you can set it up:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to your ConfigCat dashboard.&lt;/li&gt;
&lt;li&gt;Turn on the feature flag.&lt;/li&gt;
&lt;li&gt;Select "TARGET % OF USERS".
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2Kxfs29L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y0ojtabzadp1g9qbmiqy.jpg" alt="ConfigCat targetting options" width="500" height="125"&gt;
&lt;/li&gt;
&lt;li&gt;Type 50% in any of the text boxes.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LaKcG4D4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nuduxhdtdn2pk2fkdq5u.jpg" alt="ConfigCat percentage targetting" width="500" height="169"&gt;
&lt;/li&gt;
&lt;li&gt;Save Changes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ConfigCat’s targetting feature requires a unique identifier for users when fetching the value of a feature flag. I’ll set that up in the website’s code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getServerSideProps&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;configcatClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;configcat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR-SDK-KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// feature flag key&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ffKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;showdownloadsize&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// ConfigCat requires a unique identifier when using targetting; I'm using a made-up number here&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;343467&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// set userId as the identifier&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;showSizeEnabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;configcatClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getValueAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ffKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="c1"&gt;// add userId to the props object, we'll use it in the 'HomePage' function later&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;showSizeEnabled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ffKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;HomePage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;showSizeEnabled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ffKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Done! Now, if people visit the website, some of them will see the control and some of them will see the variant. I need to know how many users are clicking the button and what segment they belong to. For that, I’ll need an analytics platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Amplitude
&lt;/h2&gt;

&lt;p&gt;Amplitude is an analytics platform used for tracking user interactions in websites and applications. I’ll use Amplitude to collect and analyze data from my website for the A/B test.&lt;/p&gt;

&lt;p&gt;If you don’t have an Amplitude account, &lt;a href="https://amplitude.com/"&gt;create one here&lt;/a&gt; and follow the sign-up process until you reach the SDK selection page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FYx-oDf1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/al1fivw1fekpjpvtamyb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FYx-oDf1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/al1fivw1fekpjpvtamyb.jpg" alt="Amplitude: Choose SDK page" width="750" height="382"&gt;&lt;/a&gt;&lt;br&gt;
Select "JavaScript SDK" and you’ll be taken to the next page. There, you’ll have to set up Amplitude in your code and log a test event.&lt;/p&gt;

&lt;p&gt;Take the following steps to log an event:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open your command line application in your project’s root directory and run the command below.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;amplitude-js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;2 . Import amplitude-js in the &lt;code&gt;index.js&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;amplitude&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;amplitude-js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3 . Create an Amplitude instance in the &lt;code&gt;HomePage&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;amplitudeInstance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;amplitude&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR-API-KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Make sure to replace &lt;code&gt;YOUR-API-KEY&lt;/code&gt; with the key from your Amplitude account. You can find it under the Settings &amp;gt; Projects &amp;gt; ‘Your Project’ page.&lt;/p&gt;

&lt;p&gt;4 . Create an event handler to send data to Amplitude when a user clicks the button.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;amplitude&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amplitudeInstance&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;logEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Download Button Clicked!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5 . Click the button in your app to log the event to Amplitude.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i9jIjN8o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/43692l9uvuok0ccluq72.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i9jIjN8o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/43692l9uvuok0ccluq72.jpg" alt="Amplitude: Event received" width="500" height="229"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Make sure to turn off any adblockers you have. They might prevent you from logging the event to Amplitude.&lt;/p&gt;

&lt;p&gt;ConfigCat offers official Amplitude integration. It’s not required for working with Amplitude but it’s recommended. Check out how to &lt;a href="https://configcat.com/docs/integrations/amplitude/"&gt;activate the integration here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’ve now connected Amplitude to my site. Let’s see how to send event data over to Amplitude.&lt;/p&gt;
&lt;h2&gt;
  
  
  Sending Data to Amplitude
&lt;/h2&gt;

&lt;p&gt;Amplitude does not automatically know what my user segments are. I’ll have to send the status of the feature flag to Amplitude for each user that clicks the button. To do this, I’ll need to create a custom &lt;a href="https://help.amplitude.com/hc/en-us/articles/115002380567-User-properties-and-event-properties"&gt;user property&lt;/a&gt; with Amplitude’s Identify API.&lt;br&gt;
Add this code under the Amplitude initialization in the &lt;code&gt;HomePage&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;identity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;amplitude&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Identify&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// custom Amplitude user property&lt;/span&gt;
&lt;span class="nx"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ffKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;showSizeEnabled&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;amplitude&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;identify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;set()&lt;/code&gt; method receives a key-value pair as its argument. It creates a custom user property with the key as the name, "showdownloadsize" in this case. The value for this user property will be &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;, depending on what segment the user falls under.&lt;/p&gt;

&lt;p&gt;If you’re following along, your code should look similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Head&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/head&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Image&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Layout&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./components/layout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;configcat&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;configcat-js-ssr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;amplitude&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;amplitude-js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getServerSideProps&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;configcatClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;configcat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR-SDK-KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// feature flag key&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ffKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;showdownloadsize&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// ConfigCat requires a unique identifier when using targetting; I'm using a made-up number here&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;343467&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// set userId as the identifier&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;showSizeEnabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;configcatClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getValueAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ffKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;showSizeEnabled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ffKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;HomePage&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;showSizeEnabled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ffKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;amplitudeInstance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;amplitude&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR-API-KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;identity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;amplitude&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Identify&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// custom Amplitude user property&lt;/span&gt;
    &lt;span class="nx"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ffKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;showSizeEnabled&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;amplitude&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;identify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handleClick&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;amplitude&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amplitudeInstance&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;logEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Download Button Clicked!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="c1"&gt;// markup here&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s all the code we’ll need for the A/B test. All that’s left is to set up a chart in Amplitude and view the results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up Segmentation Charts in Amplitude
&lt;/h2&gt;

&lt;p&gt;Amplitude offers several charts for analysing events. For this A/B test, I’ll use the Event Segmentation chart to view how many users have clicked the button depending on the segment they fall under.&lt;/p&gt;

&lt;p&gt;Let’s create an Event Segmentation chart:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click the "New" button on the sidebar.&lt;/li&gt;
&lt;li&gt;Select the "Analysis" option.&lt;/li&gt;
&lt;li&gt;Click "Segmentation".&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once you’ve created the chart, you should see this area with several input fields:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pWome_3j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/niamos89wslx8c73f201.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pWome_3j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/niamos89wslx8c73f201.jpg" alt="Event segmentation modules" width="650" height="239"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can select events from the Events module on the left. You can use the Segmentation module on the right to categorise your users based on the conditions you want. My site’s visitors are segmented based on who sees the download size and who doesn’t.&lt;/p&gt;

&lt;p&gt;Let’s reflect this in the chart:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select "Download button clicked!" from the "Select Event" dropdown.&lt;/li&gt;
&lt;li&gt;Rename "All Users" in the Segmentation module to "Download Size Enabled".&lt;/li&gt;
&lt;li&gt;Click "Add Segment" and name the new segment "Download Size Disabled".&lt;/li&gt;
&lt;li&gt;Click the "Select property" button under each segment and pick the custom "showdownloadsize" property sent from the website. Set the property values to "True" and "False" respectively.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1mv7bVuM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/75xrh5l74axfqll2bqf4.jpg" alt="Event segmentation fully set up" width="650" height="343"&gt;
&lt;/li&gt;
&lt;li&gt;Give your chart a name and click "Save" in the top right corner.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You should now see the chart populated with data. Click the "Uniques" tab so you can see the number of unique users who triggered events in your app. You can also change the chart view to "Bar chart" for a more straightforward look at things.&lt;/p&gt;

&lt;p&gt;That’s all for setting up, now it’s time to analyze the data.&lt;/p&gt;
&lt;h2&gt;
  
  
  Analyzing the Results
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_56Ce4uW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6mki0jbyoza7dffa6be5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_56Ce4uW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6mki0jbyoza7dffa6be5.jpg" alt="Bar chart showing results" width="700" height="361"&gt;&lt;/a&gt;&lt;br&gt;
From the chart, we can see that more users clicked the button with the download size enabled. This means that the change was a positive one. I’d still however have to wait a little while and get more data before I come to a conclusion.&lt;/p&gt;

&lt;p&gt;When you’ve completed the A/B test, there’s no need to keep the feature flag logic in your code anymore. Make sure to remove the logic from your code and delete the feature flag from your dashboard. Check out &lt;a href="https://configcat.com/blog/2022/05/02/what-is-ab-testing/"&gt;this post&lt;/a&gt; for more A/B testing best practices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Since this isn’t a live website, I don’t have any users (except myself). I mimicked different users by changing the value of the identifier that I initialized ConfigCat with. This way, Amplitude thinks that it’s receiving unique user events from the website.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// old userId - 343467&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;55555&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;A/B testing helps you clear the doubts you have when introducing small changes to your apps. Feature flags simplify the A/B testing process, making it easier for you to improve your app’s user experience and grow your business.&lt;/p&gt;

&lt;p&gt;To recap, we learned how to conduct an A/B test by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;creating user segments in ConfigCat.&lt;/li&gt;
&lt;li&gt;installing the Amplitude SDK in a Next.js app.&lt;/li&gt;
&lt;li&gt;sending event data to Amplitude.&lt;/li&gt;
&lt;li&gt;analyzing user event data in Amplitude.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find the complete code for the &lt;a href="https://github.com/configcat-labs/ab-testing-nextJS-amplitude-sample"&gt;sample app here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can stay up to date with ConfigCat on &lt;a href="https://twitter.com/configcat"&gt;Twitter&lt;/a&gt;, &lt;a href="https://www.facebook.com/configcat"&gt;Facebook&lt;/a&gt;, &lt;a href="https://github.com/configcat"&gt;Github&lt;/a&gt;, and &lt;a href="https://www.linkedin.com/company/configcat/"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>abtesting</category>
      <category>amplitude</category>
      <category>segmentation</category>
      <category>nextjs</category>
    </item>
  </channel>
</rss>
