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
Please enter at least three characters to search
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

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • Running a Mobile App API Locally With Docker and Postman
  • Releasing MuleSoft API
  • API-Led Example: MuleSoft
  • Demystifying APIs for Product Managers

Trending

  • AWS to Azure Migration: A Cloudy Journey of Challenges and Triumphs
  • How to Ensure Cross-Time Zone Data Integrity and Consistency in Global Data Pipelines
  • Understanding IEEE 802.11(Wi-Fi) Encryption and Authentication: Write Your Own Custom Packet Sniffer
  • Unit Testing Large Codebases: Principles, Practices, and C++ Examples
  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
Likes (3)
Comment
Save
Tweet
Share
1.9K 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

  • Running a Mobile App API Locally With Docker and Postman
  • Releasing MuleSoft API
  • API-Led Example: MuleSoft
  • Demystifying APIs for Product Managers

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and 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:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!