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
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.
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.
Environment Deployment Flow
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.
- For QA and integration testing, teams can create a branch prefixed with
Production Release Preparation:
- To prepare for a release, create a
release/xyzbranch from the trunk. - The
release/xyzbranch 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.
- To prepare for a release, create a
Feature Flags: Examples and Usage
Flag Structure:
- Store feature flags in a configuration file (e.g.,
config/feature-flags.json):
CODE{
"feature_xyz": false,
"feature_abc": true
}
- Store feature flags in a configuration file (e.g.,
Use environment variables to control flags during runtime:
CODEFEATURE_XYZ=true FEATURE_ABC=false npm start
Backend Example:
- Toggle flags in code:
CODEconst featureFlags = require('./config/feature-flags');
if (featureFlags.feature_xyz) {
console.log('Feature XYZ is enabled!');
} else {
console.log('Feature XYZ is disabled.');
}
- Toggle flags in code:
Frontend Example:
- Use flags to conditionally render UI components:
CODEif (process.env.REACT_APP_FEATURE_XYZ === 'true') {
render(<NewFeatureComponent />);
} else {
render(<OldFeatureComponent />);
}
- Use flags to conditionally render UI components:
Toggling Flags During Testing:
- To toggle a flag for testing, update the configuration or environment variables and restart the relevant service (frontend or backend):
CODEFEATURE_XYZ=true npm start
- To toggle a flag for testing, update the configuration or environment variables and restart the relevant service (frontend or backend):
- For CI/CD pipelines, ensure that the appropriate flag values are injected into the environment during deployment.
Testing in Production
Traffic Routing for Beta Testing:
- Use Nginx configurations to control traffic allocation:
CODEhttp {
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;
}
}
}
- Use Nginx configurations to control traffic allocation:
- Route 5% of production traffic to servers running the new version by adjusting load balancer weights.
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.
- QA teams can attach a custom cookie (e.g.,
Stabilizing the Release
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/xyzbranch.
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). - Release notes are generated for documentation and shared with stakeholders.
- After all issues are resolved and the branch is stable, the release branch is tagged with a semantic version (e.g.,
Hotfix Process
Creating Hotfix Branches:
- For urgent fixes, create a
hotfix/xyzbranch directly from the latest production tag. - Hotfix branches follow the same stabilization and tagging process as release branches.
- For urgent fixes, create a
Versioning:
- Hotfixes increment the patch version (e.g., from
v1.2.0tov1.2.1) following Semantic Versioning (SemVer) standards.
- Hotfixes increment the patch version (e.g., from
Branch Cleanup
- Regularly clean up merged branches to prevent clutter.
- 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:
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.
- QA adds a custom header (e.g.,
IP-Based Routing:
- Restrict traffic to the new version based on QA’s IP addresses.
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.
SOCIAL SHARE CARD GENERATOR