package com.example.demo.feature;
import org.togglz.core.Feature;
import org.togglz.core.annotation.EnabledByDefault;
import org.togglz.core.annotation.Label;
import org.togglz.core.context.FeatureContext;
public enum FeatureFlags implements Feature {
@EnabledByDefault
@Label("New User Dashboard")
NEW_USER_DASHBOARD,
@Label("Payment Integration V2")
PAYMENT_V2,
@Label("Advanced Search")
ADVANCED_SEARCH,
@EnabledByDefault
@Label("Email Notifications")
EMAIL_NOTIFICATIONS;
public boolean isActive() {
return FeatureContext.getFeatureManager().isActive(this);
}
}
package com.example.demo.config;
import com.example.demo.feature.FeatureFlags;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.togglz.core.manager.EnumBasedFeatureProvider;
import org.togglz.core.spi.FeatureProvider;
import org.togglz.spring.security.SpringSecurityUserProvider;
@Configuration
public class TogglzConfig {
@Bean
public FeatureProvider featureProvider() {
return new EnumBasedFeatureProvider(FeatureFlags.class);
}
@Bean
public org.togglz.core.user.UserProvider userProvider() {
return new SpringSecurityUserProvider("ROLE_ADMIN");
}
}
package com.example.demo.service;
import com.example.demo.feature.FeatureFlags;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private final PaymentServiceV1 paymentV1;
private final PaymentServiceV2 paymentV2;
public UserService(PaymentServiceV1 paymentV1, PaymentServiceV2 paymentV2) {
this.paymentV1 = paymentV1;
this.paymentV2 = paymentV2;
}
public String getUserDashboard(Long userId) {
if (FeatureFlags.NEW_USER_DASHBOARD.isActive()) {
return generateNewDashboard(userId);
} else {
return generateOldDashboard(userId);
}
}
public void processPayment(PaymentRequest request) {
if (FeatureFlags.PAYMENT_V2.isActive()) {
paymentV2.process(request);
} else {
paymentV1.process(request);
}
}
public List<User> searchUsers(String query) {
if (FeatureFlags.ADVANCED_SEARCH.isActive()) {
return advancedSearch(query);
} else {
return basicSearch(query);
}
}
private String generateNewDashboard(Long userId) {
// New dashboard implementation
return "new-dashboard";
}
private String generateOldDashboard(Long userId) {
// Old dashboard implementation
return "old-dashboard";
}
private List<User> advancedSearch(String query) {
// Advanced search with filters, fuzzy matching
return List.of();
}
private List<User> basicSearch(String query) {
// Simple search
return List.of();
}
}
togglz:
enabled: true
console:
enabled: true
path: /togglz-console
secured: true
features:
NEW_USER_DASHBOARD:
enabled: true
PAYMENT_V2:
enabled: false
ADVANCED_SEARCH:
enabled: true
strategy: gradual-rollout
param:
percentage: 20
Feature flags (feature toggles) enable/disable functionality without code deployment. I use libraries like Togglz or FF4J for flag management. Flags support A/B testing, canary releases, and emergency kill switches. Strategy pattern determines flag states—user-based, percentage-based, date-based. Admin UI manages flags in production. Database or configuration stores flag states. Flags reduce deployment risk by decoupling releases from deployments. Remove flags after feature stabilization to avoid technical debt. Feature flags enable trunk-based development and continuous deployment. They're essential for modern DevOps practices, allowing teams to ship code continuously while controlling feature exposure. Proper flag management prevents flag sprawl and maintains code quality.