Zentrale Service Einstellungen?

Dienstspezifische Einstellungen sollten bei dem jeweiligen Dienst abgelegt werden.

Kohäsion

Es ist eine Frage der Kohäsion. Dinge, die fachlich zusammengehören, sollten auch zusammen und in enger Nähe platziert werden. Es gibt wenig schlimmeres als ein Feature so aufzuteilen, dass es im ganzen System verteilt ist (hier ein bisschen Code, hier ein bisschen Config, hier wieder ein bisschen Code, da ein bisschen Doku, usw.). Dienste sollten möglichst autark sein und bestenfalls eigenständig existieren können. Je mehr Kopplung im System besteht, desto schwieriger wird die Weiterentwicklung. Deswegen sind zentrale Aspekte besonders gefährlich. Manche Funktionen müssen zentral realisiert werden, wie zum Beispiel Authentifizierung, weil es nicht anders geht. Alle Dinge, die nicht zwingend zentralisiert werden müssen, sollten es auch nicht. Für den Anwenderkomfort können zentrale Darstellung mit Hilfe von konsolidierenden Oberflächen und Backends For Frontends geschaffen werden.
Im weiteren zeige ich zusätzlich noch drei eindeutige Vorteile der dezentralen Konfiguration.

Atomare Änderungen

Änderungen und Weiterentwicklungen eines Dienstes müssen nicht mehr an zwei entfernten Stellen (Code und Config) nachgezogen werden und in zwei Phasen committed werden. Diese zwei Stellen haben in der Regel eine zeitliche Kopplung, die zu einer fest vorgegebenen Reihenfolge der Aktualisierung führt, die eingehalten werden muss.
Wenn Code und Config an der gleichen Stelle liegen, kann der Code weiterentwickelt und neue Einstellungen direkt mit angepasst werden. Beides kann mit einem einzigen Commit in der richtigen Reihenfolge atomar deployt werden.

Prepare and Forget

Eine Einstellung kann dann auch nicht fälschlicherweise zu früh deployt werden, was zu Fehlern in der aktuellen Version führen kann, sondern wird erst unmittelbar mit dem Code deployt. Ein Entwickler muss nach seinem Commit nicht erst darauf warten, dass sein Code in Test und schließlich nach Production deployt wird, damit er endlich die Einstellung für die jeweilige Umgebung einstellen kann. Die Einstellung wird automatisch angewendet, wenn der Code in die jeweilige Umgebung freigegeben und deployt wird. Der Entwickler brauch sich nicht mehr kümmern (wenn alles nach Plan läuft). Natürlich muss er die Einstellung für Test, Production und alle anderen Umgebungen kontrolliert vorbereiten, aber das tatsächliche Anwenden passiert transparent im Hintergrund, zur richtigen Zeit automatisiert.
Eventuelle Geheimnisse, die der Entwickler nicht kennen darf, müssen ihm verschlüsselt von der jeweils zuständigen Person mitgeteilt oder in Kooperation vorbereitet werden.
Auch das Wiederherstellen einer früheren Version ist aus der Perspektive der Einstellungen problemlos möglich, da die Einstellungen mit dem selben Commit assoziiert sind.

Verantwortung des Service beim Team

Solange das Team, das den Service entwickelt, auch die Verantwortung für den Betrieb des Services übernehmen soll, müssen alle relevanten Artefakte mit dem Service verwaltet werden. Das Repository repräsentiert auch eine organisatorische Klammer. Durch das Klonen des Repositories bekommen Entwickler nicht nur vollständigen Zugriff auf den Quelltext, sondern auch auf tatsächliche Einstellungen, Build Skripte und Deployment Ressourcen. Ein Entwickler kann sich also jederzeit eine funktionsfähige und sinnvoll konfigurierte Umgebung auf seinem Computer erstellen und für Test- und Entwicklungszwecke betreiben. Isolierte Weiterentwicklungen können dabei sogar problemlos in eigenen Branches erfolgen. Ein Entwickler hat jederzeit alle nötigen Ressourcen zur Verfügung, um die Anwendung eigenständig weiterzuentwickeln.

 

Einwände

Und wenn sich nur die Einstellung ändert?

Wenn Einstellung und Service in der selben Pipeline deployt werden, ist das erstmal nicht schlimm. Nachteilig ist lediglich, dass es vielleicht etwas länger dauert, bis die Einstellung aktiv wird. Soll die Änderungen sofort gelten, kann sie manuell oder über das K8S Dashboard vorgenommen werden, und wenn sie endgültig sein soll, kann diese Änderung durch commit dokumentiert werden. Durch das anschließende Deployment wird der Wert dann nicht nochmal geändert, weil er bereits auf dem Zielwert steht.

Und wenn sich eine Einstellung auf viele Dienste bezieht?

Einstellungen, die keinem Dienst zugeordnet werden können (Logging, Message Bus,…), können in der Regel der Umgebung zugeordnet werden. Daher sollten sie an einem dafür vorgesehehen Ort für Infrastruktur Konfiguration abgelegt werden.
Einstellungen, die von einem Dienst für andere Dienste bereitgestellt werden, sollten bei dem bereitstellenden Dienst abgelegt werden. Eine Änderung dieser Einstellungen sollte einen automatisierten Neustart der Konsumeten verursachen.

Und wenn sich alle Einstellungen ändern?

Generell sollten Einstellungen so platziert werden, dass wahrscheinliche und häufige Änderungen mit wenig Aufwand vorgenommen werden können, während für unwahrscheinliche und seltene Änderungen höhere Aufwände akzeptiert werden können. Es macht mehr Sinn für Weiterentwicklung einzelner Dienste zu optimieren als für das Umziehen des ganzen Systems in neue Umgebungen. Sollten sich dienstübergreifende Dinge, wie zum Beispiel die Domain, ändern, dann müssen im schlimmsten Fall viele Commits vorgenommen werden. Auch hier kann eine sofortige Änderung manuell oder im K8S Dashboard erfolgen, die dann anschließend noch committed werden muss. Sollten sich dienstübergreifende Dinge häufig ändern, sollten diese hoffentlich wenigen Einstellungen besser an dem Ort für Infrastruktur Konfiguration abgelegt werden.

Better WPF DataBinding with CalcBinding

If you are like me, you want to bind Boolean properties to Visibility properties. We implement IValueConverter and therefore increase the complexity in the system. The new Type has to be named and placed correctly and we have to make sure that there is no value converter that already does what we just created. How can we speed up and improve binding and value conversion?

Recently I came across the very sweet WPF library CalcBinding. It allows for custom binding expressions without the need for an IValueConverter implementation. It is a markup extension that lets us write inversions and calculations directly in XAML. Those binding expressions also support two way binding automatically. That prevents the ViewModel from getting all kinds of highly View-related properties, which can obfuscate the abstract view model. Just like the CodeBehinds did in preMVVM age.
In this blog post I give you a quick run through of all the features I find most impressive. You can easily install it with “Install-Package CalcBinding”. Once you import its namespace in the XAML you can take advantage of the new binding like below.

1

The screenshot above shows a multi binding together with arithmetic expressions to produce a single result. The screenshot below shows additional calculations that can be used inside the binding expression. Remarkably, you get the back conversion for free. Not all calculations belong to the View. Keep in mind that calculations in the view can not easily be unit tested.

2

The screenshot below shows how to bind a Boolean to Visibility. Visibility is a Type that lives in View-land and I do not want view-specific Types to be dependencies of my ViewModels. I believe the ViewModel should be as pure as possible. This way I can easily reuse them to support not only WPF views in a MvvmCross solution, for example. Unfortunately MvvmCross has its own framework specific Types like MvxCommand.

3

The screenshot below shows how to use the ternary operator (?:) within the binding expression to concatenate strings. This could be useful if the binding’s StringFormat is not enough.

4

There is one more thing I would like CalcBinding to do. If you already looked into AngularJS or compiled data binding in the Universal Windows Platform, you may like how they support binding to events. Would it not be great if we could do the same in WPF? Exactly. And here is how, utilizing the CalcBinding markup extension. Unfortunately, even though I created a pull request, this is not yet included/supported in the official version of CalcBinding.

5

CalcBinding is amazing. Especially when binding bool properties, it is a huge time saver. And on the side it keeps my ViewModels much cleaner and prevents them from turning into huge CodeBehinds. The only disadvantage I found is that while Visual Studio seems to accept the extensive binding expressions, ReSharper lists them as errors in its solution wide error analysis view. Maybe we can teach it to ignore those. For now, it has a very positive cost-benefit profile and I am going to continue using it. If you like it as well, give credit to Alex141 and star his github project at the link above.

Null propagation for pre-C#6

C#6 will support null propagation. But what if a system is stuck on C#4 or C#5? Result.OrDefault is my take on simple null propagation for pre-C#6. It is open source under the MIT license meaning you can freely use and modify it.

Do you know requirements like “display the employee’s name in the invoice view”? Then probably your ViewModel code looks a lot like this.

public string EmployeeName
{
   get
   {
      if (SelectedItem != null && SelectedItem.Invoice != null && SelectedItem.Invoice.Employee != null && SelectedItem.Invoice.Employee.Name != null)
      {
         return SelectedItem.Invoice.Employee.Name.ToString();
      }
      else
      {
         return string.Empty;
      }
   }
}

The requirements code is cluttered in null checks, since you don’t want the user to get NullReferenceExceptions when no invoice is selected or the selected invoice does not have an employee yet and so on. If you want to change the code to not use the Employee property anymore but to use the Sender property, make sure to change the two null checks as well. Not very DRY. Disadvantage of the above code is that the actual requirement is hidden in safeguard-code. There are multiple layers of abstraction and much code to maintain. And this is just a simple example. What would you have had to do if the sample had used methods with side effects instead of properties? Sure, CQRS but lets be honest. In order to not invoke a method twice, local variables are needed and your IF with a few conditions very quickly turns into a bloated mess. Good luck with cyclomatic complexity (have you heard about the C.R.A.P. metric?).

I don’t want to write all that safeguard-code since it blurs the view on what was originally required and makes it difficult to see why the code was written in the first place. I just want to write what the requirement is.

public string EmployeeName
{
   get
   {
      return SelectedItem.Invoice.Employee.Name.ToString();
   }
}

Yes, that would not work. In C#6 we could write it like this.

public string EmployeeName
{
   get
   {
      return SelectedItem?.Invoice?.Employee?.Name?.ToString();
   }
}

But what if a system is stuck on C#4 or C#5? Result.OrDefault lets you write the requirement like this.

public string EmployeeName
{
   get
   {
      return Result.OrDefault(() => SelectedItem.Invoice.Employee.Name.ToString());
   }
}

Result.OrDefault uses reflection to invoke one step at a time, checks for null and invokes the next step only if the result of the current step is not null (it only takes twice as long as with IFs for null checks). It allows property access, field access and method calls to be chained. Most important to me, it offers a much cleaner syntax since the focus is on the requirement and not on low level safeguard-code.

Unterstriche erhöhen die Lesbarkeit

Unterstriche in Testklassennamen und Testmethodennamen optimieren unterbewusste Augenbewegungen, entlasten damit die Augen und ermöglichen schnellere Erfassung der Zusammenhänge!

Der Mensch liest nicht buchstabenweise, sondern erfasst Wortbilder oder Teile davon nach spontanen, willkürlichen Bewegungen des Auges, den Sakkaden. Das Auge springt also von einem Zielpunkt zum Nächsten. Das sind die Sakkaden. Dann findet die Fixation statt, indem das Auge sich auf ein Wort fokussiert. Nur bei den Fixationen kommt es zu einer Informationsaufnahme. Fixationen nehmen rund 90 bis 95 Prozent der Gesamtlesezeit ein. Falls das Wort nicht erkannt wird oder irgendetwas dazwischen kommt, springt es automatisch wieder dahin zurück von wo es gesprungen ist. Das sind die Regressionen.

In “KadschfunHuAugenbewegungJfdskHundOhrHeIzr” existieren keine Wortzwischenräume sodass die Zielpunkte für die Sakkaden schwer zu finden ist. Dagegen in “Kadschfun Hu Augenbewegung Jfdsk Hund Ohr HeIzr” sind die Zielpunkte schneller zu finden, weil die Wortzwischenräume automatisch Zielpunkte für die Augen bilden. Ein optimaler Wortzwischenraum unterstützt Sakkaden und den Fixationsprozess. Es finden weniger Regressionen statt.

Testklassennamen oder Testmethodennamen bestehen häufig aus mehreren Eigenworten wie beispielsweise Klassennamen und Methodennamen. Ein gutes Beispiel dafür ist “InvoiceSubModuleVerschiedeneRvgStaendeTest“. Diesen Namen kann der menschliche Leser wesentlich langsamer lesen als “InvoiceSubModule VerschiedeneRvgStaende Test“.

Das liegt daran, dass hier die Augen das Wort InvoiceSubModule in der zweiten Schreibweise viel schneller nämlich unterbewusst erkennen können und weniger suchen müssen.

Wenn die Zielpunkte schwerer zu finden sind, muss das Auge mehr suchen, also hin und herspringen. Und je öfter das Auge springen muss, um einen Text zu erfassen, desto schneller ermüdet es. Außerdem wird der Lesefluss erheblich gestört und die Lesebereitschaft nimmt ab was dazu führt, dass ähnliche Texte fälschlicherweise als gleich angesehen werden.

Quellen

“Der menschliche Lesefluss findet in drei Phasen statt: Sakkaden, Regression und Fixation. Sakkaden sind spontane Blickbewegungen, die willkürlich und zielgerichtet ausgeführt werden, um bekannte Wortbilder oder Teilstücke davon zu identifizieren. Geübte Leser erfassen Texte nicht aus einzelnen Buchstaben, sondern erkennen an der Form des Wortbildes das einzelne Wort. Es genügt oft schon, wenn die Anfangs- und Endbuchstaben sowie die Buchstabenanzahl stimmen, um ein Wort zu erkennen – auch wenn im Wortinneren die Buchstaben einmal durcheinander geraten sind.
Das Auge springt von einem Zielpunkt zum nächsten und findet mühelos zurück, falls sich Ungereimtheiten auftun. Dieses Zurückspringen wird als Regression bezeichnet. Je schwieriger ein Text zu lesen ist, desto häufiger kommt es zum Hin- und Herspringen.
Sowohl die Sakkaden als auch die Regression dienen zunächst nur dem Erfassen des Textes; ein Sinnzusammenhang wird noch nicht erkannt. Dies geschieht erst in einer Fixation genannten Ruhephase, in der das Auge für einen kurzen Moment eine Ruheposition einnimmt, um dem Gehirn das Entschlüsseln der Botschaft zu ermöglichen. Diese kurzen Momente der Fixation nehmen jedoch beim Lesen mit bis zu 90% den überwiegenden Teil der gesamten Lesezeit ein.”

Aus <http://kadekmedien.com/2010/03/03/lesbarkeit-von-texten/ >

Ein optimaler Wortzwischenraum unterstützt den Fixationsprozess und dient somit derLesbarkeit einer Schriftsatzarbeit. […]
Bis in die 1970er Jahre wurde in der typographischen Literatur, z.B. in der von Jan Tschichold (1902–1974), die Formulierung »tadelloser Ausschluß« verwendet. Heute könnte man diese Begrifflichkeit als »idealer Wortzwischenraum« bezeichnen. […]
Wortzwischenräume werden in der relativen Maßeinheit Geviertgemessen.

Aus <http://www.typolexikon.de/w/wortzwischenraum.html >

Für den optimalen Wortzwischenraum gibt es die Faustformel 1/3 eines Gevierts […]
Starke Schwankungen des Wortzwischenraums behindern den Lesefluß. Das Auge erfaßt beim Lesen nicht Buchstaben und setzt sie zu Wörtern und Sätzen zusammen, es sieht Wortgebilde als bekannte Muster. Hierbei sind Unregelmäßigkeiten äußerst hinderlich. […]
Als optimal werden 50 bis 60 Zeichen pro Zeile für längere Lesetexte angesehen. Dies bringt im Blocksatz gute Wortzwischenräume.

Aus <http://edoc.hu-berlin.de/e_rzm/18/bynum/7.pdf >

Ohne Interpunktion und Wortzwischenräume wäre das schnelle und stille Lesen undenkbar. (Vgl. Fritzsche, S. 108.) […]
Die Wortzwischenräume erleichtern das schnelle und einfache Erfassen der Wortbilder bei längeren Zeilen und bieten eine Orientierung beim Zeilensprung des Auges. (Willberg/Forssman: Lesetypografie, S. 90.)

Aus <http://bmb.htwk-leipzig.de/fileadmin/fbmedien_bmp/downloads/Abschlussarbeiten/Zweisprachige_Mikrotypografie_Amelie_Solbrig_VH-02.pdf >

Argumente gegen Unterstriche

“Coding Styles gelten für Test-Code und Produktivcode” Was sagt Uncle Bob wirklich? Qualitätsansprüche oder Namenskonventionen? Es geht um die Qualität des Tests und wenn der Methodenname aussagekräftiger ist, desto besser. Ein Name wie “FinalizeDossierTest” ist zu allgemein und beschreibt nicht, was die Test-Methode testet, sondern nur, welche Produktivcode-Methode sie aufruft. “FinalizeDossierTest” trifft für beliebig viele Testszenarien zu, die hoffentlich nicht alle in der einen Test-Methode implementiert sind.

“Es ist besser wenn für Produktivcode-Methoden und Testmethoden gleiche Konventionen gelten, Qualität und so” eben nicht! Tests haben eine ganz andere Existenzberechtigung und damit auch andere Anforderungen als Produktivcode. Zum Beispiel die Verwendung des “Test”-Postfix (eigentlich sinnlos da Test-Attribut), des TestMethodAttribute und von Microsoft.VisualStudio.TestTools.UnitTesting.Assert ist nur in Tests sinnvoll. Auch sollten Tests nicht von anderen Tests abhängig sein. All diese “Konventionen” sind nur in Tests sinnvoll. Auch wenn die Testmethode die “gleichen Konventionen” einhält wie die anderen Methoden, wird die Qualität dadurch absolut nicht besser. Eine schlechte Testmethode bleibt schlecht. Einfachheit, Verständlichkeit und Vertrauenswürdigkeit macht eine Testmethode qualitativ besser. Nach Roy Osherove: Readable, Maintainable, Trustworthy http://artofunittesting.com/definition-of-a-unit-test/

“Unterstriche kommen in den Guidelines von .NET nicht vor” gemeint ist CA1707: Identifiers should not contain underscores (http://msdn.microsoft.com/en-us/library/ms182245.aspx) und General Naming Conventions (http://msdn.microsoft.com/en-us/library/ms229045.aspx) aber da geht es um API-Design, nicht um Test-Szenarien. Das wird auch dadurch deutlich, dass Jeff Prosise in “Framework Design Guidelines” (Seite 73) die Benutzung von Unterstrichen zur Kennzeichnung von privaten Feldern legitimiert. Auch Mike Fanning (Seite 377) legitimiert Unterstriche in Membern, die nicht extern sichtbar sind, wie beispielsweise Event Handlern, die nicht öffentlich sind. Auch Roy Osherove nutzt Unterstriche nicht in Wörtern, sondern zur Abgrenzung der drei Bereiche. Worum es in der Guidelines aber explizit geht ist “readability over brevity” (Lesbarkeit vor Kürze)

“Namen der Testmethoden werden zu lang” dann wird wahrscheinlich zu viel getestet. Durch kürzere Testmethodennamen werden die Tests auch nicht besser, sondern zwingen den Programmierer den Testcode vollständig zu lesen um zu verstehen, was eigentlich getestet wird (Zeit!). Robert C. Martin sagt: “The name of a function should correspond inversely to the size of its scope.” Wenn die Methode von vielen aufrufbar ist, sollte sie einen kurzen Name haben. Methoden, die von wenigen aufgerufen werden, können längere Namen haben. Testmethoden werden nur vom Testrunner aufgerufen und haben daher einen ganz kleinen Scope. Also sind längere Namen erlaubt.

“Prägnante Namen sind besser” bei Namen der Testmethoden geht es nicht um Prägnanz sondern Aussagekraft! DossierFinalizeTest() ist zwar prägnant aber sagt nur, welche Methode getestet wird -> schlecht. Die Testmethode sollte kurz sein und der Name aussagekräftig, nicht anders rum.

Das einzige was besser wird, ist die Professionalität des Teams, die es schafft, sich an eine Regel zu halten, um der Regel Willen. Das hat mit Qualität nichts zu tun.

Kompromiss

Wenn Ihre Coding Quidelines trotzdem Unterstriche verbieten, installieren Sie sich doch einfach meine Visual Studio Extension, die die Unterstriche in Test-Methodennamen einfach wieder anzeigt: https://github.com/halllo/MethodNameRepainting

CleanCode & Software Craftsmanship

CleanCode ist für mich persönlich, damit mein Code gut wird. CleanCode ist es nicht würdig darüber zu diskutieren oder zu verteidigen. Ich gewinne nichts, wenn ich versuche meinen Pairings Partner zu überzeugen: a men convinced against his will is of the same opinion still.
Ich verliere nur Zeit, Nerven, Motivation. Mein CleanCode ist wichtig. CleanCode anderer ist unwichtig. Wenn mein Pairings Partner zu meinem Code einen “Vorschlag” hat, prüfe ich diesen auf Korrektheit, nicht auf CleanCode. Er darf den Vorschlag dann implementieren. Ich sage nichts, mache mir aber bewusst, welche Nachteile der Vorschlag in Bezug auf CleanCode hat. Als Konsequenz versuche ich öfter mit Partnern zu pairen, die CleanCode machen und weniger mit welchen die nicht. Um mich und mein CleanCode zu schützen. CleanCode und Software Craftsmanship ist MEIN Handwerk!