~/goldbarth / decisions $ ls

decisions

Begründungen hinter technischen Entscheidungen — hauptsächlich damit ich sie später nicht neu aufrollen muss. Was funktioniert hat, was nicht, und unter welchen Annahmen.

KI-gestützte Entwicklung: gemessene Aufwandsrechnung

MetricGate wurde solo, nebenbei und mit Claude Code als primärer Implementierungshilfe gebaut. Warum ich Kalenderschätzungen aus den Planungsdocs gestrichen habe, wie ich den Aufwand über Commit-Zeitstempel messe — und was die Geschwindigkeit kostet.

Redis Lua & atomare Ops

Warum MetricGate Token-Bucket-Updates als Lua-Script ausführt statt als naiven GET-SET-Zyklus — was ohne Lua schiefgeht, wie atomare Ausführung das löst und was Lua kostet.

Integrationstests mit Testcontainers

Wie MetricGate PostgreSQL, Redpanda und EF-Migrationen in einem Testlauf koordiniert — und welche Domain-Regeln Unit Tests schlicht nicht abfangen können.

Was ich baue

Die Entscheidung, nachdem es ein paar Tage gesackt ist.

Architecture, vom Compiler durchgesetzt

Warum ServiceDeskLite Clean Architecture Dependency-Regeln durch Project-References statt Naming Conventions durchsetzt — und was das kostet.

Minimal API ohne MediatR

Warum ServiceDeskLite Handler direkt in Endpoints injiziert statt über einen Mediator zu dispatchen — und wann diese Entscheidung überdacht werden sollte.

Result Pattern an der Application Boundary

Wie ServiceDeskLite einen expliziten Result-Typ verwendet, um Handler-Ergebnisse sichtbar zu machen — und wo DomainExceptions noch hingehören.

RFC 9457 als einheitlicher Error-Contract

Warum ServiceDeskLite ProblemDetails mit benutzerdefinierten Extension-Feldern als einziges Error-Format verwendet — und wie der Contract geteilt wird, ohne das Web an die API zu koppeln.

Stark typisierte Domain-IDs

Warum ServiceDeskLite Guid in einem TicketId Record Struct kapselt — was der Compiler abfängt, was der Mapping-Overhead kostet und wie UUIDv7 es verbessert.

Swappable Persistence als Port-Beweis

Warum ServiceDeskLite zwei vollständige Persistence-Implementierungen mitbringt — und warum der eingebaute InMemory Provider von EF Core keine davon ist.

Chunk-basierte Batch-Verarbeitung

Warum Ingestor große Import-Dateien in 500-Zeilen-Chunks verarbeitet statt auf einmal — begrenzter Blast Radius, Partial-Success-Semantik und die Trade-offs, die damit kamen.

Idempotency Key Strategy

Deterministische Idempotency Keys für eine Import-Pipeline entwerfen — warum SHA256 über File-Content, was der Unique Index durchsetzt und wie HTTP-Clients davon profitieren.

Outbox vs. Message Broker

Warum Ingestor mit einem database-backed Outbox statt RabbitMQ startet — und was das umschaltbare Dispatcher-Pattern später ermöglicht.

Result Pattern statt Exceptions

Warum Ingestor einen Result<T>-Typ statt Exceptions an Application-Boundaries verwendet — was er explizit macht, was er kostet und wo Exceptions noch hingehören.

State Machine mit expliziten Transitions

Wie eine explizite Domain State Machine mit neun States und einer aufgezählten Transition-Tabelle stille State-Korruption in einer lang laufenden Pipeline verhindert.