In many projects, the development of new features is done in one or another branching model, such as Git Flow: Features are first developed on a separate branch and only integrated again after the feature is "finished". When merging the changes, there are sometimes conflicts and if you are unlucky, you end up in "merging hell".
Modern VCSs such as GIT make branching and merging much easier, but they do not change the fact that it is occasionally difficult to integrate competing changes into a runnable deployment. The main problem with feature branches, however, is a different one: When is a feature "ready", i.e. can it be integrated? If a story is accepted on the basis of the feature branch, further quality assurance must take place after integration - otherwise errors could creep in when combined with features developed in parallel. If quality assurance only takes place after integration on a release branch, it could turn out that the feature is not ready after all because one or the other requirement has not been met. Continuous Integration therefore follows a different path: development takes place on the HEAD and each commit is integrated directly in an automated manner. Jenkins, TeamCity or other tools help to regularly create an up-to-date build every few minutes and deploy it to a CI server.
Very good automated test coverage is a prerequisite for ensuring that the software is executable at all times and that all requirements remain met. Feature toggles ensure that a feature is not activated until it has been completed and approved by quality assurance. For this purpose, the new features of the software are toggled using simple if statements:
if (Features.NEW_FANCY_FEATURE.isActive()) {
useMyFancyNewFeature();
} else {
doTheOldBoringStuff()
}OldBoringStuff() }
Using feature toggles gives us a whole new set of options for making features live. Namely, features can also be activated gradually via "valves" (Valves, Activation Strategies):
So the final decision whether a new feature goes online can be done in the live environment, which helps to deploy regularly and short timed. In the "Discover" team, we have been using Feature Toggles for some time now. Currently, we go live in our team alone(the system consists of several loosely coupled applications) several times a week, sometimes several times a day. Integrating Feature Toggles into the software is easy. A rudimentary solution is quickly developed in-house, but there are also ready-made solutions. Some time ago we switched from a self-developed solution to the Togglz Library: a fancy, lean library that can be integrated very easily into all kinds of Java applications and also comes with a console to manage the toggles. The above mentioned"Activation Strategies" are supported (among others), a rights management can be easily integrated and the persistence of toggle settings is also easily possible.
Togglz Administration Console toggles are used in various situations:
The Togglz Library supports such groupings via the FeatureGroup annotation. We have written our own annotations for the various use cases. Features that are still under development are switched via the InDevelopment, for example:
@FeatureGroup
@Label("Features in development")
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InDevelopment {
}
public enum Features implements Feature {
@Label("Only load product recommendations asynchronously using AJAX")
@InDevelopment
USE_AJAX_FOR_PRODUCT_RECOMMENDATIONS,
...
In the Togglz Console, the FeatureGroups are displayed as tabs in which the features belonging to the group are automatically sorted.We had to get used to the development with Feature Toggles: If too few toggles are used, existing features can be affected by page effects. But if we overdo it, the readability and testability of the code suffers. Above all, however, it is important to remove toggles that are no longer needed from the code.The advantages far outweigh these initial difficulties, however. In addition to the new possibilities for live development, the toggles allow us to continuously integrate and frequently move into production. Feedback cycles become very short this way and we can push frequent small commits directly on the HEAD, avoiding costly merges: One new test, one small change, one commit. In combination with BDD, TDD, build pipelines, non-disruptive deployments and a rollback-capable application, we are on the best way towards Continuous Delivery with Feature Toggles.
Ich hätte noch ein paar Fragen zum Verständnis sofern das erlaubt ist. Falls nicht bzw. wenn ich einfach falsch liege oder eine falsche Annahme mache, bitte gerne korrigieren!
Am Anfang beschreibst du ein Problem bezüglich der Qualitätssicherung nach der Fertigstellung eines Features. Den Merging Hell lass ich mal außen vor. Ist halt so. Du stellst die Frage wann etwas getestet werden muss. Meiner Meinung nach, wird doch in dem jeweiligem Branch nach Fertigstellung des Features oder idealerweise täglich ein Rebase mit dem Master sowie nach der Fertigstellung des Features ein Rebase mit dem Master durchgeführt. Und da es sich beim Master immer um eine lauffähige Version handelt, könnte man dann doch problemlos auch im Branch testen. Was nun passieren könnte, wäre dass während des Testens sich der Master nochmal verändert hat. Aber wenn man diese Annahme zugrunde legt, müsste man Prinzipiell nach der Fertigstellung eines Features immer alles Testen, da es dann quasi immer Seiteneffekte haben könnte. Daher wahrscheinlich der Schritt Richtung CI um automatisiert testen zu können. Ich nehme mal an, dass die Software ohnehin schon lose gekoppelt war um es generell leicht testen zu können.
Weiterhin hätte man die Feature Toggles auch unabhängig von allem dem einführen können oder? Denn dann würde ich den Artikel so verstehen, dass man gerne im Sinne von TDD über BDD mit Hilfe von Continuous Integration hin zum Continuous Delivery kommen möchte um unterbrechungsfrei deployen zu können. Und das dabei die Feature Toggles sehr Hilfreich sind und damit eine Neuerung! Oder habe ich das falsch verstanden.
Hallo José,
natürlich findet man auch mit einem Branching-Modell einen Weg, Features zu testen, keine Frage. Branches führen aber tendenziell zu etwas längeren Release-Zyklen, als wenn man den CI-Ansatz verfolgt, in dem zumindest theoretisch jeder Commit Livegestellt werden kann. CI und Feature-Branches vertragen sich nicht sonderlich gut miteinander: Wenn man mit Feature-Branches arbeitet, werden die Features auf dem Branch "fertig"gestellt, bevor integriert wird. Die Integration wird dadurch sehr viel grob-granularer, als wenn kontinuierlich integriert wird.
CI führt zwangsläufig zu einer sehr hohen automatisierten Testabdeckung. Feature-Branches sind in dieser Hinsicht etwas weniger anspruchsvoll. Branches führen eher zu "Releases", während CI in Richtung Continuous Delivery führt.
Vor der Livestellung eines Features sorgfältig zu testen ist in beiden Modellen erforderlich. Bei Feature-Branches erfolgt dieser Test aber nach Fertigstellung, während Feature-Toggles zumindest prinzipiell die Livestellung "unfertiger" Features ermöglichen. Die Integration der einzelnen Commits erfolgt bei Branches also deutlich später, was zu größeren Änderungen in den Deployments führt, die dann aufwändiger zu testen sind (viele Änderungen erhöhen das Risiko, dass sich viele Fehler eingeschlichen haben).
Toggles lassen sich auch in der Entwicklung mit Feature-Branches einsetzen: beispielsweise um Features "im Notfall" zu deaktivieren, für A/B-Tests oder um ein Feature nach und nach auszurollen. Ganz bestimmt kann man auch Wege finden, Branches und Toggles zu kombinieren. Es sind halt Werkzeuge, die man bewusst einsetzen muss.
Viele Grüße, Guido
We have received your feedback.