Feature Flagging for Monolith Apps
No Microservices Required — Feature Flags Without Re-architecture
Start Free TrialWhy Platforms Assume Microservices
Most enterprise feature flag platforms are designed for distributed systems with hundreds of services. Their documentation, SDKs, and examples assume you have:
- •Multiple services communicating via APIs
- •Separate frontend and backend deployments
- •Service mesh for cross-service communication
- •Different teams owning different services
Most companies run monoliths. That's not a bad thing — it's often the right architecture. You shouldn't need microservices to use feature flags safely.
Monolith-Specific Workflows
1. Single Deployment Rollout
In a monolith, you deploy everything at once. Use flags to control which features activate after deployment.
// Deploy with flag OFF
if (flags.newCheckoutFlow) {
return <NewCheckout />;
}
return <LegacyCheckout />;
// Later: flip flag ON in dashboard
// New code activates without redeploy2. Database Migration Safety
Monoliths often share a single database. Use flags to switch between old and new schemas.
if (flags.useNewUserTable) {
return db.query('SELECT * FROM users_v2');
}
return db.query('SELECT * FROM users');3. Feature Isolation in One Codebase
Test risky features in production without affecting the entire app.
// Old code still runs for most users
if (flags.betaAIRecommendations && user.isBetaTester) {
return getAIRecommendations(user);
}
return getStandardRecommendations(user);Simple Patterns for Single Codebase
✅ Centralized Flag Loading
// Load once at app startup const flags = await loadFlags(); app.locals.flags = flags;
Load flags once when your monolith starts. Share across routes via app context.
✅ Middleware Pattern
app.use((req, res, next) => {
req.flags = app.locals.flags;
next();
});Make flags available to all routes without passing them explicitly.
✅ Template Helpers
<!-- In your views -->
{% if flags.showNewNav %}
<NewNavigation />
{% endif %}Use flags directly in server-rendered templates for UI changes.
✅ Background Job Flags
if (flags.useNewEmailService) {
sendViaNewProvider(email);
} else {
sendViaLegacyProvider(email);
}Control background processes without separate deployments.
Examples in One Codebase
Rails Monolith
# config/initializers/feature_flags.rb
FLAGS = RemoteEnv.fetch(api_key: ENV['REMOTEENV_KEY'])
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
helper_method :feature_enabled?
def feature_enabled?(flag)
FLAGS[flag.to_s] == true
end
end
# app/views/layouts/application.html.erb
<% if feature_enabled?(:new_dashboard) %>
<%= render 'dashboard/new_layout' %>
<% else %>
<%= render 'dashboard/legacy_layout' %>
<% end %>Django Monolith
# settings.py
import requests
FEATURE_FLAGS = requests.get(
'https://api.remoteenv.com/v1/flags',
headers={'Authorization': f'Bearer {REMOTEENV_KEY}'}
).json()
# middleware.py
class FeatureFlagMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
request.flags = settings.FEATURE_FLAGS
return self.get_response(request)
# views.py
def checkout(request):
if request.flags.get('new_payment_flow'):
return render(request, 'checkout/new.html')
return render(request, 'checkout/legacy.html')Monoliths Are Not Legacy
The industry has unfairly stigmatized monolithic architectures. The truth:
Monoliths Are Great For:
- ✓ Small to medium teams (<50 engineers)
- ✓ Rapid iteration and deployment
- ✓ Shared data models
- ✓ Easier debugging and testing
- ✓ Lower operational complexity
Who Runs Monoliths:
- • Shopify (Ruby on Rails monolith)
- • GitHub (Rails monolith for years)
- • Stack Overflow (ASP.NET monolith)
- • Basecamp (Rails, proudly monolithic)
If it's good enough for billion-dollar companies, it's good enough for you.
Feature Flags Without Re-architecture
RemoteEnv works perfectly with your monolith. No microservices required.
Try RemoteEnv Free