Copilot

  • MCP
  • Prompts
  • Instructions
  • Skills
  • Custom Agents
  • Hooks

Angular Debugging

  • Redux Devtools
  • Angular Devtools
  • JS Debugging

Simplicity

Complexity

  • Essential Complexity
  • Accidental Complexity
  • Cognitive Load

Abstraktion

  • Versteckt Details / Komplexität
  • Benennt ein Konzept
  • Entkoppelt Konzept von Verwendung
  • Definiert Contract
  • Schlechte Abstraktion => hohe Accidental Complexity

Gute Abstraktion

  • Bildet tatsächliche Konzepte ab
  • Versteckt viele Details hinter einem schmalen Interface
  • Implementation änderbar ohne breaking change
  • Leicht wiederzuverwenden
  • Namen und Parameter sind selbsterklärend

Schlechte Abstraktion

  • Bildet nicht die Realität ab
  • Versteckt wenige Details hinter kompliziertem Interface
  • Name reicht nicht als Beschreibung
  • Erschwert Änderungen / Erweiterungen, erfordert Workarounds

Leaky abstraction

  • Aufrufer muss Teile der Implementation verstehen

Simplicity

  • simpel != einfach
  • Minimiert Anzahl verschiedener Konzepte
  • Minimiert Accidental Complexity
  • Langweilig, Offensichtlich

Simplicity vs Clean Code

  • Viele Überschneidungen
  • Simplicity:
    • Fokus auf niedrige Komplexität (Verständnis)
    • Abstraktionen drängen sich auf
    • Risiko: Duplication, Technical Debt
  • Clean Code:
    • Fokus auf Bewältigung von Komplexität (Änderbarkeit)
    • Empfiehlt eher frühzeitige Abstraktion
    • Risiko: Accidental Complexity

Wie gewöhne ich mir Simplicity an?

  • Verständnis Probleme als Code smell beachten
  • Häufig fragen: "Brauchen wir das wirklich?"
  • Im Code Review sollte jeder verstehen, wie etwas funktioniert
  • Bei "cooler" Lösung innehalten und prüfen, ob das notwendig ist

Simplicity: Low hanging fruits

  • Namen für Conditions
  • Early return / validation
  • Nesting vermeiden
  • Klarer Control Flow mit Fokus auf Happy Path
  • CQS: Schreibzugriffe / Mutation von Queries und Logik trennen
  • Deklarativer Code

Beispiele

public TransportStatus CalcStatus()
{
    var entladeStopIsHub = transportArt is AuftragTransportArt.Abholung or AuftragTransportArt.Kommissionierung;
    var ladeStopIsHub = transportArt is AuftragTransportArt.Zustellung;
    var wirdNichtUmgeladen = auftrag.zustellungArt == AuftragZustellungArt.WirdNichtUmgeladen;

    if (wirdNichtUmgeladen)
    {
        if (entladeStopIsHub && entladeStop.istAngekommen)
            return TransportStatus.Abgeladen;
        if (ladeStopIsHub && GetPreviousTransport()?.status == TransportStatus.Abgeladen)
            return TransportStatus.Beladen;
    }

    if ((entladeStop.istAbgefahren && !entladeStopIsHub)
        || (entladeStopIsHub && verladungAuftrag?.status == VerladeStatus.VollstaendigVerladen))
        return TransportStatus.Abgeladen;

    if ((ladeStop.istAbgefahren && !ladeStopIsHub)
        || (ladeStopIsHub && verladungAuftrag?.status == VerladeStatus.VollstaendigVerladen))
        return TransportStatus.Beladen;
    ...
public TransportStatus CalcStatus()
{
    var isAbholung = transportArt is AuftragTransportArt.Abholung or AuftragTransportArt.Kommissionierung;
    var isZustellung = transportArt is AuftragTransportArt.Zustellung;
    var wirdNichtUmgeladen = auftrag.zustellungArt == AuftragZustellungArt.WirdNichtUmgeladen;

    var abholungIstAngekommen = isAbholung && entladeStop.istAngekommen;
    var abholungIstAbgeladen = isAbholung && verladungAuftrag?.status == VerladeStatus.VollstaendigVerladen;
    var beiEmpfaengerAngekommen = !isAbholung && entladeStop.istAbgefahren;

    if (beiEmpfaengerAngekommen || abholungIstAbgeladen || (wirdNichtUmgeladen && abholungIstAngekommen))
        return TransportStatus.Abgeladen;

    var beiAbsenderLosgefahren = !isZustellung && ladeStop.istAbgefahren;
    var zustellungIstBeladen = isZustellung && verladungAuftrag?.status == VerladeStatus.VollstaendigVerladen;
    var vorgaengerIstAngekommen = isZustellung && GetPreviousTransport()?.status == TransportStatus.Abgeladen;

    if (beiAbsenderLosgefahren || zustellungIstBeladen || (wirdNichtUmgeladen && vorgaengerIstAngekommen))
        return TransportStatus.Beladen;

                                                                                                                
    ...
...
if (tour.status == command.status)
{
    tour.SetFahrzeugOrFahrzeugName(newFahrzeug, command.fahrzeugName);
    tour.SetFahrerOrFahrerName(newFahrer, "");
}
else if (tour.status == TourStatus.Verladen)
{
    throw new HttpResponseException("Tourstatus ändern", "Die Tour wurde bereits verladen.");
}
else
{
    switch (command.status)
    {
        case TourStatus.Erstellt:
            tour.SetErstellt();
            tour.SetFahrzeugOrFahrzeugName(newFahrzeug, command.fahrzeugName);
            tour.SetFahrerOrFahrerName(newFahrer, "");
            break;
        case TourStatus.FertigGeplant:
            tour.SetFahrzeugOrFahrzeugName(newFahrzeug, command.fahrzeugName);
            tour.SetFahrerOrFahrerName(newFahrer, "");
            tour.SetFertigGeplant();
            break;
        case TourStatus.Verladen:
            throw new InvalidOperationException("Tour kann nicht direkt auf Verladen gesetzt werden");
    }
}
...
...
var changeStatus = tour.status != command.status;
if (changeStatus && command.status == TourStatus.Verladen)
    throw new HttpResponseException("Tourstatus ändern", "Die Tour wurde bereits verladen.");
if (changeStatus && tour.status == TourStatus.Verladen)
    throw new HttpResponseException("Tourstatus ändern", "Die Tour kann nicht als verladen markiert werden.");
    
if (changeStatus && command.status == TourStatus.Erstellt)
    tour.SetErstellt();
    
tour.SetFahrzeugOrFahrzeugName(newFahrzeug, command.fahrzeugName);
tour.SetFahrerOrFahrerName(newFahrer, "");

if (changeStatus && command.status == TourStatus.FertigGeplant)
    tour.SetFertigGeplant();
...













...
switch (tour.status, command.status)
{
    case (TourStatus.Erstellt, TourStatus.Erstellt):
    case (TourStatus.FertigGeplant, TourStatus.FertigGeplant):
    case (TourStatus.Verladen, TourStatus.Verladen):
        tour.SetFahrzeugOrFahrzeugName(newFahrzeug, command.fahrzeugName);
        tour.SetFahrerOrFahrerName(newFahrer, "");
        break;
    case (TourStatus.Erstellt, TourStatus.FertigGeplant):
        tour.SetFahrzeugOrFahrzeugName(newFahrzeug, command.fahrzeugName);
        tour.SetFahrerOrFahrerName(newFahrer, "");
        tour.SetFertigGeplant();
        break;
    case (TourStatus.FertigGeplant, TourStatus.Erstellt):
        tour.SetErstellt();
        tour.SetFahrzeugOrFahrzeugName(newFahrzeug, command.fahrzeugName);
        tour.SetFahrerOrFahrerName(newFahrer, "");
        break;
    case (TourStatus.Verladen, _):
        throw new HttpResponseException("Tourstatus ändern", "Die Tour wurde bereits verladen.");
    case (_ ,TourStatus.Verladen):
        throw new HttpResponseException("Tourstatus ändern", "Die Tour kann nicht als verladen markiert werden.");
}
...




Simple Infrastruktur

  • Keine "Magie" (Accidental Complexity)
  • Muss nicht für jeden Sonderfall erweitert werden
  • Integriert gut in Sprache und Framework

Programmier-Paradigmen

  • Style kommt auf Kontext an
  • Tests enthalten Abweichungen vom Standardfall
  • Commands haben oft prozedurale Abläufe
  • Entitäten und Value Objects sind Objekt-Orientiert

Essential complexity: unvermeidbar, kommt durch Problemstellung Domain Logik, UX Accidental complexity: vermeidbar, kommt durch schlechte Entscheidungen Premature Optimization, Legacy Code

Beispiel: EF Core