It’s common that internal projects in Objectivity suffer from constant shortages and high turnover of developers. This is usually paired with pressure on frequent (1-2 weeks’) releases and rapidly changing requirements. This raises a number of challenges to design a software development process in a way that it’s simple to master by the ever-changing team and provides a quick and accurate delivery of new functionalities.
I was faced with these issues together with my team. A bare minimum was to introduce a Continuous Integration (CI) system, automated unit testing and deployments for all environments (Dev, Test and Production). We’ve already done it in the past, so it was obvious to us to introduce git as a source code repository and Team City as a CI server. Automated deployments were implemented by means of our top notch PSCI library.
The real problem came at a time, when we did not know what to do with functionalities, which take more than one release cycle to implement. How not to disclose fully unimplemented or unapproved functionality? Can this be done in a simple and manageable way without losing the benefits of CI?
TWO PATHS, ONE GOAL
As you might guess, the problem has already been solved a long time ago and clearly described by the “Chief Scientist” of ThoughtWorks, Martin Fowler. One solution is the practice of feature branching, the creation of a separate branch in the code repository for each of the new functionalities. Distributed Version Control Systems (DVCS) such as git has supported this approach for a long time.
Figure 1: Feature branch example (source)
Such process often forces complex code merges and a number of challenges in the CI mechanism. Setting of Continuous Builds, on every branch with every commit, is not a problem in the Team City, but it is not a Continuous Integration. In order to test the integrity of each branch and its production-readiness, it is necessary to merge it frequently with the mainline and release to a dedicated test environment to assess its quality. Since this is a complex process, it often leads to the so-called ad-hoc or Promiscuous Integration - while the main branch of code regularly undergoes the CI process and is ready for immediate release, the individual feature branches are not. So the question arises whether there is another, simpler solution?
An alternative approach are the feature toggles. Rather than create a new branch of code for every new feature, all work is done on the main branch, but one can conditionally enable a feature by using the so-called toggle (other names are feature bits, flags, flippers, switches and the like). This greatly simplifies implementation of the CI process, but transfers the problem to the actual code and the toggle itself.
TO TOGGLE, OR NOT TO TOGGLE?
The basic idea of the toggle is the possibility of conditional switching of a functionality, which for example can be a user interface element or part of the algorithm in the application code. Such a switch can be implemented as a simple class with Enabled property that returns a value of true / false, with optional configuration file.
Conditional checks for the jumpers should occur only in those code places where it is absolutely necessary to hide a given functionality. Do not secure every possible path, just concentrate on the entry points. If you spend a lot of time on adding, maintaining and removing the toggles, it is a sign that we are doing something wrong. Simple conditional check (if) in the code may be the easiest to implement, but remember about much more efficient mechanisms of object-oriented programming like polymorphism and abstraction that offers ability to swap class implementation when needed.
TYPES OF TOGGLES
We can distinguish two types of toggles:
- Release toggles - related to the specific version of the application
- Business toggles - freely switched on and off between different versions of the application
Release toggles are fully controlled by the developer and permanently deleted once given feature is implemented and approved for release. It is not required, and even not recommended to disclose them to the application users. As the state of jumpers is set by developers, their switching takes place directly in the application code, at the compile time.
Business toggles are under the control of the end user or business sponsor. Then they are an integral part of the application and must be maintained for a long time. This toggle type requires an administrative panel to enable toggles management by the end users of the application. Business jumpers must therefore be set during the application run time.
The rest of this article will focus on the release toggle - much simpler to implement, test and manage. Such jumpers were used in our project.
Feature toggles at the server side were implemented by means of the Jason Roberts’s library – FeatureToggle. It’s quite rich and popular “toggle” library in a .NET world with comprehensive documentation and even a plural sight course! The client-side toggle library for AngularJS was written by us, based on the existing angular-feature-flags module.
I will not elaborate on different types of toggles and how to use them, but rather demonstrate approaches to the toggling mechanism we used in our project.
Feature toggle declaration
Listing1: Feature toggle for the Comments feature.
As you can see, the feature toggle class is a simple class inheriting from the base toggle class. We define toggle classes in a separate C# project, which simplifies management and exposure to other libraries / layers.
Figure 2: Feature toggles’ definitions in the server-side project
Figure 3: Feature toggles’ helpers in the client-side project
Listing 2: Configuration file for the Comments toggle
Some toggle types can be configured by an application .config file. This is not a requirement, but the convenience. We might as well define a permanent state of jumpers directly in the code.
Exposing toggles to other layers
Listing 4: WebAPI service exposing feature toggles to the client (AspNetBoilerplate)
Feature toggles testing
Unit tests ensure the developers consciously turn the toggles on or off. Such tests are automatically executed with regular unit test as part of the automated CI process. This allows for quick capture of any inconsistencies in the configuration.
Toggling in the server and client side code
Listing 6: Feature toggling in the server code (C#)
The above C# code conditionally creates a Web API controller, so if the comments functionality is not available, it is not even possible to call this a service. This applies to a situation where we start implementation from the server side. When the server code is implemented and tested, the toggle condition is removed, and the client code is disabled, as shown below.
Listing 7: Feature toggling in the client code (HTML)
The problem of continuous and effective releasing of new functionalities can be solved by implementing a feature toggle approach.
Based on our internal project example, it is an interesting alternative to the popular code branching technique. The advantage of this approach is ease of CI process implementation and development on only one, main branch of the code. This gives us the flexibility of conditional inclusion of the application functions, even just before the release! The actual toggle mechanism is simple enough that the new team members are able to assimilate it very quickly.
It turns out quickly that the main difficulty lies not in the toggle mechanism, but in the application design. Considering the goal of the progressive introduction of the new functionalities and reducing the number of jumpers in the code, it soon becomes clear that our allies are good, old-fashioned SOLID practices of the object-oriented programming, like:
- Designing classes with the single responsibility (single responsibility principle);
- Extension, rather than modification of an existing class implementation (open/closed principle);
- Building dependencies upon abstractions, not actual implementations (dependency inversion principle).
The feature toggle mechanism should not be an excuse for good programming practices, but rather complement them. The sooner we understand it, the easier and more likely we will use it.
The article was published in Programista Magazine 11/2015 (no. 42)