DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Low-Code Development: Leverage low and no code to streamline your workflow so that you can focus on higher priorities.

DZone Security Research: Tell us your top security strategies in 2024, influence our research, and enter for a chance to win $!

Launch your software development career: Dive head first into the SDLC and learn how to build high-quality software and teams.

Open Source Migration Practices and Patterns: Explore key traits of migrating open-source software and its impact on software development.

Related

  • Demystifying APIs for Product Managers
  • Improving Customer-Facing App Quality Using Tricentis Testim
  • What Is GraphQL?
  • How To Integrate the Stripe Payment Gateway Into a React Native Application

Trending

  • Developer Git Commit Hygiene
  • How To Use Builder Design Pattern and DataFaker Library for Test Data Generation in Automation Testing
  • The AI Revolution: Empowering Developers and Transforming the Tech Industry
  • Unleashing the Power of Redis for Vector Database Applications
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Deployment
  4. Feature Flags for Coordinated Spring API and Mobile App Rollouts

Feature Flags for Coordinated Spring API and Mobile App Rollouts

Feature flags enable coordinated releases across back-end APIs and mobile apps, ensuring smooth rollouts of new features without disrupting the user experience.

By 
Amol Gote user avatar
Amol Gote
DZone Core CORE ·
May. 29, 24 · Tutorial
Like (3)
Save
Tweet
Share
1.3K Views

Join the DZone community and get the full member experience.

Join For Free

As part of our FinTech mobile application deployment, we usually faced challenges coordinating the releases across backend APIs and mobile applications on iOS and Android platforms whenever we released significant new features. Typically, we will first deploy the backend APIs, as they are quick to deploy along with the database changes. Once the backend APIs are deployed, we publish the mobile applications for both iOS and Android platforms. The publishing process often takes time. The mobile application gets approved within a few hours and sometimes within a few days. If we raise the tickets with stores, the SLA (Service Level Agreement) for those tickets will span multiple days. The delays we saw were predominantly with Android in the last year or so.       

Once the back-end APIs are deployed, they initiate new workflows related to the new features. These could be new screens or a new set of data. However, the mobile application version available at that time for both platforms is not ready to accept these new screens as the newer app version has not been approved yet and would be in the store review process. This inconsistency can lead to a poor user experience which can manifest in various ways, such as the app not functioning correctly, the application crashing, or displaying an oops page or some internal errors.  This can be avoided by implementing feature flags on the backend APIs.

Feature Flags

The feature flags are the configurations stored in the database that help us turn specific features on or off in an application without requiring code changes. By wrapping new functionality behind feature flags, we can deploy the code to production environments while keeping the features hidden from end-users until they are ready to be released.

Once the newer versions of the mobile apps are available, we enable these new features from the database so that backend APIs can orchestrate the new workflows or data for the new set of features. Additionally, we need to consider that both iOS and Android apps would get published at different times, so we need to ensure that we have platform-specific feature flags. In our experience, we have seen iOS apps get approved in minutes or hours, and Android apps sometimes take a day to a few hours. 

In summary, backend APIs need to orchestrate new workflows and data when the corresponding platform application's latest version is available in the app store. For existing users who have the app installed already, we force a version upgrade at the app launch. To avoid version discrepancy issues during the new feature rollout, we follow a coordinated release strategy using feature flags, as explained below. 

Coordinated Release

Backend APIs Release With Feature Flags Off

We first deploy the backend APIs with feature flags with the value Off for all the platforms. Typically, when we create the feature flags, we keep the default value as Off or 0.

Mobile Application Publishing

The mobile application teams for iOS and Android submit the latest validated version to the App Store and Play Store, respectively. The respective teams monitor the publishing process for rejections or clarifications during the review process.  

Enable New Feature

Once the respective mobile application team confirms that the app has been published, then we enable the new feature for that platform.

Monitoring

After the new feature has been enabled across the platforms, we monitor the production environment for backend APIs for any errors and mobile applications for any crashes. If any significant issue is identified, we turn off the feature entirely across all platforms or specific platforms, depending on the type of the issue. This allows us to instantaneously roll back a new feature functionality, minimizing the impact on user experience. 

Feature Flags Implementation in Spring Boot Application

Feature Service

Below is an example of a FeatureServiceV1Impl Spring service in the Spring Boot application, which handles feature flags configuration.

We have defined the bean's scope as the request scope. This ensures a new service instance is created for each HTTP request, thus ensuring that the updated configuration data is available for all new requests. 

The initializeConfiguration method is annotated with @PostConstruct, meaning it is called after the bean's properties have been set. This method fetches the configuration data from the database when the service is first instantiated for each request. With request scope, we only fetch the feature flags configuration from the database once. If there are feature checks at multiple places while executing that request, there would be only one database call to fetch the configuration.   

This service's main functionality is to check whether a specific feature is available. It does this by checking the feature flag configuration values from the database. In the example below, the isCashFlowUWAvailable method checks if the "Cash Flow Underwriting" feature is available for a given origin (iOS, Android, or mobile web app).

Java
 
@RequestScope
@Service
@Qualifier("featureServiceV1")
public class FeatureServiceV1Impl implements FeatureServiceV1 {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private List<Config> configs;

    @Autowired
    ConfigurationRepository configurationRepository;

    @PostConstruct
    private void initializeConfiguration() {
        logger.info("FeatureService::initializeConfiguration - Initializing configuration");
        if (configs == null) {
            logger.info("FeatureService::initializeConfiguration - Fetching configuration");
            GlobalConfigListRequest globalConfigListRequest = new GlobalConfigListRequest("ICW_API");
            this.configs = this.configurationRepository.getConfigListNoError(globalConfigListRequest);
        }
    }

    @Override
    public boolean isCashFlowUWAvailable(String origin) {
        boolean result = false;
        try {
            if (configs != null && configs.size() > 0) {
                if (origin.toLowerCase().contains("ios")) {
                    result = this.isFeatureAvailableBasedOnConfig("feature_cf_uw_ios");
                } else if (origin.toLowerCase().contains("android")) {
                    result = this.isFeatureAvailableBasedOnConfig("feature_cf_uw_android");
                } else if (origin.toLowerCase().contains("mobilewebapp")) {
                    result = this.isFeatureAvailableBasedOnConfig("feature_cf_uw_mobilewebapp");
                }
            }
        } catch (Exception ex) {
            logger.error("FeatureService::isCashFlowUWAvailable - An error occurred detail error:", ex);
        }
        return result;
    }

    private boolean isFeatureAvailableBasedOnConfig(String configName) {
        boolean result = false;
        if (configs != null && configs.size() > 0) {
            Optional<Config> config = Optional
                    .of(configs.stream().filter(o -> o.getConfigName().equals(configName)).findFirst()).orElse(null);
            if (config.isPresent()) {
                String configValue = config.get().getConfigValue();
                if (configValue.equalsIgnoreCase("1")) {
                    result = true;
                }
            }
        }
        return result;
    }
}


Consuming Feature Service

We will then reference and auto-wire the FeatureServiceV1 in the controller or other service in the Spring Boot application, as shown below. We annotate the FeatureServiceV1 with the @Lazy annotation. The @Lazy annotation will ensure that the FeatueServiceV1 is instantiated when the FeatrueServiceV1 method is invoked from particular methods of the controller or service. This will prevent the unnecessary loading of the feature-specific database configurations if any other method of the controller or service is invoked where the feature service is not referenced.  This helps improve the application start-up time. 

Java
 
@Autowired
@Lazy
private FeatureServiceV1 featureServiceV1;


We then leverage FeatureServiceV1 to check the availability of the feature and then branch our code accordingly. Branching allows us to execute feature-specific code when available or default to the normal path. Below is an example of how to use the feature availability check and to branch the code:

Java
 
if (this.featureServiceV1.isCashFlowUWAvailable(context.origin)) {
    logger.info("Cashflow Underwriting Path");
    // Implement the logic for the Cash Flow Underwriting path
} else {
    logger.info("Earlier Normal Path");
    // Implement the logic for the normal path
}


Here’s how we can implement this conditional logic in a controller or service method:

Java
 
@RestController
@RequestMapping("/api/v1/uw")
public class UnderwritingController {

    @Autowired
    @Lazy
    private FeatureServiceV1 featureServiceV1;

    @RequestMapping("/loan")
    public void processLoanUnderwriting(RequestContext context) {
        if (this.featureServiceV1.isCashFlowUWAvailable(context.origin)) {
            logger.info("Cashflow Underwriting Path");
            // Implement the logic for the Cash Flow Underwriting path
        } else {
            logger.info("Earlier Normal Path");
            // Implement the logic for the normal path
        }
    }
}


Conclusion

Feature flags play is important, particularly when coordinating releases across multiple platforms. In our case, we have four channels: two native mobile applications (iOS and Android), a mobile web application (browser-based), and an iPad application. Feature flags help in smooth and controlled rollouts, minimizing disruptions to the user experience. They ensure that new features are only activated when the corresponding platform-specific latest version of the application is available in the app stores.

API mobile app Release (computing) feature flag

Opinions expressed by DZone contributors are their own.

Related

  • Demystifying APIs for Product Managers
  • Improving Customer-Facing App Quality Using Tricentis Testim
  • What Is GraphQL?
  • How To Integrate the Stripe Payment Gateway Into a React Native Application

Partner Resources


Comments

ABOUT US

  • About DZone
  • Send feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: