Ready to make your web app a little more egg-citing? In this tutorial, we’ll walk you through building a playful and interactive Easter-themed Flask application using feature flags powered by LaunchDarkly. You will learn how to create a quick Flask application that displays eggs on your website, controlled by a feature flag that can be enabled to reveal jokes, or disabled to reveal information from a different data set.
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.
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 easter-python
cd easter-python
python3 -m venv venv
source venv/bin/activate
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 easter-python
cd easter-python
python -m venv venv
venv\Scripts\activate
pip install flask python-dotenv launchdarkly-server-sdk
The last command uses pip, the Python package installer, to install the three 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.
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 the root folder of your project with the following lines:
FLASK_APP=main.py
FLASK_ENV=development
These incredibly helpful lines will save you time when it comes to testing and debugging your project.
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.
Get a basic Flask app up and running
Let’s stand up a minimal Flask application just to make sure our configurations are correct. After that, we’ll get fancy and add some feature flags.
Create a new file named main.py in the easter-python project folder. Add the import statements and create a quick and simple route to render a homepage on the Flask application:
from flask import Flask, render_template
import os
import ldclient
from ldclient import Context
from ldclient.config import Config
app = Flask(__name__)
from dotenv import load_dotenv
load_dotenv()
@app.route("/")
def home():
return "The Earth laughs in flowers. – Ralph Waldo Emerson"
if __name__ == "__main__":
try:
app.run(debug=True)
pass
In your terminal, run “python3 main.py”. Go to http://localhost:5000/ to see the wholesome spring quote on your screen.
Create a feature flag
In the LaunchDarkly app, create a feature flag with the following configuration:
- Flag name: “change data source”
- Configuration: Custom
- Description: when enabled, changes the data source from egg facts to egg jokes.
- Flag type: String
- Variants: tell a joke
- Variants: share a fact
- Serve when targeting is ON: tell a joke
- Serve when targeting is OFF: share a fact
Remember the name of the key “change-data-source” which will be used shortly.
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.
Add a feature flag to the Flask application
Now you will add new functions that are necessary to use the LaunchDarkly SDK client and evaluate the feature flag that was created in the previous section. Navigate back to the main.py file. Add the following lines of code to print the value of the evaluated flag. Remove the current return statement to change the behavior of the home() function:
sdk_key = os.getenv("LAUNCHDARKLY_SDK_KEY")
egg_info_flag_key = "change-data-source"
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
egg_info_flag_key_value = ldclient.get().variation(egg_info_flag_key, context, False)
if egg_info_flag_key_value == "joke":
return render_template('jokes.html', jokes=egg_jokes)
if egg_info_flag_key_value == "fact":
return render_template('index.html', facts=egg_facts)
After the LaunchDarkly client is initialized, we create a context variable that tells LaunchDarkly what specific attributes exist for this request. 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 “index.html” and “jokes.html”.
If the “egg_info_flag_key_value” is set to true, then the website will change all the assets on the page to display a joke with the answer behind each egg once clicked on. Copy this code into jokes.html.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Egg Jokes</title>
<style>
body {
font-family: 'Arial', sans-serif;
background-color: #9ee4a9;
margin: 0;
padding: 20px;
text-align: center;
background: linear-gradient(135deg, #ffebee, #e8f5e9);
background-image: url('data:image/svg+xml;utf8,<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="50" r="3" fill="%23ffd3e0" opacity="0.5"/></svg>');
}
.container {
max-width: 1200px;
margin: 0 auto;
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-template-rows: repeat(2, 1fr);
gap: 30px;
padding: 20px;
justify-items: center;
align-items: center;
}
.egg {
width: 180px;
height: 240px;
background: linear-gradient(45deg, #fff5e6, #ffebee);
border: 3px solid #ffd3e0;
border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
cursor: pointer;
transition: transform 0.3s ease;
position: relative;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 20px;
transform-origin: center bottom;
animation: wobble 3s ease-in-out infinite;
}
.egg:hover {
background: linear-gradient(45deg, #ffebee, #fff5e6);
transform: scale(1.1) rotate(5deg);
}
.question {
font-weight: bold;
margin-bottom: 10px;
color: #ff8a8a;
font-size: 1.2em;
text-shadow: 1px 1px 2px rgba(0,0,0,0.1);
}
.answer {
display: none;
color: #4a9e5c;
font-size: 1.1em;
font-style: italic;
transition: all 0.5s ease;
transform: translateY(10px);
}
.egg.active .answer {
display: block;
transform: translateY(0);
}
h1 {
font-family: 'Comic Sans MS', cursive;
color: #ff6b6b;
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
text-align: center;
margin-bottom: 30px;
animation: bounce 2s ease infinite;
}
.nav {
margin-bottom: 30px;
}
.nav a {
text-decoration: none;
color: #333;
margin: 0 15px;
padding: 8px 15px;
border-radius: 5px;
background-color: #fff;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
transition: all 0.3s ease;
}
.nav a:hover {
background-color: #4d8d56;
transform: translateY(-2px);
}
@keyframes wobble {
0%, 100% { transform: rotate(0deg); }
25% { transform: rotate(-3deg); }
75% { transform: rotate(3deg); }
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
.container::before,
.container::after {
content: '🐰';
font-size: 2em;
position: fixed;
bottom: 20px;
}
.container::before {
left: 20px;
}
.container::after {
right: 20px;
}
body::after {
content: '🧺';
font-size: 3em;
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
}
</style>
</head>
<body>
<h1>Click on the Eggs to See the Punchline!</h1>
<div class="container">
{% for joke in jokes %}
<div class="egg" onclick="this.classList.toggle('active')">
<div class="question">{{ joke.question }}</div>
<div class="answer">{{ joke.answer }}</div>
</div>
{% endfor %}
</div>
</body>
</html>
Go to the index.html page and paste the following contents to the file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Egg Facts</title>
<style>
body {
font-family: 'Arial', sans-serif;
background-color: #afd696;
margin: 0;
padding: 20px;
text-align: center;
}
.container {
max-width: 1200px;
margin: 0 auto;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 20px;
padding: 20px;
}
.egg {
width: 150px;
height: 200px;
background-color: #fff;
border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
margin: 0 auto;
cursor: pointer;
transition: transform 0.3s ease;
position: relative;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.egg:hover {
transform: scale(1.1);
}
.fact {
display: none;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(255, 255, 255, 0.9);
padding: 15px;
border-radius: 10px;
width: 80%;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
.egg.active .fact {
display: block;
}
h1 {
color: #333;
margin-bottom: 30px;
}
.nav {
margin-bottom: 30px;
}
.nav a {
text-decoration: none;
color: #333;
margin: 0 15px;
padding: 8px 15px;
border-radius: 5px;
background-color: #fff;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
transition: all 0.3s ease;
}
.nav a:hover {
background-color: #f0f0f0;
transform: translateY(-2px);
}
</style>
</head>
<body>
<h1>Click on the Eggs to Discover Fun Facts!</h1>
<div class="container">
{% for fact in facts %}
<div class="egg" onclick="this.classList.toggle('active')">
<div class="fact">{{ fact }}</div>
</div>
{% endfor %}
</div>
</body>
</html>
Great, it’s time to flip the switch and see the feature flags in action.
Initialize the LaunchDarkly client
Add the following code below the Flask route 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 egg_info_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. This context should appear on your
# LaunchDarkly contexts dashboard soon after you run the demo.
context = \
Context.builder('example-user-key').kind('user').name('Sandy').build()
# Check the value of the feature flag.
# check if the variable value names are correct
egg_info_flag_value = ldclient.get().variation(egg_info_flag_key, context, False)
show_evaluation_result(egg_info_flag_key, egg_info_flag_value)
try:
app.run(debug=True)
Python will first check if the SDK key is set in the environment before it can initialize the client.
The object named “egg_info_flag_value” is used to evaluate the “change-data-source” 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.
Add two different data sets to the Flask project
In the main.py file, add the following lines before defining the Flask route to create two lists to store string data about random egg facts and jokes. Feel free to change them according to your interests and humor.
# List of egg facts
egg_facts = [
"The largest chicken egg ever laid weighed 12 ounces!",
"Eggs contain all 9 essential amino acids.",
"The color of an egg's shell is determined by the breed of the chicken.",
"Eggs can be stored in the refrigerator for up to 5 weeks.",
"The average hen lays 300-325 eggs per year.",
"Eggs are one of the few foods that naturally contain Vitamin D.",
"The world record for most eggs laid by a chicken in one day is 7.",
"Eggs are considered a complete protein source.",
"The yolk color depends on the hen's diet.",
"Eggs are one of the most versatile ingredients in cooking."
]
# List of egg jokes (question and answer)
egg_jokes = [
{"question": "Why did the egg go to school?", "answer": "To get egg-ucated!"},
{"question": "What do you call an egg from outer space?", "answer": "An egg-stra terrestrial!"},
{"question": "You know egg prices are out of control when…", "answer": "Even Humpty Dumpty can’t afford to fall anymore."},
{"question": "What did the cashier say when they rang up the eggs at the market?", "answer": "Would you like to opt in for the payment plan option?"},
{"question": "Why did the egg cross the road?", "answer": "To prove he wasn't a chicken!"},
{"question": "What do you call an egg that's always late?", "answer": "An egg-stra slow egg!"},
{"question": "Why was the Easter egg so good at stand-up comedy?", "answer": "Because it always cracked people up!"},
{"question": "How do you send a letter to an Easter egg?", "answer": "By egg-spress delivery!"},
{"question": "Why did the egg get promoted?", "answer": "Because it was egg-cellent at its job!"},
{"question": "What do you call an egg that's a great dancer?", "answer": "An egg-straordinary mover!"}
]
Your totally egg-citing jokes are ready to be hidden on the site and revealed with a flip of the feature flag switch! Or should I say… egg-citing yolks instead of jokes… hahaha.
Remember that the list of egg facts will be served to users first.
Navigate the feature flag dashboard on LaunchDarkly
At this point, the coding portion is complete. Feel free to check out the GitHub repository to make sure you have everything.
In order to change the flag value, navigate to the flags dashboard. Click on the feature flag name “change-data-source” to be redirected to the “Targeting” section. Notice that the toggle is already set to “Off” to serve data from the list of egg facts, and that the default rule is to serve a joke.
In case the Flask application stopped running at this point, rerun the command “python3 main.py”.
Go to http://localhost:5000/ and see a bunch of eggs on the screen. Go ahead and click on a random egg to reveal a fact.
It’s the Easter holiday! Let’s have some fun and crack some jokes instead.
Navigate back in the dashboard, toggle the flag “On” and click the Review and save button to see the screenshot below:
Give it a silly comment such as “let’s crack a joke!” to make yourself laugh. Save changes. Go back to http://localhost:5000/ and refresh the page to see a more colorful website.
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.
Whether you're delivering fun facts or punchline-packed eggs, you've learned how to use LaunchDarkly to manage features, debug efficiently, and create engaging user experiences. Don’t stop here — experiment with more flags, expand your content, and keep your users cracking up. Happy coding, and may your Easter eggs always come with a laugh!
Consider building your next project with FastAPI and using LaunchDarkly to upgrade your APIs safely with progressive rollouts. Maybe create a scavenger hunt with Google Maps with Feature Flags for the springtime activities. You can also add multiple feature flags to your Flask application.
Join us on Discord, connect with me on LinkedIn, or find me @dianedotdev on Bluesky or X/Twitter. I would love to know what you’re building!