Running a seasonally themed signup experiment with LaunchDarkly and ExpressJS featured image

As a software engineer, you probably have lots of ideas about how to improve the products you work on. How can you convince other people your proposed changes have merit? A/B experiments are a way of gathering data about the impact of changes to your product.

Often, experiments are run by product managers. But maybe your product manager is too busy, or you work at a small company and need to wear a lot of hats, or you work on a product team such as Growth whose mandate is to quickly iterate and test lots of ideas. In those cases, running your own experiments can be a valuable skill.

In this tutorial you’ll learn how to run an A/B experiment to give your signup flow some 🍁seasonally themed flavor ☠️, using LaunchDarkly and ExpressJS.

Our example app is a costume shop. All experiments should start with a hypothesis. Will changing the design to be a bit spooookier increase our conversion rate? Let’s find out! 

Prerequisites

  • A free LaunchDarkly account - sign up for one here
  • A developer environment with git, npm, and Node.js installed

Getting started with ExpressJS and LaunchDarkly

Some configuration is required before we get spooky.  Clone the example repository. Open the project folder in your editor of choice.

git clone https://github.com/annthurium/expressjs-launchdarkly-signup

In the LaunchDarkly app, select the Project you’d like to work in or create a new one. Click on the dropdown with three dots next to the Production environment. Select “Copy SDK key.”

Screenshot showing how to copy LaunchDarkly SDK key from a production environment.

Paste the SDK key into the .env.example file. Rename the file to “.env” and save it. This step protects you from accidentally committing credentials to source control.

Run these commands to install dependencies and start the server:

cd expressjs-launchdarkly-signup
npm install
npm start

Go to http://localhost:3000/signup.html in your browser. Behold the storefront.

Screenshot of a signup form for "Pizza Rat's Costume Emporium." Bisexual gradient text on a boring white background.

Pretty cool, but it could be better.


Load http://localhost:3000/signup-2.html in your browser. Try not to scream with terror or delight.

Screenshot of signup form for Pizza Rat's Costume Emporium, with an orange-purple Halloween gradient and a font that appears to be dripping blood.

Let’s add some code to wrap these signup flows in an A/B experiment.

Building a LaunchDarkly experiment

To run a LaunchDarkly A/B experiment, you need to understand the following concepts:

  • Metrics are the user action we are trying to measure, which help us validate our hypothesis. In this case, we’re counting conversions.
  • Flags enable you to conditionally change your app’s behavior without deploying or modifying the code. Flags are used in this demo to determine which experiment variation is shown to the end user.

Experiments connect flags and metrics to measure end-user / audience behavior.

Creating a metric

Head over to the LaunchDarkly app. Click in “Metrics” on the left hand menu. Click either of the “Create” buttons. Select “Create metric” from the dropdown menu.

Screenshot of a LaunchDarkly environment with no existing Metrics, and an arrow pointing towards 2 buttons a user could use to create one.

Create a metric using the following configuration:

  • Event kind: custom
  • Event key: signup-conversion
  • Occurrence (conversion:binary)
  • Metric name: signup-conversion
  • Metric key: signup-conversion 
  • Description: When the user inputs their credentials and clicks the submit button to sign up for a new account.

Screenshot demonstrating how to configure a LaunchDarkly metric for an A/B experiment.

Creating an experiment

This metric isn’t connected to anything yet. Click “Experiments” in the left navigation menu, and click “Create experiment” on the following screen. 

Configure your experiment as follows, then click Next:

  • Name: spooky styling
  • Hypothesis: Adding seasonal themed CSS will increase signup conversions.
  • Experiment type: Feature change

Screenshot of the initial experiment configuration dialog.

In the subsequent section, choose these values:

  • Randomization unit: user
  • Attributes: leave this blank

Screenshot of the second experiment configuration section, where you can select a randomization unit.

From the metrics dropdown, select “signup-conversion.” Click Next, then click “Create flag.”

Creating a flag

Create a flag with the following configuration:

  • Name: show-seasonal-css
  • Description: Show seasonally themed CSS on the login page
  • Flag type: String
  • Variations: Control, Spooky CSS

Screenshot demonstrating flag configuration for experiment.

After creating your flag, you’ll be taken back to the experiment creation flow. Now it’s time to set the audience. Select 100% as the audience size. In a real-life situation, you’d probably select a smaller percentage first, increasing size as you build confidence that the new variation doesn’t introduce bugs or regressions. Just for the purposes of this tutorial, setting the audience size to 100% will demonstrate the end results more quickly.

When you set the audience amount to 100%, each variation will automatically be served 50%. Your configuration should look like this:

Experiment configuration showing the experiment being served to 100% of users, 50% seeing Control and 50% seeing the new variation.

Click Finish. You’ll be prompted to click “Open flag targeting” and turn the flag on.

Screenshot of user flow reminding you that you need to turn the flag on before your experiment can begin.

Re flag rules: when targeting is On, serve Control – the Experiment configuration will determine which variants are shown to users.

Screenshot of flag targeting rule configuration for an A/B signup experiment.

Click “Review and save.” If your LaunchDarkly configuration requires confirmation, write a comment explaining these changes, such as “start experiment" or even "GETTING SPOOKY UP IN HERE 🩻." Click “Save changes.”

screenshot of the flag confirmation modal, again!

Finally, we’ll need to start the experiment. Click on “Experiments” in the left navigation menu, and then select “spooky styling.” Click “Start.” Tada! 👻

Screenshot of experiment configuration with a pretty gradient button prompting the user to Start the experiment.

Adding LaunchDarkly event tracking and flag evaluation to the ExpressJS routing layer


Roll back over to your code editor. Open index.js. You can copy and replace all the code in the file with this, and check out the commented lines to see what has changed.

const express = require("express");
const path = require("path");
const serveStatic = require("serve-static");
const bodyParser = require("body-parser");


const LaunchDarkly = require("@launchdarkly/node-server-sdk");
// add express-session middleware to generate session IDs
const session = require("express-session");
require("dotenv").config();


const app = express();


app.use(serveStatic(path.join(__dirname, "public")));
app.use(bodyParser.urlencoded({ extended: false }));


// use the session function to generate session IDs for each request
app.use(
 session({
   secret: "session",
   resave: false,
   saveUninitialized: true,
   cookie: { secure: false },
 })
);


// add a GET function that redirects the root route
// depending on which experiment group the user falls into
app.get("/", async (req, res) => {
 const context = {
   kind: "user",
   anonymous: true,
   key: req.sessionID,
 };


 // Evaluate LaunchDarkly flag
 const showSeasonalStyling = await ldClient.variation(
   "show-seasonal-css",
   context,
   false
 );


 // redirect to a signup form with different styling based on flag value
 let url;
 if (showSeasonalStyling === "Spooky CSS") {
   url = "signup-2.html";
 } else {
   url = "signup.html";
 }
 res.redirect(url);
});


// Initialize the LaunchDarkly client
const ldClient = LaunchDarkly.init(process.env.LAUNCHDARKLY_SDK_KEY);


// add a POST method to be called when the user finishes the signup flow
app.post("/login", async (req, res) => {
 const context = {
   kind: "user",
   anonymous: true,
   key: req.sessionID,
 };
 console.log("Session ID: ", req.sessionID);
 // track the LaunchDarkly event
 await ldClient.track("signup-conversion", context, 1);


 res.send(
   `Thanks for signing up! Look for the confirmation email in your inbox: ${req.body.email}`
 );
});


// Add the waitForInitialization function to ensure the client is ready before starting the server
const timeoutInSeconds = 5;
ldClient.waitForInitialization({ timeout: timeoutInSeconds }).then(() => {
 const port = 3000;
 const server = app.listen(port, function (err) {
   if (err) console.log("Error in server setup");
   console.log(`Server listening on http://localhost:${port}`);
 });
});


// Add the following new function to gracefully close the connection to the LaunchDarkly server.
process.on("SIGTERM", () => {
 debug("SIGTERM signal received: closing HTTP server");
 ld.close();
 server.close(() => {
   debug("HTTP server closed");
   ldClient.close();
 });
});

Navigate to http://localhost:3000. You will see one of the signup variations. Go through the signup flow by putting in your email address, a password, and clicking “Submit.” Open a private browser and do the same. You have a 50/50 chance of seeing a different variation. 😉 Regardless, you should have a different session ID for each browser in the server logs.

Server listening on http://localhost:3000
Session ID:  5hLlQ1WnPyzxtoBb8z63ghxAROFYegsL
Session ID:  fDpvOJbp_KdY8IIwjRQdJEBH6meqqCEg

Metrics are visible in the LaunchDarkly app. If you click on “Metrics” in the left navigation menu, the same session IDs we saw in the logs appear here:

Screenshot of conversion metrics in the LaunchDarkly app.

So far, spooky CSS is winning. 🕸️ 🎃 We’d need a lot more data to confidently validate our hypothesis though. If you need a refresher on statistics, LaunchDarkly’s new sample size calculator can help you estimate sample size and duration.

Conclusion: Running experiments with LaunchDarkly to test the impact of seasonal CSS on signup conversions


If you’ve been following along, you’ve added a LaunchDarkly experiment to your web app that tests two versions of a signup flow to see if one converts at a higher rate.

This code demonstrates a simplified version of a signup experiment. There is so much more you can do with LaunchDarkly’s experimentation platform. For example, you can test more than two variations (multivariate experiments). You can use funnels to track how many steps a user has taken towards a goal

The following posts could further assist you on your journey:

Go forth and run experiments so you can argue with your boss or product manager, now that your opinions are backed up with data. 


Thanks for reading! If you have any questions, or want to tell me what your favorite spooky emoji is, you can reach me via email (tthurium@launchdarkly.com), X/Twitter, Mastodon, Discord or LinkedIn.

Like what you read?
Get a demo
Related Content
October 28, 2024