Code
Grenzen gehören in den Build
Seit der Ausbildung 2015 schreibe ich C# und .NET, von WebForms bis zu containerisierten Services auf Azure. Heute schreibe ich weniger Code als früher. Ich bleibe aber in Reviews, in den Templates und in den Tests, die Architektur durchsetzen.
Architektur, die der Build prüft
Eine Modulgrenze, die nur im Review existiert, existiert nicht. Deshalb steht sie als Test in der Pipeline. Greift ein Modul an seinem Contract vorbei, wird der Build rot, bevor das Review anfängt.
// Eine Modulgrenze, die nur im Review existiert, existiert nicht.
[Fact]
public void Abrechnung_kennt_Kurse_nur_ueber_Contracts()
{
var ergebnis = Types.InAssembly(typeof(AbrechnungModul).Assembly)
.That().ResideInNamespace("Abrechnung")
.ShouldNot().HaveDependencyOn("Kurse.Domain")
.GetResult();
Assert.True(ergebnis.IsSuccessful);
}
$ dotnet test --filter Abrechnung_kennt_Kurse_nur_ueber_Contracts
Bestanden! Fehler: 0, erfolgreich: 1, übersprungen: 0, gesamt: 1
Ein Beispiel mit NetArchTest, die Modulnamen sind ausgedacht. So sehen die Tests aus, mit denen bei uns Modulgrenzen im Build stehen statt im Review.
Womit ich arbeite
Werkzeuge, die ich lange genug benutze, um sie begründen zu können. Die Liste ist kurz, weil ich selten wechsle.
Sprache und Laufzeit
3LTS. Neue Projekte starten hier, bestehende ziehen mit, sobald das Support-Ende in Sicht ist.
Records und Pattern Matching haben die Hälfte meiner Guard-Klauseln ersetzt.
Skripte, die unter Windows, macOS und im Linux-Container gleich laufen.
Editor und Terminal
3Refactorings über eine Solution mit 60 Projekten, ohne dass die IDE stehen bleibt.
Vier Profile in einem Fenster: Build, Logs, Git, Notizen.
Privat mein Zuhause für Repos, Issues und Merge Requests. Beruflich auf dem Weg nach draußen.
Bibliotheken
3Migrationen liegen im Repo und laufen im Deploy. LINQ so lange, bis das Profiling widerspricht.
Ein Test pro Regel, benannt nach der Regel. Der Testname ist die Dokumentation.
Lastszenarien als C#-Code neben den fachlichen Tests, nicht in einem separaten Werkzeug.
Daten und Nachrichten
3Wenn ich wählen darf. Transaktionen, JSONB und ein Backup, das ich selbst zurückspiele.
Wenn der Kunde schon dort ist. Kein Grund, eine Migration zu erfinden.
Jede Nachricht trägt einen Idempotency-Key; Duplikate sind der Normalfall, nicht die Ausnahme.
Betrieb
3App Service und Container Apps. Infrastruktur als Code erst dort, wo sie sich wiederholt.
Dieselbe .gitlab-ci.yml lokal wie in der Pipeline. Ein Build, der nur auf dem Server läuft, ist keiner. Privat.
Eine Korrelations-ID vom Request bis in den Consumer. Traces vor Dashboards.
Schreiben
3Entscheidungen und Specs liegen neben dem Code. Was woanders liegt, veraltet.
Globales Tool für Architecture Decision Records. adr new schreibt die Entscheidung als Markdown nach docs/adr, MADR-Vorlage.
Diagramme als Text im Repo, versioniert wie alles andere.
Bewusst nicht dabei
Ein modularer Monolith lässt sich teilen, sobald die Grenze steht. Andersherum wird es teuer.
Zwei Wege in dieselbe Tabelle sind ein Weg zu viel.
Code, den niemand im Team erklären kann, ist eine Abhängigkeit ohne Vertrag.
Stand September 2026. Was ich austausche und warum, steht im Blog.
KI-gestützte Entwicklung im Team
Das Thema, bei dem ich am meisten lerne und am wenigsten festgelegt bin. Im Team läuft KI-gestütztes Coding über JetBrains AI und eigene Gateways für Modelle aus Azure AI Foundry und IONOS – inklusive Testgenerierung und Wissensaufbau.
Ich sammle Prompt-Muster für Refactorings über Modulgrenzen hinweg und notiere ehrlich, wo ich danach mehr Zeit gebraucht habe als ohne. In einer großen Solution entscheiden Kontextgrenzen und Konventionen darüber, ob am Ende Zeit gespart wird.
Offene Fragen
Wo ist die Grenze zwischen Assistenz und Verantwortung?
Was gehört in eine Konvention, damit ein Agent sie einhalten kann?