Feature flags are a nifty tool to help developers build and change configurations at runtime. You might be a web developer looking for ways to test out a webpage design. Instead of killing the process and rerunning the application each time, or commenting out frontend designs, consider using a feature flag to test out different themes for your site.
It is also a great way to share the website design in real time to different groups of users and gather feedback before you choose the design you want.
In this tutorial, you will learn how to create a quick Flask application that tests out two feature flags on a webpage that can hide or display gifs, images, or audio files. The example in this article is inspired by the song “Maps” by the Yeah Yeah Yeahs.
Tutorial requirements
- Python 3.6 or newer. If your operating system does not provide a Python interpreter, you can go to python.org to download an installer.
- Visual Studio Code or your favorite IDE.
LaunchDarkly account. If you haven’t done so already, create a free account.
Create a feature flag
In the LaunchDarkly app, create a feature flag with the following configuration:
- Flag name: “animate-gif”
- Configuration: Custom
- Flag type: Boolean
- Variants: animate file - true
- Variants: static file - false
- Serve when targeting is ON: animate file
- Serve when targeting is OFF: static file
Configuration
If you are using a Unix or Mac OS system, open a terminal and enter the following commands to do the tasks described below:
$ mkdir dancingflag
$ cd dancingflag
$ python3 -m venv venv
$ source venv/bin/activate
(venv) $ pip install flask python-dotenv launchdarkly-server-sdk
For those of you following the tutorial on Windows, enter the following commands in a command prompt window:
$ md dancingflag
$ cd dancingflag
$ python -m venv venv
$ venv\Scripts\activate
(venv) $ pip install flask python-dotenv launchdarkly-server-sdk
The last command uses pip, the Python package installer, to install the five packages that we are going to use in this project, which are:
- The Flask framework, to create the web application.
- The python-dotenv package, to read a configuration file.
The LaunchDarkly SDK to initiate and activate feature flags on the server.
Set up the developer environment for the Flask application
Make sure that you are currently in the virtual environment of your project’s directory in the terminal or command prompt.
Create a file named .env and add the following line to define the environment variable.
LAUNCHDARKLY_SDK_KEY="sdk-###############"
Navigate to the Organizations settings / Projects dashboard page to locate a list of projects in your account. Find the project you created your initial flag in.
On the General page, select Environments.
Find the Test section and click on the “...” to find your SDK key. Copy the SDK key into your .env file. Save the file.
Since we will be utilizing Flask throughout the project, we will need to set up the development server. Add a .flaskenv file (make sure you have the leading dot) to your project with the following lines:
FLASK_APP=app.py
FLASK_ENV=development
FLASK_APP tells the Flask framework where our application is located.
FLASK_ENV configures Flask to run in debug mode.
These lines are convenient because every time you save the source file, the server will reload and reflect the changes.
Build the Flask application to add more feature flags
Create a new file named main.py in the dancingflag project folder so that it can run a functional Flask application. Add the import statements below:
from flask import Flask, render_template
import os
import ldclient
from ldclient import Context
from ldclient.config import Config
from threading import Lock, Event
app = Flask(__name__)
from dotenv import load_dotenv
load_dotenv()
Create a route to render a homepage on the Flask application under the LaunchDarkly initialization
# Set sdk_key to your LaunchDarkly SDK key.
sdk_key = os.getenv("LAUNCHDARKLY_SDK_KEY")
# Set feature_dance_flag_key to the feature flag key you want to evaluate.
feature_dance_flag_key = "animate-gif"
# this is printed in the terminal
def show_evaluation_result(key: str, value: bool):
print()
print(f"*** The {key} feature flag evaluates to {value}")
@app.route("/")
def home():
# Set up the evaluation context
context = Context.builder("example-user-key").kind("user").name("Sandy").build()
# Check the value of the feature flag
dance_flag_value = ldclient.get().variation(feature_dance_flag_key, context, False)
# Render different templates based on the flag value
if dance_flag_value:
return render_template("dance.html")
else:
return render_template("wait.html")
Contexts are defined and passed through at the time of initiating the SDK after login. This process determines how feature flags should behave for a specific user or entity.
The "example-user-key" is the unique identifier for the context that ensures that the flag evaluations are consistent for the same user or entity across sessions.
The “kind” attribute is set to “user” to represent an individual user, who is named “Sandy” in this case. It is optional to change the name for personal debugging and viewing purposes.
Set up the HTML Flask page
Create a new subdirectory in the application named “templates” with two new HTML files: dance.html and wait.html.
If the “dance_flag_value” is set to true, then our character Lain is ready to dance on the dance.html page. Copy this code into dance.html.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lain Dancing</title>
</head>
<body>
<h1>They don't love you like I love you 💃</h1>
<img src="https://media1.tenor.com/m/O8k-DvUjaeEAAAAd/lain-dance.gif" alt="Dancing Lain">
<br>
</body>
</html>
Modify the wait.html file with the following code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lain Waiting</title>
</head>
<body>
<h1>WAIT 😲✋🏻</h1>
<p>Turn on the flag to see Lain dance.</p>
<img src="{{ url_for('static', filename='lain.jpg') }}" alt="Lain" />
</body>
</html>
Create a new subdirectory in the root folder named “static” to store the static image of Lain that is rendered in wait.html.
Download the image from the GitHub repository and add it to the “static” folder.
Great, it’s time to flip the switch and see the feature flags in action.
Initialize the Python script
Copy this code into main.py to define the main script for the Python interpreter, launch the LaunchDarkly SDK, and put the feature flags in action:
if __name__ == "__main__":
if not sdk_key:
print("*** Please set the LAUNCHDARKLY_SDK_KEY env first")
exit()
if not feature_dance_flag_key:
print("*** Please set the LAUNCHDARKLY_FLAG_KEY env first")
exit()
ldclient.set_config(Config(sdk_key))
if not ldclient.get().is_initialized():
print("*** SDK failed to initialize. Please check your internet connection and SDK credential for any typo.")
exit()
print("*** SDK successfully initialized")
# Set up the evaluation context.
context = \
Context.builder('example-user-key').kind('user').name('Sandy').build()
# Check the value of the feature flag.
dance_flag_value = ldclient.get().variation(feature_dance_flag_key, context, False)
show_evaluation_result(feature_dance_flag_key, dance_flag_value)
try:
app.run(debug=True)
Event().wait()
except KeyboardInterrupt:
pass
Python will first check if the SDK key is set in the environment before it can initialize the client.
The object named “dance_flag_value” is used to evaluate the “animate-gif” flag that was defined and displayed in the LaunchDarkly dashboard. To assist with the debugging process, the evaluation result will be deployed on the terminal to indicate the status of the feature flag value.
Navigate the feature flag dashboard on LaunchDarkly
In order to change the flag to false, navigate to the flags dashboard. Click on the feature flag name “animate-gif” to be redirected to the “Targeting” section. Notice that the toggle is already set to “Off” to serve a static file and that the default rule is to serve animate file, which is true.
In your terminal, run “python3 main.py”.
Go to http://localhost:5000/ and notice that Lain, or the gif, is not moving. She’s waiting for the cue to dance.
Navigate back in the dashboard, toggle the flag “On” and click the Review and save button to see the screenshot below:
Save changes. Kill the process running on the terminal and restart the app with “python3 main.py”. Go back to http://localhost:5000/ and notice that Lain, or the gif, is activated!
Create another flag from the LaunchDarkly dashboard
Click on the blue + Create button. Give the new flag a name such as “musicflag” and a description “activate audio file” as seen below:
Specify the flag’s configuration as:
- Configuration: Custom
- Temporary: Yes
- Flag type: Boolean
- Variations: display audio file - true
- Variations: hide audio file - false
- Serve when targeting is ON: display audio file
- Serve when targeting is OFF: hide audio file
Add the new flag to the Python Flask app
Navigate back to the main.py file and add the new flag to the file so that the code matches the snippet below:
feature_dance_flag_key = "animate-gif"
feature_music_flag_key = "musicflag"
@app.route("/")
def home():
# Set up the evaluation context
context = Context.builder("example-user-key").kind("user").name("Sandy").build()
# Check the value of the feature flag
dance_flag_value = ldclient.get().variation(feature_dance_flag_key, context, False)
music_flag_value = ldclient.get().variation(feature_music_flag_key, context, False)
# Render different templates based on the flag value
if dance_flag_value is True and music_flag_value is False:
return render_template("dance.html")
if dance_flag_value is True and music_flag_value is True:
return render_template("dance.html", playmusic = True)
else:
return render_template("wait.html")
Scroll down to the Python script and add these objects at the end to evaluate the values and results.
dance_flag_value = ldclient.get().variation(feature_dance_flag_key, context, False)
music_flag_value = ldclient.get().variation(feature_music_flag_key, context, False)
show_evaluation_result(feature_dance_flag_key, dance_flag_value)
show_evaluation_result(feature_music_flag_key, music_flag_value)
There are now two cases. If the dance flag is set to true and the music flag is false, then our character Lain will only be dancing without music. However, if both flags are set to true, than an audio player will appear
Add the audio and video to the Flask webpage
Let’s allow our character Lain to dance to music. Download the mp3 from GitHub and save it in the static folder of your project.
Add the following HTML snippet to the dance.html file under the gif.
{% if playmusic %}
<p>playmusic is true</p>
<audio controls autoplay>
<source
src="{{ url_for('static', filename='audio.mp3') }}"
type="audio/mpeg"
/>
Your browser does not support the audio element.
</audio>
{% endif %}
Since all new flags are automatically set to OFF, go back to the dashboard and make sure both flags are set to true. Time to turn the music ON!
Refresh the page and you should see the Lain dancing gif as well as an option to play some music.
Hit the play button for some background music while enjoying this gif. Feel free to toggle the music flag off to turn the music off if you want to kill the party.
Notice the flag change in the terminal.
What’s next for building Flask applications with feature flags?
Incorporating feature flags for your next Flask application is not only a great tool to have under your belt, but it can be fun to add it to a website and see how different assets can be rendered on the site. Challenge yourself by adding more flags to toggle new site UI designs. Perhaps test out the Flask application across various devices such as your mobile phone or tablet to see how it can change dynamically.
Consider building your next project with FastAPI and using LaunchDarkly to upgrade your APIs safely with progressive rollouts. It’s always great to explore a fun API such as the Star Wars API and build another creative project.
Join us on Discord or send me an email at dphan@launchdarkly.com, or connect with me on LinkedIn and let us know what you're building.