DEV Community

Cover image for Streamlined Release Process for a Web Application: Trunk-Based Development with Feature Flags
Piyush Chauhan
Piyush Chauhan

Posted on

Streamlined Release Process for a Web Application: Trunk-Based Development with Feature Flags

In this article, we will outline a robust and efficient release process for web applications, built around trunk-based development and environment-based feature flags. This methodology ensures continuous integration, easy testing in production, and a smooth path from development to release while maintaining high-quality standards.


Core Principles

  1. Trunk-Based Development:

    • The trunk branch serves as the single source of truth for all development work.
    • Developers create feature branches (e.g., feature/xyz) from the trunk for new features or Jira tickets.
    • Pull requests (PRs) are submitted from these feature branches to the trunk for review and merging after successful tests.
  2. Environment-Based Feature Flags:

    • Feature flags are used to control the activation of features across environments.
    • Flags are stored in environment-specific configuration files or as part of the CI/CD pipeline configuration.
    • In the trunk branch, all feature flags are set to OFF by default.
    • Flags can be toggled ON in specific environments (e.g., sandbox, staging, or production) as needed.

JIRA Versions in Sprint

Image description


Environment Deployment Flow

  1. Sandbox or Staging Environment:

    • For QA and integration testing, teams can create a branch prefixed with sandbox/ (e.g., sandbox/xyz) from the trunk.
    • This branch is deployed to a dedicated sandbox or staging environment using CI/CD pipelines.
    • QA teams can validate new features, and integration tests can ensure compatibility.
    • Feature flags are toggled ON in this environment for testing specific features.
  2. Production Release Preparation:

    • To prepare for a release, create a release/xyz branch from the trunk.
    • The release/xyz branch serves as the release candidate and is initially deployed to 5% of production traffic for beta testing.
    • Feature flags for new features are toggled ON in this branch to allow testing in production.
    • Nginx or a similar load balancer can handle this traffic split, ensuring only a subset of users see the changes.

Feature Flags: Examples and Usage

  1. Flag Structure:

    • Store feature flags in a configuration file (e.g., config/feature-flags.json):
     {
       "feature_xyz": false,
       "feature_abc": true
     }
    
  • Use environment variables to control flags during runtime:

     FEATURE_XYZ=true FEATURE_ABC=false npm start
    
  1. Backend Example:

    • Toggle flags in code:
     const featureFlags = require('./config/feature-flags');
    
     if (featureFlags.feature_xyz) {
         console.log('Feature XYZ is enabled!');
     } else {
         console.log('Feature XYZ is disabled.');
     }
    
  2. Frontend Example:

    • Use flags to conditionally render UI components:
     if (process.env.REACT_APP_FEATURE_XYZ === 'true') {
         render(<NewFeatureComponent />);
     } else {
         render(<OldFeatureComponent />);
     }
    
  3. Toggling Flags During Testing:

    • To toggle a flag for testing, update the configuration or environment variables and restart the relevant service (frontend or backend):
     FEATURE_XYZ=true npm start
    
  • For CI/CD pipelines, ensure that the appropriate flag values are injected into the environment during deployment.

Testing in Production

  1. Traffic Routing for Beta Testing:

    • Use Nginx configurations to control traffic allocation:
     http {
         upstream stable_backend {
             server stable_backend_1;
             server stable_backend_2;
         }
    
         upstream canary_backend {
             server canary_backend_1;
             server canary_backend_2;
         }
    
         upstream mixed_backend {
             server stable_backend_1 weight=45;
             server stable_backend_2 weight=45;
             server canary_backend_1 weight=5;
             server canary_backend_2 weight=5;
         }
    
         server {
             listen 80;
             server_name my-app.example.com;
    
             location / {
                 if ($http_x_qa_test = "true") {
                     proxy_pass http://canary_backend;
                     break;
                 }
    
                 proxy_pass http://mixed_backend;
             }
         }
     }
    
  • Route 5% of production traffic to servers running the new version by adjusting load balancer weights.
  1. Dedicated QA Testing in Production:
    • QA teams can attach a custom cookie (e.g., qa-test=true) to their requests.
    • Nginx checks this cookie and routes these requests to the new version 100% of the time, ensuring targeted testing in production.

Stabilizing the Release

  1. Fixing Issues:

    • Developers fix any issues identified during beta testing by opening PRs to the trunk branch.
    • Once merged, these fixes are cherry-picked into the release/xyz branch.
  2. Finalizing the Release:

    • After all issues are resolved and the branch is stable, the release branch is tagged with a semantic version (e.g., v1.2.0), triggering deployment to the stable backend.
    • Release notes are generated for documentation and shared with stakeholders.

Hotfix Process

  1. Creating Hotfix Branches:

    • For urgent fixes, create a hotfix/xyz branch directly from the latest production tag.
    • Hotfix branches follow the same stabilization and tagging process as release branches.
  2. Versioning:

    • Hotfixes increment the patch version (e.g., from v1.2.0 to v1.2.1) following Semantic Versioning (SemVer) standards.

Branch Cleanup

  • Routinely delete merged branches to avoid clutter.
  • Periodically remove unused feature flags to maintain organization.
  • Automate branch deletion post-merge using GitHub Actions or similar tools.

Alternative QA and Testing Strategies

Instead of cookies, additional strategies for routing QA traffic in production include:

  1. Header-Based Routing:

    • QA adds a custom header (e.g., X-QA-Test: true) to their requests.
    • Nginx routes these requests to the new version for testing.
  2. IP-Based Routing:

    • Restrict traffic to the new version based on QA’s IP addresses.
  3. Authentication Token-Based Routing:

    • QA logs in with a specific test account tied to a role or token that ensures requests are routed to the new version.

Conclusion

This release process leverages trunk-based development and environment-based feature flags to create a scalable, testable, and production-safe deployment workflow. By using sandbox environments, traffic routing, and dedicated testing strategies, teams can deliver high-quality features while minimizing risk. The approach ensures that issues are caught early and addressed efficiently, paving the way for seamless feature rollouts and hotfixes.

Top comments (0)