<?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>Forem: Chavez Harris</title>
    <description>The latest articles on Forem by Chavez Harris (@codedbychavez).</description>
    <link>https://forem.com/codedbychavez</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%2F747071%2Ff887c982-1578-4166-b545-2187614ca77b.jpg</url>
      <title>Forem: Chavez Harris</title>
      <link>https://forem.com/codedbychavez</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/codedbychavez"/>
    <language>en</language>
    <item>
      <title>Using Feature Flags with Machine Learning Models</title>
      <dc:creator>Chavez Harris</dc:creator>
      <pubDate>Fri, 26 Apr 2024 13:10:52 +0000</pubDate>
      <link>https://forem.com/codedbychavez/using-feature-flags-with-machine-learning-models-e3l</link>
      <guid>https://forem.com/codedbychavez/using-feature-flags-with-machine-learning-models-e3l</guid>
      <description>&lt;p&gt;Machine learning models are the core building blocks of artificial intelligence. As of this writing, a popular AI chatbot circulating in the media and tech industry is &lt;a href="https://openai.com/blog/chatgpt" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt;. It uses several large generative language models under the hood and can perform tasks that some might describe as super-human.&lt;/p&gt;

&lt;p&gt;This advancement in AI showcases the potential of machine learning models and their transformative impact. With the number of machine learning libraries available on the internet, you can even develop your custom models. What's even better is that you can decouple the features of your model and control how they behave using feature flags.&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%2Ff3myhyqyptmzf434aup7.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%2Ff3myhyqyptmzf434aup7.png" alt="Using feature flags with machine learning models" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Using feature flags with machine learning models
&lt;/h2&gt;

&lt;p&gt;A &lt;a href="https://configcat.com/featureflags/" rel="noopener noreferrer"&gt;feature flag&lt;/a&gt; can be thought of as a toggle switch for a feature it is linked to. Using a conditional if-statement in your code, you can decide to render this feature if its flag is toggled on or choose to hide it otherwise if it is toggled off. However, feature flags aren't solely used for showing or hiding features from users. They can also be configured to augment various processes, including Canary Releases, Progressive Deployments, A/B Testing, DevOps, and CI/CD pipelines, to name a few.&lt;/p&gt;

&lt;p&gt;Feature flags can bring these benefits to your machine-learning models as well. In the following section, I'll show you how to create two machine-learning models for classifying text using Python, as it is a widely used programming language for developing machine-learning models. Then, I'll use a feature flag to control what model to use for the classification. For this, I'll use &lt;a href="https://configcat.com/" rel="noopener noreferrer"&gt;ConfigCat&lt;/a&gt;. ConfigCat is a feature flag service with unlimited team size, awesome support, and a reasonable price tag that takes 10 mins to learn. Using their &lt;a href="https://configcat.com/docs/sdk-reference/overview/" rel="noopener noreferrer"&gt;extensive list of SDKs&lt;/a&gt;, you can easily integrate feature flags in just about any programming language and technology. Let's dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  Guide: How to use feature flags with machine learning models
&lt;/h2&gt;

&lt;p&gt;Using a popular machine learning library called &lt;a href="https://spacy.io/" rel="noopener noreferrer"&gt;SpaCy&lt;/a&gt;, I'll create two text classification models. One will serve as a base model, and the other as a pro model. These can typically fit into a scenario where you only need to give paid users access to the pro model while other users use the base model.&lt;/p&gt;

&lt;p&gt;Both models take a sentence or a piece of text as input and classify it to an intent based on the training data each model was trained on. Here are the differences between the two models and the intents that they are capable of classifying text to.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Base model&lt;/strong&gt;: goodbye, greeting, business_hours
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pro model&lt;/strong&gt;: goodbye, greeting, business_hours, payment_methods&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key difference is that the pro model is capable of classifying a text such as:&lt;/p&gt;

&lt;p&gt;"Can I pay you with my Google Pay wallet?" to &lt;code&gt;payment_methods&lt;/code&gt;, whereas the base model would not.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup of a demo application
&lt;/h3&gt;

&lt;p&gt;I've created a &lt;a href="https://github.com/configcat-labs/feature-flags-with-ml-models-sample" rel="noopener noreferrer"&gt;sample app&lt;/a&gt; to demonstrate how a feature flag can be used to control which model classifies the text entered by a user.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;app.py&lt;/code&gt; file, I've imported the resources needed to build and test each model. This is followed by two functions: &lt;code&gt;train_classifier(model_type)&lt;/code&gt; and &lt;code&gt;classify_text(text, model_type)&lt;/code&gt;. The &lt;code&gt;train_classifier&lt;/code&gt; function takes &lt;code&gt;model_type&lt;/code&gt; as a parameter (either 'pro' or 'base'), and the &lt;code&gt;classify_text&lt;/code&gt; takes the text to classify and the &lt;code&gt;model_type&lt;/code&gt; to use to perform the classification.&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;# app.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.svm&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LinearSVC&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.model_selection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;train_test_split&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.feature_extraction.text&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CountVectorizer&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.feature_extraction.text&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TfidfTransformer&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pickle5&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pickle&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;train_classifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_type&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Load the training data
&lt;/span&gt;    &lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;./models/{0}/classification_data.json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_type&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;train_test_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;intent&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;random_state&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="c1"&gt;# Train the model
&lt;/span&gt;    &lt;span class="n"&gt;count_vect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CountVectorizer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;X_train_counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;count_vect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit_transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;tfidf_transformer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TfidfTransformer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;X_train_tfidf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tfidf_transformer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit_transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train_counts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LinearSVC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dual&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train_tfidf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Save the vectorizer 
&lt;/span&gt;    &lt;span class="n"&gt;vec_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;./models/{0}/vectorizer.pickle&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pickle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count_vect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vec_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c1"&gt;# Save the model
&lt;/span&gt;    &lt;span class="n"&gt;mod_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;./models/{0}/classifier.model&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pickle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mod_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c1"&gt;# Print training complete to the terminal
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Training completed for {0} model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_type&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;classify_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model_type&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Load the vectorizer
&lt;/span&gt;    &lt;span class="n"&gt;vec_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;./models/{0}/vectorizer.pickle&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;count_vect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pickle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vec_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c1"&gt;# Load the model
&lt;/span&gt;    &lt;span class="n"&gt;mod_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;./models/{0}/classifier.model&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pickle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mod_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c1"&gt;# Classify the text
&lt;/span&gt;    &lt;span class="n"&gt;text_counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;count_vect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;predicted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text_counts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decision_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text_counts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;model_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;intent&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;predicted&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;confidence&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;interact.py&lt;/code&gt; file, I've imported both the &lt;code&gt;train_classifier&lt;/code&gt; and &lt;code&gt;classify_text&lt;/code&gt; functions from &lt;code&gt;app.py&lt;/code&gt; and added a &lt;strong&gt;test_query()&lt;/strong&gt; function, which takes the text entered by the user and passes it to the &lt;code&gt;classify_text&lt;/code&gt; function, printing the result to the terminal.&lt;/p&gt;

&lt;p&gt;The second function, &lt;strong&gt;run_app()&lt;/strong&gt;, performs the command line interaction. Finally, a while loop was added at the bottom to keep the &lt;strong&gt;run_app()&lt;/strong&gt; function active until the user exits the interaction.&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;# interact.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;train_classifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;classify_text&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_query&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hi I&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;m a chatbot. Lets talk: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;classify_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pro&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&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;run_app&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;option&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1 - Interact | 2 - Train | 3 - Exit: &lt;/span&gt;&lt;span class="sh"&gt;"&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;option&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;test_query&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Model to train: 1 - Base | 2 - Pro: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;selected_model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pro&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="nf"&gt;train_classifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selected_model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;run_app&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adding a feature flag
&lt;/h3&gt;

&lt;p&gt;With the help of a feature flag, we can control which model can perform the text classification. One exciting benefit of feature flags in such a use-case scenario is that you can do this remotely without redeploying your application.&lt;/p&gt;

&lt;p&gt;1- To create a feature flag, sign up for a &lt;a href="https://app.configcat.com/sign-up" rel="noopener noreferrer"&gt;free ConfigCat account&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;2- In the &lt;a href="https://app.configcat.com/" rel="noopener noreferrer"&gt;dashboard&lt;/a&gt;, create a feature flag with the following details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name: &lt;code&gt;Pro Model&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Key: &lt;code&gt;proModel&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Hint: &lt;code&gt;When on, the pro classifier model will be used&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3- To use the feature flag, I'll install the &lt;a href="https://configcat.com/docs/sdk-reference/python/" rel="noopener noreferrer"&gt;ConfigCat SDK for Python&lt;/a&gt;. The SDK is used to connect the app to ConfigCat and download the latest value of the feature flag.&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-client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4- Import the ConfigCat SDK in &lt;code&gt;interact.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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;configcatclient&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5- Create the ConfigCat client and pass in your SDK key:&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;configcat_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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;YOUR-CONFIGCAT-SDK-KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;# Replace with your SDK key
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;6- I'll modify the &lt;strong&gt;test_query()&lt;/strong&gt; function in &lt;code&gt;interact.py&lt;/code&gt; to only use the pro model for making a classification when the feature flag is on and the base model when it is off:&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;train_classifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;classify_text&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;configcatclient&lt;/span&gt; &lt;span class="c1"&gt;# Import the ConfigCat SDK
&lt;/span&gt;
&lt;span class="n"&gt;configcat_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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;YOUR-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="c1"&gt;# Replace with your SDK key
&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;test_query&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hi I&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;m a chatbot. Lets talk: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Get the value of the proModel feature flag
&lt;/span&gt;    &lt;span class="n"&gt;proModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;configcat_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;proModel&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;# Use the pro model if the feature flag is on
&lt;/span&gt;    &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pro&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;proModel&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;classify_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ...
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Running the app
&lt;/h3&gt;

&lt;p&gt;Before testing the app and the feature flag, you'll need to ensure that you have the following installed:&lt;/p&gt;

&lt;h4&gt;
  
  
  Prerequisites
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.python.org/downloads/" rel="noopener noreferrer"&gt;Python 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pip.pypa.io/en/stable/installation/" rel="noopener noreferrer"&gt;Pip3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Steps
&lt;/h4&gt;

&lt;p&gt;1- Clone the repository&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone git@github.com:configcat-labs/feature-flags-with-ml-models-sample.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2- Create and activate a virtual environment&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv

&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3- Install dependencies and run the app&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 interact.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4- Train both base and pro models by entering the appropriate option in the command line. For this example, I'll train the base model first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;1 - Interact | 2 - Train | 3 - Exit: 2

Model to train: 1 - Base | 2 - Pro: 1

Training completed &lt;span class="k"&gt;for &lt;/span&gt;base model
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Train the pro model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;1 - Interact | 2 - Train | 3 - Exit: 2

Model to train: 1 - Base | 2 - Pro: 2

Training completed &lt;span class="k"&gt;for &lt;/span&gt;pro model
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5- Now, copy your SDK key from the &lt;a href="https://app.configcat.com" rel="noopener noreferrer"&gt;ConfigCat dashboard&lt;/a&gt; and paste it into the &lt;code&gt;YOUR-CONFIGCAT-SDK-KEY&lt;/code&gt; placeholder in the &lt;code&gt;interact.py&lt;/code&gt; file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Did it work
&lt;/h3&gt;

&lt;p&gt;1- Head over to CC and &lt;strong&gt;enable the feature flag&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;2- Run the app again and enter a text to classify. The output should show that the pro model is used for classifying the text:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
1 - Interact | 2 - Train | 3 - Exit: 1

Hi, I&lt;span class="s1"&gt;'m a chatbot. Let'&lt;/span&gt;s talk: can I pay you with my Google Pay wallet?

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'model'&lt;/span&gt;: &lt;span class="s1"&gt;'pro'&lt;/span&gt;, &lt;span class="s1"&gt;'intent'&lt;/span&gt;: &lt;span class="s1"&gt;'payment_methods'&lt;/span&gt;, &lt;span class="s1"&gt;'confidence'&lt;/span&gt;: 2.0369560833357596, &lt;span class="s1"&gt;'text'&lt;/span&gt;: &lt;span class="s1"&gt;'Can I pay you with my Google Pay wallet?'&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3- &lt;strong&gt;Disable the feature flag&lt;/strong&gt;. You should now see that the base model is used to classify the text.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The ConfigCat SDK downloads and stores the latest values automatically every 60 seconds. You can change this default behavior. Check out the &lt;a href="https://configcat.com/docs/sdk-reference/python/" rel="noopener noreferrer"&gt;ConfigCat SDK Docs&lt;/a&gt; to learn more.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;1 - Interact | 2 - Train | 3 - Exit: 1

Hi, I&lt;span class="s1"&gt;'m a chatbot. Let'&lt;/span&gt;s talk: can I pay you with my Google Pay wallet?

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'model'&lt;/span&gt;: &lt;span class="s1"&gt;'base'&lt;/span&gt;, &lt;span class="s1"&gt;'intent'&lt;/span&gt;: &lt;span class="s1"&gt;'greeting'&lt;/span&gt;, &lt;span class="s1"&gt;'confidence'&lt;/span&gt;: 0.32370119051141377, &lt;span class="s1"&gt;'text'&lt;/span&gt;: &lt;span class="s1"&gt;'Can I pay you with my Google Pay wallet?'&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the above, you can see that the feature flag was used to control which model to use in classifying the text. When the flag was off, the base model was used, and the result intent was greeting, which is an incorrect classification. When the flag was on, the pro model was used, and the result intent was &lt;code&gt;payment_methods&lt;/code&gt;, which is the correct classification.&lt;/p&gt;

&lt;h2&gt;
  
  
  The future of feature flags and machine learning models
&lt;/h2&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%2Fmkpv8zzdd4j08vi01np6.jpg" 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%2Fmkpv8zzdd4j08vi01np6.jpg" alt="The future of feature flags and machine learning models" width="798" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With machine learning models becoming increasingly popular, I believe that feature flags will play a significant role in how they are used. Feature flags can be used to control the behavior of these models, what data to use when training a model, and even what model to use for a specific task. I'm excited to see how feature flags will be used with machine learning models in the future.&lt;/p&gt;

&lt;p&gt;If you want to try what you've learned, check out &lt;a href="https://configcat.com/" rel="noopener noreferrer"&gt;ConfigCat&lt;/a&gt;. They have a &lt;a href="https://app.configcat.com/auth/signup" rel="noopener noreferrer"&gt;forever-free plan&lt;/a&gt; that you can use to get started. You can also check out the &lt;a href="https://configcat.com/docs/sdk-reference/python/" rel="noopener noreferrer"&gt;ConfigCat Python SDK Docs&lt;/a&gt; to dive deeper into using feature flags with Python. Deploy any time, release when confident.&lt;/p&gt;

&lt;p&gt;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://twitter.com/configcat" rel="noopener noreferrer"&gt;X (Twitter)&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>machinelearning</category>
      <category>featuremanagement</category>
      <category>mlmodels</category>
    </item>
    <item>
      <title>Using ConfigCat for Staged Rollouts and Canary Releases</title>
      <dc:creator>Chavez Harris</dc:creator>
      <pubDate>Thu, 18 Jan 2024 15:07:11 +0000</pubDate>
      <link>https://forem.com/codedbychavez/using-configcat-for-staged-rollouts-and-canary-releases-7k0</link>
      <guid>https://forem.com/codedbychavez/using-configcat-for-staged-rollouts-and-canary-releases-7k0</guid>
      <description>&lt;p&gt;The primary goal of software developers is to ensure user satisfaction with the features or updates they introduce. However, achieving this goal can be challenging without the right release strategy. The question often asked, then, is, "How can developers be certain that a new update or feature delivers optimal results to end users?"&lt;/p&gt;

&lt;p&gt;Two strategies that can be employed to address this concern are staged rollouts and canary releases. These strategies can be implemented using feature flags, and in this article, we explore how &lt;a href="https://configcat.com/" rel="noopener noreferrer"&gt;ConfigCat&lt;/a&gt;, a popular feature flag provider, can be used to perform staged rollouts and canary releases.&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%2F393u60rucsx7xu78tu73.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%2F393u60rucsx7xu78tu73.png" alt="Using ConfigCat for Staged Rollouts and Canary Releases cover" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What are staged rollouts and canary releases
&lt;/h2&gt;

&lt;p&gt;Staged rollouts and canary releases are both techniques used in software deployment, but they differ in their approach and purpose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Staged Rollouts: In a staged rollout, the new feature or software update is gradually released to a larger percentage of users over time. It typically starts with a small group and increases in stages until all users have access to the update. This method is used to monitor its performance with users and gather iterative feedback while minimizing the impact of any potential bug or issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Canary Releases: Canary releases on the other hand involve deploying the new version or feature to a small subset of users initially, often chosen at random or based on specific criteria, before pushing them out to the entire user base. This group, referred to as the 'canary', acts as a test to gauge the new version's stability and performance. If no issues arise, the release is then rolled out to the entire user base. This approach is more focused on identifying and addressing issues before they affect all users.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In essence, while both strategies aim to mitigate risk by releasing software updates to a subset of users initially, staged rollouts do so in a more gradual, phased manner, whereas canary releases target a smaller, more specific group for initial testing. This ensures that the new changes introduced work as expected while minimizing any negative impact on the user experience.&lt;/p&gt;

&lt;p&gt;Here is a diagram that illustrates a staged rollout and a canary release:&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%2Fj374m0t4mf1b0xu18e80.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%2Fj374m0t4mf1b0xu18e80.png" alt="Diagram of staged rollout and canary release" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The role of ConfigCat in staged rollouts and canary releases
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://configcat.com/" rel="noopener noreferrer"&gt;ConfigCat&lt;/a&gt; is a feature flag management service that serves as a &lt;a href="https://app.configcat.com" rel="noopener noreferrer"&gt;central dashboard&lt;/a&gt; to easily manage your feature flags and provides the essential tools needed to perform a staged rollout or canary release.&lt;/p&gt;

&lt;p&gt;But how can these strategies be implemented and used? The simple answer is to use feature flags.&lt;/p&gt;

&lt;p&gt;The first step is to create a feature flag for the new feature. Then, wrap the feature's code in a conditional if-statement that checks if the feature flag is enabled. This would allow the feature to be included in the app's code base, but until its flag is toggled on, it will be hidden from users.&lt;/p&gt;

&lt;p&gt;Instead of showing the new feature to everyone when the flag is on, you can configure the flag to allow the adoption of either strategy. This is where the magic of ConfigCat comes in, specifically its &lt;a href="https://configcat.com/docs/advanced/targeting/" rel="noopener noreferrer"&gt;user-targeting capabilities&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performing a canary release with ConfigCat
&lt;/h2&gt;

&lt;p&gt;To perform a canary release using ConfigCat, one can leverage its user-targeting abilities. Once the feature flag is created, user-targeting rules can be configured to target users based on their email, country, or any other custom attribute.&lt;/p&gt;

&lt;p&gt;For instance, if a new feature should be rolled out to only users within a company, you can set an email target rule. In the ConfigCat dashboard, the &lt;code&gt;CONTAINS&lt;/code&gt; operator can be used to target all email addresses that contain &lt;code&gt;@yourcompany.com&lt;/code&gt;. This will ensure that only users with email addresses containing &lt;code&gt;@yourcompany.com&lt;/code&gt; will have access to the new feature. Here's how to do this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new feature flag in the &lt;a href="https://app.configcat.com" rel="noopener noreferrer"&gt;ConfigCat dashboard&lt;/a&gt; with the following details:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Name: &lt;code&gt;Chat feature&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Key: &lt;code&gt;chatFeature&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Description: &lt;code&gt;A new chat feature for our mobile app&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&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%2Fwrmbsrwpwf1t5eleqy1g.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%2Fwrmbsrwpwf1t5eleqy1g.png" alt="Adding a feature flag in ConfigCat's dashboard" width="800" height="772"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click the &lt;em&gt;TARGET SPECIFIC USERS&lt;/em&gt; button and select &lt;code&gt;Email&lt;/code&gt; as the comparison attribute to target. Then, select the &lt;code&gt;CONTAINS&lt;/code&gt; comparator from the dropdown and enter &lt;code&gt;@yourcompany.com&lt;/code&gt; as the comparison value. Here is what that looks like in the ConfigCat dashboard:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="/assets/using-cc-for-staged-and-canary-releases/setting-a-targetting-rule.png" class="article-body-image-wrapper"&gt;&lt;img src="/assets/using-cc-for-staged-and-canary-releases/setting-a-targetting-rule.png" alt="Setting a targeting rule on the figure feature flag"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that the feature flag and targeting rule have been created, the next step is to integrate ConfigCat's SDK client for the appropriate &lt;a href="https://configcat.com/docs/sdk-reference/overview" rel="noopener noreferrer"&gt;coding language&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The code snippet below shows how &lt;a href="https://configcat.com/docs/sdk-reference/js/" rel="noopener noreferrer"&gt;ConfigCat's JavaScript SDK&lt;/a&gt; can be used to check if the chat feature is enabled for a user. Start by installing the 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 i configcat-js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Import the ConfigCat SDK&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="s2"&gt;configcat-js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Initialize the SDK with your SDK key&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="nf"&gt;getClient&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-CONFIGCAT-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;// Define the user object&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="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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;f8c9c1a5-7d9c-4c7e-9e4c-9d3d2b5b3c5c&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jane@yourcompany.com&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="c1"&gt;// Get the value of the feature flag, while passing in the user object&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chatFeature&lt;/span&gt; &lt;span class="o"&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;getValueAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chatFeature&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;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Check if the chat feature is enabled&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;chatFeature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Show the chat feature&lt;/span&gt;
  &lt;span class="nf"&gt;loadChatFeature&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="c1"&gt;// Hide the chat feature&lt;/span&gt;
  &lt;span class="nf"&gt;doSomethingElse&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;Because the user Jane has an email address that contains &lt;code&gt;@yourcompany.com&lt;/code&gt;, she will have access to the chat feature.&lt;/p&gt;

&lt;p&gt;Scenarios can be different, and email is not the only way to target users. Users can also be targeted based on their country, or any other custom attribute. More information on how to do this can be found &lt;a href="https://configcat.com/docs/advanced/targeting/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best practices for using ConfigCat for staged rollouts and canary releases
&lt;/h2&gt;

&lt;p&gt;Rollouts and canary releases are great ways to test and evaluate new features and changes before making them live for the entire user base. To achieve the best results, a well-thought-out plan and design are required. Here are some general best practices to keep in mind when using ConfigCat for staged rollouts and canary releases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Start small&lt;/strong&gt;: When performing a staged rollout or canary release, it's best to start with a small subset of users. This will allow the new changes to be tested on just a few users before rolling it out to everyone. In addition to the example described above, ConfigCat also provides a percentage option for setting the percentage of users that should have access to the new feature. Here is an example of what that setting looks like:&lt;/li&gt;
&lt;/ul&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%2Ftyqu7xuv8bkarwdoklwg.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%2Ftyqu7xuv8bkarwdoklwg.png" alt="Using a percentage option" width="800" height="183"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Roll out gradually&lt;/strong&gt;: Once the new changes are released to a small subset of users, continue rolling it out gradually if the feedback is satisfactory. This will allow the new changes to be evaluated by larger subsets of users before they become available to everyone. In the example provided, if the feedback is positive, you can add more targeting rules to the feature flag to make the chat feature available to more users.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Monitor and analyze&lt;/strong&gt;: This will help provide valuable insights to determine if the new feature or update is working as expected. Using commercial tools like &lt;a href="https://amplitude.com/" rel="noopener noreferrer"&gt;Amplitude&lt;/a&gt; and &lt;a href="https://www.datadoghq.com/" rel="noopener noreferrer"&gt;Datadog&lt;/a&gt; can aid in monitoring and analyzing the results of each rollout in real-time. Here are links to some blog articles that discuss this topic in more detail:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://configcat.com/blog/2020/12/10/custom-segmentation/" rel="noopener noreferrer"&gt;Amplitude and ConfigCat: A Guide on User Segmentation with Custom User Attributes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://configcat.com/blog/2023/02/14/how-to-implement-ab-testing-in-react-native/" rel="noopener noreferrer"&gt;How to A/B test your React Native application using feature flags&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://configcat.com/blog/2023/06/09/how-to-ab-test-kotlin/" rel="noopener noreferrer"&gt;Running an A/B Test in Android Kotlin Using ConfigCat and Amplitude&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Have a recovery strategy&lt;/strong&gt;: Even though the new changes are tested on a small subset of users, there is still a chance that they may not work as expected when the feature reaches more users. Having a recovery strategy in place is essential. It allows you to roll back the changes if they do not work as expected. With ConfigCat, performing a feature rollback is just a few clicks away, and the best part is that you can do this from the &lt;a href="https://app.configcat.com" rel="noopener noreferrer"&gt;dashboard&lt;/a&gt; without ever needing to edit or modify your application's code.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Has ConfigCat proven itself in the real world?
&lt;/h2&gt;

&lt;p&gt;The short answer is, yes! ConfigCat has been used by many companies to perform more than just staged rollouts and canary releases. Kantan.tech is one of those companies. In the blog article '&lt;a href="https://configcat.com/blog/2022/03/24/how-kantan-successfully-uses-configcat-in-its-ci-pipeline/" rel="noopener noreferrer"&gt;Kantan.tech Moves Faster than Their Competition&lt;/a&gt;', they describe that before deploying to production, they would turn flags on for internal users using ConfigCat and run one last smoke test. Once satisfied, they'll enable the flags for all users. Many other companies are also using ConfigCat in similar ways; you can read more &lt;a href="https://configcat.com/customer-success-stories/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Staged rollouts and canary releases are effective strategies for testing new features and updates before rolling them out to all users. The benefit of both strategies is that they allow for testing in production on real users, which is the best way to get feedback on how a new feature or update is working. With the power of feature flags and the &lt;a href="https://configcat.com/docs/advanced/targeting/" rel="noopener noreferrer"&gt;targeting capabilities&lt;/a&gt; of &lt;a href="https://configcat.com" rel="noopener noreferrer"&gt;ConfigCat&lt;/a&gt;, these strategies can be seamlessly integrated into any development workflow.&lt;/p&gt;

&lt;p&gt;As we aim to improve software for end users, staged rollouts and canary releases will remain vital. The implementation of these strategies may often vary, but feature flags will undoubtedly remain a key component. In addition to what we've discussed, feature flags have many other applications, as they can also be used for A/B testing, dark launches, and more; the possibilities are endless. Furthermore, they are compatible with a variety of &lt;a href="https://configcat.com/docs/sdk-reference/overview/" rel="noopener noreferrer"&gt;programming languages and popular technologies&lt;/a&gt; such as Azure, AWS, Zoho, and &lt;a href="https://configcat.com/docs/integrations/overview/" rel="noopener noreferrer"&gt;more&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Excited to give feature flags a try? You can get started with ConfigCat for free &lt;a href="https://app.configcat.com/auth/signup" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Deploy any time, release when confident. I have been using their free-forever plan in all my side projects and it has been great.&lt;br&gt;
You can also find more information on ConfigCat &lt;a href="https://configcat.com/#product" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>stagedrollouts</category>
      <category>canaryrelease</category>
      <category>featuremanagement</category>
      <category>featureflags</category>
    </item>
    <item>
      <title>Feature Flag Services Under $300</title>
      <dc:creator>Chavez Harris</dc:creator>
      <pubDate>Tue, 16 Jan 2024 20:32:19 +0000</pubDate>
      <link>https://forem.com/codedbychavez/feature-flag-services-under-300-5988</link>
      <guid>https://forem.com/codedbychavez/feature-flag-services-under-300-5988</guid>
      <description>&lt;p&gt;A few weeks ago, I set out to find the best feature flag service to fit my $300 budget. Like most software developers, I turned to Google and searched for 'Best feature flag services under $300'. While the search results provided a few intriguing options, During my search, I came across three companies that appeared promising when it comes to feature flags.&lt;/p&gt;

&lt;p&gt;I want to share these companies with you to help you choose the one that might be the best fit for your specific needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Companies that stood out to me
&lt;/h3&gt;

&lt;p&gt;Rather than making a decision solely based on my $300 budget, I wanted to take this exercise further to investigate each provider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://app.configcat.com" rel="noopener noreferrer"&gt;ConfigCat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/azure-app-configuration/overview" rel="noopener noreferrer"&gt;Azure's App configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://launchdarkly.com/" rel="noopener noreferrer"&gt;LaunchDarkly&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To make the best decision possible, I'll evaluate each provider based on the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pricing - Does the service fit my budget well?&lt;/li&gt;
&lt;li&gt;Supported platforms - Can the service integrate with my tech stack?&lt;/li&gt;
&lt;li&gt;Ease of use - Does the provider offer a simple management interface?&lt;/li&gt;
&lt;li&gt;Permission and team management - Can I assign permission roles for controlling access?&lt;/li&gt;
&lt;li&gt;Enterprise-level features - What benefits do I get at an enterprise level?&lt;/li&gt;
&lt;li&gt;Support - is a support team available to assist with difficulties or obstacles?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the above in mind, let's see what each provider offers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signing up
&lt;/h2&gt;

&lt;p&gt;The first thing I like to do for any new online service is to get a hands-on feel of the sign-up process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Signing up for ConfigCat
&lt;/h3&gt;

&lt;p&gt;Signing up for a &lt;a href="https://app.configcat.com/signup" rel="noopener noreferrer"&gt;free ConfigCat account&lt;/a&gt; was easy. I signed up using my Google account, and it took me straight to the dashboard, which I found intuitive and user-friendly.&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%2Fji4p9n41dskew5qfyu8v.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%2Fji4p9n41dskew5qfyu8v.png" alt="ConfigCat - post signup" width="800" height="277"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Signing up for Azure App Configuration
&lt;/h3&gt;

&lt;p&gt;Azure is a platform that includes various Microsoft services. You can sign up using a Microsoft or GitHub account. Afterward, you'll need to search for and create a resource for 'App Configuration' before setting up your feature flags.&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%2Fyfo2dy0yk13ymrzs40ja.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%2Fyfo2dy0yk13ymrzs40ja.png" alt="Azure App Configuration - post signup" width="800" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Signing up for LaunchDarkly
&lt;/h3&gt;

&lt;p&gt;After a quick sign-up, I received a confirmation email to finalize the sign-up process. During my first login, a popup wizard assisted me with navigation tips. Here is what the dashboard looks like:&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%2Fuz9ll88iaw3hpsl5fnhx.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%2Fuz9ll88iaw3hpsl5fnhx.png" alt="LaunchDarkly - post signup" width="800" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;p&gt;Most of the time, pricing can be the primary decision factor. Let's look at how each company ranks.&lt;/p&gt;

&lt;h3&gt;
  
  
  ConfigCat
&lt;/h3&gt;

&lt;p&gt;To my surprise, ConfigCat's price list includes a free account option. What's even more amazing about this company is that they allow an unlimited number of team members, which, in my opinion, is remarkable.&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%2Fspbkpbndb73w766f6fet.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%2Fspbkpbndb73w766f6fet.png" alt="ConfigCat Pricing" width="800" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Azure App Configuration
&lt;/h3&gt;

&lt;p&gt;Azure comes as an entire package. They offer 12 months of free services with $200 in credit for the first 30 days to attract customers. However, beyond that period, you'd have to switch to a paid subscription to continue. Whether this poses a problem or not for you depends on your specific needs.&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%2Fmp33pqfxbr9oznkb4vx6.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%2Fmp33pqfxbr9oznkb4vx6.png" alt="Azure pricing" width="800" height="319"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Launch darkly
&lt;/h3&gt;

&lt;p&gt;LaunchDarkly does not offer a free plan. After signing up, users are auto-enrolled in a 14-day free trial of their pro version, priced at $200 per month. After the trial period was over, I was restricted to reader-only access.&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%2F9kfjit1gsymgehq5f8ai.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%2F9kfjit1gsymgehq5f8ai.png" alt="LaunchDarkly pricing" width="800" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ease of use
&lt;/h2&gt;

&lt;p&gt;Apart from the price tag, ease of use is another factor to consider in making a decision. How the UI looks for a particular provider will often be a matter of preference and vary between users. But most importantly, How easy is it for a user to log in and complete a task without hassle?&lt;/p&gt;

&lt;h3&gt;
  
  
  UX: ConfigCat
&lt;/h3&gt;

&lt;p&gt;The first thing I noticed about ConfigCat's UI is its simplicity. I can create a feature flag by clicking the "Add Feature Flag" button, which triggers a centered popup form with just four input fields.&lt;/p&gt;

&lt;p&gt;The experience seems intentionally designed to be user-friendly and straightforward. From what I see, ConfigCat wants to make things as simple as possible for users. The configuration options are self-explanatory, so you won't find yourself scratching your head too often.&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%2Fsoh529jzwtzxalidc351.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%2Fsoh529jzwtzxalidc351.png" alt="ConfigCat - ease of use" width="800" height="229"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  UX: Azure App Configuration
&lt;/h3&gt;

&lt;p&gt;My first impression of Azure's interface was that it was not straightforward and took extra effort to navigate and get things done. The UI can seem overwhelming due to the multitude of options available, making it easy to feel lost even if you're familiar with the product. This sort of overload may be a common challenge. Opinions may differ, but in the end, it comes down to personal preference and finding the provider that works best for you.&lt;/p&gt;

&lt;p&gt;Other than that, I find Azure useful. It has its place, but as a beginner, you might need to invest time learning to navigate.&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%2Fmbefczvmjl49lgpq5cuz.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%2Fmbefczvmjl49lgpq5cuz.png" alt="Azure App Configuration - ease of use" width="720" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  UX: LaunchDarkly
&lt;/h3&gt;

&lt;p&gt;The interface appears to be forthright for creating a feature flag. To summarize, here are the steps you can follow to add a feature flag:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log in to the app.&lt;/li&gt;
&lt;li&gt;Select the &lt;strong&gt;Feature Flag&lt;/strong&gt; menu item from the left sidebar.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Create flag&lt;/strong&gt; button, which opens a right sidebar.&lt;/li&gt;
&lt;li&gt;In the right sidebar, enter the details to create your feature flag.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both developers and non-technical users can easily follow the above steps. However, when compared to ConfigCat, it may not be as clear.&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%2F8731x55zd3sp6jnyrirx.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%2F8731x55zd3sp6jnyrirx.png" alt="LaunchDarkly - Ease of use" width="800" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Permission and team management
&lt;/h2&gt;

&lt;p&gt;Permission is about authorizing people to do certain things. In this section, we'll explore how each provider offers this capability.&lt;/p&gt;

&lt;h3&gt;
  
  
  ConfigCat
&lt;/h3&gt;

&lt;p&gt;ConfigCat provides the capability to manage users and teams. When new users get invited to ConfigCat,  they get assigned to the default administrator group. It is worth noting that LaunchDarkly offers more granular control during the invitation process. They allow you to set specific permissions, such as general read, write, or admin privileges to invited users. But this may not be necessary for first-time users to wrap their heads around.&lt;/p&gt;

&lt;h3&gt;
  
  
  Azure App Configuration
&lt;/h3&gt;

&lt;p&gt;App Configuration relies on another service in the Azure ecosystem called Azure Active Directory to manage permissions across users and groups. I am not familiar with using it, but I'll provide a link to it &lt;a href="https://learn.microsoft.com/en-us/azure/azure-app-configuration/concept-enable-rbac" rel="noopener noreferrer"&gt;here&lt;/a&gt; in case you want to check it out.&lt;/p&gt;

&lt;h3&gt;
  
  
  LaunchDarkly
&lt;/h3&gt;

&lt;p&gt;LaunchDarkly provides team management functionality, allowing teams to manage themselves. However, it's worth noting that this feature is available exclusively on their enterprise plan. In this plan, you can manage, create, and assign members to specific roles. To utilize the enterprise plan, you must make direct contact with LaunchDarkly.&lt;/p&gt;

&lt;p&gt;You can add and manage up to 10 members with their professional plan. More than ten seats can incur additional costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enterprise-level features
&lt;/h2&gt;

&lt;p&gt;In some cases, an organization might demand complete control of its infrastructure. Since I don't have first-hand experience using enterprise-level features, here are some helpful links to learn more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://configcat.com/pricing/" rel="noopener noreferrer"&gt;ConfigCat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://azure.microsoft.com/en-us/products/app-configuration/" rel="noopener noreferrer"&gt;Azure app configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://launchdarkly.com/pricing/" rel="noopener noreferrer"&gt;LaunchDarkly&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When assigning the appropriate rights to user accounts in a Windows environment, you can choose a feature flag provider that supports Microsoft Active Directory. So, if you want to avoid the complications of Azure's user interface, it may be worth considering ConfigCat. They provide a documentation guide on how to succeed in this aspect, which can be found &lt;a href="https://configcat.com/docs/advanced/team-management/saml/identity-providers/azure-ad/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication
&lt;/h2&gt;

&lt;p&gt;While the features provided by these services are fantastic, it is crucial to ensure the security and confidentiality of your feature flag settings. While setting a username and password is a good start, you can use additional measures to protect against password breaches. Hence, choose a provider with support for Two-factor authentication (2FA).&lt;/p&gt;

&lt;p&gt;Fortunately, The mentioned providers support Two-factor authentication, enabling an extra layer of security to protect your account and feature flag settings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Support and Team
&lt;/h2&gt;

&lt;p&gt;Having a good support team is like having superheroes on standby. For me, a prompt and reachable support team saves the day. If you're facing trouble or have a question about how something works, you should be able to reach out and get the help you need.&lt;/p&gt;

&lt;h3&gt;
  
  
  ConfigCat
&lt;/h3&gt;

&lt;p&gt;From my observations, ConfigCat allows users to request support by filling out a form &lt;a href="https://configcat.com/support/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. In addition, both free and paid users can chat with their developers via &lt;a href="https://configcat.com/slack/" rel="noopener noreferrer"&gt;Slack&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%2Fylwope8njxp0jdcm47l6.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%2Fylwope8njxp0jdcm47l6.png" alt="ConfigCat support team" width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Microsoft App configuration
&lt;/h3&gt;

&lt;p&gt;Seeking help with customer support issues involves creating a support ticket. For support, you'll have to be subscribed to a support plan. You can find more information about the available support plans &lt;a href="https://azure.microsoft.com/en-us/support/plans" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  LaunchDarkly
&lt;/h3&gt;

&lt;p&gt;LaunchDarkly provides the ability to request support by filling out a support request ticket &lt;a href="https://support.launchdarkly.com/hc/en-us/requests/new" rel="noopener noreferrer"&gt;here&lt;/a&gt;, but they do not offer direct chat access to their support team.&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%2F9353j3pdfbaevxu4l9tn.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%2F9353j3pdfbaevxu4l9tn.png" alt="LaunchDarkly support team" width="800" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Final thoughts
&lt;/h3&gt;

&lt;p&gt;While pricing can be one of the main factors to consider when deciding which feature flag provider to go with, it may not be wise. It is possible that a cheap price will not offer you the quality of service you need, and an expensive price may exceed your budget. It is better to strike a balance somewhere in the middle.&lt;/p&gt;

&lt;p&gt;When experimenting with different providers, I tend to lean towards the one with a wide range of features available, as with &lt;a href="https://configcat.com/" rel="noopener noreferrer"&gt;ConfigCat&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So, who's the clear winner? The answer to this question depends on your specific plans and expectations from a feature flag provider. Nevertheless, here's my answer:&lt;/p&gt;

&lt;p&gt;The decision between LaunchDarkly and ConfigCat has been a long one for me. Finally, I chose ConfigCat mainly because it allows me to invite unlimited users.&lt;/p&gt;

&lt;p&gt;Additionally, ConfigCat provides various communication channels, including Slack, live chat, and email for interacting with customers. They also consider working hours and time zones to prevent gaps in their customer support. An interesting aspect is that customers have direct access to developers instead of first-level support or a call center. However, navigating through complex documents can be challenging and time-consuming. Nonetheless, ConfigCat stands out by providing a high-quality service offering precise instructions for implementing specific tasks, contributing to a smoother workflow.&lt;/p&gt;

&lt;p&gt;You can follow &lt;a href="https://configcat.com/" rel="noopener noreferrer"&gt;ConfigCat&lt;/a&gt; on &lt;a href="https://twitter.com/configcat" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://www.facebook.com/configcat" rel="noopener noreferrer"&gt;Facebook&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;. In my opinion, it is the most lovable feature flag service ever.&lt;/p&gt;

</description>
      <category>featureflags</category>
      <category>comparison</category>
      <category>featuremanagement</category>
      <category>featureflagging</category>
    </item>
    <item>
      <title>Skipping Test Environments for Faster and Safer Deployments</title>
      <dc:creator>Chavez Harris</dc:creator>
      <pubDate>Sat, 16 Dec 2023 14:56:25 +0000</pubDate>
      <link>https://forem.com/codedbychavez/skipping-test-environments-for-faster-and-safer-deployments-215k</link>
      <guid>https://forem.com/codedbychavez/skipping-test-environments-for-faster-and-safer-deployments-215k</guid>
      <description>&lt;p&gt;For a long time, it was normal to initially release a new feature or update into a test environment. If the feature passed, it was then released to the production environment. While this approach was highly respected and beneficial, it introduced more complexity into software development workflows, and releases took longer to reach end-users. Fortunately, with a mechanism known as &lt;a href="https://configcat.com/featureflags/" rel="noopener noreferrer"&gt;feature flagging&lt;/a&gt;, you can deploy directly to production and ship releases faster while maintaining reliability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Definition and purpose of test environments
&lt;/h2&gt;

&lt;p&gt;Let's unpack what a test environment is and describe why it is useful. A test environment is an isolated space separate from the production environment. It serves as a sandbox where developers and testers can evaluate new features and updates. When the test results become acceptable, the new feature is deployed to the production environment. Without a test environment, developers run the risk of introducing bugs that could affect the user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges and limitations of test environments
&lt;/h2&gt;

&lt;p&gt;While there are many benefits to setting up and using test environments, it can come with challenges that may outweigh the benefits. These can include higher costs, increased complexity, and even coverage gaps. Let's talk about these.&lt;/p&gt;

&lt;h3&gt;
  
  
  Higher cost
&lt;/h3&gt;

&lt;p&gt;A test environment may require technical resources to host and maintain it. Whether or not you actively use a test environment or spin one up on demand, it still utilizes storage space, CPU, RAM, etc. If a change you need to test is as simple as changing the text on a button, and your release policy is to run it through your test environment, you'd have to spin up your test environment to preview and test the changes. Let's say you now need an extra 32GB of RAM to run the new version of your software; then, you need to mirror the same in your test environment, thus increasing the cost.&lt;/p&gt;

&lt;h3&gt;
  
  
  Complexity
&lt;/h3&gt;

&lt;p&gt;A test environment adds complexity to development workflows and steepens the learning curve for new developers. This can result in longer debugging times when issues arise.&lt;/p&gt;

&lt;p&gt;As an example, imagine that you added a new feature to your app. During testing, you noticed that you need to upgrade a specific npm package. If you forgot to do the same in your production environment to match, you could potentially introduce a bug. The same applies to hardware, where you provision the correct amount of resources in the test environment but forget to make similar adjustments in the production environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Coverage
&lt;/h3&gt;

&lt;p&gt;Coverage issues can arise when your test environment is unable to account for and test all the conditions usually found in a production environment. In cases where companies choose to invest only in the bare minimum resources to run their test environment, it can be difficult to truly test performance bottlenecks because these tend to manifest only in a production environment with real users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing in production with feature flags
&lt;/h2&gt;

&lt;p&gt;What if you could achieve the same or better benefits without the complexity and cost of a test environment? Enter the world of &lt;a href="https://configcat.com/#product" rel="noopener noreferrer"&gt;feature flags&lt;/a&gt;. They allow you to remotely switch your features on or off at will, just as you do with the lights in your home. As a result, you can show or hide a feature by toggling its flag, eliminating the need for editing and redeploying your application. This approach enables you to test the new feature on a portion or even your entire user base in production.&lt;/p&gt;

&lt;p&gt;Let's look at some of the benefits of testing in production:&lt;/p&gt;

&lt;h4&gt;
  
  
  Faster feedback
&lt;/h4&gt;

&lt;p&gt;Most often, developers are eager to know how a new feature performs in the real world. A test environment usually slows down this feedback loop and can disrupt developer momentum. By releasing a new feature directly to production, this momentum remains intact. If new bugs show up, the new feature can be rolled back with just a click, allowing developers to rectify them.&lt;/p&gt;

&lt;h4&gt;
  
  
  Higher quality
&lt;/h4&gt;

&lt;p&gt;Because real users are present in a production environment, feedback from a new feature release can be of better quality. This provides a better chance of minimizing or even preventing test regressions.&lt;/p&gt;

&lt;h4&gt;
  
  
  Lower risk
&lt;/h4&gt;

&lt;p&gt;Releasing a new feature to production can be risky without a &lt;a href="https://configcat.com" rel="noopener noreferrer"&gt;feature management system&lt;/a&gt;. Feature flags can keep your risk low by allowing you to roll back a problematic feature with just a few clicks. If you need to get even more creative, you can apply targeting rules to your feature flags and perform a &lt;a href="https://configcat.com/blog/2022/01/14/progressive-delivery/" rel="noopener noreferrer"&gt;canary release or progressive deployment&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Companies testing in production
&lt;/h2&gt;

&lt;p&gt;Up to this point, we talked about the benefits that testing in production brings to the table, but the question is, has it proven itself? Short answer, yes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Kantan.tech moves faster than their competition
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://kantan.co.uk/" rel="noopener noreferrer"&gt;Kantan.tech&lt;/a&gt; is a company that outpaces its competitors by offering a platform via a React Native app. This platform facilitates tradespeople in managing customers, while customers can conveniently connect and book their services.&lt;/p&gt;

&lt;p&gt;The company's emphasis on speed has allowed them to accelerate the release of product features. Engineers employ feature flags for new features, which are set to be off by default. These flags are initially activated for internal users for testing and subsequently rolled out to production, becoming available to all users.&lt;/p&gt;

&lt;p&gt;Following a successful deployment and testing period, feature flags are removed from the codebase approximately one week later. This approach enables Kantan.tech to swiftly introduce and refine new features while maintaining operational stability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Eucalyptus growth is improved
&lt;/h3&gt;

&lt;p&gt;Eucalyptus has achieved notable growth by adopting a Telehealth platform that utilizes a shared tech stack to establish distinctive brands. Central to their success is the wholehearted integration of feature flags, which play a pivotal role in their operations. By effectively managing features through the use of these flags, Eucalyptus has gained a competitive edge within the market. This approach allows them to reduce potential risks and enhance their response times, resulting in an improved market position compared to their counterparts in the industry.&lt;/p&gt;

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

&lt;p&gt;Using test environments was the de facto standard for testing. It offered benefits but added more complexity to software development workflows. With a good &lt;a href="https://configcat.com" rel="noopener noreferrer"&gt;feature management system&lt;/a&gt; in place, you can skip using them and test directly in production with the help of feature flags. If set up correctly, you can easily roll back new updates or changes without the risk of damage while performing improvements and fixes. Putting aside the theory, I've mentioned two case studies of companies that took advantage of this approach. It has served them well along the way.&lt;/p&gt;

&lt;p&gt;Eager to give feature flags a try? Get a generous &lt;a href="https://app.configcat.com/auth/signup" rel="noopener noreferrer"&gt;free tier ConfigCat account&lt;/a&gt; for low-volume use cases which is ideal for getting started with feature flags. Created by developers for developers with ❤️.&lt;/p&gt;

&lt;p&gt;For more awesome content, keep up with ConfigCat on &lt;a href="https://twitter.com/configcat" rel="noopener noreferrer"&gt;X&lt;/a&gt;, &lt;a href="https://www.facebook.com/configcat" rel="noopener noreferrer"&gt;Facebook&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>testing</category>
      <category>featuremanagement</category>
      <category>featureflags</category>
      <category>testenvironment</category>
    </item>
    <item>
      <title>How to Use ConfigCat Feature Flags with Docker</title>
      <dc:creator>Chavez Harris</dc:creator>
      <pubDate>Thu, 30 Nov 2023 13:36:13 +0000</pubDate>
      <link>https://forem.com/codedbychavez/how-to-use-configcat-feature-flags-with-docker-9jo</link>
      <guid>https://forem.com/codedbychavez/how-to-use-configcat-feature-flags-with-docker-9jo</guid>
      <description>&lt;p&gt;&lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; is a platform that enables developers to build apps and run them in mini virtual machines called containers. As a result, developers can just focus on writing code without needing to set up or configure an environment for running that code. Docker also allows easy application sharing because its environment is abstracted away from the host machine. This allows the containerized application to run on any host machine Docker is installed on. Developers can extend the functionality of Docker's desktop application with extensions. But the goodness doesn't stop there. You can use feature flags to control smaller feature components of these extensions without rebuilding and updating them.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://configcat.com/featureflags/" rel="noopener noreferrer"&gt;Feature flags&lt;/a&gt; allow developers to switch features in software applications on or off remotely without modifying the code. A feature flag works similarly to a light switch. By setting its boolean value to either true or false you can control whether a feature should be shown to users or not.&lt;/p&gt;

&lt;p&gt;The main benefits of using feature flags would include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Through a simple GUI &lt;a href="https://app.configcat.com" rel="noopener noreferrer"&gt;dashboard&lt;/a&gt; you can toggle particular features on or off without the need to edit and redeploy your application's code.&lt;/li&gt;
&lt;li&gt;If a new feature is deployed and is causing bugs, you can easily roll it back by turning off its flag.&lt;/li&gt;
&lt;li&gt;Feature flags give developers the freedom and confidence to experiment quickly.&lt;/li&gt;
&lt;li&gt;Conducting A/B tests can be streamlined when combined with feature flags.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Importantly, you can use feature flags in many programming languages or frameworks. &lt;a href="https://configcat.com/" rel="noopener noreferrer"&gt;ConfigCat&lt;/a&gt; offers an &lt;a href="https://configcat.com/docs/sdk-reference/overview/" rel="noopener noreferrer"&gt;extensive list of SDKs&lt;/a&gt; for integrating its feature flag service in your technology stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  ConfigCat: A Cloud-Hosted Feature Flag Provider
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://configcat.com/" rel="noopener noreferrer"&gt;ConfigCat&lt;/a&gt; allows you to manage your feature flags from an easy-to-use &lt;a href="https://app.configcat.com" rel="noopener noreferrer"&gt;dashboard&lt;/a&gt;, including the ability to set targeting rules for releasing features to a specific segment of users. These rules can be based on country, email, and custom identifiers such as age, eye color, etc.&lt;/p&gt;

&lt;p&gt;But let's get to the point: Feature flags are cool but how do we use them with Docker?&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use ConfigCat Feature Flags with Docker
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; offers developers a simple API to create extensions that add extra functionalities to the Docker desktop application. This can be achieved using the &lt;a href="https://docs.docker.com/desktop/extensions/" rel="noopener noreferrer"&gt;Extensions SDK&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the upcoming section, I'll show you how to build and install a simple extension that displays a table of Docker containers currently present on your machine. We'll then proceed to add a feature flag to control the visibility of one of its child components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Developing a Docker Desktop Extension
&lt;/h2&gt;

&lt;p&gt;Let's jump right into building our Docker desktop extension.&lt;/p&gt;

&lt;p&gt;Before getting started check if you've got the following prerequisites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.docker.com/products/docker-desktop/" rel="noopener noreferrer"&gt;Docker Desktop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A code editor such as &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;VS Code&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Basic knowledge of &lt;a href="https://react.dev/" rel="noopener noreferrer"&gt;React&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; To get started, open up a terminal and point it to the folder where you want the extension to live. Then, set up the directory for your extension and generate the needed files with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker extension init my-extension
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To verify that the generated extension works, build and install it using the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; my-extension &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker extension &lt;span class="nb"&gt;install &lt;/span&gt;my-extension
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the above was successful, you should see the extension available in your Docker desktop application:&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%2F9pi7ak9d6jdo4hpkaypr.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%2F9pi7ak9d6jdo4hpkaypr.png" alt="default quick start extension" width="800" height="247"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; The &lt;code&gt;docker extension init my-extension&lt;/code&gt; command has also generated a backend service for the extension. For this demo, we'll only focus on the front-end so it is safe to delete the &lt;code&gt;backend&lt;/code&gt; folder. Update the following files as follows to remove all references to the backend:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Dockerfile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;--platform=$BUILDPLATFORM node:18.12-alpine3.16&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;client-builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /ui&lt;/span&gt;
&lt;span class="c"&gt;# cache packages in layer&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ui/package.json /ui/package.json&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ui/package-lock.json /ui/package-lock.json&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cache,target&lt;span class="o"&gt;=&lt;/span&gt;/usr/src/app/.npm &lt;span class="se"&gt;\
&lt;/span&gt;    npm &lt;span class="nb"&gt;set &lt;/span&gt;cache /usr/src/app/.npm &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    npm ci
&lt;span class="c"&gt;# install&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ui /ui&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; alpine&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; org.opencontainers.image.title="My extension" \&lt;/span&gt;
    org.opencontainers.image.description="My awesome Docker extension" \
    org.opencontainers.image.vendor="Awesome Inc." \
    com.docker.desktop.extension.api.version="0.3.4" \
    com.docker.extension.screenshots="" \
    com.docker.desktop.extension.icon="" \
    com.docker.extension.detailed-description="" \
    com.docker.extension.publisher-url="" \
    com.docker.extension.additional-urls="" \
    com.docker.extension.categories="" \
    com.docker.extension.changelog=""

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; docker-compose.yaml .&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; metadata.json .&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; docker.svg .&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=client-builder /ui/build ui&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;docker-compose.yaml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;my-extension&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${DESKTOP_PLUGIN_IMAGE}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;metadata.json&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"icon"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"docker.svg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"vm"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"composefile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"docker-compose.yaml"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ui"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dashboard-tab"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"My Extension"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"index.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"root"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ui"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3.&lt;/strong&gt; Modify the &lt;code&gt;App.tsx&lt;/code&gt; file so that the extension will display a table of Docker containers on your system:&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;// ui/src/App.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&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;react&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;Stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;TableBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;TableCell&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;TableContainer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;TableHead&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;TableRow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Typography&lt;/span&gt;&lt;span class="p"&gt;,&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="s2"&gt;@mui/material&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;createDockerDesktopClient&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="s2"&gt;@docker/extension-api-client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;//obtain docker desktop extension client&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ddClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createDockerDesktopClient&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;function&lt;/span&gt; &lt;span class="nf"&gt;App&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;containers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setContainers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;any&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchAllContainers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &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;ddClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cli&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ps&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--all&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;--format&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;"{{json .}}"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;result&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;// result.parseJsonLines() parses the output of the command into an array of objects&lt;/span&gt;
      &lt;span class="nf"&gt;setContainers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseJsonLines&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;useEffect&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 all containers&lt;/span&gt;
    &lt;span class="nf"&gt;fetchAllContainers&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;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Stack&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="nx"&gt;Typography&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;testid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;heading&lt;/span&gt;&lt;span class="dl"&gt;"&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="s2"&gt;h3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nx"&gt;Container&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Typography&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Typography&lt;/span&gt;
          &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;testid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;subheading&lt;/span&gt;&lt;span class="dl"&gt;"&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="s2"&gt;body1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text.secondary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="nx"&gt;sx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;mt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&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;Simple&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;containers&lt;/span&gt; &lt;span class="nx"&gt;using&lt;/span&gt; &lt;span class="nx"&gt;Docker&lt;/span&gt; &lt;span class="nx"&gt;Extensions&lt;/span&gt; &lt;span class="nx"&gt;SDK&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;/Typography&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TableContainer&lt;/span&gt; &lt;span class="nx"&gt;sx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;mt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;}}&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="nx"&gt;Table&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="nx"&gt;TableHead&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="nx"&gt;TableRow&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="nx"&gt;TableCell&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Container&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TableCell&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TableCell&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TableCell&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TableCell&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Command&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TableCell&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TableCell&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Created&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TableCell&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TableCell&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TableCell&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TableRow&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TableHead&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TableBody&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;containers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;container&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TableRow&lt;/span&gt;
                  &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;container&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="nx"&gt;sx&lt;/span&gt;&lt;span class="o"&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;&amp;amp;:last-child td, &amp;amp;:last-child th&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;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}}&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="nx"&gt;TableCell&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;container&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TableCell&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TableCell&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;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Image&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;/TableCell&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TableCell&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;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Command&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;/TableCell&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TableCell&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;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CreatedAt&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;/TableCell&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TableCell&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;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Status&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;/TableCell&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TableRow&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&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;/TableBody&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Table&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TableContainer&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Stack&lt;/span&gt;&lt;span class="err"&gt;&amp;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;To see the new changes, build and update the extension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; my-extension &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker extension update my-extension
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F54w5bfhb1thu3a0csytp.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%2F54w5bfhb1thu3a0csytp.png" alt="Modified extension" width="800" height="231"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The extension now displays a table of all Docker containers on your machine. Instead of showing all containers, including the ones that are turned off, let's add a filter switch to display only the running containers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.&lt;/strong&gt; Modify the &lt;code&gt;App.tsx&lt;/code&gt; file as follows:&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;// ui/src/App.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&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;react&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;Stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;TableBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;TableCell&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;TableContainer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;TableHead&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;TableRow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Typography&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Switch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;FormControlLabel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;FormGroup&lt;/span&gt;&lt;span class="p"&gt;,&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="s2"&gt;@mui/material&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;createDockerDesktopClient&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="s2"&gt;@docker/extension-api-client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;//obtain docker desktop extension client&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ddClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createDockerDesktopClient&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;function&lt;/span&gt; &lt;span class="nf"&gt;App&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;containers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setContainers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;any&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchAllContainers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &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;ddClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cli&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ps&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--all&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;--format&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;"{{json .}}"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;result&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;// result.parseJsonLines() parses the output of the command into an array of objects&lt;/span&gt;
      &lt;span class="nf"&gt;setContainers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseJsonLines&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;fetchRunningContainers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &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;ddClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cli&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ps&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--format&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;"{{json .}}"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;result&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;// result.parseJsonLines() parses the output of the command into an array of objects&lt;/span&gt;
      &lt;span class="nf"&gt;setContainers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseJsonLines&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;handleFetchContainers&lt;/span&gt; &lt;span class="o"&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;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ChangeEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLInputElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&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="k"&gt;if &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;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;fetchRunningContainers&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="nf"&gt;fetchAllContainers&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;useEffect&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 all containers&lt;/span&gt;
    &lt;span class="nf"&gt;fetchAllContainers&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;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Stack&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="nx"&gt;Typography&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;testid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;heading&lt;/span&gt;&lt;span class="dl"&gt;"&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="s2"&gt;h3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nx"&gt;Container&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Typography&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Typography&lt;/span&gt;
          &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;testid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;subheading&lt;/span&gt;&lt;span class="dl"&gt;"&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="s2"&gt;body1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text.secondary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="nx"&gt;sx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;mt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&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;Simple&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;containers&lt;/span&gt; &lt;span class="nx"&gt;using&lt;/span&gt; &lt;span class="nx"&gt;Docker&lt;/span&gt; &lt;span class="nx"&gt;Extensions&lt;/span&gt; &lt;span class="nx"&gt;SDK&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;/Typography&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;FormGroup&lt;/span&gt; &lt;span class="nx"&gt;sx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;mt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}}&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="nx"&gt;FormControlLabel&lt;/span&gt; &lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Switch&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;handleFetchContainers&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="sr"&gt;/&amp;gt;} label="Show only running containers" /&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;/FormGroup&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TableContainer&lt;/span&gt; &lt;span class="nx"&gt;sx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;mt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;}}&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="nx"&gt;Table&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="nx"&gt;TableHead&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="nx"&gt;TableRow&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="nx"&gt;TableCell&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Container&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TableCell&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TableCell&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TableCell&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TableCell&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Command&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TableCell&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TableCell&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Created&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TableCell&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TableCell&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TableCell&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TableRow&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TableHead&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TableBody&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;containers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;container&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TableRow&lt;/span&gt;
                  &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;container&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="nx"&gt;sx&lt;/span&gt;&lt;span class="o"&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;&amp;amp;:last-child td, &amp;amp;:last-child th&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;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}}&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="nx"&gt;TableCell&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;container&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TableCell&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TableCell&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;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Image&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;/TableCell&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TableCell&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;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Command&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;/TableCell&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TableCell&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;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CreatedAt&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;/TableCell&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TableCell&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;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Status&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;/TableCell&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TableRow&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&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;/TableBody&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Table&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TableContainer&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Stack&lt;/span&gt;&lt;span class="err"&gt;&amp;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;&lt;strong&gt;5.&lt;/strong&gt; Build and update the extension as we did before, and you should see:&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%2Fj8082vw3agiaqqgn7vjt.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%2Fj8082vw3agiaqqgn7vjt.png" alt="Modified extension with switch" width="800" height="221"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6.&lt;/strong&gt; Let's create a feature flag in the &lt;a href="https://app.configcat.com" rel="noopener noreferrer"&gt;ConfigCat dashboard&lt;/a&gt; with the following details. This feature flag will control the visibility of the filter switch:&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%2F5r7nxzdbxskt7s02zdzv.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%2F5r7nxzdbxskt7s02zdzv.png" alt="Modified extension" width="800" height="751"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since we're using React, let's install and use &lt;a href="https://configcat.com/docs/sdk-reference/react/" rel="noopener noreferrer"&gt;ConfigCat's React SDK&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7.&lt;/strong&gt; Install the React SDK with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i configcat-react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;8.&lt;/strong&gt; In the &lt;code&gt;main.tsx&lt;/code&gt; file, Import the &lt;code&gt;ConfigCatProvider&lt;/code&gt; and wrap the &lt;code&gt;&amp;lt;App /&amp;gt;&lt;/code&gt; component using it:&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="s2"&gt;configcat-react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Newly added&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;App&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;./App&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StrictMode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*
      If you eject from MUI (which we don't recommend!), you should add
      the `dockerDesktopTheme` class to your root &amp;lt;html&amp;gt; element to get
      some minimal Docker theming.

      Newly added &amp;lt;ConfigCatProvider /&amp;gt;
    */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ConfigCatProvider&lt;/span&gt; &lt;span class="nx"&gt;sdkKey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR-CONFIGCAT-SDK-KEY&lt;/span&gt;&lt;span class="dl"&gt;"&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="nx"&gt;DockerMuiThemeProvider&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="nx"&gt;CssBaseline&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="nx"&gt;App&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;/DockerMuiThemeProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ConfigCatProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/React.StrictMode&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;&lt;strong&gt;9.&lt;/strong&gt; In the &lt;code&gt;App.tsx&lt;/code&gt; file, add the following line beneath the existing import statements:&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;useFeatureFlag&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-react&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;strong&gt;10.&lt;/strong&gt; Within the &lt;code&gt;App()&lt;/code&gt; function block, add the following hook:&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isFilterSwitchEnabled&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useFeatureFlag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;filterswitch&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;isFilterSwitchEnabled&lt;/code&gt; will hold a value of true when the feature flag (&lt;code&gt;filterswitch&lt;/code&gt;) is enabled and false otherwise. Let's use it to render the filter switch when it is true:&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="nx"&gt;isFilterSwitchEnabled&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;FormGroup&lt;/span&gt; &lt;span class="nx"&gt;sx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;mt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}}&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="nx"&gt;FormControlLabel&lt;/span&gt; &lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Switch&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;handleFetchContainers&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="sr"&gt;/&amp;gt;} label="Show only running containers" /&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;/FormGroup&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;h3&gt;
  
  
  Test Drive
&lt;/h3&gt;

&lt;p&gt;Turning off the feature flag in your &lt;a href="https://app.configcat.com" rel="noopener noreferrer"&gt;Dashboard&lt;/a&gt; will hide the feature:&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%2Fwymqcbcnaoy0f92im4gl.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%2Fwymqcbcnaoy0f92im4gl.png" alt="Turning off the feature flag" width="800" height="115"&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%2F9u9iy5zr5mcx4y21wu1i.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%2F9u9iy5zr5mcx4y21wu1i.png" alt="Extension with feature flag turned off" width="800" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Interested in learning more? In the next section, I'll share some additional information that will make using Docker and ConfigCat feature flags even easier for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices to Consider When Using ConfigCat Feature Flags with Docker
&lt;/h2&gt;

&lt;p&gt;While there isn't any right or wrong way to use feature flags in general, there are some best practices that can help you get the most out of using them with Docker.&lt;/p&gt;

&lt;h3&gt;
  
  
  Developing Your Docker Extensions with Feature Flags in Mind
&lt;/h3&gt;

&lt;p&gt;Imagine you built and deployed your first Docker extension. As it scales and more users hop on board, more features are needed, even ones you haven't thought about. If you design the extension components to be controlled by feature flags, you can easily determine how and when they are released.&lt;/p&gt;

&lt;p&gt;What this means is that you can choose to render each feature component in your code based on the value of its corresponding feature flag. This can be done by checking whether the flag's boolean value is set to true with a conditional IF-statement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimizing for Faster Performance
&lt;/h3&gt;

&lt;p&gt;In my experience using ConfigCat, although it's relatively fast, there are ways to further optimize its performance and potentially limit the HTTP requests made by your containers for querying feature flag values. One way to achieve this is by utilizing a custom cache.&lt;/p&gt;

&lt;p&gt;If you decide to use a custom cache such as Redis, you can integrate it with your ConfigCat SDK client based on the instructions documented &lt;a href="https://configcat.com/docs/sdk-reference/react/#using-custom-cache-implementation" rel="noopener noreferrer"&gt;here&lt;/a&gt;. With this approach, you can free up network request processes on the client side. This is because your client SDK will instead evaluate your feature flags based on the cache data. Check out the &lt;a href="https://configcat.com/blog/2023/06/05/how-to-use-configcat-with-redis/" rel="noopener noreferrer"&gt;using ConfigCat with Redis&lt;/a&gt; guide to learn more.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary of Key Points
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Docker is a tool for building and sharing code in containerized environments.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://configcat.com" rel="noopener noreferrer"&gt;ConfigCat&lt;/a&gt; is a cloud-hosted feature flag service that simplifies feature flag management from a &lt;a href="https://app.configcat.com" rel="noopener noreferrer"&gt;central dashboard&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A feature flag is like a switch for a feature. It can be used to show or hide the feature it is linked to.&lt;/li&gt;
&lt;li&gt;The Docker extension SDK provides an API for developers to extend Docker desktop functionalities.&lt;/li&gt;
&lt;li&gt;You can use feature flags in your Docker extension to control feature components without the need to build and redeploy the extension.&lt;/li&gt;
&lt;li&gt;To make the most of using ConfigCat feature flags with Docker, consider the best practices we've discussed.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The advantages of using Docker make it an attractive choice for developers when it comes to deploying their applications. This, in turn, facilitates effortless sharing and deployment of these applications, owing to the isolated environments created by Docker's containerization approach. Through extensions, developers can incorporate extra functionalities into Docker. When combined with feature flags, developers can readily deploy their extensions to the public community and introduce new features effortlessly with a simple click of a button.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Source Code
&lt;/h3&gt;

&lt;p&gt;You can find the complete code for the extension &lt;a href="https://github.com/configcat-labs/configcat-with-docker-sample" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Further Reading
&lt;/h3&gt;

&lt;p&gt;If you'd like to learn more, check out the following resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/desktop/extensions-sdk/quickstart/" rel="noopener noreferrer"&gt;Docker extensions Quick start&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://configcat.com/docs/sdk-reference/react/" rel="noopener noreferrer"&gt;ConfigCat React SDK Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://configcat.com/docs/sdk-reference/react/#using-custom-cache-implementation" rel="noopener noreferrer"&gt;Custom cache implementation with ConfigCat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://configcat.com/blog/2023/06/05/how-to-use-configcat-with-redis/" rel="noopener noreferrer"&gt;How to use ConfigCat with Redis&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Eager to give what you learned a try? Sign up for a free-tier ConfigCat account &lt;a href="https://app.configcat.com/signup" rel="noopener noreferrer"&gt;here&lt;/a&gt; and stay on top of the latest posts and announcements from ConfigCat on &lt;a href="https://twitter.com/configcat" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://www.facebook.com/configcat" rel="noopener noreferrer"&gt;Facebook&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>featuremanagement</category>
      <category>featureflags</category>
      <category>docker</category>
      <category>configcat</category>
    </item>
    <item>
      <title>How to use ConfigCat's public API to conduct integration tests</title>
      <dc:creator>Chavez Harris</dc:creator>
      <pubDate>Mon, 20 Nov 2023 13:56:14 +0000</pubDate>
      <link>https://forem.com/codedbychavez/how-to-use-configcats-public-api-to-conduct-integration-tests-15e6</link>
      <guid>https://forem.com/codedbychavez/how-to-use-configcats-public-api-to-conduct-integration-tests-15e6</guid>
      <description>&lt;p&gt;Feature flags are essential for effective feature release and management. Using them, we can control what features end users can see and which should remain hidden. Feature flagging allows developers to plan, launch and test new features remotely without editing code. While these benefits are fantastic, what about code testing? Having some methods in place for testing the integration of feature flags in our code can increase the likelihood of smooth feature integrations.&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%2Fxcc4yc0t4mibjgtwv2kq.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%2Fxcc4yc0t4mibjgtwv2kq.png" alt="Using feature flags in integration tests cover" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Have &lt;a href="https://jestjs.io/docs/getting-started" rel="noopener noreferrer"&gt;JEST&lt;/a&gt; installed - A testing framework for testing your JavaScript code&lt;/li&gt;
&lt;li&gt;Have &lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt; installed - A platform used for testing and documenting APIs&lt;/li&gt;
&lt;li&gt;Basic JavaScript and Testing knowledge&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ConfigCat's public management API
&lt;/h2&gt;

&lt;p&gt;Besides evaluating your feature flags with &lt;a href="https://configcat.com/docs/sdk-reference/overview/" rel="noopener noreferrer"&gt;ConfigCat's SDKs&lt;/a&gt;, you may want to perform the same actions as you normally do while working with the &lt;a href="https://app.configcat.com" rel="noopener noreferrer"&gt;Dashboard UI&lt;/a&gt; in your code. This is where the &lt;a href="https://api.configcat.com/docs/" rel="noopener noreferrer"&gt;ConfigCat Public Management API&lt;/a&gt; comes in useful. And to show you how it works, I'll demo how we can use it to perform actions such as setting up a testing environment and creating a new feature flag using JavaScript. Then finally, we'll write an integration test to ensure all the functions work with each other using the &lt;a href="https://jestjs.io/" rel="noopener noreferrer"&gt;JEST JavaScript Testing Framework&lt;/a&gt;. If you're eager to peek at the API docs before we get started, I have linked it &lt;a href="https://configcat.com/docs/advanced/public-api/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is integration testing?
&lt;/h2&gt;

&lt;p&gt;Compared to a unit test, which only tests a single piece of code or function, an integration test involves testing multiple units to determine if they work together.&lt;/p&gt;

&lt;p&gt;Imagine you want to test if a feature flag integrates well with your app before performing a feature deployment. A recommended way to do this is to write an integration test to catch integration bugs that could potentially seep into production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing an integration test
&lt;/h2&gt;

&lt;p&gt;Let's put the theory aside for integration tests and write one in JavaScript. Before starting, let's write some functions that utilize the public API to create resources that our testing function will evaluate.&lt;/p&gt;

&lt;p&gt;To help you follow along, I've added the following &lt;a href="https://github.com/configcat-labs/public-api-guide-integration-testing-sample" rel="noopener noreferrer"&gt;sample app&lt;/a&gt;. If you wish to code along with me, you can check out the &lt;a href="https://github.com/configcat-labs/public-api-guide-integration-testing-sample/tree/starter-code" rel="noopener noreferrer"&gt;starter-code branch&lt;/a&gt;. If you end up getting stuck, feel free to view the complete code on the &lt;code&gt;main&lt;/code&gt; branch.&lt;/p&gt;

&lt;p&gt;Because this process involves making HTTP calls to the API, I'll use an HTTP client called &lt;a href="https://axios-http.com/docs/intro" rel="noopener noreferrer"&gt;Axios&lt;/a&gt;. &lt;a href="https://api.configcat.com/docs/" rel="noopener noreferrer"&gt;ConfigCat's Public Management API&lt;/a&gt; will only process HTTP requests that contain a &lt;a href="https://configcat.com/docs/advanced/public-api/#authentication" rel="noopener noreferrer"&gt;Basic HTTP Authentication Scheme&lt;/a&gt;, so I'll add a &lt;strong&gt;basic auth username&lt;/strong&gt; and &lt;strong&gt;password&lt;/strong&gt; to the HTTP request header.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create your public API credentials on the &lt;a href="https://app.configcat.com/my-account/public-api-credentials" rel="noopener noreferrer"&gt;Public API credentials management page&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I'll create an Axios instance using the credentials. This will allow you to make requests without supplying the required headers each time. Add the following code and input your credentials in the &lt;code&gt;utils/axios/axios-instance.js&lt;/code&gt; file.&lt;br&gt;
&lt;/p&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="c1"&gt;// axios-instance.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;axios&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;axiosInstance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;axios&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="na"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.configcat.com/v1/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&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;X-CONFIGCAT-SDKKEY&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-CONFIGCAT-SDKKEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;username&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-AUTH-USERNAME&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;password&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-AUTH-PASSWORD&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;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;axiosInstance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Do this only to follow me. In a production environment keep your credentials secure: do not embed them directly in your code and do not share them.&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the &lt;code&gt;utils/feature/feature-integration.js&lt;/code&gt; file, import the &lt;em&gt;axiosInstance&lt;/em&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;const&lt;/span&gt; &lt;span class="nx"&gt;axiosInstance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../axios/axios-instance&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;ol&gt;
&lt;li&gt;After reviewing and looking at the examples in the &lt;a href="https://api.configcat.com/docs/" rel="noopener noreferrer"&gt;API docs&lt;/a&gt;, I wrote some individual functions that set up the necessary testing resources. Add these to &lt;code&gt;utils/feature/feature-integration.js&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="c1"&gt;// Create Test Product&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createTestProduct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;organizationId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;08da0156-fc5b-4132-88e5-1d42032078f5&lt;/span&gt;&lt;span class="dl"&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;await&lt;/span&gt; &lt;span class="nx"&gt;axiosInstance&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="s2"&gt;`organizations/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;organizationId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/products`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Test Product&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A product for testing&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// throw new Error(error);&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&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="c1"&gt;// Create Test Environment&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createTestEnvironment&lt;/span&gt; &lt;span class="o"&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;productId&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axiosInstance&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="s2"&gt;`products/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/environments`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Test Environment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;An environment for testing&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&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="c1"&gt;// Create Test Config&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createTestConfig&lt;/span&gt; &lt;span class="o"&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;productId&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axiosInstance&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="s2"&gt;`products/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/configs`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Test Config&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A config for testing&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&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="c1"&gt;// Create Test Feature Flag&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createTestFeatureFlag&lt;/span&gt; &lt;span class="o"&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;configId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;environmentId&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axiosInstance&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="s2"&gt;`configs/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;configId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/settings`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;hint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A feature flag for testing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;testfeatureflag&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Test Feature Flag&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;settingType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;boolean&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;initialValues&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="na"&gt;environmentId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;environmentId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;I'll bundle the individual functions together in a function called &lt;code&gt;setupFeatureIntegration&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setupFeatureIntegration&lt;/span&gt;&lt;span class="p"&gt;()&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;productId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createTestProduct&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;configId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createTestConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;environmentId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createTestEnvironment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&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;createTestFeatureFlagResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createTestFeatureFlag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;configId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;environmentId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;createTestFeatureFlagResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;productId&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;createTestFeatureFlagResponse&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="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've added the &lt;code&gt;productId&lt;/code&gt; variable to the response object returned from the &lt;code&gt;createTestFeatureFlag&lt;/code&gt; function to delete the &lt;code&gt;Test Product&lt;/code&gt; when the test completes. Deleting a product will remove all its' child resources like Environments, Configs, and Feature Flags.&lt;/p&gt;

&lt;p&gt;You can find the complete code &lt;a href="https://github.com/configcat-labs/public-api-guide-integration-testing-sample/blob/master/feature-integration.test.js" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Imagine for a moment that you have a function like this in your app that depends on a specific return value produced by the &lt;code&gt;setupFeatureIntegration&lt;/code&gt; function:
&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;appFunctionThatUsesTheFeatureFlag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;featureFlagKey&lt;/span&gt;&lt;span class="p"&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;featureFlagKey&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 can use the feature flag key&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;// I can not use the feature flag key... handle error&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;Let's write a simple integration test that expects the &lt;em&gt;feature key&lt;/em&gt; to be returned from the &lt;code&gt;setupFeatureIntegration&lt;/code&gt; function. The test function checks that the response object contains a property &lt;strong&gt;key&lt;/strong&gt;. If it doesn't, then the test will fail. In the &lt;code&gt;feature-integration.test.js&lt;/code&gt; file, add 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="c1"&gt;// feature-integration.test.js&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;axiosInstance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./utils/axios/axios-instance&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;setupFeatureIntegration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./utils/feature/feature-integration&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Remove Test Resources&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;removeTestResources&lt;/span&gt; &lt;span class="o"&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;productId&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axiosInstance&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`products/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;then&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Testing resources removed...&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;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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&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="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return an object containing a property key&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="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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setupFeatureIntegration&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;key&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;productId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;removeTestResources&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&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;I'll run the test with the &lt;code&gt;npm run test&lt;/code&gt; command, which will print the test results to the console:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;chavez@Chavezs-MBP public-api-guide-integration-testing-sample % npm run &lt;span class="nb"&gt;test&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; public-api-guide-integration-testing-sample@1.0.0 &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; jest

  console.log
    Testing resources removed...

      at log &lt;span class="o"&gt;(&lt;/span&gt;feature-integration.test.js:8:25&lt;span class="o"&gt;)&lt;/span&gt;

 PASS  ./feature-integration.test.js
  ✓ should &lt;span class="k"&gt;return &lt;/span&gt;an object containing a property key &lt;span class="o"&gt;(&lt;/span&gt;3301 ms&lt;span class="o"&gt;)&lt;/span&gt;

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        3.578 s
Ran all &lt;span class="nb"&gt;test &lt;/span&gt;suites.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Integration tests allow us to uncover and fix integration bugs. It comes in handy for checking if individual units of code function together to achieve specific functionality. I've also discussed and demonstrated how to use it to test the integration of a feature flag in some JavaScript code using &lt;a href="https://api.configcat.com/docs/" rel="noopener noreferrer"&gt;ConfigCat's Public Management API&lt;/a&gt;. If you're excited about trying this, I highly recommend signing up for a &lt;a href="https://app.configcat.com/signup" rel="noopener noreferrer"&gt;free tier ConfigCat account&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For more awesome posts and other announcements, follow ConfigCat on &lt;a href="https://twitter.com/configcat" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://www.facebook.com/configcat" rel="noopener noreferrer"&gt;Facebook&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;. The most lovable feature flag service, ever.&lt;/p&gt;

</description>
      <category>featuremanagement</category>
      <category>featureflags</category>
      <category>testing</category>
      <category>configcat</category>
    </item>
    <item>
      <title>Interview with ConfigCat Engineers</title>
      <dc:creator>Chavez Harris</dc:creator>
      <pubDate>Sun, 19 Nov 2023 19:22:51 +0000</pubDate>
      <link>https://forem.com/codedbychavez/interview-with-configcat-engineers-4fi5</link>
      <guid>https://forem.com/codedbychavez/interview-with-configcat-engineers-4fi5</guid>
      <description>&lt;p&gt;As a front-end developer, I spend most of my time writing code and developing front-end applications. Several months ago, I wondered what it would be like to run a tech startup. It turns out that I spend my spare time writing blog articles for such a company. Meet &lt;a href="https://configcat.com/" rel="noopener noreferrer"&gt;ConfigCat&lt;/a&gt;, a thriving tech startup that offers a cloud-hosted feature flagging solution to other tech companies.&lt;/p&gt;

&lt;p&gt;Most importantly, I was curious to know how the company was created and the secret behind its success, as well as how they are able to handle high-end user demands while delivering a seamless feature flagging solution. To answer these questions, I decided to conduct an online interview with the core engineering team to satisfy my curiosity and to share what I found with you, the reader.&lt;/p&gt;

&lt;h2&gt;
  
  
  But first, what is ConfigCat?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://configcat.com" rel="noopener noreferrer"&gt;ConfigCat&lt;/a&gt; is an online feature flagging service that allows users to manage feature flags from a &lt;a href="https://app.configcat.com" rel="noopener noreferrer"&gt;central dashboard&lt;/a&gt;. A feature flag is a boolean value that is linked to a feature in a software application. Most often, it is used in a conditional statement to show or hide the feature when it is true or false, respectively. This means I can control what features to show or hide in my app remotely with a few clicks without editing and redeploying my app.&lt;/p&gt;

&lt;p&gt;The company makes available an extensive list of &lt;a href="https://configcat.com/docs/sdk-reference/overview/" rel="noopener noreferrer"&gt;SDKs&lt;/a&gt; that allow developers to use their feature flag services in many programming languages and frameworks.&lt;/p&gt;

&lt;p&gt;Being a front-end guy, I mainly used the &lt;a href="https://configcat.com/docs/sdk-reference/js/" rel="noopener noreferrer"&gt;JavaScript SDK&lt;/a&gt; in my apps. I was amazed by how seamlessly the SDK works and wanted to see what the architecture supporting these processes was like.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture
&lt;/h2&gt;

&lt;p&gt;Here is a diagram of ConfigCat's architecture.&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%2Fpjp72841p91076w32y83.jpg" 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%2Fpjp72841p91076w32y83.jpg" alt="ConfigCat's architecture" width="800" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It may appear as a simple diagram, but how do each of its parts work independently, and how do they interact? Here's what I've learned, and I've also incorporated some information from the &lt;a href="https://configcat.com/architecture/" rel="noopener noreferrer"&gt;respective documentation&lt;/a&gt; to provide a clearer explanation.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Dashboard
&lt;/h3&gt;

&lt;p&gt;The dashboard is a central user interface for creating and managing feature flags. Apart from toggling a feature flag on or off, I can also target a specific group of users based on country, email, and any custom attribute, which is a neat feature. The configurations I set in the dashboard are stored in a database for later access.&lt;/p&gt;

&lt;h3&gt;
  
  
  The API
&lt;/h3&gt;

&lt;p&gt;ConfigCat offers a &lt;a href="https://configcat.com/docs/advanced/public-api/" rel="noopener noreferrer"&gt;Public API&lt;/a&gt; for managing configurations. It allows you to do pretty much the same things as you would using the &lt;a href="https://app.configcat.com/" rel="noopener noreferrer"&gt;dashboard interface&lt;/a&gt;. But this half of the company's API is primarily used for testing and scripts. The other half of the API is responsible for serving up your feature flag values to requests coming from an SDK client installed in your app (more on this soon). To shorten and optimize the requests and response time to and from the API, ConfigCat relies on &lt;a href="https://www.cloudflare.com/" rel="noopener noreferrer"&gt;Cloudflare's CDN cache&lt;/a&gt; network.&lt;/p&gt;

&lt;h3&gt;
  
  
  The CDN (Cloudflare Edge Cache)
&lt;/h3&gt;

&lt;p&gt;After taking a look at Cloudflare's documentation, I understood why using it would make sense when it comes to added security and optimized performance. Cloudflare is a large network of servers working together. The servers can process huge loads of user requests with this combined power. This allows ConfigCat to serve millions of user requests.&lt;/p&gt;

&lt;p&gt;Here's how Cloudflare is helping ConfigCat:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Traffic Security and Filtering&lt;/strong&gt; - Because Cloudflare functions as a reverse proxy, meaning it sits in front of ConfigCat’s public API, it can apply security rules and filter traffic. This is to counteract and protect the API from malicious and direct HTTP requests. A service like this is critical in a world where Distributed Denial of Service (DDoS) attacks are prevalent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caching&lt;/strong&gt; - Cloudflare also employs a network edge cache for shortening response time by storing ConfigCat’s data in a Cloudflare data center closest to the user.&lt;/p&gt;

&lt;h3&gt;
  
  
  The SDKs
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://configcat.com/docs/sdk-reference/overview/" rel="noopener noreferrer"&gt;SDKs&lt;/a&gt; are merely software packages that can be installed in a software application. They provide a connecting link between the application and ConfigCat. The SDK is then able to access the API through the Cloudflare Edge Cache to download and cache the feature flag values from the dashboard for evaluating feature flags and targeting rules.&lt;/p&gt;

&lt;p&gt;The feature flag values are downloaded in the form of a JSON file by the installed SDK. This file is downloaded periodically based on a time interval that you can modify. When the application code queries the value of a feature flag, the SDK parses the downloaded JSON file and returns the feature flag value without uploading information about you or your users.&lt;/p&gt;

&lt;p&gt;With the optimized power from Cloudflare's CDN, their service is capable of handling a large volume of requests. Here's a short dialect from Lali, one of the core engineers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Our CDN infrastructure is pretty simple as we only serve small .json files. We put CloudFlare's edge cache above our CDN nodes, which can handle a huge amount of requests without any trouble. ConfigCat values your data privacy and security, so the infrastructure is set up to only query the users' feature flag configuration. However, it never exposes personal user data like email and billing information.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Optimizing for faster code releases
&lt;/h2&gt;

&lt;p&gt;As a developer myself, I came to understand that the work of software development doesn't stop after the initial build. That's just the beginning. Because the company's entire operation is based on software, I was curious to know how they release new updates and bug fixes.&lt;/p&gt;

&lt;p&gt;ConfigCat leverages the power of automated tasks that execute every time an update is made to their code base. These automated tasks all work in tandem towards the end goal of releasing the updated software to users. The overall term used to describe this is called &lt;a href="https://en.wikipedia.org/wiki/DevOps" rel="noopener noreferrer"&gt;DevOps&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Github Actions
&lt;/h3&gt;

&lt;p&gt;One of the tools used as part of the DevOps process is &lt;a href="https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions" rel="noopener noreferrer"&gt;&lt;strong&gt;GitHub actions&lt;/strong&gt;&lt;/a&gt;. Here is an overview of how the company is using it:&lt;/p&gt;

&lt;p&gt;When code is pushed or a new feature branch is merged, it triggers a GitHub Actions workflow configured by the engineers.&lt;/p&gt;

&lt;p&gt;The workflow consists of individual tasks that run asynchronously, one after the other. Each task is simply an action that needs to be executed. The company's typical GitHub workflow includes tasks such as:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code reference scanning&lt;/strong&gt;: This is a tool developed by the team to scan the source code of an application and report on feature flag usages. It tracks where specific feature flags are used and points out the unused ones, hence avoiding the accumulation of technical debt. This tool is available to anyone; you can read more about it &lt;a href="https://configcat.com/docs/advanced/code-references/overview/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deploying to Cloudflare Pages&lt;/strong&gt;: Apart from changes to the backend code, it is beneficial to interact with a live preview of the frontend changes. The company uses Cloudflare Pages to build and deploy a preview of each commit to the company's website.&lt;/p&gt;

&lt;h3&gt;
  
  
  Azure DevOps
&lt;/h3&gt;

&lt;p&gt;Another DevOps tool that the company uses is &lt;a href="https://azure.microsoft.com/en-us/products/devops" rel="noopener noreferrer"&gt;&lt;strong&gt;Azure DevOps&lt;/strong&gt;&lt;/a&gt;. It functions similarly to GitHub Actions. This tool monitors the repository for code updates and executes a series of tasks defined in an Azure pipeline .yml file. The difference is that it runs on Azure's DevOps platform as opposed to GitHub.&lt;/p&gt;

&lt;p&gt;The pipeline contains tasks for scanning the source code to ensure it is securely written and free of known security vulnerabilities. It also contains tasks for running the code in a Docker container which is later deployed to the Azure cloud platform.&lt;/p&gt;

&lt;p&gt;Now that my curiosity about the technical side was satisfied there was also the non-technical side, the soft skills of running a company. More specifically, I wanted to understand how they dealt with the challenges of running a company.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overcoming challenges
&lt;/h2&gt;

&lt;p&gt;In my experience, taking on any feat comes with challenges. Sige, a co-founder of the company, told me that together, they formed a strong and willing team, backed by mutual trust and respect, and this has paved the way for possibilities. Here's what he mentioned:&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenges of starting and running the company
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Working together and sharing responsibilities&lt;/strong&gt;: At first, everyone on the team had different opinions. While there was an immense amount of work to be done, it was a challenge deciding who was doing what.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Technical hurdles&lt;/strong&gt;: As a team of engineers passionate about building high-quality software, We took what we already knew to a different level. We focused on creating a scalable software infrastructure that could adapt to the growing number of users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accounting and legal&lt;/strong&gt;: Scalability and software were indeed crucial, and we had to put our business caps on, learning how a company works. We shifted some of our focus toward understanding the accounting and legal aspects of running a thriving and profitable company.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The first paying customer&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Our first customer subscribed to a paid plan came around 1-1.5 years after we started to build ConfigCat. We loved building ConfigCat but it was pretty hard to wait for the first paying customer that could 'validate' our idea of building ConfigCat." - Lali, Core Engineer at ConfigCat.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As a tech user, sometimes parts break or I need some help using a piece of tech. Now that I understand the challenges and the tech side of things, I was eager to learn how the company handles customer-related issues and general support.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customer relations
&lt;/h2&gt;

&lt;p&gt;A famous quote I found reads:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Nobody cares how much you know until they know how much you care." - Theodore Roosevelt&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To get some other perspective from others who are using ConfigCat, I looked at the &lt;a href="https://www.capterra.com/p/187099/ConfigCat/" rel="noopener noreferrer"&gt;company's reviews up on Capterra&lt;/a&gt;. From what I saw, most of them were positive. Was ConfigCat capable of providing a speedy response to users having issues using their product?&lt;/p&gt;

&lt;h3&gt;
  
  
  Can ConfigCat react quickly to customer support requests?
&lt;/h3&gt;

&lt;p&gt;From my experience, I found that reaching out to the company's support team was easy. I was able to use the &lt;a href="https://configcat.com/support/" rel="noopener noreferrer"&gt;contact form&lt;/a&gt; on their website to fill out a message and chat directly with the developer team on &lt;a href="https://configcat.com/slack/" rel="noopener noreferrer"&gt;Slack&lt;/a&gt;. If you need an almost instant reply, I would recommend using Slack. After joining their Slack community, I saw developers posting issues they encountered and receiving quick assistance from the core engineers and customer success team.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;Building a feature flag service can come with challenges, but the rewards can be substantial. One of the key insights I gained from this interview (apart from the technical aspects), is the team's unwavering passion for creating an exceptional product and providing top-notch customer care.&lt;/p&gt;

&lt;p&gt;This dedication serves as their primary motivation. From my interaction with the team, it is evident why ConfigCat has garnered a significant user base, including myself, who rely on their platform to efficiently manage and release new features. When I asked, "What would be different if ConfigCat started over?" I got the following response:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I wouldn't change anything. I think we learned a lot of things during our journey with ConfigCat and I wouldn't want to change anything." - Lali, Core Engineer at ConfigCat&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Stay up-to-date
&lt;/h2&gt;

&lt;p&gt;For more posts like this and other announcements, follow ConfigCat on &lt;a href="https://twitter.com/configcat" rel="noopener noreferrer"&gt;X&lt;/a&gt;, &lt;a href="https://www.facebook.com/configcat" rel="noopener noreferrer"&gt;Facebook&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>featuremanagement</category>
      <category>interview</category>
      <category>featureflag</category>
      <category>configcat</category>
    </item>
    <item>
      <title>How to use feature flags with AWS Lambda</title>
      <dc:creator>Chavez Harris</dc:creator>
      <pubDate>Wed, 25 Oct 2023 22:34:09 +0000</pubDate>
      <link>https://forem.com/codedbychavez/how-to-use-feature-flags-with-aws-lambda-2lao</link>
      <guid>https://forem.com/codedbychavez/how-to-use-feature-flags-with-aws-lambda-2lao</guid>
      <description>&lt;p&gt;One of the most important technological breakthroughs of the century has been the internet — a digital network that makes the rest of the world feel like our next-door neighbor. Within the realm of the Internet, a recent technology known as cloud computing has paved the way for software developers to rent and manage remote servers in the cloud for hosting their applications. A smaller component of this technology is called Function as a Service, abbreviated as FaaS. FaaS removes the complexity of managing a full-blown backend server, enabling developers to focus solely on writing and executing the necessary functions required to run their applications.&lt;/p&gt;

&lt;p&gt;When FaaS and feature flags are combined, you can toggle individual functions or code blocks in those functions on or off without touching its code. Let's take a closer look.&lt;/p&gt;

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

&lt;p&gt;A feature flag is a boolean value linked to a feature that enables you to toggle the feature on or off by changing its value. Feature flags can be used with many programming languages and technologies including cloud platforms like &lt;a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;AWS Lambda&lt;/a&gt;. Today, I'll demonstrate how to use a feature flag to control the response of a &lt;a href="https://nodejs.org/en" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; API endpoint in AWS Lambda. Let's get started.&lt;/p&gt;

&lt;p&gt;Before we begin, let's review the essential prerequisites you'll need:&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://portal.aws.amazon.com/billing/signup" rel="noopener noreferrer"&gt;A verified AWS account&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Basic &lt;a href="https://nodejs.org/en/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; and AWS knowledge&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Using feature flags with AWS Lambda
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;AWS Lambda&lt;/a&gt; is described on &lt;a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;AWS's Website&lt;/a&gt; as: "Run code without thinking about servers or clusters". This means, rather than setting up a full-blown backend server to handle requests from your app, you can define functions in the language or runtime of your choice to be executed via a trigger (more on this shortly).&lt;/p&gt;

&lt;p&gt;To demonstrate how to use feature flags with AWS Lambda I'll use &lt;a href="https://configcat.com" rel="noopener noreferrer"&gt;ConfigCat's hosted feature flag service&lt;/a&gt;. They offer a &lt;a href="https://app.configcat.com/signup" rel="noopener noreferrer"&gt;free tier account&lt;/a&gt; for experimentation, and there's no limit to the number of feature flags you can implement. ConfigCat also provides an extensive &lt;a href="https://configcat.com/docs/sdk-reference/overview/" rel="noopener noreferrer"&gt;list of SDKs&lt;/a&gt; for easy integration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Up and running with AWS Lambda
&lt;/h3&gt;

&lt;p&gt;To make this demo fun, I'll create a music search app that returns a JSON object of songs produced by a specific artist. I'll use "Eminem" for example. To get up and running with AWS Lambda, you'll need to do the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Sign up for a &lt;a href="https://aws.amazon.com/free/" rel="noopener noreferrer"&gt;Free Tier AWS account&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the console, click the services menu at the top left, select &lt;strong&gt;compute&lt;/strong&gt; &amp;gt; &lt;strong&gt;Lambda&lt;/strong&gt; then create a new function:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&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%2Fo1rxw2n9bj60075hsvq6.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%2Fo1rxw2n9bj60075hsvq6.png" alt="Getting started - create a function" width="800" height="174"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new function with the following details (starting from top to bottom):&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Select the first option - Author from scratch&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Function name: &lt;code&gt;myMusicSearchFunction&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Runtime: &lt;code&gt;Node.js 18.x&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Architecture: &lt;code&gt;x86_64&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Permissions: - Leave the default option&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then click &lt;strong&gt;Create function&lt;/strong&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%2Fn0gupxidzpizn1j05jd1.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%2Fn0gupxidzpizn1j05jd1.png" alt="Creating a Lambda function" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add a trigger for executing the function. I've selected an API Gateway trigger. As a result, when a GET request is sent to the function endpoint it will trigger the execution of the function.&lt;/li&gt;
&lt;/ol&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%2Foqe2mhexwh1zys5mg18z.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%2Foqe2mhexwh1zys5mg18z.png" alt="Creating an API Gateway trigger" width="800" height="773"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If all went correctly you should be able to see the following response by sending a GET request to the endpoint generated by the trigger:&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%2Fqzlvg599x3cvccb5mqq9.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%2Fqzlvg599x3cvccb5mqq9.png" alt="API Gateway trigger created" width="800" height="186"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="s2"&gt;"Hello from Lambda!"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Coding the sample app
&lt;/h3&gt;

&lt;p&gt;Let's change the default code to create a simple music search endpoint that returns a list of songs produced by Eminem.&lt;/p&gt;

&lt;p&gt;If you prefer to follow along, I've included a sample Node.js app &lt;a href="https://github.com/configcat-labs/feature-flags-aws-lambda-sample/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Feel free to clone it to your computer.&lt;/p&gt;

&lt;h4&gt;
  
  
  Setting up the sample app
&lt;/h4&gt;

&lt;p&gt;Because my function will be executed in a node runtime environment, I've included &lt;a href="https://configcat.com/docs/sdk-reference/node/" rel="noopener noreferrer"&gt;ConfigCat's Node SDK&lt;/a&gt; as a dependency. With this SDK, the app will be able to access my &lt;a href="https://app.configcat.com" rel="noopener noreferrer"&gt;ConfigCat dashboard&lt;/a&gt; and probe the feature flag value. I'll use the &lt;code&gt;node-fetch&lt;/code&gt; package to fetch the list of songs. The &lt;code&gt;package.json&lt;/code&gt; contains everything the app needs:&lt;/p&gt;

&lt;p&gt;Contents of &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"feature-flags-aws-lambda-sample"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A companion repo for How to use feature flags with AWS Lambda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Error: no test specified&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;amp;&amp;amp; exit 1"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Chavez Harris"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"license"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ISC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"configcat-node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^9.1.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"node-fetch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^3.3.1"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;npm install&lt;/code&gt; to install the packages.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open the &lt;code&gt;index.mjs&lt;/code&gt; file. At the top, import the ConfigCat node and fetch package, then create the ConfigCat client using your SDK key (More on this in the next section):&lt;br&gt;
&lt;/p&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="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="s2"&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node-fetch&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;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="nf"&gt;getClient&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-CONFIGCAT-SDK-KEY&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;ol&gt;
&lt;li&gt;Below the above code block, I'll create a &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/nodejs-handler.html" rel="noopener noreferrer"&gt;handler function&lt;/a&gt; to be executed with the following code in the body:
&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&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;event&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;// Query the value of the feature flag and assign it to a constant&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isSearchByArtisteEnabled&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="s2"&gt;searchbyartiste&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;response_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// If the feature flag is enabled&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;isSearchByArtisteEnabled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Fetch a list of songs by eminem&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://api.deezer.com/search?q=eminem`&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;myDataAsJson&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;myData&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;response_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;myDataAsJson&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="c1"&gt;// If the feature flag is disabled&lt;/span&gt;
    &lt;span class="nx"&gt;response_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sorry, this feature is not available&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="c1"&gt;// Construct and return a response&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&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;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response_message&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="nx"&gt;response&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;Using a feature flag, I will control how the API responds. When the feature flag is on, it will return the list of songs by Eminem and when it is off the response will be "Sorry, this feature is not available". To do this, I'll use &lt;a href="https://configcat.com" rel="noopener noreferrer"&gt;ConfigCat's feature flag service&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Integrating with ConfigCat
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;First, you'll need to &lt;a href="https://app.configcat.com/signup" rel="noopener noreferrer"&gt;sign up for a free account&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In your &lt;a href="https://app.configcat.com" rel="noopener noreferrer"&gt;dashboard&lt;/a&gt;, create a feature flag with the following details.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&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%2Fckydyp8b63h0lpm4bnn1.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%2Fckydyp8b63h0lpm4bnn1.png" alt="Creating a feature flag" width="800" height="752"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ensure that you update the code in &lt;code&gt;index.mjs&lt;/code&gt; with your &lt;strong&gt;ConfigCat SDK key and feature key&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Uploading the code to AWS
&lt;/h3&gt;

&lt;p&gt;To update the code of the AWS Lambda function with the code I wrote, I'll zip all the files, including the &lt;code&gt;node_modules&lt;/code&gt; folder, and upload it to AWS.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the root folder, run the following command to zip all the files:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zip &lt;span class="k"&gt;function&lt;/span&gt;.zip &lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a &lt;code&gt;function.zip&lt;/code&gt; file.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;On the function page, with the code tab selected, upload your &lt;code&gt;function.zip&lt;/code&gt; file&lt;/li&gt;
&lt;/ol&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%2Flud0theldn9smgwy5siv.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%2Flud0theldn9smgwy5siv.png" alt="Uploading the zip file" width="800" height="129"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Deploy the function.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By visiting the API endpoint in your browser, you should get the following response:&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%2F1h815d5m50s77f73koyd.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%2F1h815d5m50s77f73koyd.png" alt="Getting an API response in the Browser" width="800" height="247"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Did it work?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Head over to the &lt;a href="https://app.configcat.com" rel="noopener noreferrer"&gt;ConfigCat dashboard&lt;/a&gt; and &lt;strong&gt;turn off&lt;/strong&gt; the feature flag.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Refresh the page, and you should see:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&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%2Fl025mgl23dcwd7u2tuh9.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%2Fl025mgl23dcwd7u2tuh9.png" alt="App - feature flag turned off" width="800" height="147"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congrats! You did it!&lt;/p&gt;

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

&lt;p&gt;The flexibility of feature flags lends itself to many technologies and we've explored how to use them in AWS Lambda without any difficulties.&lt;br&gt;
Combining feature flags with cloud functions can unlock a world of many possibilities because you can extend your cloud functions' capabilities. ConfigCat makes it easy to integrate feature flags in &lt;a href="https://configcat.com/docs/sdk-reference/overview/" rel="noopener noreferrer"&gt;many languages and frameworks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In addition to adding feature flags, you can set up user targeting rules for releasing features to specific user groups. This is most useful when setting up and doing A/B testing. With an account, you get a generous &lt;a href="https://app.configcat.com/signup" rel="noopener noreferrer"&gt;free tier&lt;/a&gt; for low-volume use cases which is ideal for getting started with feature flags.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stay connected
&lt;/h2&gt;

&lt;p&gt;For more posts and other announcements, follow ConfigCat on &lt;a href="https://twitter.com/configcat" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://www.facebook.com/configcat" rel="noopener noreferrer"&gt;Facebook&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;. The most lovable feature flag service ever.&lt;/p&gt;

</description>
      <category>featuremanagement</category>
      <category>featureflags</category>
      <category>aws</category>
      <category>howto</category>
    </item>
    <item>
      <title>How to use ConfigCat's public API to conduct integration tests</title>
      <dc:creator>Chavez Harris</dc:creator>
      <pubDate>Sat, 12 Aug 2023 15:23:04 +0000</pubDate>
      <link>https://forem.com/codedbychavez/how-to-use-configcats-public-api-to-conduct-integration-tests-c25</link>
      <guid>https://forem.com/codedbychavez/how-to-use-configcats-public-api-to-conduct-integration-tests-c25</guid>
      <description>&lt;p&gt;Feature flags are essential for effective feature release and management. Using them, we can control what features end users can see and which should remain hidden. Feature flagging allows developers to plan, launch and test new features remotely without editing code. While these benefits are fantastic, what about code testing? Having some methods in place for testing the integration of feature flags in our code can increase the likelihood of smooth feature integrations.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Have &lt;a href="https://jestjs.io/docs/getting-started" rel="noopener noreferrer"&gt;JEST&lt;/a&gt; installed - A testing framework for testing your JavaScript code&lt;/li&gt;
&lt;li&gt;Have &lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt; installed - A platform used for testing and documenting APIs&lt;/li&gt;
&lt;li&gt;Basic JavaScript and Testing knowledge&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ConfigCat's public management API
&lt;/h2&gt;

&lt;p&gt;Besides evaluating your feature flags with &lt;a href="https://configcat.com/docs/sdk-reference/overview/" rel="noopener noreferrer"&gt;ConfigCat's SDKs&lt;/a&gt;, you may want to perform the same actions as you normally do while working with the &lt;a href="https://app.configcat.com" rel="noopener noreferrer"&gt;Dashboard UI&lt;/a&gt; in your code. This is where the &lt;a href="https://api.configcat.com/docs/" rel="noopener noreferrer"&gt;ConfigCat Public Management API&lt;/a&gt; comes in useful. And to show you how it works, I'll demo how we can use it to perform actions such as setting up a testing environment and creating a new feature flag using JavaScript. Then finally, we'll write an integration test to ensure all the functions work with each other using the &lt;a href="https://jestjs.io/" rel="noopener noreferrer"&gt;JEST JavaScript Testing Framework&lt;/a&gt;. If you're eager to peek at the API docs before we get started, I have linked it &lt;a href="https://configcat.com/docs/advanced/public-api/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is integration testing?
&lt;/h2&gt;

&lt;p&gt;Compared to a unit test, which only tests a single piece of code or function, an integration test involves testing multiple units to determine if they work together.&lt;/p&gt;

&lt;p&gt;Imagine you want to test if a feature flag integrates well with your app before performing a feature deployment. A recommended way to do this is to write an integration test to catch integration bugs that could potentially seep into production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing an integration test
&lt;/h2&gt;

&lt;p&gt;Let's put the theory aside for integration tests and write one in JavaScript. Before starting, let's write some functions that utilize the public API to create resources that our testing function will evaluate.&lt;/p&gt;

&lt;p&gt;To help you follow along, I've added the following &lt;a href="https://github.com/configcat-labs/public-api-guide-integration-testing-sample" rel="noopener noreferrer"&gt;sample app&lt;/a&gt;. If you wish to code along with me, you can check out the &lt;a href="https://github.com/configcat-labs/public-api-guide-integration-testing-sample/tree/starter-code" rel="noopener noreferrer"&gt;starter-code branch&lt;/a&gt;. If you end up getting stuck, feel free to view the complete code on the &lt;code&gt;main&lt;/code&gt; branch.&lt;/p&gt;

&lt;p&gt;Because this process involves making HTTP calls to the API, I'll use an HTTP client called &lt;a href="https://axios-http.com/docs/intro" rel="noopener noreferrer"&gt;Axios&lt;/a&gt;. &lt;a href="https://api.configcat.com/docs/" rel="noopener noreferrer"&gt;ConfigCat's Public Management API&lt;/a&gt; will only process HTTP requests that contain a &lt;a href="https://configcat.com/docs/advanced/public-api/#authentication" rel="noopener noreferrer"&gt;Basic HTTP Authentication Scheme&lt;/a&gt;, so I'll add a &lt;strong&gt;basic auth username&lt;/strong&gt; and &lt;strong&gt;password&lt;/strong&gt; to the HTTP request header.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create your public API credentials on the &lt;a href="https://app.configcat.com/my-account/public-api-credentials" rel="noopener noreferrer"&gt;Public API credentials management page&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I'll create an Axios instance using the credentials. This will allow you to make requests without supplying the required headers each time. Add the following code and input your credentials in the &lt;code&gt;utils/axios/axios-instance.js&lt;/code&gt; file.&lt;br&gt;
&lt;/p&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="c1"&gt;// axios-instance.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;axios&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;axiosInstance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;axios&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="na"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.configcat.com/v1/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&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;X-CONFIGCAT-SDKKEY&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-CONFIGCAT-SDKKEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;username&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-AUTH-USERNAME&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;password&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-AUTH-PASSWORD&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;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;axiosInstance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Do this only to follow me. In a production environment keep your credentials secure: do not embed them directly in your code and do not share them.&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the &lt;code&gt;utils/feature/feature-integration.js&lt;/code&gt; file, import the &lt;em&gt;axiosInstance&lt;/em&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;const&lt;/span&gt; &lt;span class="nx"&gt;axiosInstance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../axios/axios-instance&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;ol&gt;
&lt;li&gt;After reviewing and looking at the examples in the &lt;a href="https://api.configcat.com/docs/" rel="noopener noreferrer"&gt;API docs&lt;/a&gt;, I wrote some individual functions that set up the necessary testing resources. Add these to &lt;code&gt;utils/feature/feature-integration.js&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="c1"&gt;// Create Test Product&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createTestProduct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;organizationId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;08da0156-fc5b-4132-88e5-1d42032078f5&lt;/span&gt;&lt;span class="dl"&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;await&lt;/span&gt; &lt;span class="nx"&gt;axiosInstance&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="s2"&gt;`organizations/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;organizationId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/products`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Test Product&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A product for testing&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// throw new Error(error);&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&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="c1"&gt;// Create Test Environment&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createTestEnvironment&lt;/span&gt; &lt;span class="o"&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;productId&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axiosInstance&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="s2"&gt;`products/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/environments`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Test Environment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;An environment for testing&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&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="c1"&gt;// Create Test Config&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createTestConfig&lt;/span&gt; &lt;span class="o"&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;productId&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axiosInstance&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="s2"&gt;`products/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/configs`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Test Config&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A config for testing&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&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="c1"&gt;// Create Test Feature Flag&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createTestFeatureFlag&lt;/span&gt; &lt;span class="o"&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;configId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;environmentId&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axiosInstance&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="s2"&gt;`configs/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;configId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/settings`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;hint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A feature flag for testing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;testfeatureflag&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Test Feature Flag&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;settingType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;boolean&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;initialValues&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="na"&gt;environmentId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;environmentId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;I'll bundle the individual functions together in a function called &lt;code&gt;setupFeatureIntegration&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setupFeatureIntegration&lt;/span&gt;&lt;span class="p"&gt;()&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;productId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createTestProduct&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;configId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createTestConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;environmentId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createTestEnvironment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&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;createTestFeatureFlagResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createTestFeatureFlag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;configId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;environmentId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;createTestFeatureFlagResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;productId&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;createTestFeatureFlagResponse&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="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've added the &lt;code&gt;productId&lt;/code&gt; variable to the response object returned from the &lt;code&gt;createTestFeatureFlag&lt;/code&gt; function to delete the &lt;code&gt;Test Product&lt;/code&gt; when the test completes. Deleting a product will remove all its' child resources like Environments, Configs, and Feature Flags.&lt;/p&gt;

&lt;p&gt;You can find the complete code &lt;a href="https://github.com/configcat-labs/public-api-guide-integration-testing-sample/blob/master/feature-integration.test.js" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Imagine for a moment that you have a function like this in your app that depends on a specific return value produced by the &lt;code&gt;setupFeatureIntegration&lt;/code&gt; function:
&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;appFunctionThatUsesTheFeatureFlag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;featureFlagKey&lt;/span&gt;&lt;span class="p"&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;featureFlagKey&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 can use the feature flag key&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;// I can not use the feature flag key... handle error&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;Let's write a simple integration test that expects the &lt;em&gt;feature key&lt;/em&gt; to be returned from the &lt;code&gt;setupFeatureIntegration&lt;/code&gt; function. The test function checks that the response object contains a property &lt;strong&gt;key&lt;/strong&gt;. If it doesn't, then the test will fail. In the &lt;code&gt;feature-integration.test.js&lt;/code&gt; file, add 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="c1"&gt;// feature-integration.test.js&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;axiosInstance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./utils/axios/axios-instance&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;setupFeatureIntegration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./utils/feature/feature-integration&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Remove Test Resources&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;removeTestResources&lt;/span&gt; &lt;span class="o"&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;productId&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axiosInstance&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`products/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;then&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Testing resources removed...&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;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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&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="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return an object containing a property key&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="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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setupFeatureIntegration&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;key&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;productId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;removeTestResources&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&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;I'll run the test with the &lt;code&gt;npm run test&lt;/code&gt; command, which will print the test results to the console:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;chavez@Chavezs-MBP public-api-guide-integration-testing-sample % npm run &lt;span class="nb"&gt;test&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; public-api-guide-integration-testing-sample@1.0.0 &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; jest

  console.log
    Testing resources removed...

      at log &lt;span class="o"&gt;(&lt;/span&gt;feature-integration.test.js:8:25&lt;span class="o"&gt;)&lt;/span&gt;

 PASS  ./feature-integration.test.js
  ✓ should &lt;span class="k"&gt;return &lt;/span&gt;an object containing a property key &lt;span class="o"&gt;(&lt;/span&gt;3301 ms&lt;span class="o"&gt;)&lt;/span&gt;

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        3.578 s
Ran all &lt;span class="nb"&gt;test &lt;/span&gt;suites.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Integration tests allow us to uncover and fix integration bugs. It comes in handy for checking if individual units of code function together to achieve specific functionality. I've also discussed and demonstrated how to use it to test the integration of a feature flag in some JavaScript code using &lt;a href="https://api.configcat.com/docs/" rel="noopener noreferrer"&gt;ConfigCat's Public Management API&lt;/a&gt;. If you're excited about trying this, I highly recommend signing up for a &lt;a href="https://app.configcat.com/signup" rel="noopener noreferrer"&gt;free tier ConfigCat account&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For more awesome posts and other announcements, follow ConfigCat on &lt;a href="https://twitter.com/configcat" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://www.facebook.com/configcat" rel="noopener noreferrer"&gt;Facebook&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;. The most lovable feature flag service, ever.&lt;/p&gt;

</description>
      <category>featuremanagement</category>
      <category>featureflags</category>
      <category>testing</category>
      <category>configcat</category>
    </item>
    <item>
      <title>How to use feature flags in GODOT Engine</title>
      <dc:creator>Chavez Harris</dc:creator>
      <pubDate>Sat, 24 Jun 2023 16:17:19 +0000</pubDate>
      <link>https://forem.com/codedbychavez/how-to-use-feature-flags-in-godot-engine-81m</link>
      <guid>https://forem.com/codedbychavez/how-to-use-feature-flags-in-godot-engine-81m</guid>
      <description>&lt;p&gt;According to an &lt;a href="https://www.cnet.com/tech/gaming/why-video-games-are-primed-for-a-big-comeback-in-2023/" rel="noopener noreferrer"&gt;article&lt;/a&gt; published by CNET, the growth of the gaming industry is expected to increase. Due to this, new game titles are on the rise as greater demands are placed on gaming companies to remain competitive by keeping their users engaged with new features and updates. With the proper feature flagging mechanism, new features and updates can be effectively managed and released to users.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Feature Flag?
&lt;/h2&gt;

&lt;p&gt;Today, feature flags are known as the driving force behind feature rollouts. A feature flag is a boolean value linked to a feature. When the flag is false, the feature it controls is hidden. This brilliant yet simple mechanism can be integrated and used within &lt;a href="https://configcat.com/docs/sdk-reference/overview/" rel="noopener noreferrer"&gt;many programming languages and technologies&lt;/a&gt; you may already be using. One of those technologies is an open source Gaming Engine called &lt;a href="https://godotengine.org/" rel="noopener noreferrer"&gt;GODOT&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pre-requisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/download" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Have &lt;a href="https://godotengine.org/download/windows/" rel="noopener noreferrer"&gt;GODOT&lt;/a&gt; installed&lt;/li&gt;
&lt;li&gt;Have &lt;a href="https://dotnet.microsoft.com/en-us/download/dotnet/7.0" rel="noopener noreferrer"&gt;.NET 7.0&lt;/a&gt; installed&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Let's talk about GODOT
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;GODOT&lt;/strong&gt; is an open-source gaming engine for developing 2D and 3D games. Compared to similar technologies like &lt;a href="https://unity.com/" rel="noopener noreferrer"&gt;Unity&lt;/a&gt; and &lt;a href="https://www.unrealengine.com/" rel="noopener noreferrer"&gt;Unreal Engine&lt;/a&gt;, GODOT is lighter and less taxing on your hardware resources. It is the ideal choice for building simple games.&lt;/p&gt;

&lt;p&gt;To demonstrate the usage of feature flags in this technology, I've created a &lt;a href="https://github.com/configcat-labs/using-feature-flags-in-godot-sample/tree/starter-code" rel="noopener noreferrer"&gt;sample project&lt;/a&gt; to help you follow along.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup and installation
&lt;/h2&gt;

&lt;p&gt;To use the GODOT Engine, you'll need to install and set it up on your computer.&lt;/p&gt;

&lt;p&gt;For this demo, I'll use the latest version of the GODOT Engine, &lt;code&gt;version 4.0.1 - .NET&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; &lt;a href="https://godotengine.org/download/windows/" rel="noopener noreferrer"&gt;Download and install GODOT&lt;/a&gt;. Your download will contain a zipped folder. Unzip the folder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; You can launch the GODOT Engine by running the application file from the extracted folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the sample app
&lt;/h2&gt;

&lt;p&gt;With GODOT installed, let's look at running the sample GODOT project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; Clone the &lt;a href="https://github.com/configcat-labs/using-feature-flags-in-godot-sample/tree/starter-code" rel="noopener noreferrer"&gt;starter-code&lt;/a&gt; branch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; Launch the GODOT Engine, browse to the folder, and open the project.&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%2Frx7iq7p32dmo4id91a8c.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%2Frx7iq7p32dmo4id91a8c.png" alt="Opening the starter code" width="800" height="241"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.&lt;/strong&gt; If you'd like to use VS Code as your editor, you can configure it as an external editor, as shown 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%2F5z0mrr4lrl6i3ot2u95b.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%2F5z0mrr4lrl6i3ot2u95b.png" alt="Setting VS Code as GODOT external editor" width="800" height="649"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Using feature flags in GODOT
&lt;/h2&gt;

&lt;p&gt;One way to integrate and use feature flags in GODOT is to use a script. You can attach the script to one of your existing nodes, and when the node loads into your scene the script will be executed. Let's look at how to do this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; With the project open and the &lt;strong&gt;Scene&lt;/strong&gt; tab selected, create a new node by clicking the &lt;strong&gt;+ Other Node&lt;/strong&gt; button in the left pane.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; Create a &lt;strong&gt;Sprite 2D&lt;/strong&gt; Node. You can also create just about any node with an attached script.&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%2Fa1m48n2dhr6l5idh3tz6.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%2Fa1m48n2dhr6l5idh3tz6.png" alt="Creating a Sprite node" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.&lt;/strong&gt; Right-click on the node to open the context menu, and select attach script as shown 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%2F4r56ppdbx4ydtg8z3v8q.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%2F4r56ppdbx4ydtg8z3v8q.png" alt="Attaching a node script" width="800" height="816"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The above will create a new file with the name &lt;code&gt;Sprite.cs&lt;/code&gt; in your project folder. Let's look at how we can print a message to the console based on the value of a feature flag when the node is loaded.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.&lt;/strong&gt; Save the scene. &lt;strong&gt;Scene&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Save Scene&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up a feature flag
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://configcat.com/" rel="noopener noreferrer"&gt;ConfigCat&lt;/a&gt; offers a cloud-based solution for creating and managing feature flags. Let's use it to add a feature flag to use in GODOT.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; To sign up for a ConfigCat account, click &lt;a href="https://app.configcat.com/auth/signup" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; In the &lt;a href="https://app.configcat.com" rel="noopener noreferrer"&gt;Dashboard&lt;/a&gt;, create a feature flag with the following details:&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%2Fn2t74yhskh7k17wi44yv.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%2Fn2t74yhskh7k17wi44yv.png" alt="Adding a feature flag" width="800" height="749"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Connecting GODOT to ConfigCat
&lt;/h3&gt;

&lt;p&gt;To connect the GODOT script to ConfigCat for querying the status of the flag, we'll need to download a client SDK. Because our GODOT app uses C#, we can download and integrate &lt;a href="https://configcat.com/docs/sdk-reference/dotnet/" rel="noopener noreferrer"&gt;ConfigCat's .NET client SDK&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; To install the SDK into our project, we'll first need to download and install Microsoft's &lt;a href="https://dotnet.microsoft.com/en-us/download/dotnet/7.0" rel="noopener noreferrer"&gt;.NET 7.0 SDK&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; After the installation completes, double-check that &lt;code&gt;dotnet&lt;/code&gt; is installed by running this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet &lt;span class="nt"&gt;--info&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3.&lt;/strong&gt; Launch a terminal window in the project folder, and run the following command to install the &lt;a href="https://configcat.com/docs/sdk-reference/dotnet/" rel="noopener noreferrer"&gt;ConfigCat .NET client SDK&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package ConfigCat.Client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4.&lt;/strong&gt; Open the &lt;code&gt;Sprite.cs&lt;/code&gt; file in the project folder. At the top of the file, add the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;ConfigCat.Client&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;5.&lt;/strong&gt; Let's create a &lt;code&gt;Ready()&lt;/code&gt; method to execute when the node enters the scene. In the body of the class, add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Sprite&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Sprite2D&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Initialize ConfigCat client&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;IConfigCatClient&lt;/span&gt; &lt;span class="n"&gt;configCatClient&lt;/span&gt; &lt;span class="p"&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="s"&gt;"YOUR_CONFIGCAT_SDK_KEY"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;isMyGodotFeatureFlag&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;_Ready&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;

      &lt;span class="n"&gt;isMyGodotFeatureFlag&lt;/span&gt; &lt;span class="p"&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;GetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mygodotfeatureflag"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&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="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isMyGodotFeatureFlag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;GD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Your feature flag is enabled!!"&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="n"&gt;GD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Your feature flag is disabled!!"&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;If you build and run the project by clicking the "&lt;em&gt;Play&lt;/em&gt;" icon button at the top right, you should see the sprite node icon displayed in the window and the following message logged to your console:&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%2Fbxdl40vxpg8g0jd50qh5.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%2Fbxdl40vxpg8g0jd50qh5.png" alt="Console output from GODOT" width="800" height="139"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's take it a step further and make the note rotate when the feature flag is enabled. To do this, modify the &lt;code&gt;Sprite&lt;/code&gt; class as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Sprite&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Sprite2D&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;speed&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;AngularSpeed&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;Mathf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Initialize ConfigCat client&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;IConfigCatClient&lt;/span&gt; &lt;span class="n"&gt;configCatClient&lt;/span&gt; &lt;span class="p"&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="s"&gt;"YOUR_CONFIGCAT_SDK_KEY"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;isRotateSpriteEnabled&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;_Ready&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;isRotateSpriteEnabled&lt;/span&gt; &lt;span class="p"&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;GetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mygodotfeatureflag"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&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="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isRotateSpriteEnabled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;GD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Your feature flag is enabled!!"&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="n"&gt;GD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Your feature flag is disabled!!"&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;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;_Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="p"&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="n"&gt;isRotateSpriteEnabled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;Rotation&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;AngularSpeed&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;delta&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;You can view the complete &lt;code&gt;Sprite.cs&lt;/code&gt; file &lt;a href="https://github.com/configcat-labs/using-feature-flags-in-godot-sample/blob/master/Sprite.cs" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6.&lt;/strong&gt; If you run the project again, and the feature flag is enabled, the sprite node will rotate:&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%2Fn84813cqllq2ud6yc2s7.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%2Fn84813cqllq2ud6yc2s7.png" alt="Rotating Sprite" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;Feel free to check out the completed GODOT project &lt;a href="https://github.com/configcat-labs/using-feature-flags-in-godot-sample" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;The flexible nature of feature flagging lends itself to many software technologies, and game development engines are no exception. You've seen how simple it is to add a feature flag to GODOT, and we've only scratched the surface of what is possible with feature flagging. With a feature flagging solution, you can enhance canary deployments, A/B testing, and software updates. One of the most sought-after benefits of flagging features is that you can switch them on and off remotely without touching your code.&lt;/p&gt;

&lt;p&gt;ConfigCat also supports many other frameworks and languages. Check out the entire list of supported SDKs &lt;a href="https://configcat.com/docs/sdk-reference/overview/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For more awesome content, keep up with ConfigCat on &lt;a href="https://twitter.com/configcat" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://www.facebook.com/configcat" rel="noopener noreferrer"&gt;Facebook&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>godot</category>
      <category>featuremanagement</category>
      <category>featureflags</category>
      <category>gaming</category>
    </item>
    <item>
      <title>How to use ConfigCat with Redis</title>
      <dc:creator>Chavez Harris</dc:creator>
      <pubDate>Wed, 07 Jun 2023 19:51:18 +0000</pubDate>
      <link>https://forem.com/codedbychavez/how-to-use-configcat-with-redis-1f1a</link>
      <guid>https://forem.com/codedbychavez/how-to-use-configcat-with-redis-1f1a</guid>
      <description>&lt;p&gt;Ever since the dawn of feature releases, feature flags have become the de facto standard for managing and controlling features in software applications. Many software development methodologies these days such as agile, are heavily focused on releasing continuous updates and features. In addition, a few companies have based their entire business around serving clients a cloud-based feature flagging solution. But in limited bandwidth situations or when you need to optimize the performance of your client-facing applications making API requests may not be ideal. This can be handled by implementing a process called caching with the help of a popular tool called &lt;a href="https://link-to-redis" rel="noopener noreferrer"&gt;Redis&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;A feature flag can be described as a light switch for features. It holds a boolean value, that when set to true, the feature it controls can be displayed to the user and otherwise hidden.&lt;/p&gt;

&lt;p&gt;The awesome thing about feature flagging is that it can be applied to a wide range of &lt;a href="https://github.com/orgs/configcat-labs/repositories" rel="noopener noreferrer"&gt;programming languages and frameworks&lt;/a&gt;. And because of its flexible nature, it can be used alongside other technologies. Let's look at how it can be implemented with a &lt;a href="https://redis.io/" rel="noopener noreferrer"&gt;Redis cache&lt;/a&gt; to see how it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pre-requisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A code editor (e.g. &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/en/" rel="noopener noreferrer"&gt;NodeJS&lt;/a&gt; (version 16 or higher)&lt;/li&gt;
&lt;li&gt;Basic &lt;a href="https://nodejs.org/en" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; and &lt;a href="https://redis.io/" rel="noopener noreferrer"&gt;Redis&lt;/a&gt; knowledge.&lt;/li&gt;
&lt;li&gt;Have &lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; installed on your computer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To help you follow through with me, I've created a Node.js sample app that you can clone from the following &lt;a href="https://github.com/configcat-labs/configcat-with-redis-sample/tree/starter-cod" rel="noopener noreferrer"&gt;link&lt;/a&gt;. Later on, we'll cover how to set it up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a feature flag
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; To get started, sign up for a &lt;a href="https://app.configcat.com/signup" rel="noopener noreferrer"&gt;free ConfigCat account&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; In your dashboard, create a feature flag with the following information:&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%2F0k09exxa6aknfy61mk4w.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%2F0k09exxa6aknfy61mk4w.png" alt=" " width="686" height="602"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Redis cache client
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;3.&lt;/strong&gt; From the &lt;a href="https://github.com/configcat-labs/configcat-with-redis-sample/tree/starter-code" rel="noopener noreferrer"&gt;&lt;code&gt;starter-code&lt;/code&gt;&lt;/a&gt; branch, open the file called &lt;code&gt;configcat-redis-cache.js&lt;/code&gt; and add the following functions:&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;// ...&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;configcatRedisCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redisClientOptions&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;isRedisAvailable&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cacheClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redisClientOptions&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;cacheClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&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;cacheClient&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="s2"&gt;connect&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="o"&gt;=&amp;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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Connected to Redis&lt;/span&gt;&lt;span class="dl"&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;isRedisAvailable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&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;cacheClient&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="s2"&gt;error&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;error&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Redis 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="s2"&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;isRedisAvailable&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;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastCacheItems&lt;/span&gt; &lt;span class="o"&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;configcatRedisCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&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="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;isRedisAvailable&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;lastCacheItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;];&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getAsync&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;util&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;promisify&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;cacheClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&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;cacheClient&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;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&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="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Cache read failed! \n &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="s2"&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;lastCacheItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&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="nx"&gt;configcatRedisCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&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;lastCacheItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;item&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;setAsync&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;util&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;promisify&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;cacheClient&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&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;cacheClient&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&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="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Cache write failed! \n &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="s2"&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="c1"&gt;// ...&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Here's what each function does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;configcatRedisCache&lt;/code&gt;&lt;/strong&gt; - Takes the client options as its parameter and sets up a connection to the docker Redis instance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;configcatRedisCache.prototype.get&lt;/code&gt;&lt;/strong&gt; - Uses the provided &lt;code&gt;key&lt;/code&gt; to obtain the cached item from Redis.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;configcatRedisCache.prototype.set&lt;/code&gt;&lt;/strong&gt; - Takes the &lt;code&gt;key&lt;/code&gt; and &lt;code&gt;item&lt;/code&gt; as parameters. Puts the &lt;code&gt;item&lt;/code&gt; into the redis cache that can be later accessed using the &lt;code&gt;key&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4.&lt;/strong&gt; Open the &lt;code&gt;index.js&lt;/code&gt; file and add 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="c1"&gt;// ...&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;redisOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6379&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="nf"&gt;getClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0yDbCLmNK0qIUakB2LFJDA/u0Z5j4oDjkuHKOVJkIo9Dw&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&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;PollingMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AutoPoll&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;configcatRedisCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redisOptions&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;setInterval&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;getValueAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;isMyAwesomeFeatureEnabled&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="nf"&gt;then&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;=&amp;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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&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="nf"&gt;toTimeString&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt; isMyAwesomeFeatureEnabled: &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="s2"&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="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what the code is doing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The configuration required to establish a connection to the Redis docker instance was saved to the &lt;code&gt;redisOptions&lt;/code&gt; variable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;That variable was then used in the &lt;code&gt;configcat.getClient&lt;/code&gt; method passed as an argument to set up the &lt;code&gt;cache&lt;/code&gt; property.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;setInterval&lt;/code&gt; function runs every 5 seconds to fetch the value of the feature flag, but instead of reaching out to &lt;a href="https://configcat.com" rel="noopener noreferrer"&gt;ConfigCat&lt;/a&gt;, it obtains the value from the Redis cache and logs the result with the date to the console.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find the complete code for the &lt;code&gt;index.js&lt;/code&gt; file &lt;a href="https://github.com/configcat-labs/configcat-with-redis-sample/blob/master/index.js" rel="noopener noreferrer"&gt;here&lt;/a&gt;. If you're curious about learning more about using a custom cache with &lt;a href="https://configcat.com" rel="noopener noreferrer"&gt;ConfigCat&lt;/a&gt; as we did, refer to &lt;a href="https://configcat.com/docs/sdk-reference/node/#using-custom-cache-implementation" rel="noopener noreferrer"&gt;this documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With the code in place, let's look at how we can run the sample app to ensure it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the sample app
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; Clone the &lt;a href="https://github.com/configcat-labs/configcat-with-redis-sample" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; Run the following commands to install the required node packages:&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;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;configcat-node&lt;/code&gt; and &lt;code&gt;redis&lt;/code&gt; npm packages will also be installed using this command. The &lt;code&gt;configcat-node&lt;/code&gt; package will be used as a client to connect to and pull the feature flag status from your &lt;a href="https://app.configcat.com" rel="noopener noreferrer"&gt;ConfigCat dashboard&lt;/a&gt;. &lt;code&gt;redis&lt;/code&gt; will be used similarly and would connect to a local docker instance of Redis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.&lt;/strong&gt; Using docker, pull the latest Redis docker image and run the container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker pull redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; container-redis &lt;span class="nt"&gt;-p&lt;/span&gt; 6379:6379 &lt;span class="nt"&gt;-d&lt;/span&gt; redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4.&lt;/strong&gt; Start the node application with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the following output logged to your terminal every 5 seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; configcat-with-redis-sample@1.0.0 start
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; node index.js

Connected to Redis
17:56:55 GMT-0300 &lt;span class="o"&gt;(&lt;/span&gt;French Guiana Time&lt;span class="o"&gt;)&lt;/span&gt; isMyAwesomeFeatureEnabled: &lt;span class="nb"&gt;false
&lt;/span&gt;17:57:00 GMT-0300 &lt;span class="o"&gt;(&lt;/span&gt;French Guiana Time&lt;span class="o"&gt;)&lt;/span&gt; isMyAwesomeFeatureEnabled: &lt;span class="nb"&gt;false
&lt;/span&gt;17:57:05 GMT-0300 &lt;span class="o"&gt;(&lt;/span&gt;French Guiana Time&lt;span class="o"&gt;)&lt;/span&gt; isMyAwesomeFeatureEnabled: &lt;span class="nb"&gt;false
&lt;/span&gt;17:57:10 GMT-0300 &lt;span class="o"&gt;(&lt;/span&gt;French Guiana Time&lt;span class="o"&gt;)&lt;/span&gt; isMyAwesomeFeatureEnabled: &lt;span class="nb"&gt;false
&lt;/span&gt;17:57:15 GMT-0300 &lt;span class="o"&gt;(&lt;/span&gt;French Guiana Time&lt;span class="o"&gt;)&lt;/span&gt; isMyAwesomeFeatureEnabled: &lt;span class="nb"&gt;false
&lt;/span&gt;17:57:20 GMT-0300 &lt;span class="o"&gt;(&lt;/span&gt;French Guiana Time&lt;span class="o"&gt;)&lt;/span&gt; isMyAwesomeFeatureEnabled: &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Toggling on or off the feature flag within your &lt;a href="https://app.configcat.com" rel="noopener noreferrer"&gt;ConfigCat dashboard&lt;/a&gt; will force the cache to refresh. As a result, The new value will be pulled from &lt;a href="https://configcat.com" rel="noopener noreferrer"&gt;ConfigCat&lt;/a&gt; to replace the existing value in the cache.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;If you're planning on cutting back or saving bandwidth utilization and optimizing for better performance on the client side then a caching solution like Redis can help. And, as we've seen from the code examples, Redis integrates quite easily with &lt;a href="https://configcat.com" rel="noopener noreferrer"&gt;ConfigCat&lt;/a&gt;. With a caching solution in place, you can supercharge the way you do standard feature releases, canary deployments, and A/B testing. Besides Node.js, ConfigCat also supports &lt;a href="https://github.com/orgs/configcat-labs/repositories" rel="noopener noreferrer"&gt;many languages and frameworks&lt;/a&gt; and provides &lt;a href="https://configcat.com/docs/sdk-reference/overview/" rel="noopener noreferrer"&gt;SDKs&lt;/a&gt; for easy configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stay updated
&lt;/h2&gt;

&lt;p&gt;For more posts like this and other announcements, follow ConfigCat on &lt;a href="https://twitter.com/configcat" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://www.facebook.com/configcat" rel="noopener noreferrer"&gt;Facebook&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>featuremanagement</category>
      <category>featureflags</category>
      <category>redis</category>
      <category>configcat</category>
    </item>
    <item>
      <title>Automating and managing your ConfigCat resources with Terraform</title>
      <dc:creator>Chavez Harris</dc:creator>
      <pubDate>Sun, 04 Jun 2023 13:53:42 +0000</pubDate>
      <link>https://forem.com/codedbychavez/automating-and-managing-your-configcat-resources-with-terraform-3k04</link>
      <guid>https://forem.com/codedbychavez/automating-and-managing-your-configcat-resources-with-terraform-3k04</guid>
      <description>&lt;p&gt;It can be time-consuming to create and manage the infrastructure that drives your software applications as they grow and become larger. Also, what about ongoing updates and releases of new features? Luckily, there is a solution to this problem in the form of a tool designed by &lt;a href="https://www.hashicorp.com/" rel="noopener noreferrer"&gt;Hashicorp&lt;/a&gt; called Terraform. This allows us to define our infrastructure in a central configuration file without having to create it on every provider platform we use.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; is a popular &lt;a href="https://en.wikipedia.org/wiki/Infrastructure_as_code" rel="noopener noreferrer"&gt;Infrastructure as Code&lt;/a&gt; tool that allows you to automate and manage your infrastructure by defining how it should be in an easy-to-read configuration file.&lt;/p&gt;

&lt;p&gt;Upon executing the file, Terraform does the heavy lifting for you (that you would have otherwise had to do manually) by connecting to your cloud providers of choice to provision your infrastructure on the fly. By also keeping track of the state of your infrastructure changes along the way, Terraform can figure out what parts of your infrastructure have remained the same from what has changed and only executes those changes.&lt;/p&gt;

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

&lt;p&gt;In addition to being the go-to choice for many companies managing their infrastructure as code, Terraform can also be ideal for managing and provisioning your &lt;a href="https://configcat.com/" rel="noopener noreferrer"&gt;ConfigCat&lt;/a&gt; resources programmatically. Using the &lt;a href="https://registry.terraform.io/providers/configcat/configcat/latest/docs" rel="noopener noreferrer"&gt;ConfigCat Feature Flags Provider&lt;/a&gt; for Terraform, you can gain the same level of control and management over these resources similarly as you do with your other infrastructure components.&lt;/p&gt;

&lt;p&gt;Here are some reasons why you might want to consider using ConfigCat and Terraform together for your next infrastructure build:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Supports a multienvironment setup&lt;/strong&gt; - If your company already has multiple environments like test, staging, and production for other elements of their infrastructure you can define a similar setup using the ConfigCat provider in your Terraform config file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best for managing multiple products&lt;/strong&gt; - Even though the ConfigCat dashboard is easy and intuitive to navigate, you can often lose track of all your products and their respective configurations. It would be better to define them with Terraform. With this approach, you can prevent overlapping and duplications to ensure that your resources are used efficiently with less clutter.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Collaboration, and centralized infrastructure management&lt;/strong&gt; - Terraform scripts usually live in a git repository, making it ideal for managing your &lt;a href="https://configcat.com/" rel="noopener noreferrer"&gt;ConfigCat&lt;/a&gt; resources in a central place. With the added benefit of git operations, you can perform actions such as branching, pull requests and approve strategy.&lt;br&gt;&lt;br&gt;
&lt;em&gt;For example&lt;/em&gt; - Suppose you were tasked with creating a new environment in ConfigCat. You can either do it manually without supervision or you can open a pull request with your modified Terraform&lt;br&gt;
scripts and that pull request can be approved. This way you have strict control over who can create resources and or what resources are created.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Let's examine an example of how you can use Terraform to set up a feature flag resource from scratch.&lt;/p&gt;

&lt;p&gt;To get started, check that you have &lt;a href="https://developer.hashicorp.com/terraform/downloads" rel="noopener noreferrer"&gt;Terraform installed and configured&lt;/a&gt; on your computer. It is also quite handy to install the &lt;a href="https://marketplace.visualstudio.com/items?itemName=HashiCorp.terraform" rel="noopener noreferrer"&gt;Terraform VS Code extension&lt;/a&gt; for Terraform if you’re using the &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;VS Code Editor&lt;/a&gt; to follow along.&lt;/p&gt;

&lt;p&gt;The sample ConfigCat infrastructure that we're about to set up will include the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;An Organization&lt;/strong&gt; - Because organizations can contain specific information about a company or individual's billing makes them read-only from Terraform. It is recommended that you first create an empty organization in your &lt;a href="https://app.configcat.com/" rel="noopener noreferrer"&gt;ConfigCat dashboard&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A Product&lt;/strong&gt; - Represents your application, and includes your Configs, Environments, and Team members.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;An Environment&lt;/strong&gt; - Represents the environment in your development lifecycle like production, staging, development, etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A Feature flag&lt;/strong&gt; - A feature toggle (boolean value) that you can turn on or off to enable or disable a feature.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Finding your credentials
&lt;/h3&gt;

&lt;p&gt;To grant Terraform access to your &lt;a href="https://app.configcat.com/" rel="noopener noreferrer"&gt;ConfigCat account&lt;/a&gt; you’ll need to create your ConfigCat public API credentials &lt;a href="https://app.configcat.com/my-account/public-api-credentials" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Integrating with Terraform
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;With a blank folder opened in your code editor, create two files: &lt;code&gt;variables.tf&lt;/code&gt; and &lt;code&gt;main.tf&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In the &lt;a href="http://variables.tf" rel="noopener noreferrer"&gt;&lt;code&gt;variables.tf&lt;/code&gt;&lt;/a&gt; file, add the credentials you created:
&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="nx"&gt;variable&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;configcat_basic_auth_username&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;INPUT_YOUR_AUTH_USERNAME_HERE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;configcat_basic_auth_password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;INPUT_YOUR_AUTH_PASSWORD_HERE&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;ol&gt;
&lt;li&gt;In the &lt;code&gt;main.ts&lt;/code&gt; file:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;a. Define the ConfigCat provider along with your credentials:&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;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;configcat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;configcat/configcat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;~&amp;gt; 1.0&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="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;configcat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;basic_auth_username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configcat_basic_auth_username&lt;/span&gt;
  &lt;span class="nx"&gt;basic_auth_password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configcat_basic_auth_password&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;b. Define the following resources to be created:&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;data&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;configcat_organizations&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my_organizations&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;resource&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;configcat_product&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my_product&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;organization_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configcat_organizations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;my_organizations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;organizations&lt;/span&gt;&lt;span class="p"&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;organization_id&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My product&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My example product&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;configcat_environment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my_environment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;configcat_product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;my_product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Production&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The production environment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;configcat_config&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my_config&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;configcat_product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;my_product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My config&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My example config&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;configcat_setting&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;is_awesome&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;config_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;configcat_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;my_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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="s2"&gt;isMyAwesomeFeatureEnabled&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My awesome feature flag&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;hint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;this is the hint for my awesome feature flag&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;setting_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;boolean&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;Execute this with Terraform using the &lt;code&gt;plan&lt;/code&gt; and &lt;code&gt;apply&lt;/code&gt; commands respectively, and within a few seconds, you should see the specified resources created in your Dashboard.&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%2F75jwdlyucjyu9kzaotcf.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%2F75jwdlyucjyu9kzaotcf.png" alt="ConfigCat resources created by Terraform" width="800" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Terraform takes care of what has to be created, updated, and destroyed based on changes you make to your Terraform config file. Using it with &lt;a href="https://app.configcat.com/" rel="noopener noreferrer"&gt;ConfigCat&lt;/a&gt; has its proven benefits as we discussed earlier. In my opinion, it supercharges your development, feature releases, and canary deployment workflows while giving you high-level and automated management over your current infrastructure within a simple &lt;code&gt;.tf&lt;/code&gt; config file.&lt;/p&gt;

&lt;p&gt;Why not give it a go? Sign up for a free-tier ConfigCat account &lt;a href="https://app.configcat.com/signup" rel="noopener noreferrer"&gt;here&lt;/a&gt; and stay on top of the latest posts and announcements from ConfigCat on &lt;a href="https://twitter.com/configcat" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://www.facebook.com/configcat" rel="noopener noreferrer"&gt;Facebook&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>featuremanagement</category>
      <category>terraform</category>
      <category>introduction</category>
    </item>
  </channel>
</rss>
