Dynamic Feature Control: Using URL Parameters for Feature Toggles

In modern software development, the ability to deploy code frequently and safely is paramount. We often want to merge code into the main branch continuously, even if the features aren’t fully ready for all users. How can we deploy code containing unfinished or experimental features without exposing them prematurely? The answer often lies in Feature Toggles (also known as Feature Flags).

As extensively documented by experts like Martin Fowler, feature toggles are essentially conditional switches in your code that allow you to turn features on or off without deploying new code. This decouples deployment (getting code to production) from release (making features available to users).

There are many ways to manage the state of these toggles – from complex configuration servers to simple configuration files. Today, we’ll explore a straightforward, dynamic method suitable for certain scenarios: controlling feature toggles via URL query parameters.

What are Feature Toggles?

A feature toggle is a technique that allows teams to modify system behavior without changing code. You wrap new features in conditional logic:

if (featureIsEnabled("new-dashboard")) {
  // Show the new dashboard component
  showNewDashboard();
} else {
  // Show the old dashboard component
  showOldDashboard();
}

The magic lies in how the featureIsEnabled function determines whether "new-dashboard" is active.

Why Use Feature Toggles?

  • Trunk-Based Development: Allows developers to merge incomplete features into the main branch (trunk) behind a toggle, reducing merge conflicts and integration pain.
  • Continuous Delivery/Deployment: Deploy code anytime, knowing risky or unfinished features are safely turned off in production.
  • Risk Reduction: Roll out features gradually to a subset of users or internal testers first. If issues arise, quickly disable the feature by flipping the toggle, without needing a rollback deployment.
  • A/B Testing & Experimentation: Show different versions of a feature to different user segments based on toggle states.
  • Kill Switches: Instantly disable problematic features in production.

A Simple Implementation: URL Query Parameter Toggles

One way to control toggles, especially useful for development, testing, or specific demo scenarios, is through URL query parameters. This method allows anyone with the URL to activate or deactivate features for their session.

Let’s define a simple convention using a query parameter named featuretoggle:

  • The value is a comma-separated list of feature names.
  • To activate a toggle, include its name directly (e.g., feature2).
  • To deactivate a toggle that might be on by default elsewhere, prefix its name with a minus sign (-) (e.g., -feature1).

Example URL:

<your-app-url>/?featuretoggle=-feature1,feature2

This URL intends to:

  1. Deactivate the feature named feature1.
  2. Activate the feature named feature2.

Any features not mentioned in the parameter might rely on a default state (e.g., defined in code or a base configuration).

Checking Feature State: Implementation Sketch

We need a mechanism to parse the URL and provide the featureIsEnabled function. Here’s a conceptual implementation in TypeScript:

import { useLocation } from 'react-router-dom'; // Example for React Router
// Or access window.location directly in vanilla JS/other frameworks

// Store the parsed toggle states (could be a simple object or Map)
// Default state might be 'false' unless overridden
const featureStates = new Map<string, boolean>();
let areTogglesParsed = false; // Prevent parsing on every check

function parseUrlFeatureToggles(): void {
  // In a real app, get the search string reliably (e.g., from routing library)
  const queryString = window.location.search;
  const urlParams = new URLSearchParams(queryString);
  const toggleParam = urlParams.get('featuretoggle');

  if (toggleParam === null) {
      // No featuretoggle parameter found, rely on defaults (or clear states)
      featureStates.clear(); // Assuming default off if not specified
      areTogglesParsed = true;
      return;
  }

  featureStates.clear(); // Start fresh based on URL parameters
  const toggleParts = toggleParam.split(',');

  for (const part of toggleParts) {
    const trimmedPart = part.trim();
    if (trimmedPart.startsWith('-')) {
      // Deactivate: Feature name starts after '-'
      const featureName = trimmedPart.substring(1);
      if (featureName) {
        featureStates.set(featureName, false);
        console.log(`Feature Toggle: '${featureName}' explicitly DEACTIVATED via URL.`);
      }
    } else if (trimmedPart) {
      // Activate: Feature name is the part itself
      const featureName = trimmedPart;
      featureStates.set(featureName, true);
       console.log(`Feature Toggle: '${featureName}' explicitly ACTIVATED via URL.`);
    }
  }
   areTogglesParsed = true;
}

/**
 * Checks if a specific feature toggle is enabled based on URL parameters.
 * Assumes parseUrlFeatureToggles() has been called appropriately (e.g., on app load or route change).
 * Features not mentioned in the URL parameter are considered disabled by default in this simple example.
 *
 * @param featureName The name of the feature to check.
 * @returns True if the feature is enabled, false otherwise.
 */
export function featureIsEnabled(featureName: string): boolean {
    // Ensure parsing happens at least once or on relevant changes
    if (!areTogglesParsed) {
        parseUrlFeatureToggles();
    }

    // Return the state from the map, defaulting to false if not present
    return featureStates.get(featureName) ?? false;
}

// --- Example Usage (e.g., in a component) ---
/*
// Call parseUrlFeatureToggles() early in your app setup or route handling.
// parseUrlFeatureToggles(); // Might be called in App.tsx or similar

function MyComponent() {
    const showNewFeature = featureIsEnabled("feature3");
    const allowSave = featureIsEnabled("save-profile-data");

    return (
        <div>
            {showNewFeature && <NewShinyComponent />}
            {!showNewFeature && <OldComponent />}

            {allowSave && <button>Save Profile</button>}
        </div>
    );
}
*/

Important: This is a simplified example. In a real application, you’d need to decide:

  • When to parse the URL (e.g., once on app load, or on every route change if using a router).
  • What the default state of a toggle is if not mentioned in the URL (is it off by default, or does it inherit from another configuration source?). The example above assumes ‘off’ if not explicitly activated.
  • How this interacts with other configuration sources (e.g., user settings, backend configuration).

Example Usage Scenarios

Let’s revisit the examples from the notes:

  1. URL: http://127.0.0.1:8080/?featuretoggle=-feature1,feature2

    • featureIsEnabled("feature1") would return false.
    • featureIsEnabled("feature2") would return true.
    • featureIsEnabled("anotherFeature") would return false (based on our example implementation’s default).
  2. URL: http://127.0.0.1:8080/?featuretoggle=feature3,save-profile-data

    • featureIsEnabled("feature3") would return true.
    • featureIsEnabled("save-profile-data") would return true.
    • featureIsEnabled("feature1") would return false.

Considerations and Caveats

While simple and effective for certain use cases, URL-based feature toggles have limitations:

  • Security & Discoverability: They are visible and easily manipulated by end-users. Do not use this method to control access to sensitive features or data. Users might accidentally enable beta features.
  • Persistence: The toggle state is tied to the current URL. It won’t persist across sessions or if the user navigates away and comes back without the parameters, unless you add extra logic (e.g., saving the state to local storage).
  • Scalability: Managing a large number of toggles via long URL strings becomes cumbersome and error-prone.
  • Testing Complexity: Automated tests might need to be aware of URL parameters to ensure consistent behavior.
  • Limited Targeting: You can’t easily target specific user groups (e.g., “enable for beta testers only”) with this method alone.

For more robust, production-grade feature flagging, consider:

  • Configuration files deployed with your application.
  • Database-driven toggle states.
  • Dedicated Feature Flag management services (like LaunchDarkly, Optimizely Rollouts, Flagsmith, etc.).

Conclusion

Feature toggles are an indispensable technique for modern software development, enabling safer, faster release cycles. Using URL query parameters is a straightforward way to control toggle states dynamically, particularly valuable during development, QA testing, or for demos where explicit, temporary control is needed.

However, due to visibility and scalability concerns, this method is often best suited for non-sensitive features or internal use cases. Always consider the trade-offs and choose the right toggling mechanism for your specific context, keeping the insights from resources like Martin Fowler’s Bliki in mind.