Feature Toggles, auch als Feature Flags, Flippers, Schalter, Feature Bits oder latente Code-Elemente bezeichnet, sind Konfigurationsoptionen, die festlegen, ob eine Funktion innerhalb einer Software aktiviert oder deaktiviert werden soll. Durch das Umschließen neuer Funktionen in solche Konfigurationen kann Code in die Produktion überführt werden, ohne sofort für alle Nutzer zugänglich zu sein. Wenn der Toggle ausgeschaltet ist, wird der betreffende Code nicht ausgeführt. Feature Toggles ermöglichen es, Code zu verstecken, der noch ungetestet oder unvollständig ist. Doch warum sollte ungetesteter oder unvollständiger Code in die Produktion gelangen?

Stellen Sie sich vor, Ihr Entwicklungsteam hat eine neue Funktion zu liefern. Es wird geschätzt, dass die Entwicklung ein paar Wochen dauert, aber Ihre Organisation folgt einem täglichen Release-Zyklus. Ihr Team erstellt einen Feature-Branch, an dem es arbeitet, und fügt diesen am Ende der zwei Wochen wieder in das Haupt-Repository ein. Der Code, der während dieser zwei Wochen entwickelt wird, wird dann auf einmal in die Produktion überführt. Bei täglichen Release-Zyklen kann diese Menge an Code ein großes Risiko darstellen. Eine Alternative besteht darin, die neue Funktion als Teil des täglichen Releases zu integrieren. Anstatt den Code erst am Ende der zwei Wochen freizugeben, wird er täglich in kleinen Schritten veröffentlicht. Die Funktion ist noch nicht vollständig, aber der Code ist durch einen Feature Toggle deaktiviert. Auf diese Weise wird der Code kontinuierlich in die Produktion übernommen, was das Risiko reduziert, da jedes Release klein und leichter zu handhaben ist. Feature Toggles minimieren das Risiko, indem sie den Nutzern erst dann neue Funktionen zugänglich machen, wenn sie sich bereits eine Zeit lang in der Produktion befunden haben. Das Entwicklungsteam und die Geschäftsbenutzer können den Code in der realen Umgebung testen und mögliche Probleme entdecken. Sollte etwas schiefgehen, kann die Funktion einfach wieder ausgeschaltet werden.

Feature Toggles bieten zahlreiche Vorteile in Entwicklungsprozessen, insbesondere bei Unternehmen wie Google, Facebook oder Amazon, die über schnelle Commit-Zyklen verfügen. Diese Unternehmen setzen Feature Toggles ein, um ihre Software kontinuierlich zu verbessern und gleichzeitig Risiken zu minimieren. Wenn mehrere Entwickler gleichzeitig auf denselben Code zugreifen und ständig neue Features integrieren, ermöglicht der Einsatz von Feature Toggles eine fehlerfreie und effiziente Bereitstellung von neuen Funktionen.

Bevor jedoch Tests für einen Feature Toggle durchgeführt werden, sollte man ein gutes Verständnis darüber haben, wie der Toggle implementiert wurde. Die Art des Toggles, die Werte, die er annehmen kann, sowie die Häufigkeit, mit der der Wert abgefragt wird, sind entscheidend. Pete Hodgson unterscheidet mehrere Arten von Toggles:

  • Release-Toggles: Diese ermöglichen es, unvollständige und ungetestete Code-Pfade in die Produktion zu überführen.

  • Experiment-Toggles: Sie werden verwendet, um multivariate oder A/B-Tests durchzuführen.

  • Ops-Toggles: Diese steuern operationelle Aspekte des Systems, wie z. B. das absichtliche Deaktivieren nicht-kritischer Funktionen bei hoher Last.

  • Permissioning-Toggles: Diese ändern die Funktionen oder das Produkterlebnis für bestimmte Benutzer, wie etwa die Aktivierung von Premium-Funktionen für treue Kunden.

Es gibt auch Unterschiede in der Dauer eines Toggles und seiner Dynamik. Ein Release-Toggle, das nur für einen kurzen Zeitraum existiert, etwa bis eine Funktion einsatzbereit ist, hat ein anderes Risikoprofil als ein Permissioning-Toggle, das permanent vorhanden ist und sich häufig ändert. Es ist wichtig, zu verstehen, wer den Toggle nutzen wird und wie.

Die Art des Werts, den der Toggle annehmen kann, beeinflusst ebenfalls die Teststrategie. Ein einfacher Boolean-Toggle erfordert weniger Tests als ein Toggle, der mehrere Werte annehmen kann, wie Listen, Arrays oder logische Bedingungen. Es ist auch wichtig zu klären, wie der Toggle gesetzt wird. Wird die Konfiguration zur Build-Zeit oder zur Laufzeit ausgelesen? Wie einfach ist es, zwischen verschiedenen Optionen zu wechseln? Und haben Sie Zugang zu den Konfigurationsänderungen in Ihrer Testumgebung, um den Produktionsprozess zu simulieren?

Die Implementierung eines Feature Toggles sollte so einfach wie möglich erfolgen. Martin Fowler betont, dass die sogenannten "Toggle-Tests" nur an den minimal notwendigen Stellen im Code auftauchen sollten. Es ist nicht erforderlich, jeden Codepfad mit einem Toggle zu schützen, sondern nur die Einstiegspunkte, die die Nutzer dorthin führen. Wenn die Erstellung, Pflege oder Entfernung von Toggles viel Zeit in Anspruch nimmt, ist das ein Hinweis darauf, dass zu viele Toggle-Tests existieren.

Wenn Sie mit einem Entwickler zusammenarbeiten, um die Implementierung eines Feature Toggles zu überprüfen, sollten Sie darauf achten, ob der Test an den Einstiegspunkten oder an jedem einzelnen Punkt im Code durchgeführt wird. Wenn die Lösung zu komplex erscheint, kann die erste Aufgabe darin bestehen, die Implementierung zu vereinfachen. Außerdem sollten Sie fragen, wie der Toggle angewendet wird. Wird die Konfiguration zur Build-Zeit oder zur Laufzeit gesetzt? Ist es einfach, zwischen den verschiedenen Optionen zu wechseln?

Feature Toggles stellen eine besondere Herausforderung für das Testing dar, da sie die Komplexität erhöhen. Mit einem einzelnen Toggle kann es notwendig sein, doppelte Tests zu erstellen, da Sie sicherstellen müssen, dass sowohl der Fall getestet wird, wenn der Toggle aktiviert als auch, wenn er deaktiviert ist. Bei mehreren Toggles entstehen jedoch zahlreiche mögliche Zustände, was zu einer exponentiellen Zunahme der möglichen Testkonfigurationen führen kann. Es ist nicht erforderlich, jede einzelne Kombination zu testen, aber es ist wichtig, die Konfigurationen zu prüfen, die in der Produktion aktiv sein werden, einschließlich derjenigen, bei denen der Toggle aktiviert oder deaktiviert wird. Häufig testen Teams auch eine Fallback-Konfiguration, bei der alle vorgesehenen Toggles deaktiviert sind.

Die entscheidende Frage für die Tests bleibt jedoch, ob die Konfigurationen, die in der Produktion verwendet werden, gründlich überprüft werden. Wenn Sie diese Tests regelmäßig durchführen, können Sie sicherstellen, dass sich keine unerwarteten Fehler in die Produktion einschleichen, was das Risiko von Fehlern nach einer Veröffentlichung minimiert.

Die Bedeutung von Dogfooding, Dark Launching und Testing in DevOps-Umgebungen

Im Kontext der Softwareentwicklung ist das Konzept des „Dogfooding“ von großer Bedeutung. Es bezeichnet den Prozess, bei dem Entwickler und Mitarbeiter eines Unternehmens die eigene Software in der realen Welt verwenden, um potenzielle Schwächen frühzeitig zu erkennen. Dabei gibt es jedoch verschiedene Aspekte zu beachten, die die Effektivität dieser Methode beeinflussen können. Ein häufiger Kritikpunkt ist die unterschiedliche Wahrnehmung der Qualität zwischen den Entwicklern und den Endnutzern. Wenn Mitarbeiter eine Vorabversion der Software nutzen, sind ihre Erwartungen an die Qualität möglicherweise höher als die der tatsächlichen Nutzer. In diesem Fall kann es zu zwei unerwünschten Ergebnissen kommen: Entweder wird das Feedback übermäßig von Problemen geprägt, die für die Nutzer weniger relevant sind, oder es fällt spärlicher aus, weil die Mitarbeiter mit mehr Unvollkommenheiten leben können, die für die Nutzer störend wären. Diese Probleme verdeutlichen, dass Dogfooding nicht immer eine zuverlässige Methode ist, um die tatsächliche Benutzererfahrung zu simulieren.

Eine verwandte, aber differenzierte Methode ist das sogenannte „Dark Launching“. Bei dieser Strategie wird der neue Code bereits in der Produktionsumgebung integriert, aber so implementiert, dass er für den Benutzer unsichtbar bleibt. Der Vorteil dieser Methode liegt darin, dass der Code parallel zum alten System läuft und Fehler im Hintergrund identifiziert werden können, ohne dass der Endbenutzer davon betroffen ist. Facebook verwendete dieses Konzept 2008 beim Rollout der Facebook Chat-Funktion, als das Backend-System getestet wurde, während die Benutzeroberfläche noch nicht aktiv war. So konnten Entwicklungs- und Qualitätsteams die neuen Funktionen in einer realen Umgebung überwachen und Fehler beheben, bevor die Benutzer sie in ihrer vollen Form erlebten. Dark Launching erlaubt es den Entwicklern, das Nutzerverhalten zu simulieren und unvorhergesehene Probleme zu identifizieren, bevor sie an die breite Masse ausgerollt werden. Diese Methode ist jedoch nicht ohne Tücken, da sie davon ausgeht, dass Nutzer soziale Funktionen nicht anders verwenden als ursprünglich erwartet. Wenn das Nutzerverhalten abweicht, können Probleme entstehen, die der Entwicklung möglicherweise nicht bewusst sind.

Im Bereich von DevOps haben sich die Bedingungen für das Testen erheblich verändert. Durch die Einführung von Virtualisierung, Automatisierung und cloudbasierter Infrastruktur sind neue Herausforderungen und Chancen entstanden. DevOps hat die Art und Weise, wie Entwicklungsumgebungen geschaffen und genutzt werden, grundlegend verändert. Heutzutage ist es für ein Entwicklerteam einfacher denn je, eine produktionsähnliche Umgebung auf seinem eigenen Rechner zu schaffen, um neue Funktionen zu testen, ohne auf die Unterstützung von Systemadministratoren angewiesen zu sein. Dies ist möglich durch die Einführung von „Infrastructure as Code“, das die manuelle Konfiguration von Servern ersetzt und automatisierte, wiederholbare Prozesse ermöglicht. In einem DevOps-Umfeld können Entwickler nicht nur den Code schreiben, sondern auch die Infrastruktur, auf der der Code ausgeführt wird.

Infrastructure as Code hat weitreichende Vorteile. Indem die Serverkonfiguration als Code definiert wird, wird dieser Prozess transparent, skalierbar und vor allem fehlerresistenter. In der Vergangenheit war das Aufsetzen von Servern ein mühsamer, manueller Prozess, der viel Zeit in Anspruch nahm und häufig fehleranfällig war. Heutzutage können Entwickler mit Hilfe von Skripten Serverumgebungen für Dutzende oder sogar Tausende von Maschinen konfigurieren. Diese Automatisierung führt zu einer höheren Zuverlässigkeit der Systeme, da jede Komponente des Systems auf die gleiche, getestete Weise aufgebaut wird. Bei Fehlern kann die Infrastruktur schnell wiederhergestellt werden, indem die gleichen Skripte verwendet werden, um die vorherige Konfiguration wiederherzustellen. Diese Methode hat nicht nur Auswirkungen auf die Effizienz der Entwicklungsprozesse, sondern auch auf die Sicherheitsstandards, da Änderungen an der Infrastruktur auf einfache Weise rückgängig gemacht werden können, sollte es zu Problemen kommen.

In DevOps-Umgebungen ist zudem „Configuration Management“ von entscheidender Bedeutung. Diese Praxis ermöglicht es, Änderungen an Systemen auf strukturierte und koordinierte Weise vorzunehmen, sodass die Integrität der Systeme über die Zeit hinweg gewahrt bleibt. Werkzeuge wie Puppet, Chef, Ansible und SaltStack sind gängige Lösungen für das Configuration Management, da sie Entwicklern und Systemadministratoren helfen, komplexe Serverinfrastrukturen effizient zu verwalten. Diese Tools bieten die Möglichkeit, große und verteilte Infrastrukturen zu konfigurieren und sicherzustellen, dass diese Konfigurationen jederzeit nachvollziehbar und wiederholbar sind.

Die Kombination dieser modernen Ansätze, die in DevOps-Umgebungen Anwendung finden, verändert die Dynamik der Softwareentwicklung. Entwickler sind nicht nur für die Erstellung von Anwendungen verantwortlich, sondern auch für die Infrastruktur, auf der sie ausgeführt werden. Dies führt zu einer engeren Verzahnung zwischen der Applikation und der zugrundeliegenden Technologie, was eine effizientere und skalierbarere Entwicklung von Software ermöglicht.

Es ist wichtig zu verstehen, dass diese Technologien und Ansätze nicht nur technische Veränderungen darstellen, sondern auch eine kulturelle Transformation in Unternehmen erfordern. Die engen Verbindungen zwischen Entwicklung, Testing und Infrastruktur schaffen eine neue Arbeitsweise, bei der die Entwickler eine viel größere Verantwortung für das gesamte System tragen. Diese Verschmelzung von Entwicklungs- und Operationsaufgaben erfordert ein neues Denken und eine tiefere Zusammenarbeit zwischen verschiedenen Teams. Nur so können die vollen Vorteile der DevOps-Philosophie genutzt werden.

Wie Puppet, Chef, Ansible und SaltStack die Installation und Verwaltung von Apache erleichtern

Die Verwaltung von Infrastruktur als Code ist eine moderne Praxis, die immer mehr an Bedeutung gewinnt, um die Bereitstellung und Verwaltung von Software und Servern zu vereinfachen und zu beschleunigen. Besonders in großen IT-Infrastrukturen ist es notwendig, Prozesse wie die Installation und Konfiguration von Servern zu automatisieren, um Geschwindigkeit, Zuverlässigkeit und Compliance sicherzustellen. Hier kommen Tools wie Puppet, Chef, Ansible und SaltStack ins Spiel, die eine einfache und konsistente Verwaltung von Servern und Software ermöglichen. Um die Unterschiede und Anwendungsfälle dieser Tools zu veranschaulichen, betrachten wir die Installation des Apache-Webservers mit jedem dieser Konfigurationsmanagement-Tools.

Puppet verwendet sogenannte „Manifeste“, die in der Ruby-Syntax geschrieben sind und mit der Endung .pp gespeichert werden. Ein Manifest ist eine Sammlung von Anweisungen, die Puppet mitteilen, welche Ressourcen auf den Zielmaschinen installiert und konfiguriert werden sollen. Zum Beispiel könnte ein einfaches Puppet-Manifest, um Apache zu installieren und zu starten, so aussehen:

puppet
# execute 'apt-get update'
exec { 'apt-update': command => '/usr/bin/apt-get update', } # install apache2 package package { 'apache2': require => Exec['apt-update'], ensure => installed, } # ensure apache2 service is running service { 'apache2': ensure => running, }

Das Manifest definiert die Schritte, die zur Installation von Apache notwendig sind. Zuerst wird ein Update des Paketmanagers durchgeführt, bevor Apache installiert und der Dienst gestartet wird. Puppet arbeitet in einem Master-Agent-Architekturmodell, bei dem der Master Server die Anweisungen an die Agenten überträgt.

Ähnlich wie Puppet verwendet auch Chef Ruby, jedoch sind die Anweisungen in „Rezepten“ (recipes) formuliert und mit der Endung .rb gespeichert. Ein einfaches Rezept, um Apache zu installieren, könnte so aussehen:

ruby
name "webserver"
description "Systems that serve HTTP and HTTPS" run_list( "recipe[apache2]", "recipe[apache2::mod_ssl]" ) default_attributes( "apache" => { "listen" => ["*:80", "*:443"] } )

In Chef werden Rezepte in sogenannten Kochbüchern (cookbooks) organisiert. Chef kann sowohl im Client-Server-Modus als auch im Standalone-Modus (chef-solo) ausgeführt werden, wobei letzterer SSH für die Kommunikation zwischen Servern verwendet.

Ansible, im Gegensatz zu Puppet und Chef, verwendet YAML, eine menschenlesbare Markup-Sprache, um Playbooks zu erstellen, die mit der Endung .yml gespeichert werden. Ein einfaches Ansible-Playbook zur Installation von Apache könnte folgendermaßen aussehen:

yaml
--- - hosts: apache sudo: yes tasks: - name: install apache2
apt: name=apache2 update_cache=yes state=latest

Ansible ist besonders benutzerfreundlich, da keine spezielle Software auf den Zielmaschinen installiert werden muss. Stattdessen nutzt Ansible SSH zur Kommunikation und ermöglicht eine sehr schnelle Konfiguration neuer Server.

SaltStack hingegen arbeitet mit sogenannten „States“, die in YAML geschrieben sind und mit der Endung .sls gespeichert werden. Ein einfaches SaltStack-Modul zur Installation von Apache könnte so aussehen:

yaml
apache: pkg: - installed

SaltStack verwendet ebenfalls eine Master-Minion-Architektur, bei der der Master Server die Minions (Clients) steuert. Die Nutzung von „States“ ermöglicht eine deklarative und wiederholbare Konfiguration.

Jedes dieser Tools hat seine eigenen Besonderheiten und Stärken, die je nach den Anforderungen des Projekts von Vorteil sein können. Puppet und Chef setzen beide auf Ruby und bieten eine umfangreiche Sammlung von Funktionen zur Verwaltung der Infrastruktur. Ansible zeichnet sich durch seine Einfachheit und Benutzerfreundlichkeit aus, während SaltStack eine hohe Flexibilität und Geschwindigkeit bietet, besonders in größeren Umgebungen.

Es ist auch wichtig zu verstehen, dass die Auswahl eines dieser Tools oft von den spezifischen Anforderungen der Infrastruktur und der verwendeten Programmiersprachen abhängt. Während Ansible besonders für kleinere Teams und weniger komplexe Umgebungen geeignet ist, bieten Puppet und Chef umfangreiche Funktionalitäten, die für größere, komplexere Systeme notwendig sein können. SaltStack bietet eine starke Leistung in großen verteilten Systemen und ist daher besonders nützlich für Unternehmen mit einer Vielzahl von Servern.

Die Entscheidung für das richtige Tool erfordert also eine gründliche Analyse der Systemanforderungen, der vorhandenen Ressourcen und der zukünftigen Skalierbarkeit. Es gibt keine universelle Antwort, welches Tool das beste ist, da jedes seine eigenen Vor- und Nachteile in bestimmten Szenarien hat.