Features

Toggle Features

Enable. and disable features at runtime - no deployment. Implement multiple paths in your code with predicates evaluated dynamically (if/then/else).

Zoom

Role-based Toggling

Enable features not only with flag values but also drive access with roles and groups (Canary Release). Different frameworks supported starting by Spring Security.

Zoom

Strategy-based Toggling

Implement custom predicates (Strategy Pattern) to evaluate if a feature is enabled. Some are provided out of the box: White/Black lists ,Time based, Expression based. Connect external source like a Drools rule engine.

Zoom

AOP-driven Toggling

Keep your code clean and readable: Avoid nested if statements but use annotations. Thanks to Spring AOP target implementation is pick at runtime, and thus driven by feature statuses.

Zoom

Features Monitoring

For each features execution, ff4j evaluates the predicate therefore it's possible to collect and record events, metrics to compute nice dashboards or draw curves for features usage over time.

Zoom

Audit Trail

Each action (create, update, delete, toggles) can be traced and saved in the audit trail for troubleshooting. With permissions management (AuthorizationManager) it's possible to identify users.

Zoom

Web Console

Administrate FF4j (including features and properties) with the web UI. Packaged as a servlet in the library you will expose it in your backend applications. Almost 10 languages available.

Zoom

Wide choice of Databases

Our proud: we support 20+ databases technologies to store your features, properties and events. Same business model, multiple implementations. Thanks to extension points it's easy to build your own.

Zoom

Command Line Interface

To automate things or because web ports may be blocked (you know, production...) you can work through SSH using our Command Line Interface (cli), our Shell #devOps. It will interact directly with storages.

Zoom

JMX and MBeans

Limited set of operations can be performed through JMX. ff4j exposes some Mbeans to read metrics or toggle features from external tools (Nagios...). Not all applications are web based.(batches, shell, standalone...)

Zoom

Properties (CMDB)

Store not only feature statuses but any property value.. Create properties you can change at runtime . It is integrated with most used frameworks like Spring, Archaius, commons-config or Consul.

Zoom

(Distributed) Cache

Evaluating predicates may put pressure on DB (high hit ratio). ff4j provides local and distributed caches to help. (edit feature also evict cache). Leveraging JSR-107 it supports most of cache solutions.

Zoom

Spring Boot Starter

Import ff4j-spring-boot-starter dependency in your microservices to get the web console and rest api working immediately. (To be used for the backend app. Now compliant with Spring Boot 2x: 👉 SAMPLES

Zoom

Use FF4j through a WEB API and connect with others languages, specially javascript frontends.( also avoid microservices to connect to DB directly). Click the image for doc and demo, it might take 30s to start.

Zoom

Getting Started


1. Run DEMO on my laptop


A live demo is running at https://ff4j-demo.herokuapp.com/ (it can take up to 30s to start, heroku start the demo only on-demand). The following steps will help you build this sample.


1.a - Using DOCKER
If you already have Docker install on your machine you can simply run the demo with the command below, it should start at http://localhost:8080. 🤘

docker run -p 8080:8080 ff4j/ff4j-sample-springboot2x:1.8.11

1.b - Using MAVEN
The source code of this demo is available on youtube at https://github.com/ff4j/ff4j-samples/tree/master/spring-boot-2x/ff4j-sample-springboot2x let's clone and run the sample. It should (also) start at http://localhost:8080. You can now start to copy paste or retro engineering, this is sometimes the fastest way right.

git clone https://github.com/ff4j/ff4j-samples.git

cd spring-boot-2x/ff4j-sample-springboot2x

mvn spring-boot:run


This is what it look likes

2. Build the project step-by-step


2.a - What application
FF4j can be used in any java application (including Android). 90% of the code is working with JDK1.6+ but it is now recommended to use at JDK8+. The single dependency you have to specified in pom.xml is ff4j-core. Tt contains an in-memory implementation for each store. On top of it, you add extra dependencies ff4j-core-* to define which database technology to use. FeatureStore, PropertyStore and EventRepository don't have to use the same storage technology.


As FF4j provides REST-API and a web console we may want to create a web application to get the most of this tutorial. We will use Spring-Boot but the same steps could be adapted for application.

2.b - Create project skeleton

You can create your project in multiple ways using your IDE or a maven archetype. For this tutorial we will use the website https://start.spring.io/ entering some package name, project name and adding the Spring WEB module and then click GENERATE to download the app.

This is what it look likes

Unzip, import the project in your favorite IDE, open your pom.xml file and add the following dependencies.
<dependency>
 <groupId>org.ff4j</groupId>
 <artifactId>ff4j-spring-boot-starter</artifactId>
 <version>${ff4j.version}</version>
</dependency>
<dependency>
 <groupId>org.ff4j</groupId>
 <artifactId>ff4j-web</artifactId>
 <scope>test</scope>
 <version>${ff4j.version}</version>
</dependency>       

You will define the property ff4j.version to match the current version   --
<properties>
  <ff4j.version>...</ff4j.version>
</properties>


2.c - Define FF4j Object
With the Spring framework we use the annotations @Configuration and @Bean to define our FF4j object. FeatureStore, PropertyStore and EventRepository are all initialized with in-memory implementations by default.At the FF4j level exist some flags to enable features like audit or feature autocreation (default behaviour is throwing FeatureNotFoundException)

import org.ff4j.FF4j;
import org.ff4j.audit.repository.InMemoryEventRepository;
import org.ff4j.property.store.InMemoryPropertyStore;
import org.ff4j.store.InMemoryFeatureStore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FF4jConfig {
    
    @Bean
    public FF4j getFF4j() {
        FF4j ff4j = new FF4j();
        
        /* 
         * Implementation of each store. Here this is boiler plate as if nothing 
         * is specified the inmemory is used. Those are really the one that will
         * change depending on your technology.
         */
        ff4j.setFeatureStore(new InMemoryFeatureStore());
        ff4j.setPropertiesStore(new InMemoryPropertyStore());
        ff4j.setEventRepository(new InMemoryEventRepository());

        // Enabling audit and monitoring, default value is false
        ff4j.audit(true);

        // When evaluting not existing features, ff4j will create then but disabled 
        ff4j.autoCreate(true);

        // To define RBAC access, the application must have a logged user
        //ff4j.setAuthManager(...);

        // To define a cacher layer to relax the DB, multiple implementations
        //ff4j.cache([a cache Manager]);
        
        return ff4j;
    }
}


2.d - Expose the Web Console
Exposing the web console is only relevant for backend applications (probably centralized) and not in each component using ff4j. Even you secure with user/password, there is not point to open that breach. That been said, let's do it.

The servlet to expose is org.ff4j.web.FF4jDispatcherServlet. In Spring Boot application you do not use the web.xml file (yet here is a sample) but rather define the proper bean and annotate with @Configuration. Using SpringMVC we extends the SpringBootServletInitializer
@Configuration
// Enable the REST API documentation
@EnableFF4jSwagger
// The class should be on classpath : FF4jDispatcherServlet
@ConditionalOnClass({FF4jDispatcherServlet.class})
// Setup FF4j first, not is required
@AutoConfigureAfter(FF4jConfig.class)
public class FF4jWebConsoleConfiguration extends SpringBootServletInitializer {
    @Bean
    @ConditionalOnMissingBean
    public FF4jDispatcherServlet defineFF4jServlet(FF4j ff4j) {
        FF4jDispatcherServlet ff4jConsoleServlet = new FF4jDispatcherServlet();
        ff4jConsoleServlet.setFf4j(ff4j);
        return ff4jConsoleServlet;
    }
    @Bean
    @SuppressWarnings({"rawtypes","unchecked"})
    public ServletRegistrationBean registerFF4jServlet(FF4jDispatcherServlet ff4jDispatcherServlet) {
        return new ServletRegistrationBean(ff4jDispatcherServlet, "/ff4j-web-console/*");
    }
}

That's it you should now be able to start the application and having the web console responsing at /ff4j-web-console/ and the rest api responding at /api/ff4j.


2.e - Define a sample controller
With the previous steps you do have a working sample but no features or properties to play with. In the demo we added a rest controller mapped to root path /. In the one we define 3 features (showWebConsoleURL, showRestApiURL, showUserName) and 1 property (username).

@RestController
@RequestMapping("/")
public class HomeController {
    
    private static final Logger LOGGER = LoggerFactory.getLogger(HomeController.class);
    private static final String FEATURE_SHOW_WEBCONSOLE = "showWebConsoleURL";
    private static final String FEATURE_SHOW_REST_API   = "showRestApiURL";
    private static final String FEATURE_SHOW_USERNAME   = "showUserName";
    private static final String PROPERTY_USERNAME       = "username";
    
    @Autowired
    public FF4j ff4j;
    
    @PostConstruct
    public void populateDummyFeatureForMySample() {
        if (!ff4j.exist(FEATURE_SHOW_WEBCONSOLE)) {
            ff4j.createFeature(new Feature(FEATURE_SHOW_WEBCONSOLE, true));
        }
        if (!ff4j.exist(FEATURE_SHOW_REST_API)) {
            ff4j.createFeature(new Feature(FEATURE_SHOW_REST_API, true));
        }
        if (!ff4j.exist(FEATURE_SHOW_USERNAME)) {
            ff4j.createFeature(new Feature(FEATURE_SHOW_USERNAME, true));
        }
        if (!ff4j.getPropertiesStore().existProperty(PROPERTY_USERNAME)) {
            ff4j.createProperty(new PropertyString(PROPERTY_USERNAME, "cedrick"));
        }
        LOGGER.info(" + Features and properties have been created for the sample.");
    }

Then we display a text on screen based on the value of the flag. We that simply displaying the home page will trigger the features and we would have to events in the monitoring screen. This is also a good way to provide URL to the web console and the rest API:
@RequestMapping(value = "/", method = RequestMethod.GET, produces = "text/html")
    public String get() {
        LOGGER.info(" + Rendering home page...");
        StringBuilder htmlPage = new StringBuilder("<html><body><ul>");
        htmlPage.append("<h2>This is home page.</h2>");
        htmlPage.append("<p>Displaying the links below is driven by features in FF4j."
                + "If you disable the feature "
                + "the link will disapear (but the servlet will still response, "
                + "this is just a trick to illustrate "
                + "response in the UI)</p>");
        htmlPage.append("<p><b>List of resources for you :"
                + "</b><br/><ul>");
        if (ff4j.check(FEATURE_SHOW_WEBCONSOLE)) {
            htmlPage.append("<li> To access the  To access the <b>REST API</b> "
                    + "please go to <a href=\"./api/ff4j\" target=\"_blank\">ff4j-rest-api </a>");
        }
        if (ff4j.check(FEATURE_SHOW_USERNAME)) {
            if (ff4j.getPropertiesStore().existProperty(PROPERTY_USERNAME)) {
                htmlPage.append("<li> " + PROPERTY_USERNAME + " value is ");
                htmlPage.append("<span style=\"color:blue;font-weight:bold\">");
                htmlPage.append(ff4j.getPropertiesStore().readProperty(PROPERTY_USERNAME).asString());
                htmlPage.append("</span>");
            } else {
                htmlPage.append("<li> " + PROPERTY_USERNAME + " does not exist.");
            }
        }
        htmlPage.append("</ul></body></html>");
        return htmlPage.toString();
}

Congratulations you are done with this getting started. You can now move to a set of SAMPLES or jump into the reference guide, which is our wiki on github.
Use Cases

Avoid Feature Branching

Feature branches lead to conflict during merges. Use trunk-based-developpement to toggle-off unfinished code when develop continously.

Blue/Green Deployments

Avoid clusters nodes inconsistency during deployment and deliver new features desactivated. Toggle "ON"" when all nodes are up-to-date and ready.

Canary Release

Do not create dedicated infrastructure to qualify new features. Open them for subset of beta-testers and directly into production environments.

Dark Launch

Measure performance impacts of new features. Activate them dynamically on a defined ratio of incoming requests and observe system responses.

Graceful degradation

Tune and protect your system of heavy loads: focus on high business value requests and discard others dynamically (clients vs prospects, carts contents..).

Thin client application

Avoid annoying frequent deployments and downloads of mobile applications by providing empty shelf: request expected active features to YOUR servers.

Business Toggle

Toggling is not only technical. Define your own rules and evaluate features based on business requirements like office hours, user profiles...

A/B Testing

Split A and B populations using a business toggle. Measure business impacts not only with CRM but also hitcounts of very same framework.

Circuit Breaker

Implement the circuit breaker pattern with a dedicated strategy and custom rules allowing to toggle off proactively not available features.

Fork me on GitHub