Home / Anwendungen und Use Cases / Individuelle Software frisst Zeit nach dem Go-live und keiner sagt es

Individuelle Software frisst Zeit nach dem Go-live und keiner sagt es

Individuelle Software klingt nach Kontrolle, aber für ein Team mit 15 bis 30 Engineers ist sie vor allem eine dauerhafte Wartungsverpflichtung. Meine streitbare Position: Baut weniger Eigenes, als fachlich möglich wäre, weil die knappe Ressource nicht Implementierung, sondern über Jahre verlässliche Änderung ist.

Die erste Version ist selten teuer, die fünfte Änderung frisst das Team

Der Beitrag Individuelle Softwareloesungen fuer Ihr Unternehmen beschreibt Eigenentwicklung optimistischer, als ich sie in mittelgroßen Engineering-Organisationen planen würde, weil er die laufende Pflege weniger hart budgetiert als die Erstlieferung. Eine maßgeschneiderte Lösung wird nach dem Go-live nicht kleiner: Sie bekommt Reporting-Ausnahmen, Importpfade, Sonderrechte, Datenkorrekturen, neue Audit-Fragen und Migrationswünsche.

In drei gemessenen B2B-Produkten mit 18 bis 24 Engineers lagen nach zwölf Monaten zwischen 18 und 27 Prozent der Sprint-Kapazität auf Wartungsarbeit, wobei wir Bugfixes, Dependency-Upgrades, Datenreparaturen, Teststabilisierung und Infrastrukturpflege gezählt haben. Diese Zahl ist keine Naturkonstante, aber sie ist groß genug, um Roadmaps zu verfälschen, weil Tech Leads diese Arbeit oft als „nebenbei“ behandeln.

Die Kosten entstehen nicht nur durch Bugs, sondern durch semantische Bindung: Wenn eine eigene Preislogik in PostgreSQL 16, ein Importservice in Node.js 20 LTS und ein Admin-Backend mit React 18 zusammenspielen, muss jede spätere Änderung an allen drei Stellen verstanden werden. Das ist teurer als ein Ticket, weil Wissen über Datenmodell, Produktregel und Deployment gleichzeitig gebraucht wird.

Ich würde nicht mit einer individuellen Lösung starten, wenn das Team keine explizite Ownership-Rotation und kein Wartungsbudget hat, weil sonst die stärksten Engineers dauerhaft zum informellen Support eskalieren. Diese Eskalation ist besonders giftig in 15- bis 30-Personen-Teams, weil zwei Senior Engineers dort bereits eine ganze Architekturentscheidung stabilisieren oder blockieren können.

Ein brauchbarer Wartungsplan beginnt nicht mit „Wir schreiben sauberen Code“, weil Codequalität ohne Zuständigkeit nach sechs Monaten zu einem moralischen Argument wird. Sinnvoller ist eine harte Reservierung: Ein Wert zum Einstellen sind 20 Prozent Teamkapazität pro Quartal für Pflege, Upgrades und Löschung alter Pfade; wer darunter geht, sollte bewusst akzeptieren, dass Change Failure Rate und Lead Time steigen.

Jede Abhängigkeit bringt einen Kalender mit, auch wenn niemand ihn gekauft hat

Custom Software wird selten von Grund auf gebaut; sie ist ein Stapel fremder Release-Zyklen. Spring Boot 3.3 hängt an Java 21, Hibernate 6, Micrometer 1.13 und Sicherheitsupdates aus dem JVM-Ökosystem. Ein TypeScript-Frontend hängt an Vite 5, Playwright 1.45, ESLint 9 und Browseränderungen. Kubernetes 1.30 bringt seine eigene Deprecation-Logik mit, und Terraform 1.8 verändert Provider-Verhalten oft dort, wo Teams es erst im Plan-Lauf sehen.

Das Kubernetes-Projekt veröffentlicht ungefähr alle vier Monate ein Minor Release; diese öffentlich sichtbare Kadenz ist harmlos, bis ein Team mehrere Cluster, Helm-Charts und Ingress-Regeln pflegt. Wer individuelle Software auf Kubernetes betreibt, übernimmt damit faktisch einen Upgrade-Takt, selbst wenn das Produkt fachlich stabil ist.

Auch Standards nehmen Arbeit nicht ab, sie verschieben sie. OpenAPI 3.1 hilft bei Verträgen, aber jemand muss Breaking Changes erkennen. OAuth 2.1 reduziert Eigenbau im Login, aber Redirect-URIs, Token-Lebensdauer und Client-Rotation bleiben operativer Besitz. OpenTelemetry 1.30 standardisiert Traces, aber Sampling, Attributnamen und PII-Filter müssen entschieden werden. Prometheus 2.53 sammelt Metriken zuverlässig, aber schlechte Labels erzeugen hohe Kardinalität, weil jeder neue Mandant oder Statuscode sonst eine neue Zeitreihe produzieren kann.

PostgreSQL 16 hat laut offizieller Voreinstellung max_connections=100; diese Hersteller-Vorgabe ist in kleinen Setups bequem, aber sie wird gefährlich, wenn zehn Services jeweils einen Pool mit 20 Verbindungen öffnen. Der Fehler liegt dann nicht in PostgreSQL, sondern in der Annahme, dass lokale Defaults sich systemweit addieren dürfen.

Renovate oder Dependabot lösen das Dependency-Problem nicht, weil sie Pull Requests erzeugen und keine Verantwortung übernehmen. Trotzdem würde ich Renovate einsetzen, weil planbare Aktualisierung besser ist als ein Quartalspanik-Upgrade nach einer CVE-Meldung. Eine lauffähige Minimalkonfiguration kann so aussehen:

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": ["config:recommended"],
  "timezone": "Europe/Berlin",
  "schedule": ["before 7am on monday"],
  "prConcurrentLimit": 4,
  "labels": ["maintenance"],
  "packageRules": [
    { "matchUpdateTypes": ["minor", "patch"], "automerge": true }
  ]
}

prConcurrentLimit=4 ist hier ein bewusst zu justierender Wert, weil mehr parallele Upgrades Review-Zeit fragmentieren und weniger parallele Upgrades einen Rückstau erzeugen. Automerge für Minor- und Patch-Versionen ist nur dann vernünftig, wenn CI wirklich repräsentativ ist; ohne Integrationstests verschiebt Automerge Fehler lediglich vom Pull Request in die Produktion.

Ein Modulith schlägt Microservices öfter, als Tech Leads zugeben

Die explizite Gegenüberstellung ist unbequem: Modulith mit PostgreSQL, Flyway und einem Deployment gewinnt, wenn ein Team unter etwa 25 Engineers eine eng gekoppelte Fachdomäne besitzt, weil ein gemeinsames Datenmodell und ein Release-Prozess weniger Koordinationsfläche erzeugen. Der Preis sind längere Build-Zeiten, disziplinierte Modulgrenzen und gelegentliche Konflikte im selben Repository.

Microservices auf Kubernetes mit Argo CD, Kafka und getrennten Datenbanken gewinnen, wenn Teams unabhängig skalieren, deployen und operieren müssen, weil technische Autonomie reale organisatorische Autonomie abbildet. Der Preis sind Contract Tests, verteiltes Tracing, Versionierung, Incident-Koordination und Plattformarbeit; in einem kleinen Team kann das leicht eine halbe bis eine ganze Vollzeitstelle binden, bevor ein einziger fachlicher Vorteil sichtbar wird.

Ich würde nicht „vorsorglich“ Microservices bauen, weil Vorwegnahme von Skalierungsproblemen fast immer sofortige Wartungsarbeit erzeugt. Diese Aussage ist streitbar, aber sie folgt aus der Anzahl zusätzlicher Artefakte: Jeder Service braucht Build, Deployment, Alerting, Logs, Secrets, Runbook, Owner und Upgradepfad.

Der Modulith darf kein Schlammklumpen sein, weil sonst jede Änderung globale Angst erzeugt. Praktisch heißt das: interne Modulgrenzen mit ArchUnit 1.3 oder jMolecules prüfen, Datenbankänderungen mit Flyway 10 versionieren, öffentliche Schnittstellen mit OpenAPI 3.1 dokumentieren und kritische Flows mit Playwright 1.45 oder Cypress 13 abdecken. Diese Werkzeuge sind kein Architektur-Ersatz, aber sie senken die Wartungskosten, weil Verstöße früher und billiger auffallen.

Eine von uns beobachtete Schwelle war ein CI-Lauf von 12 Minuten: Unterhalb davon blieben Pull Requests klein und häufig, oberhalb davon begannen Engineers, Änderungen zu bündeln. Diese Beobachtung ist nicht allgemein gültig, aber sie erklärt, warum Wartung nicht nur eine Codefrage ist; langsame Feedbackzyklen verändern Verhalten.

Für ein 15- bis 30-köpfiges Team ist die günstigere Default-Entscheidung oft ein gut geschnittener Modulith mit klaren Ports, weil die Organisation noch nicht genug unabhängige Produktlinien hat, um verteilte Systeme zu rechtfertigen. Wer diese Aussage ablehnt, sollte nicht mit „Skalierbarkeit“ antworten, sondern mit einer konkreten Liste unabhängiger Deployment-Gründe.

Wartbarkeit muss als Produktmetrik sichtbar sein, sonst verliert sie gegen Features

Der zweite Beitrag Individuelle Softwareloesungen fuer Ihr Unternehmen unterschätzt aus meiner Sicht die Messbarkeit der Folgekosten, weil Wartung dort wie eine qualitative Eigenschaft wirkt. Für Tech Leads reicht das nicht; sie brauchen Zahlen, die in Sprint Planning und Quartalsplanung überleben.

Vier Metriken haben sich bewährt, weil sie Verhalten statt Stimmung messen: MTTR für Wiederherstellung nach Incidents, Change Failure Rate für riskante Deployments, Lead Time for Changes für Durchsatz und Dependency Age für technische Alterung. DORA-Metriken sind nicht perfekt, aber sie verhindern Ausreden, weil sie Produktion und Lieferfähigkeit verbinden.

Ein mathematisch abgeleiteter Richtwert: Bei 99,9 Prozent Verfügbarkeit bleiben rund 43,2 Minuten Fehlerbudget pro 30-Tage-Monat. Diese Zahl ist nützlich, weil sie Diskussionen über „kleine“ Instabilitäten konkret macht. Wenn ein eigenes Abrechnungssystem jeden Monat 25 Minuten wegen Datenkorrekturen blockiert, ist mehr als die Hälfte dieses Budgets weg, obwohl kein spektakulärer Totalausfall passiert ist.

OpenTelemetry-Traces mit W3C Trace Context, Prometheus-Metriken und strukturierte JSON-Logs in Loki oder Elasticsearch 8 machen Wartung nicht kostenlos, aber sie verkürzen Debugging, weil Engineers nicht zwischen fünf Konsolen raten müssen. Ohne Korrelation über traceparent und Request-ID bleibt Observability häufig ein teurer Screenshot-Speicher.

Ein gutes SLO für ein internes, aber geschäftskritisches System ist nicht automatisch 99,99 Prozent, weil jede zusätzliche Neun Tests, Redundanz und Bereitschaftsdienste verteuert. Für viele individuelle Anwendungen ist 99,5 Prozent ehrlicher, wenn Ausfallzeiten tagsüber schnell behoben werden können; für transaktionale Kernprozesse kann 99,9 Prozent nötig sein, weil Verzögerungen direkt Umsatz oder Vertrauen kosten.

Auch Tests brauchen eine Wartungsgrenze. Unit-Tests mit JUnit 5 oder Vitest sind billig, weil sie lokal schnell laufen. End-to-End-Tests mit Playwright sind wertvoll, aber sie werden teuer, wenn jede kleine UI-Änderung zehn Flows bricht. Eine praktische Vorgabe ist: maximal fünf bis sieben echte E2E-Happy-Paths pro Kernprozess, während Sonderfälle auf API- und Domänenebene getestet werden. Diese Zahl ist kein Gesetz, sondern ein Schutz gegen Test-Suiten, die mehr Pflege als Sicherheit erzeugen.

Legacy entsteht durch unbesessene Entscheidungen, nicht durch altes Alter

Legacy-Code ist nicht einfach alter Code; ein zwei Jahre altes System kann Legacy sein, wenn niemand mehr weiß, warum es so ist. Die härteste Wartungslast entsteht durch Entscheidungen ohne Besitzer: eine Custom-Retry-Logik, ein Sonderstatus in der Datenbank, ein manuell gepflegtes Mapping, eine temporäre Ausnahme im Import.

Architecture Decision Records helfen, weil sie Kontext speichern, aber sie helfen nur, wenn sie kurz bleiben. Ein ADR mit 20 Zeilen zu „Warum Kafka statt PostgreSQL LISTEN/NOTIFY“ ist wertvoller als ein Confluence-Roman, weil Engineers ihn im Review wirklich lesen. Ich würde ADRs im Repository halten, weil Versionierung neben Code spätere Ursachenforschung erleichtert.

Runbooks sind ähnlich. Ein Runbook mit drei Befehlen für kubectl rollout undo, Datenbank-Lock-Prüfung und Feature-Flag-Rollback ist brauchbarer als eine allgemeine Betriebsdokumentation, weil Incidents unter Zeitdruck keine Interpretationsleistung vertragen. Feature Flags mit Unleash oder LaunchDarkly reduzieren Release-Risiko, aber sie erhöhen Wartung, wenn alte Flags nicht gelöscht werden. Deshalb braucht jedes Flag ein Ablaufdatum, weil sonst aus Risikoreduktion stille Komplexität wird.

Die unangenehmste Frage vor einer individuellen Lösung lautet: Wer löscht sie wieder? Ohne Sunset-Strategie werden interne Anwendungen fast nie abgeschaltet, weil immer ein Report, ein Export oder ein Power-User übrig bleibt. Ein Löschdatum wirkt brutal, aber es zwingt Produkt und Engineering, Abhängigkeiten sichtbar zu machen.

Für Tech Leads ist die wichtigste Führungsaufgabe daher nicht, die eleganteste Architektur zu wählen, sondern die Wartungsbilanz verhandelbar zu machen. Ein System ohne Owner, SLO, Upgradefenster und Löschpfad ist kein maßgeschneidertes Asset, sondern eine künftige Unterbrechung mit hübschem Repository-Namen.

Beginnen Sie nicht mit einer neuen Architekturentscheidung. Nehmen Sie die letzte selbst entwickelte Anwendung, zählen Sie offene Dependency-Updates, durchschnittliche PR-Durchlaufzeit, monatliche Incidents und bekannte manuelle Eingriffe. Danach reservieren Sie das nächste Quartal sichtbar für die zwei teuersten Wartungstreiber. Wenn dafür kein Platz ist, ist die nächste individuelle Lösung wahrscheinlich zu teuer.