Build a User Frustration Detection & Response System by Connecting Rage Click Detection to Guarded Releases

Published November 24, 2025

portrait of Alexis Roberson.

by Alexis Roberson

Part 2 of 3: Rage Click Detection with LaunchDarkly

In Part 1, we covered how to detect rage clicks using LaunchDarkly’s Session Replay.

Though it’s helpful to put yourself in the users’ shoes, manually reviewing sessions after users report issues is still reactive debugging. By the time you notice a spike in rage clicks, hundreds or thousands of users may have already experienced the broken feature.

If you’re reading this post, it’s because you’re proactive and care about catching problems before they impact user experience. Here, you’ll learn how to build an automated release safety net by connecting rage click detection to Guarded Releases to take actions against breached thresholds.

Introducing Guarded Releases: observability at the point of release

How Guarded Releases Work

Guarded Releases combine three powerful capabilities:

  1. Progressive rollout: Gradually expose your feature to increasing percentages of users (1%, 5%, 10%, 25%, 50%, 100%).
  2. Real-time monitoring: Track rage clicks, error rates, latency, and custom metrics for each feature variation.
  3. Automated response: When metrics exceed defined thresholds, LaunchDarkly alerts your team or automatically rolls back the feature.

This creates a closed-loop system where you’re able to detect the issue, correlate it with a specific feature flag change, and rollback with one click.

Traditional APM vs Guarded Releases

AspectTraditional APMGuarded Releases
ScopeSystem-wide monitoringFeature-specific monitoring
Attribution”Something broke, figure out what""Feature X caused this issue”
ResponseHunt through dashboards, correlate timestamps, redeployOne-click rollback of the specific feature
Blast Radius100% of users affected by time you noticeOnly the rollout percentage affected (e.g., 10%)
Debug ContextError logs and stack tracesSession replays + errors + logs + traces in one timeline

Traditional APM tools excel at tracking system-level metrics: error rates, response times, CPU usage, memory consumption. But they miss a critical dimension and that is the human experience.

With Guarded releases, when a rage click spike occurs, LaunchDarkly tells you exactly which feature flag change caused it and provides the session replay evidence to prove it (it comes with the receipts).

So, how can we build this? Let’s explore how in the next section.

Setting up Guarded Releases for rage click monitoring

Building on the implementation from Part 1, you’re already capturing rage click data automatically and can now filter for the result in the Sessions tab by typing has_rage_clicks=true in the search bar.

Image of searching for rage clicks sessions in session replay.

LaunchDarkly Sessions view showing automatic rage click detection with has_rage_clicks=true filter.

Now let’s take this a step further by connecting this data to a specific feature in a Guarded rollout/release.

Prerequisites

Step 1: Create Your Feature Flag

First, you’ll need to create the feature flag that will be associated with the Guarded rollout/release. In this case, you can use the new feature flag to enable/disable the Test Rage Click Button in the UI.

What’s cool is you can connect to the LaunchDarkly MCP server in Cursor (or any AI-enabled IDE) and use it to create new feature flags directly in the agent chat window.

The full instructions for setting this up can be found in the docs.

To get started, you’ll need your LaunchDarkly Access token with Writer access. Let’s call it Cursor MCP.

Image of grabbing access token with write access from LaunchDarkly.

Grab access token with write access from LaunchDarkly.

In Cursor, go to Settings > Cursor Settings > Tools & Integrations > MCP Tools > New MCP server.

Copy the example configuration, using your access token.

${
> "mcpServers": {
> "LaunchDarkly": {
> "command": "npx",
> "args": [
> "-y", "--package", "@launchdarkly/mcp-server", "--", "mcp", "start",
> "--api-key", "api-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
> ]
> }
> }
>}

Now, when you query the agent, you can ask it to execute LaunchDarkly specific tasks, like creating a new feature flag.

In the prompt, you want to include the name of the LaunchDarkly project, where you want the flag to be created and the name of the desired feature flag.

  • Project name: integrative-medicine-tracker
  • Feature flag: Test Rage Click Button

In the cursor AI chat window, execute this prompt: Create a new feature flag for the Test Rage Click Button in the integrative-medicine-tracker project in LaunchDarkly

Image of executing prompt tp create feature flag using LaunchDarkly MCP Server.

Execute prompt tp create feature flag using LaunchDarkly MCP Server.

If you navigate to the LaunchDarkly UI, you should now see the newly created feature flag ready to be implemented in the code.

Image of new Target Rage Click Button feature flag in the LaunchDarkly UI.

Target Rage Click Button feature flag in the LaunchDarkly UI.

The full code for this tutorial can be found here.

In the Screens/AilmentsListScreen.jsx file, where the test rage click button was created, import the LaunchDarkly client.

1import { getLaunchDarklyClient } from '../services/launchdarkly';

Then, add the following code in the same file to the AilmentsListScreen() function which will check for changes to the feature flag and the updated feature flag value.

It also includes debugging and retry logic for connecting to the LaunchDarkly client.

1useEffect(() => {
2 // Check feature flag for test rage click button
3 const checkFeatureFlag = () => {
4 const client = getLaunchDarklyClient();
5 console.log('[Feature Flag Debug] Checking flag, client available:', !!client);
6
7 if (client) {
8 try {
9 const flagValue = client.variation('test-rage-click-button', false);
10 console.log('[Feature Flag Debug] Flag value for test-rage-click-button:', flagValue);
11 setShowTestButton(flagValue);
12 } catch (error) {
13 console.error('[Feature Flag Debug] Error checking test-rage-click-button flag:', error);
14 setShowTestButton(false);
15 }
16 } else {
17 console.log('[Feature Flag Debug] LaunchDarkly client not available yet');
18 }
19 };
20
21 // Check immediately
22 checkFeatureFlag();
23
24 // Retry mechanism - check periodically until client is ready
25 let retryCount = 0;
26 const maxRetries = 10;
27 const retryInterval = setInterval(() => {
28 const client = getLaunchDarklyClient();
29 if (client) {
30 console.log('[Feature Flag Debug] Client is now available, checking flag');
31 checkFeatureFlag();
32 clearInterval(retryInterval);
33 } else {
34 retryCount++;
35 if (retryCount >= maxRetries) {
36 console.warn('[Feature Flag Debug] LaunchDarkly client not available after', maxRetries, 'retries');
37 clearInterval(retryInterval);
38 }
39 }
40 }, 500); // Check every 500ms
41
42 // Set up listener for flag changes
43 const setupListener = () => {
44 const client = getLaunchDarklyClient();
45 if (client) {
46 console.log('[Feature Flag Debug] Setting up flag change listener');
47 client.on('change:test-rage-click-button', checkFeatureFlag);
48 // Also listen for when client becomes ready
49 client.on('ready', () => {
50 console.log('[Feature Flag Debug] LaunchDarkly client is ready, checking flag');
51 checkFeatureFlag();
52 });
53 }
54 };
55
56 // Try to set up listener immediately
57 setupListener();
58
59 // Also try to set up listener after a short delay (in case client initializes)
60 const listenerTimeout = setTimeout(setupListener, 1000);
61
62 // Cleanup listeners and intervals on unmount
63 return () => {
64 clearInterval(retryInterval);
65 clearTimeout(listenerTimeout);
66 const client = getLaunchDarklyClient();
67 if (client) {
68 client.off('change:test-rage-click-button', checkFeatureFlag);
69 client.off('ready', checkFeatureFlag);
70 }
71 };
72 }, []);
73
74 // Rest of code below...

Next, replace the test rage click button with this code snippet.

1{showTestButton && (
2 <button
3 style={{
4 marginTop: '10px',
5 padding: '8px 16px',
6 backgroundColor: '#ff4444',
7 color: 'white',
8 border: 'none',
9 borderRadius: '4px',
10 cursor: 'pointer',
11 fontSize: '12px',
12 }}
13 onClick={(e) => {
14 // Intentionally does nothing - for testing rage clicks
15 e.preventDefault();
16 console.log('Test button clicked (intentionally non-functional for rage click testing)');
17 }}
18 title="Test Rage Click Detection: Click rapidly 5+ times within 2 seconds in the same spot"
19 >
20 :test_tube: Test Rage Click (Click Rapidly)
21 </button>
22 )}

To ensure everything works as expected, we’ll need to update the LaunchDarkly Client code in launchdarkly.jsx.

Here, you’ll add two functions: one to get the launchdarkly client and the other to check if the feature flag is enabled.

1/**
2 * Get the LaunchDarkly client instance
3 * @returns {object|null} The LaunchDarkly client or null if not initialized
4 */
5export function getLaunchDarklyClient() {
6 return ldClient;
7}
8
9/**
10 * Check if a feature flag is enabled
11 * @param {string} flagKey - The feature flag key
12 * @param {boolean} defaultValue - Default value if client is not initialized or flag not found
13 * @returns {boolean} The flag value
14 */
15export function isFeatureFlagEnabled(flagKey, defaultValue = false) {
16 if (!ldClient || !isInitialized) {
17 return defaultValue;
18 }
19 try {
20 return ldClient.variation(flagKey, defaultValue);
21 } catch (error) {
22 console.error(`Error checking feature flag ${flagKey}:`, error);
23 return defaultValue;
24 }
25}

Finally, you should be able to toggle your feature flag on and off and see the changes in the UI. The rest of the tutorial will require it to be enabled.

It’s also important to note that your feature flag will not work if client-id sdk is not toggled on. To do so, navigate to Flags -> Targeting, then in the right panel toggle on Available on client-side SDKs.

Image of Enabling Target Rage Click Button feature flag in the LaunchDarkly UI.

Enabling Target Rage Click Button feature flag in the LaunchDarkly UI.

Now, when user sessions with has_rage_clicks=true experience rage clicks, LaunchDarkly automatically associates those sessions with your feature flag.

Step 2: Use Automatic Rage Click Tracking

When you enabled session replay in Part 1, LaunchDarkly automatically:

  • Detects rage click patterns based on your configured thresholds (5 clicks, within 8 pixels, over 2 seconds).
  • Tags sessions with the has_rage_clicks attribute.
  • Correlates rage clicks with active feature flags.
  • Makes this data available for filtering and monitoring.

You can reference this automatic tracking directly in Guarded Releases using existing and/or custom metrics.

Step 3: Enable Guarded Releases on Your Feature Flag

How this works

Guarded rollouts which are attached to a specific feature flag gradually release features to users while monitoring custom metrics (like rage clicks). If a defined threshold is exceeded during the rollout, LaunchDarkly can automatically roll back the feature or send notifications to your team.

Taking the feature flag we created earlier in this tutorial, we can create a rage click detection metric and connect it to a guarded rollout.

Here’s what you’ll need to do:

  • Create a guarded rollout under the Test Rage Click Button feature Flag.
  • Create a metric to track click counts by the user in a session.
  • Attach the metric to the guarded rollout and configure threshold, automatic rollback, and time duration.

In the Test Rage Click Button feature flag, click Edit on the Default rule.

Image of Clicking Edit Target Rage Click Button feature flag targeting default rule.

Click Edit on the Target Rage Click Button feature flag default rule.

Select Guarded rollout from the Serve dropdown to start the configuration process.

Image of Selecting Guarded rollout as Target Rage Click Button feature flag targeting default rule.

Select Guarded rollout as Target Rage Click Button feature flag default rule.

After selecting Guarded rollout, a widget should pop up to set the metric/metric group, threshold, and action.

Image of Default rule widget after selecting Guarded rollout.

Result of Default rule widget after selecting Guarded rollout.

For this example, you’ll need to create a custom metric, by clicking select metrics or metric group, then Create.

Image showing Create button to define new custom metric for Guarded rollout.

Create button to define new custom metric for Guarded rollout.

This should redirect you to a popup tab of the Metrics page where you can add these values:

  • Event key: test-rage-click-button
  • What do you want to measure? Count
  • Metric definition: Average of event count per user where lower is better. (It is important to specify lower is better because you want the rollout to catch when there is an uptick in clicks to indicate the rage clicks.)
  • Metric name: Test Rage Click Button - Click Count
  • Description: Tracks the number of clicks on the test rage click button. Higher click counts may indicate user frustration or button malfunction.

Click Create metric.

Image of how to Create new custom metric for Guarded rollout.

Create new custom metric for Guarded rollout.

As you can see, we created the custom metric, but as the message indicates this metric is not yet connected to the rollout.

Image of landing page after creating custom metric for Guarded rollout.

Landing page after creating custom metric for Guarded rollout.

Navigate back to the Flags tab and for Metrics to monitor select the Test Rage Click Button metric, you just created.

Image of assigning newly created custom metric for Guarded rollout.

Assign newly created custom metric to Guarded rollout.

Now set the threshold to 10%, select the Automatic rollback checkbox, and set the Rollout duration to 1 hour.

This setup will still allow you to:

  • Catch issues early.
  • Catch any increase in clicks suggesting user frustration.
  • Decrease click sensitivity, while still having control.

Image of configuring the threshold, duration and automatic rollback for Guarded rollout.

Configure the threshold, duration and automatic rollback for Guarded rollout.

You can add a descriptive comment like “Created guarded rollout/release with 10% threshold for 1 hour. Automatic rollback enabled”.

Click Review and save. Then Save changes.

Image of widget to save Guarded rollout changes.

Save Guarded rollout changes.

You’re all set! And should be able to see a snapshot of the running Guarded rollout.

Image of fully setup Guarded rollout for Target Rage Click Button feature flag.

Fully setup Guarded rollout for Target Rage Click Button feature flag.

Now let’s test the Test Rage Click Button in the UI.

Testing Guarded rollout rage click detection in the JS application.

To further inspect the results, navigate to Flags -> Monitoring, you should be able to see the sharp increase in clicks from the UI and the remaining minutes for the duration you gave in the Guarded rollout.

With a 1-hour duration, LaunchDarkly divides the rollout into progressive stages that would typically run every 12min. Hence why it shows 52 minutes remaining with the next stage taking place within the next 4 minutes.

So within the current session stage, the user clicked the Test Rage Click Button an average of 13 times, which would indicate a level of user frustration.

Image of Dashboard with Guarded rollout monitoring tab.

Dashboard of Guarded rollout monitoring tab.

If you scroll down, you’ll also see the flag history that shows the flag being turned on and off as well as the Guarded release being created for the feature flag.

Image of Dashboard with Guarded rollout monitoring tab with flag change history.

Guarded rollout monitoring tab with flag change history.

Understanding Thresholds: Testing vs. Production

When setting up your guarded rollout, it’s important to understand that testing thresholds differ significantly from production thresholds due to the volume of traffic and differing requirements.

Since you’re testing on localhost with limited users, you can also adjust your settings:

Threshold: 10%
Duration: 15 minutes (shortened for testing)
Automatic rollback: Enabled
Expected behavior: Infrastructure validation, not real regression detection

Why these settings for testing:

  • Shorter duration (15 min) gives you faster feedback while following the tutorial
  • 10% threshold is configured, but won’t trigger with only one user
  • Focus is on validating that the flag → metric → rollout wiring works correctly

Important: With only 1 user, LaunchDarkly won’t have enough data to calculate statistical significance, so automatic rollback likely won’t trigger based on the threshold.

For production with real traffic (100s-1000s of users), your configuration would look different:

Threshold: 10% (relative increase in clicks)
Duration: 1-4 hours (gradual rollout)
Automatic rollback: Enabled
Minimum sample size: 100+ users per variation
Expected behavior: Real regression detection and automatic rollback

Configuring Automated Actions.

You can also configure other automated rollbacks in addition to automated rollbacks like sending alerts or pausing the rollback.

Since we’re testing locally with minimal traffic, the automatic rollback won’t trigger based on our 10% threshold (LaunchDarkly requires sufficient sample size for statistical confidence). However, you can still test the rollback mechanism manually by clicking the rollback button in the UI. In production with real traffic, automatic rollbacks will work as designed.

Decide what happens when thresholds are breached:

  • Alert only: Send notifications via Slack, PagerDuty, or email for team review.
  • Pause rollout: Stop increasing the percentage but don’t roll back (team decides next step).
  • Automatic rollback: Instantly revert to the control variation (recommended for critical metrics like errors).

What’s Next: Deep-Dive Debugging

You now understand how to connect rage click detection directly to feature releases using Guarded Releases. You can:

  • Create feature flags for your features and use them to control rollouts.
  • Set up automated monitoring for rage clicks during progressive rollouts.
  • Configure alerts and automatic rollbacks when thresholds are breached.
  • Correlate user frustration with specific feature flag variations.
  • Catch issues with 1% of traffic instead of 100%.

In Part 3, we’ll explore real-world debugging workflows: detailed walkthroughs of fixing broken features, confusing form validation, and mobile navigation issues using session replay data.

You’ll learn advanced techniques for using session replay to diagnose complex UX problems and establish debugging workflows that scale across your entire engineering organization.

Resources