Sie nannten mich den Sorgenfresser
In jedem Team gibt es Aufgaben, die niemand haben will. Bugs, die seit Wochen offen sind. Performance-Probleme, bei denen unklar ist, wo man überhaupt anfangen soll. Code, den niemand anfassen möchte, weil er „historisch gewachsen” ist.
Bei diesem Projekt war ich derjenige, der sich diese Fälle schnappte.
Nicht weil ich masochistisch veranlagt bin – sondern weil ich gemerkt habe, dass genau dort der größte Hebel liegt. Ein gelöster Blocker macht das ganze Team schneller. Eine optimierte Query spart jeden Tag Minuten. Ein refactorter Workflow verhindert die nächsten zehn Bugs.
Die Kollegen gaben mir dafür einen Spitznamen: Sorgenfresser.
Das System, das ich vorfand
Die Plattform ist eine Enterprise-Lösung für die Abrechnungsprüfung im deutschen Gesundheitswesen. Krankenkassen schicken verschlüsselte Dateien mit Abrechnungsdaten, das System prüft sie gegen ein Data Warehouse mit Milliarden von Einträgen, und schickt Entscheidungen zurück – signiert, verschlüsselt, revisionssicher.
Als ich zum Team stieß, war die Plattform kein Neuling. Das System hatte bereits Jahre auf dem Buckel: ursprünglich entwickelt, dann pausiert, dann von einem neuen Team wieder aufgegriffen. Der Code funktionierte – meistens. Aber unter der Oberfläche lauerten Altlasten.
Was ich vorfand:
- Import-Prozesse, die manchmal Minuten dauerten – manchmal Stunden
- Workflows mit dutzenden Zuständen, verteilt über Callbacks und Service Objects
- Eine Dual-Database-Architektur (App-DB + DWH, beide MS SQL), die niemand so richtig durchblickte
- Edge Cases in Produktionsdaten, die im Test nie auftraten
Das Team brauchte jemanden, der sich in diesen Dschungel wagte.
Sorge #1: Die Milliarden-Tabelle
Das Data Warehouse läuft auf Microsoft SQL Server. Eine zentrale Tabelle hat mehrere Milliarden Zeilen. Bei jedem Import müssen wir Daten daraus abfragen – Ärzte, Krankenhäuser, Versicherungen.
Meine erste Annahme: „SQL ist SQL. Was bei PostgreSQL funktioniert, funktioniert auch hier.”
Falsch.
Die Queries liefen. Aber sie liefen minutenlang. Bei einem System, das mehrmals täglich importiert, ist das nicht akzeptabel.
Ich grub mich in die Execution Plans. Analysierte, wo die Zeit verloren ging. Testete Hypothesen.
Was ich fand:
Der SQL Server Query Optimizer trifft bei Milliarden Rows manchmal falsche Annahmen. Er wählt den falschen Index. Er macht Table Scans, wo Seeks möglich wären. Er lockt Ressourcen, die niemand braucht.
Was ich tat:
| Problem | Lösung |
|---|---|
| Table Lookups bei jeder Query | Covering Indexes: Alle SELECT-Spalten im Index |
| Optimizer wählt falschen Plan | Index Hints: WITH (FORCESEEK) erzwingen |
| Locks bei Read-Only Queries | NOLOCK: Dirty Reads für DWH akzeptabel |
Das Ergebnis: Queries, die Minuten dauerten, liefen in unter einer Sekunde.
Nicht durch mehr Hardware. Nicht durch Caching. Durch Verstehen, was die Datenbank tatsächlich tut.
Sorge #2: Der Workflow-Dschungel
Die Plattform verarbeitet Abrechnungsfälle. Jeder Fall durchläuft dutzende Zustände: Importiert, In Prüfung, Anhörung, Entscheidung, Exportiert. Mit Sonderfällen. Mit automatischen Übergängen. Mit Berechtigungen, die von LDAP-Gruppen, Fallarten und Schreibrechten abhängen.
Der bestehende Code war über Jahre gewachsen. Business-Logik war verteilt über:
- Model Callbacks
- Service Objects
- Controller-Actions
- Background Jobs
Wenn etwas schiefging, war die Fehlersuche ein Ratespiel. Welcher Callback hat den State geändert? Welcher Job wurde getriggert? Warum ist dieser Fall in einem unmöglichen Zustand?
Mein Ansatz: Trailblazer Operations.
Statt Logik zu verstreuen, definiert man jeden Workflow als eine explizite Sequenz von Steps. Jeder Step macht genau eine Sache. Fehler werden explizit behandelt, nicht still verschluckt. Der Flow ist im Code lesbar – nicht in fünf Dateien versteckt.
Das Ergebnis:
- Neue Workflows waren in Stunden implementiert, nicht Tagen
- Fehler waren reproduzierbar und debuggbar
- Das Team konnte Code reviewen, ohne Archäologie zu betreiben
Sorge #3: Die Geister-Bugs
Manche Bugs existieren nur in Production. Im Test? Alles grün. Lokal? Funktioniert einwandfrei. Aber irgendwo in den echten Daten, in den echten Abläufen, passiert etwas Seltsames.
Bei diesem Projekt waren das oft Import-Probleme. Eine CSV-Datei, die „eigentlich” valide ist – aber einen Edge Case enthält, den niemand vorhergesehen hat. Ein Encoding-Problem. Ein Feld, das normalerweise gefüllt ist, aber bei diesem einen Krankenhaus leer.
Mein Ansatz: Nicht raten, sondern schauen.
Statt tagelang Tickets hin- und herzuschreiben, habe ich mir die Ansprechpartner beim Kunden in einen Zoom-Call geholt. Fünf Minuten Screen-Sharing zeigen mehr als zehn Ticket-Kommentare.
Was ich dabei gelernt habe:
- Die meisten „unerklärlichen” Bugs haben eine simple Erklärung
- Sie ist nur nicht in den Logs – sie ist im Kopf der Person, die den Fehler gesehen hat
- Direkte Kommunikation ist kein Luxus, sondern der schnellste Debugging-Pfad
Was ich hinterlassen habe
Nach 10 Monaten im Team:
Performance: Queries von Minuten auf unter 1 Sekunde. Import/Export-Prozesse, die jetzt zuverlässig laufen.
Code-Qualität: Trailblazer Operations für die komplexesten Workflows. Legacy-Code, der jetzt testbar ist. Hunderte RSpec Specs als Sicherheitsnetz – strikt nach TDD entwickelt.
Wissen: SQL-Optimierung, die im Team bleibt. Dokumentation für die nächsten Entwickler. Patterns, die wiederverwendbar sind.
Kommunikation: Direkter Draht zum Kunden. Kurze Wege statt lange Tickets.
Was ich vom Team gelernt habe
Ein Projekt wie dieses baut man nicht alleine. Was ich mitgenommen habe:
Trunk-Based Development funktioniert. Keine Feature-Branches, die wochenlang vor sich hin rotten. Kleine Änderungen, schnell integriert, schnell deployed. Weniger Merge-Konflikte, mehr Fokus auf das Wesentliche.
Healthcare-Compliance ist kein Hindernis, sondern ein Feature. Die strengen Anforderungen – PKCS#7-Verschlüsselung, LDAP-Auth, Audit-Trails – zwingen zu sauberem Code. Kein „das machen wir später”. Später ist jetzt.
Die besten Reviews kommen von Leuten, die den Code benutzen. Nicht von denen, die ihn geschrieben haben. Die Fachabteilung hat Bugs gefunden, die kein Entwickler je gesehen hätte.
Tech Stack
- Backend: Ruby on Rails 8, Ruby 3.3
- Datenbanken: MS SQL Server (App + DWH)
- Auth: Devise + LDAP, CanCanCan
- Jobs: Sidekiq mit Cron
- Architecture: Trailblazer, AASM State Machines
- Security: PKCS#7, SFTP
- Frontend: Hotwire, ViewComponent
- Infra: Docker, 3-Node Cluster, GitLab CI/CD
Fazit
Dieses Projekt ist kein Greenfield-Projekt. Es ist ein System mit Geschichte, mit Altlasten, mit echten Anforderungen aus einer regulierten Branche. Genau das macht es interessant.
Die wichtigste Erkenntnis: Die schwierigsten Probleme sind selten die offensichtlichen. Sie verstecken sich in Execution Plans, in verschachtelten Callbacks, in Edge Cases, die nur in Production auftreten. Man findet sie nicht durch Raten – sondern durch Hinschauen.
Die zweitwichtigste: Ein guter Entwickler löst Probleme. Ein besserer verhindert, dass sie andere aufhalten. Deshalb der Sorgenfresser.