{"id":935,"date":"2013-10-13T10:29:13","date_gmt":"2013-10-13T10:29:13","guid":{"rendered":"http:\/\/abramowitsch.de\/blog\/?p=935"},"modified":"2013-10-13T10:32:06","modified_gmt":"2013-10-13T10:32:06","slug":"verwenden-von-feature-toggle-patterns-um-der-merge-hoelle-zu-entgehen","status":"publish","type":"post","link":"https:\/\/abramowitsch.de\/blog\/?p=935","title":{"rendered":"Verwenden von Feature-Toggle-Patterns, um der Merge-H\u00f6lle zu entgehen"},"content":{"rendered":"<h2>Die Merge-H\u00f6lle<\/h2>\n<p>Bei der professionellen Software-Entwicklung ist eine Versionsverwaltung nicht mehr wegzudenken. Fast alle Versionskontroll-Systeme unterst\u00fctzen Branches, in denen man beispielsweise die Entwicklung von Features vornimmt, um den Code im Hauptzweig (trunk) nicht zu beeinflussen und unabh\u00e4ngig das Feature entwickeln zu k\u00f6nnen:<\/p>\n<p><img decoding=\"async\" alt=\"\" src=\"http:\/\/abramowitsch.de\/blog\/wp-content\/uploads\/2013\/10\/101313_1029_Verwendenvo1.png\" \/><\/p>\n<p>Hier ist ein Beispiel daf\u00fcr, wie ich bisher die Produkt- \/ Projektentwicklung kannte: Nachdem eine Produkt-Version released wurde, werden Feature-Branches angelegt, um die Features unabh\u00e4ngig vom Entwicklungs-Branch zu entwickeln. Das hat sehr viele Vorteile: Beispielsweise k\u00f6nnte sich das Produktmanagement entscheiden, ein Feature erst in einer sp\u00e4teren Version zu integrieren, falls sich die Entwicklung verz\u00f6gern sollte. Zudem kann die Entwicklung eines Features unabh\u00e4ngig von der Entwicklung anderer Features stattfinden.<\/p>\n<p>Wer so arbeitet, kennt auch den Begriff der &#8222;Merge-H\u00f6lle&#8220;. Diese tritt n\u00e4mlich dann auf, wenn die Entwicklungs-Branches f\u00fcr unterschiedliche Features und der trunk im Code zu weit auseinander laufen oder Code in der gleichen Datei mehrfach umgebaut wurde. Dann n\u00e4mlich funktioniert der Merge nicht mehr so einfach:<\/p>\n<p><img decoding=\"async\" alt=\"\" src=\"http:\/\/abramowitsch.de\/blog\/wp-content\/uploads\/2013\/10\/101313_1029_Verwendenvo2.png\" \/><\/p>\n<p>Wenn es sich dabei nur um eine Code-Datei handelt, ist das zwar \u00e4rgerlich, aber noch akzeptabel. Die Merge-H\u00f6lle ergibt sich, wenn sehr viele Dateien in unterschiedlichen Entwicklungszweigen ver\u00e4ndert wurden und vielleicht sogar noch neue Dateien mit gleichem Namen angelegt wurden. Dann funktioniert in der Regel der Merge nicht mehr und man verbringt Stunden und Tage damit, die Konflikte aufzul\u00f6sen und hofft, dass danach noch alles so funktioniert, wie es man gedacht war.<\/p>\n<p>Weiterhin kann es passieren, dass Probleme, die in einem Branch gefixt wurden, durch den Merge wieder verloren gehen.<\/p>\n<h2>Das Feature Toggle Pattern<\/h2>\n<p>Hier kann das Feature-Toggle-Pattern Abhilfe schaffen:<\/p>\n<p>Anstelle der Verwendung unterschiedlicher Entwicklungs-Branches findet sie Entwicklung immer auf dem gleichen Branch (trunk) statt. Martin Fowler hat in seinem Artikel &#8222;<a href=\"http:\/\/martinfowler.com\/bliki\/FeatureToggle.html\">Feature Toggle<\/a>&#8220; (<a href=\"http:\/\/martinfowler.com\/bliki\/FeatureToggle.html\">http:\/\/martinfowler.com\/bliki\/FeatureToggle.html<\/a>) detailliert beschrieben, wie das Feature-Toggle Pattern funktioniert.<\/p>\n<p>Damit ist es m\u00f6glich, auch noch &#8222;experimentellen&#8220;, noch nicht fertigen Code in Produktivsystemen zu releasen. Man muss quasi nur sicherstellen, dass der unfertige Code nie ausgef\u00fchrt wird. Dadurch arbeitet jeder immer auf dem aktuellen Entwicklungszweig und kann \u00c4nderungen an der Codebasis registrieren und darauf reagieren.<\/p>\n<p>Doch wie setzt man das Feature Toggle Pattern in der Praxis um?<\/p>\n<p>Wichtig ist, dass die Stelle, an der das Feature aktiviert bzw. deaktiviert wird, sp\u00e4ter einfach zu finden und zu entfernen ist, wenn das Feature &#8222;produktiv&#8220; geschaltet wird. Wenn ein Schalter nicht entfernt wird, b\u00fcrdet man sich unn\u00f6tigerweise &#8222;technische Schulden&#8220; auf. Am einfachsten geht das beispielsweise mit einer (abstrakten) Klasse, die eine Property &#8222;IsEnabled&#8220; zur Verf\u00fcgung stellt und von der alle Feature Schalter erben:<\/p>\n<p><span style=\"color: blue; font-family: Consolas; background-color: white;\">internal<span style=\"color: black;\"><br \/>\n<span style=\"color: blue;\">abstract<span style=\"color: black;\"><br \/>\n<span style=\"color: blue;\">class<span style=\"color: black;\"><br \/>\n<span style=\"color: darkblue;\">FeatureSwitch<span style=\"color: black;\"><br \/>\n<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/p>\n<p><span style=\"color: black; font-family: Consolas; background-color: white;\">{<br \/>\n<\/span><\/p>\n<p><span style=\"color: black; font-family: Consolas; background-color: white;\"><br \/>\n<span style=\"color: blue;\">public<span style=\"color: black;\"><br \/>\n<span style=\"color: blue;\">abstract<span style=\"color: black;\"><br \/>\n<span style=\"color: blue;\">bool<span style=\"color: black;\"><br \/>\n<span style=\"color: purple;\">IsEnabled<span style=\"color: black;\"> { <span style=\"color: darkcyan;\">get<span style=\"color: black;\">; <span style=\"color: darkcyan;\">set<span style=\"color: black;\">; }<br \/>\n<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/p>\n<p><span style=\"color: black; font-family: Consolas; background-color: white;\">}<br \/>\n<\/span><\/p>\n<p>&nbsp;<\/p>\n<p>Von dieser Klasse FeatureSwitch leitet man nun einfach einen Schalter f\u00fcr das eigene Feature (&#8222;MyFeature&#8220;) ab:<\/p>\n<p><span style=\"color: blue; font-family: Consolas; background-color: white;\">internal<span style=\"color: black;\"><br \/>\n<span style=\"color: blue;\">class<span style=\"color: black;\"><br \/>\n<span style=\"color: darkblue;\">MyFeatureSwitch<span style=\"color: black;\"> : <span style=\"color: darkblue;\">FeatureSwitch<span style=\"color: black;\"><br \/>\n<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/p>\n<p><span style=\"color: black; font-family: Consolas; background-color: white;\">{<br \/>\n<\/span><\/p>\n<p><span style=\"color: black; font-family: Consolas; background-color: white;\"><br \/>\n<span style=\"color: blue;\">private<span style=\"color: black;\"><br \/>\n<span style=\"color: blue;\">bool<span style=\"color: black;\"><br \/>\n<span style=\"color: purple;\">_isEnabled<span style=\"color: black;\"> = <span style=\"color: blue;\">true<span style=\"color: black;\">;<br \/>\n<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"color: black; font-family: Consolas; background-color: white;\"><br \/>\n<span style=\"color: blue;\">public<span style=\"color: black;\"><br \/>\n<span style=\"color: blue;\">override<span style=\"color: black;\"><br \/>\n<span style=\"color: blue;\">bool<span style=\"color: black;\"><br \/>\n<span style=\"color: purple;\">IsEnabled<span style=\"color: black;\"><br \/>\n<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/p>\n<p><span style=\"color: black; font-family: Consolas; background-color: white;\"> {<br \/>\n<\/span><\/p>\n<p><span style=\"color: black; font-family: Consolas; background-color: white;\"><br \/>\n<span style=\"color: darkcyan;\">get<span style=\"color: black;\"> { <span style=\"color: blue;\">return<span style=\"color: black;\"><br \/>\n<span style=\"color: purple;\">_isEnabled<span style=\"color: black;\">; }<br \/>\n<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/p>\n<p><span style=\"color: black; font-family: Consolas; background-color: white;\"><br \/>\n<span style=\"color: darkcyan;\">set<span style=\"color: black;\"> { <span style=\"color: purple;\">_isEnabled<span style=\"color: black;\"> = <span style=\"color: blue;\">value<span style=\"color: black;\">; }<br \/>\n<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/p>\n<p><span style=\"color: black; font-family: Consolas; background-color: white;\"> }<br \/>\n<\/span><\/p>\n<p><span style=\"color: black; font-family: Consolas; background-color: white;\">}<\/span><span style=\"font-size: 14pt;\"><br \/>\n<\/span><\/p>\n<p>Hier wird das Feature by-default eingeschaltet (_inEnabled = true). Genauso einfach kann das Feature auch wieder deaktiviert werden.<\/p>\n<p>Im Code verwendet man den Schalter dann so:<\/p>\n<p><span style=\"color: darkblue; font-family: Consolas; background-color: white;\">MyFeatureSwitch<span style=\"color: black;\"> myFeature = <span style=\"color: blue;\">new<span style=\"color: black;\"><br \/>\n<span style=\"color: darkblue;\">MyFeatureSwitch<span style=\"color: black;\">();<br \/>\n<\/span><\/span><\/span><\/span><\/span><\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"color: blue; font-family: Consolas; background-color: white;\">if<span style=\"color: black;\"> (myFeature.<span style=\"color: purple;\">IsEnabled<span style=\"color: black;\">)<br \/>\n<\/span><\/span><\/span><\/span><\/p>\n<p><span style=\"color: black; font-family: Consolas; background-color: white;\">{<br \/>\n<\/span><\/p>\n<p><span style=\"color: black; font-family: Consolas; background-color: white;\"><br \/>\n<span style=\"color: green;\">\/\/ implement feature code here &#8230;<span style=\"color: black;\"><br \/>\n<\/span><\/span><\/span><\/p>\n<p><span style=\"color: black; font-family: Consolas; background-color: white;\">}<br \/>\n<\/span><\/p>\n<p><span style=\"color: blue; font-family: Consolas; background-color: white;\">else<span style=\"color: black;\"><br \/>\n<\/span><\/span><\/p>\n<p><span style=\"color: black; font-family: Consolas; background-color: white;\">{<br \/>\n<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"color: black; font-family: Consolas; background-color: white;\">}<br \/>\n<\/span><\/p>\n<p>&nbsp;<\/p>\n<p>Man instanziiert einfach an einer beliebigen Stelle, an der das Feature verwendet wird, den FeatureSwitch. Dort pr\u00fcft man dann \u00fcber die Eigenschaft &#8222;IsEnabled&#8220;, ob das Feature &#8222;scharf geschaltet&#8220; ist und implementiert den Code so, als wenn das Feature produktiv verwendet wird.<\/p>\n<p>Alternativ zur abstrakten Basisklasse &#8222;FeatureSwitch&#8220; k\u00f6nnte man auch einen ConfigSwitch verwenden wie ich es beispielsweise im Blog von Federik Normen gesehen habe: <a href=\"http:\/\/weblogs.asp.net\/fredriknormen\/archive\/2013\/09\/28\/merge-hell-and-feature-toggle.aspx\">http:\/\/weblogs.asp.net\/fredriknormen\/archive\/2013\/09\/28\/merge-hell-and-feature-toggle.aspx<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Die Merge-H\u00f6lle Bei der professionellen Software-Entwicklung ist eine Versionsverwaltung nicht mehr wegzudenken. Fast alle Versionskontroll-Systeme unterst\u00fctzen Branches, in denen man beispielsweise die Entwicklung von Features vornimmt, um den Code im Hauptzweig (trunk) nicht zu beeinflussen und unabh\u00e4ngig das Feature entwickeln zu k\u00f6nnen: Hier ist ein Beispiel daf\u00fcr, wie ich bisher die Produkt- \/ Projektentwicklung kannte: [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[3],"tags":[126,109,107,110,111,108],"class_list":["post-935","post","type-post","status-publish","format-standard","hentry","category-net","tag-net","tag-csharp","tag-feature-toggle","tag-merge","tag-merge-hell","tag-pattern"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p3Ug90-f5","_links":{"self":[{"href":"https:\/\/abramowitsch.de\/blog\/index.php?rest_route=\/wp\/v2\/posts\/935","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/abramowitsch.de\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/abramowitsch.de\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/abramowitsch.de\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/abramowitsch.de\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=935"}],"version-history":[{"count":3,"href":"https:\/\/abramowitsch.de\/blog\/index.php?rest_route=\/wp\/v2\/posts\/935\/revisions"}],"predecessor-version":[{"id":938,"href":"https:\/\/abramowitsch.de\/blog\/index.php?rest_route=\/wp\/v2\/posts\/935\/revisions\/938"}],"wp:attachment":[{"href":"https:\/\/abramowitsch.de\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=935"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/abramowitsch.de\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=935"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/abramowitsch.de\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=935"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}