Skip to main content

Autor: Johannes Schneider

Caches sind keine Lösung

Caches lösen keine Probleme. Sie verstecken sie.

Nahaufnahme eines Server-Racks mit blauer Beleuchtung


Der Neustart, der alles offenlegt

Services neu deployt, Pods hochgefahren, alles grün. Und dann: Timeouts. Die ersten Requests laufen ins Leere, weil irgendein „3rd Level Cache“ noch kalt ist. Das Team wartet. Die Nutzer warten. Irgendwann sind die Caches warm, alles läuft wieder. Bis zum nächsten Neustart.

Das ist kein Caching-Problem. Das ist ein Architektur-Problem, das sich als Caching-Lösung verkleidet hat.

Die Verwechslung

Caching ist eine Optimierung. Eine gute, oft sinnvolle Optimierung. Aber irgendwann hat sich in vielen Teams ein Denkfehler eingeschlichen: Die Query ist zu langsam? Cache drauf. Der Service antwortet nicht schnell genug? Cache davor. Das Datenmodell gibt die Antwortzeit nicht her? Cache drumherum.

Das funktioniert — solange der Cache da ist. Aber ein Cache, den du nicht ausschalten kannst, ohne dass die App zusammenbricht, ist keine Optimierung mehr. Er ist eine Krücke.

Der Unterschied ist fundamental:

  • Optimierung: Die App funktioniert ohne Cache. Mit Cache ist sie schneller. Gut.
  • Krücke: Die App funktioniert ohne Cache nicht. Der Cache kompensiert ein Problem, das niemand gelöst hat.

Der Lackmustest

Eine einfache Frage reicht: Funktioniert die App ohne Cache?

Nicht „ist sie schnell genug für Production“. Sondern: Funktioniert sie überhaupt? Antwortet sie, ohne in Timeouts zu laufen? Kann ein Nutzer seine Aufgabe erledigen — vielleicht langsamer, aber er kann?

Wenn nein, dann hast du kein Cache-Problem. Du hast ein Performance-Problem. Der Cache versteckt es nur.

Und versteckte Probleme sind die teuersten. Sie melden sich nicht im Monitoring. Im Sprint-Planning tauchen sie nie auf. Stattdessen wachsen sie still, bis sie bei einem Neustart, einem Failover oder einer Lastspitze plötzlich sichtbar werden — zum ungünstigsten Zeitpunkt.

Was stattdessen

Die Reihenfolge ist einfach:

  1. Messen. Wo ist die App ohne Cache tatsächlich langsam? Nicht raten — messen.
  2. Ursache beheben. Fehlende Indices. N+1-Queries. Ein Datenmodell, das für den Lesezugriff nicht gemacht ist. Overfetching. Synchrone Aufrufe, die asynchron sein könnten.
  3. Dann — optional — cachen. Wenn die App ohne Cache funktioniert und der Cache sie noch schneller macht: perfekt. Das ist eine Optimierung. Das ist, wofür Caches da sind.

Der entscheidende Punkt: Schritt 3 ist optional. Wenn du nach Schritt 2 feststellst, dass die Performance reicht — brauchst du keinen Cache. Weniger bewegliche Teile, weniger Invalidierungs-Logik, weniger Fehlerquellen. Weniger ist oft besser.

Fazit

Caches sind großartig — als Optimierung. Als Sahnehäubchen auf einer App, die auch ohne sie funktioniert. Aber als Fundament? Als Voraussetzung dafür, dass der Service überhaupt antwortet?

Dann ist der Cache nicht die Lösung. Dann ist er der Grund, warum du die eigentliche Lösung nie gesucht hast.

Shadow Tech Debt — die unsichtbare Schuld der KI-Agenten

Ein Name für das, was wir schon länger ahnen

JetBrains hat im März 2026 endlich einen Namen für ein Problem gefunden, das uns seit Monaten beschäftigt: Shadow Tech Debt. Nik Tkachev, Head of Product bei JetBrains, meint damit Code, den KI-Agenten generieren — der für sich genommen funktioniert, aber die Architektur des Gesamtprojekts schleichend zerlegt.

Wir mussten beim Lesen ein bisschen nicken. Wir setzen KI-Agenten seit neun Monaten produktiv ein, in einem Kotlin/TypeScript-Monorepo mit über 600 Modulen. Das Problem kennen wir. Sehr gut sogar.

Was ist Shadow Tech Debt eigentlich?

Normale technische Schulden kennt jeder: Man nimmt eine Abkürzung, weil der Termin drückt. Schiebt ein Refactoring auf. Weiß, dass da was im Argen liegt — hat es ja selbst verbockt. Immerhin kann man es priorisieren und irgendwann(tm) aufräumen.

Shadow Tech Debt ist dagegen fieser. Denn sie ist unsichtbar. Der Code kompiliert, die Tests sind grün, das Review geht durch. Alles sieht gut aus. Aber drei Monate später merkst du, dass die Codebase sich anders anfühlt. Irgendwie inkonsistent. Und dann schaust du genauer hin:

  • Die Architektur trennt sauber zwischen Query, Transformation und Command — Daten laden, transformieren, Ergebnis persistieren. Keine Seiteneffekte in der Transformation, gut testbar, leicht nachvollziehbar. Der Agent hat das nicht verstanden und Seiteneffekte quer durch die Berechnung gestreut. Funktioniert, aber die Testbarkeit ist im Eimer.
  • Das Projekt arbeitet konsequent mit immutablen Datenstrukturen. Neue Version? Neues Objekt. Der Agent hat stattdessen mutable State eingeführt — var statt val, Listen die in-place modifiziert werden. Funktioniert auch. Bis zum ersten Concurrency-Bug.
  • Typsichere IDs via Value Classes sind Standard im Projekt. Der Agent hat String genommen. Kompiliert, Tests grün — aber jetzt kann man versehentlich eine Kunden-ID in ein Projekt-Feld stecken, und der Compiler sagt keinen Ton.
  • Die Business-Logik gehört in *Support-Klassen, HTTP-Routing in *Routing-Klassen. Der Agent hat die Geschäftslogik direkt in die Route geschrieben. Geht, klar. Aber jetzt ist die Logik nicht mehr unabhängig testbar.

Jedes einzelne Puzzleteil passt. Aber das Gesamtbild? Wird langsam matschig.

Warum entsteht Shadow Tech Debt?

Der Agent hat keine Ahnung von eurem Projekt

Ein KI-Agent sieht Dateien, Imports, Funktionssignaturen. Was er nicht sieht: Warum eine bestimmte Struktur gewählt wurde. Weshalb Klassen bei uns bestimmte Suffixe tragen (*Support für Business-Logik, *Access für Datenzugriff, *Routing für HTTP). Aus welchem Grund IDs als Value Classes modelliert sind und nicht als Strings. Oder warum wir not() statt ! schreiben.

All diese Sachen stehen halt nirgends in einer README. Stattdessen leben sie in den Köpfen des Teams — als Tribal Knowledge, in alten Code-Review-Kommentaren, in Gesprächen am Whiteboard. Der Agent hat das alles nicht. Also schreibt er Code, der technisch korrekt, aber irgendwie fremd ist. Wie ein Gastmusiker, der die Noten trifft, aber den Sound der Band nicht kapiert.

Jede Session startet bei null

Jede Interaktion mit dem Agenten beginnt ohne Kontext. Kein Gedächtnis, keine Projektgeschichte, keine Vorstellung davon, wo die Codebase hin soll. Er optimiert für jetzt — nicht für die Codebase in sechs Monaten.

Das skaliert nicht

Bei einem einzelnen Script — geschenkt. Sobald Agenten jedoch ganze Features bauen, Pull Requests erstellen und in CI/CD-Pipelines direkt committen? Dann multipliziert sich das Problem. Tkachev sagt es ziemlich direkt: „Let’s be honest: Complex codebases aren’t yet ready for pure agentic coding.“

Ja. Das trifft es.

Shadow Tech Debt in Zahlen

Die METR-Studie von 2025 hat eine Zahl produziert, die erstmal wehtut: Erfahrene Entwickler waren auf ihren eigenen, komplexen Codebases 19% langsamer mit KI-Tools. Und — das ist der gute Teil — sie glaubten trotzdem, schneller zu sein.

Das passt perfekt zum Shadow-Tech-Debt-Bild. Denn gefühlt geht’s schneller. Der Code ist ja da. Aber dann muss man ihn korrigieren, anpassen, in die bestehende Architektur einpassen. Oder — und das ist der gefährliche Fall — man macht’s einfach nicht, und die Inkonsistenzen stapeln sich leise auf.

Shadow Tech Debt bekämpfen: Was wir gelernt haben (auf die harte Tour)

Wir nutzen Claude Code als KI-Agent in der täglichen Entwicklung. Dabei ist die zentrale Erkenntnis nach neun Monaten: Der Agent ist genau so gut wie die Leitplanken, die man ihm gibt. Ohne Leitplanken produziert er genau das, was Tkachev beschreibt. Mit Leitplanken wird’s brauchbar.

Blueprints: Das Projekt komplett beschreiben — bevor eine Zeile Code entsteht

Die CLAUDE.md im Repo erklärt dem Agenten die Basics: Konventionen, Naming-Regeln, Sprachkonstrukte. Allerdings ist das nur die unterste Ebene. Für neue Projekte gehen wir deutlich weiter.

Wir arbeiten mit einem Blueprint-Konzept — einer mehrstufigen Projektspezifikation, die komplett in Markdown-Dateien lebt und dem Agenten als vollständiger Kontext dient:

  1. Use Cases — Was soll das System können? Konkrete Nutzerszenarien, keine abstrakten Requirements.
  2. Domain Model — Welche Entitäten gibt es? Wie hängen sie zusammen? Welche Zustände durchlaufen sie?
  3. Specs — Technische Spezifikationen für jede Systemkomponente, abgeleitet aus den Use Cases.
  4. UX — Wie sieht die Oberfläche aus? Welche Interaktionen gibt es?
  5. Plan — Modulstruktur, Pattern-Zuordnung, Architekturentscheidungen.
  6. Tasks — Einzelne Implementierungsschritte, geordnet nach Abhängigkeiten.

Jede Stufe baut auf der vorherigen auf. Der Agent leitet die Specs aus den Use Cases ab, den Plan aus den Specs, die Tasks aus dem Plan. Das ganze Projekt wird durchdacht, bevor die erste Zeile Code geschrieben wird.

Der Unterschied zu „schreib mir mal ein Feature“: Der Agent kennt nicht nur die aktuelle Aufgabe, sondern das gesamte Zielbild. So weiß er, wie die Presence-Komponente mit dem Meeting-System zusammenspielen soll. Gleichzeitig kennt er die Value Classes, die projektübergreifend verwendet werden. Kurz: Er hat den Systemkontext, nicht nur den Dateikontext.

Ohne Blueprint: Spring-Boot-artige Controller statt Ktor-Routing, falsche Patterns, inkonsistente Struktur. Mit Blueprint: Der Agent folgt unseren Konventionen, weil er sie kennt.

Patterns zum Abgucken

Ergänzend zum Blueprint gibt es eine Pattern-Bibliothek mit 15 dokumentierten Mustern: MongoDB-Entities, Draft/Confirmed-Zustandsmanagement, REST-Responses mit Sealed Interfaces, typsichere Filterung. Jedes Pattern hat lebenden Code als Referenz — keine abstrakten Beschreibungen, sondern „so sieht das bei uns aus“.

Der Agent kopiert dann nicht irgendein Pattern aus seinem Trainingskorpus — sondern unser Pattern.

Workflows statt „mach mal“

Wir prompten nicht frei. Wir haben 18 definierte Skills — quasi automatisierte Workflows mit klaren Ein- und Ausgaben:

  • /plan — Technische Planung mit Architektur-Analyse
  • /work — Implementierung mit Quality Gates (Build, Tests, Code-Style)
  • /review — Strukturiertes Code Review mit Checklisten
  • /polish — Stil- und Qualitätsprüfung vor dem Merge

Jeder Skill erzwingt Qualitäts-Gates. Der Agent kann nichts in den Main-Branch bringen, ohne dass Detekt, ESLint und Prettier drübergelaufen sind. Kein Shortcut möglich.

Geht trotzdem schief. Manchmal.

Ehrlich gesagt: Auch mit allen Leitplanken passieren Fehler. Beispielsweise greift der Agent mal zu einem deprecated Pattern. Oder er dupliziert Logik, die schon in einer Utility-Klasse steckt. Benennt Dinge anders als der Rest der Codebase.

Der Punkt ist: Mit den richtigen Prozessen findet man das vor dem Merge. Ohne Prozesse findet man’s drei Monate später. Oder nie.

Was wirklich gegen Shadow Tech Debt hilft

Shadow Tech Debt ist kein Tool-Problem. Bessere Modelle allein lösen das nämlich nicht. Vielmehr ist es ein Prozess-Problem:

  1. Architektur-Kontext raus aus den Köpfen, rein in Dateien. Was das Team weiß, muss der Agent auch wissen. Aufschreiben.
  2. Patterns dokumentieren — mit lebendem Code. Keine abstrakten Beschreibungen, sondern „so sieht das bei uns aus, mach’s genauso“.
  3. Automatisierte Quality Gates. Linter, Static Analysis, Style Checks. Alles, was Konventionen maschinell prüft, fängt Shadow Tech Debt ab.
  4. Definierte Workflows statt freie Prompts. Ein Agent mit Struktur produziert konsistentere Ergebnisse als einer, den man einfach drauflos schreiben lässt.
  5. Review bleibt Pflicht. KI-Code braucht die gleiche Review-Sorgfalt wie menschlicher Code. Eher mehr, weil die subtilen Inkonsistenzen schwerer zu erkennen sind.

Fazit: Shadow Tech Debt ist vermeidbar

KI-Agenten sind großartige Werkzeuge. Ernsthaft. Sie beschleunigen repetitive Aufgaben, reduzieren Boilerplate und automatisieren Workflows, die vorher nervig und fehleranfällig waren. Aber ohne Leitplanken produzieren sie technische Schulden, die man nicht sieht, die sich aufstapeln und die am Ende teurer werden als die eingesparte Zeit.

Shadow Tech Debt ist real. Die Frage ist nicht ob man KI-Agenten einsetzt — sondern ob man es mit oder ohne Leitplanken tut. Wie wir das bei Neckar IT konkret angehen, beschreiben wir in unserer AI-Strategie. Und ehrlich gesagt: Ohne Leitplanken kann man es auch gleich lassen.


Quellen:

Unsere AI-Strategie – wie die Neckar IT Software entwickeln wird

AI-Prinzip

Wir bauen eine Maschine, die Software produziert.
Deshalb darf es keine manuellen Schritte geben – alles muss automatisiert sein.

Meta-Ebene

Auch der Prozess, mit dem wir die AI steuern, darf nicht manuell sein.
Die Steuerung selbst muss automatisiert werden.

Sonst bauen wir keine Maschine, sondern eine Assistenz.

Legenden zu Mono-Repos 2: „Man verliert komplett den Überblick!“

Die Entscheidung Monorepo vs Polyrepo ist oft eine Glaubensfrage. Ein häufiges Argument gegen das Monorepo: Die Angst, dass es wie ein chaotischer Wühltisch endet. Aber ist das wirklich so? In Teil 1 unserer Serie haben wir bereits den Mythos der langen Build-Zeiten widerlegt. Heute klären wir die Frage: Verliert man wirklich den Überblick?

Polyrepo vs Monorepo

Woher kommt die Sorge?

Der Gedanke hinter diesem Mythos ist schnell erklärt: Wenn ALLES in einem einzigen Repository liegt, befürchten Entwickler im Vergleich Monorepo vs Polyrepo oft Nachteile:

  • Dateileichen und Chaos: Alte Dateien könnten den Code aufblähen.
  • Zu viel Rauschen: Es wird schwer, sich auf das Wesentliche zu konzentrieren, wenn jede Änderung Hunderte von Projekten betrifft.
  • Verantwortlichkeit: „Wer macht was?“ ist oft die große Frage.

Klingt plausibel? Aber moderne Monorepos haben clevere Lösungen, die genau das verhindern.

Mythos entzaubert: Struktur im Monorepo behalten

Die Wahrheit ist: Monorepos bieten Tools, die helfen, den Überblick zu behalten – oft sogar besser als bei vielen Einzel-Repos (Polyrepos). Schauen wir uns an, wie das geht:

1. Klare Ordnerstruktur

Ein gut organisiertes Monorepo beginnt mit einer sauberen Ordnerstruktur. Projekte, Tools und Shared Libraries werden logisch gruppiert. Das hat den großen Vorteil, dass man mehrere Unterebenen einziehen kann.

Der Vorteil gegenüber Polyrepos

Im Duell Monorepo vs Polyrepo punktet hier das Monorepo: Bei Polyrepos checkt jeder Entwickler die Projekte an unterschiedlichen Orten aus. Im Monorepo ist die Struktur zentral für alle gleich erzwungen. Das schafft Ordnung.

2. Code-Ownership und Verantwortlichkeit

Tools wie CODEOWNERS-Dateien ermöglichen es, klare Zuständigkeiten festzulegen. Jede Datei hat zugewiesene „Owner“, die Änderungen prüfen müssen. So ist die Verantwortlichkeit oft klarer geregelt als in verstreuten Repositories.

3. Smarter Code-Browser

IDEs wie VS Code oder IntelliJ bieten intelligente Filter. Damit zeigen Sie nur die Teile des Codes an, die Sie gerade brauchen. Alles andere ist ausgeblendet, aber nur einen Klick entfernt.

4. Isolation durch Module

Ein Monorepo heißt nicht, dass alles Spaghetti-Code ist. Mit Isolation durch Module stellen Sie sicher, dass Teams nur in ihrem Bereich arbeiten.

Wann kann es trotzdem unübersichtlich werden?

Natürlich gibt es Stolperfallen. Wenn die Ordnerstruktur wächst wie ein wilder Dschungel oder Dokumentation fehlt, wird es chaotisch. Das passiert aber bei Monorepo und Polyrepo gleichermaßen, wenn Disziplin fehlt.

Fazit: Monorepo vs Polyrepo – Übersicht ist Einstellungssache

Die Angst, im Monorepo den Überblick zu verlieren, ist meist unbegründet. Mit einer klaren Struktur und modernen Tools ist das Monorepo oft sogar übersichtlicher, da alles an einem zentralen Ort liegt.

Wir bei der Neckar IT nutzen diese Strukturen täglich in unserer Softwareentwicklung, um auch bei großen Projekten effizient zu bleiben.

Legenden zu Mono-Repos 1: „Builds dauern ewig“

Wer über den Einsatz eines Monorepo diskutiert, kennt den Satz, der fast immer zuerst fällt: „Aber die Builds dauern doch ewig!“ Klingt wie das perfekte Argument, um bei vielen kleinen Repositories zu bleiben, oder? Doch stimmt das wirklich?

Monorepo Architektur: Performance und schnelle Builds
Ein modernes Monorepo muss nicht langsam sein – dank Caching und Tooling.

Woher kommt der Mythos der langsamen Builds?

Ein Monorepo (kurz für Monolithic Repository) ist das zentrale Zuhause für alle Projekte eines Unternehmens. Das klingt erstmal nach einem Chaos-Magneten. „Wie soll das gehen, ohne dass der CI/CD-Server in die Knie geht?“ Wenn ein Repository mit Hunderten von Projekten wächst, denken viele an gigantische Builds, die Stunden dauern.

Aber das ist eher ein Märchen aus der Vergangenheit. Schauen wir uns die Performance moderner Architekturen an.

Mythos entzaubert: Ein Monorepo ist nicht automatisch langsam

Die Wahrheit ist: Builds in einem Monorepo sind oft effizienter als in vielen Einzel-Repos. Hier sind die wichtigsten Gründe, warum die Architektur skaliert:

1. Incremental Builds – Der Turbo

Hier können Tools wie Bazel, Nx oder Gradle genau tracken, welche Teile des Codes geändert wurden. Statt alles neu zu bauen, wird nur das gebaut, was tatsächlich betroffen ist (Affected Builds). Das spart massiv Zeit.

2. Caching ist König

Viele Build-Systeme nutzen in diesem Setup aggressive Caching-Strategien (Remote Caching). Wenn eine Komponente schon einmal gebaut wurde (auch von einem Kollegen!), wird sie einfach wiederverwendet. So laufen Builds rasend schnell und konsistent.

3. Parallelisierung rettet den Tag

Die meisten modernen CI/CD-Pipelines können Schritte parallel ausführen. Das ist ein Game-Changer für jedes Monorepo. Anstatt stumpf alle Projekte nacheinander zu bauen, nutzt du die Power moderner Hardware.

Was verursacht wirklich lange Builds?

Es gibt Fälle, in denen es lange dauert – aber das liegt meist nicht an der Monorepo-Idee selbst, sondern an der Umsetzung:

  • Fehlendes Tooling: Ohne Tools wie Nx oder Turbo können Prozesse ineffizient werden.
  • Zu große Abhängigkeiten: Wenn Projekte zu stark gekoppelt sind, leidet die Performance.
  • Unzureichende Hardware: Ein großes Repo verlangt nach moderner Infrastruktur.

Fazit: Keine Angst vor dem Monorepo

Die Legende der langsamen Builds ist ein Mythos. Mit dem richtigen Setup arbeiten wir bei Neckar IT oft schneller und zuverlässiger als mit vielen kleinen Repos.

Machen Sie sich Sorgen um die Übersichtlichkeit im Monorepo? Dann lesen Sie direkt weiter:

👉 Hier geht es zu Teil 2: „Man verliert komplett den Überblick!“

„Linke Socken in eine Schublade, rechte Socken in eine Schublade“ – Warum wir Daten nicht so speichern sollten

Stell dir vor, du würdest deine linken Socken in eine Schublade und deine rechten Socken in eine andere legen. Klingt unpraktisch, oder?

Genau so unpraktisch kann es sein, wenn wir Daten in der Software-Architektur speichern, „wie es sich gehört“, anstatt sie so zu organisieren, wie sie tatsächlich genutzt werden. In diesem Blog-Eintrag betrachten wir, warum es sinnvoller ist, Daten entsprechend ihrer Verwendung zu speichern, auch wenn das zunächst mehr Aufwand bedeutet.

Das Paradoxon der Sockenaufbewahrung

Im Alltag legen wir Socken paarweise in die Schublade, damit wir sie beim Anziehen schnell finden und nutzen können. Würden wir linke und rechte Socken getrennt aufbewahren, müssten wir jedes Mal ein Paar zusammensuchen – ein unnötiger Mehraufwand.

Übertragen auf die Software-Architektur

Ähnlich verhält es sich mit Daten: Wenn wir sie streng nach Kategorien oder logischen Strukturen speichern, die nicht ihrer tatsächlichen Nutzung entsprechen, erschweren wir den Zugriff und die Verarbeitung. Wir schaffen uns selbst Hindernisse, die den effizienten Betrieb unserer Anwendungen beeinträchtigen können.

Daten speichern, wie sie verwendet werden

Anstatt Daten so zu speichern, wie es auf den ersten Blick logisch erscheint, sollten wir uns darauf konzentrieren, wie die Daten tatsächlich genutzt werden. Dies kann bedeuten, Daten redundanter oder denormalisierter zu speichern, um den Zugriff zu beschleunigen und die Performance zu verbessern.

Vorteile dieses Ansatzes

  • Effizienter Datenzugriff: Daten liegen in der Form vor, in der sie benötigt werden, was die Verarbeitung beschleunigt.
  • Verbesserte Performance: Reduzierte Ladezeiten und schnellere Reaktionszeiten der Anwendungen.
  • Bessere Nutzererfahrung: Endbenutzer profitieren von schnelleren und zuverlässigeren Anwendungen.

Praktische Beispiele

Denormalisierung in Datenbanken

In relationalen Datenbanken streben wir oft nach einer hohen Normalisierungsstufe, um Redundanzen zu vermeiden. Doch in einigen Fällen kann Denormalisierung sinnvoll sein, um Lesezugriffe zu beschleunigen.

NoSQL-Datenbanken

NoSQL-Datenbanken wie MongoDB oder Cassandra speichern Daten oft in einem Format, das direkt den Anwendungsfällen entspricht. Dies ermöglicht schnelle Lese- und Schreibzugriffe, da die Datenstruktur auf die Nutzung ausgerichtet ist.

Caching und Materialized Views

Durch das Anlegen von Caches oder materialisierten Sichten können Daten in der benötigten Form bereitgestellt werden, ohne jedes Mal komplexe Abfragen ausführen zu müssen.

Herausforderungen und Lösungen

  • Datenkonsistenz: Redundante Speicherung kann zu Inkonsistenzen führen.
    • Lösung: Implementierung von Mechanismen zur Synchronisation und Konsistenzprüfung.
  • Erhöhter Speicherbedarf: Mehrfach gespeicherte Daten benötigen mehr Speicherplatz.
    • Lösung: Abwägung zwischen Speicherplatz und Performance; heute ist Speicher oft günstiger als Rechenzeit.
  • Komplexität bei Updates: Änderungen müssen an mehreren Stellen vorgenommen werden.
    • Lösung: Einsatz von Eventual Consistency und sorgfältige Planung der Datenflüsse.

Fazit

Der Socken-Beispiel dieses Beitrags soll verdeutlichen, dass es ineffizient ist, Daten entgegen ihrer Nutzung zu speichern – so wie es unpraktisch wäre, linke und rechte Socken getrennt aufzubewahren. In der Software-Architektur sollten wir uns von traditionellen Denkmustern lösen und Daten so organisieren, dass sie optimal genutzt werden können. Auch wenn dies anfänglich mehr Aufwand bedeutet, profitieren wir langfristig von effizienteren und leistungsfähigeren Systemen.

Die „Query-First“-Architektur: Optimierung für häufige Lesezugriffe

In der heutigen digitalen Welt, in der Daten in einem noch nie dagewesenen Tempo erzeugt und konsumiert werden, ist die Art und Weise, wie wir Daten speichern und darauf zugreifen, von entscheidender Bedeutung. Ein Ansatz, der in bestimmten Anwendungsfällen an Bedeutung gewinnt, ist die „Query-First“-Architektur. Dieser Ansatz konzentriert sich darauf, dass Daten typischerweise einmal geschrieben und sehr häufig gelesen werden.

Was ist die „Query-First“-Architektur?

Die „Query-First“-Architektur ist ein Designparadigma, bei dem der Schwerpunkt auf der Optimierung von Lesezugriffen liegt. Anstatt sich auf die Effizienz des Schreibens von Daten zu konzentrieren, werden Systeme so gestaltet, dass das Lesen schnell, effizient und skalierbar ist. Dies ist besonders nützlich in Szenarien, in denen Daten nach ihrer Erstellung selten geändert werden.

Daten werden viel öfters gelesen als geschrieben!

Vorteile

1. Verbesserte Leistung

Durch die Optimierung von Datenstrukturen für Lesezugriffe können Systeme schnellere Antwortzeiten bieten, was die Benutzerzufriedenheit erhöht.

2. Bessere Skalierbarkeit

Da Lesezugriffe oft weniger ressourcenintensiv sind als Schreibvorgänge, können Systeme leichter skaliert werden, um eine große Anzahl gleichzeitiger Benutzer zu unterstützen.

3. Vereinfachtes Caching

Mit seltenen Schreibvorgängen können aggressive Caching-Strategien implementiert werden, um die Last auf Backend-Systeme weiter zu reduzieren.

Herausforderungen

1. Komplexität beim Schreiben

Das einmalige Schreiben von Daten kann komplizierter werden, da Daten möglicherweise in mehreren Formaten oder an mehreren Orten gespeichert werden müssen, um Lesezugriffe zu optimieren.

2. Datenkonsistenz

Bei notwendigen Updates müssen alle Datenkopien konsistent gehalten werden, was zusätzliche Logik erfordert.

3. Speicherplatz

Die Optimierung für Lesezugriffe kann zu redundanter Datenspeicherung führen, was den Speicherbedarf erhöht.

Best Practices

  • Denormalisierung von Daten: Ermöglicht schnellere Abfragen auf Kosten von zusätzlichem Speicherplatz.
  • Verwendung von NoSQL-Datenbanken: Bieten Flexibilität und Leistung für leseintensive Anwendungen.
  • Eventual Consistency akzeptieren: In einigen Anwendungsfällen kann es akzeptabel sein, wenn Daten nicht sofort konsistent sind.

Fazit

Die „Query-First“-Architektur bietet einen wertvollen Ansatz für Systeme, in denen Daten einmal geschrieben und häufig gelesen werden. Durch die Fokussierung auf Lesezugriffe können Entwickler Anwendungen erstellen, die sowohl leistungsfähig als auch skalierbar sind. Wie bei jedem Architekturansatz ist es wichtig, die spezifischen Anforderungen und Einschränkungen des Projekts zu berücksichtigen, um die besten Ergebnisse zu erzielen.

Warum Startups wirklich scheitern (oder gar nicht erst gegründet werden)

Es gibt diesen berühmten Artikel mit 20 Gründen, warum Startups scheitern.

Aber: Ich bin überzeugt, dass die größeren Probleme ganz woanders zu suchen sind:

90% der Startups werden gar nicht erst gegründet oder sterben bereits in der Planungs-Phase!

Ich bin überzeugt, dass wir bereits eine ziemlich starke Suvivor-Bias finden. Und viele potenziell erfolgreiche Startups gar nicht erst gegründet werden.

Anforderungen an Gründer von Startups

Um ein Startup zu gründen, brauchen die Gründer eine ganze Menge an Eigenschaften, die sie gleichzeitig erfüllen müssen. Sobald auch nur eine davon fehlt, ist das Startup von Anfang an nur schwer überlebensfähig. Diese fehlenden Fähigkeiten müssen dann irgendwie kompensiert werden.

Fachliche Exzellenz + Idee

Die Gründer müssen fachlich exzellent sein. Wenn das fachliche Wissen fehlt, wird es sehr schnell, sehr schwierig.

Exzellent vernetzt

Ohne ein super Netzwerk wird es sehr, sehr schwer ein Startup voran zu bringen. Ohne entsprechendes Netzwerk werden viele Dinge sehr mühsam…

Kontakte werden benötigt zu Investoren, potentiellen Kunden, Multiplikatoren, …

Stark im Marketing und Vertrieb

Ohne exzellentes Marketing und Vertrieb ist jedes Startup zum scheitern verurteilt. Gerade am Anfang bleibt auch dabei ein Großteil der Aufgaben alleine aus Budgetgründen an den Gründern hängen.

Organisatorisch super

Erfolgreiche Startups müssen gut organisiert sein. Das muss man können, lernen und vor allem tun.

Teamfähig + Team-Leader

Die Teamfähigkeit von Gründern ist entscheidend für die Zusammenarbeit und den Aufbau eines kompetenten Teams. Ohne Team kein erfolgreiches Startup!

Menschenkenntnis

Irgendwie müssen die Gründe mit den richtigen Menschen zusammenarbeiten. Die richtigen Investoren finden und die richtigen Mitarbeiter.
Größtes Problem: Eine falsche Entscheidung in der frühen Phase eines Startups, kann den gesamten Erfolg gefährden.

„Bürokratisch“

Wir leben in Deutschland… Und es gibt sehr, sehr viel zu tun, um den Anforderungen der Bürokratie gerecht zu werden. Dafür braucht man definitiv ein Gen!

Risikobereit

Niemand gründet ein Startup, ohne eine ausgeprägte Risikobereitschaft. Die damit verbundene Ungewissheit ist nichts für schwache Nerven.

Flexibel

Kein Startup entwickelt sich geradlinig. Man benötigt eine ganze Menge an Flexibilität zur rechten Zeit, um auf sich änderne Dinge reagieren zu können.

„Reich“

Ein Startup bezahlt sich nicht von alleine – es ist einiges an Kapital notwendig. Ohne entsprechende Reserven und Investitionskapital wird es sehr, sehr schwer.

Deshalb werden die meisten Startups gar nicht erst gegründent!

Da so gut wie niemand auch nur einen Großteil dieser Anforderungen erfüllt, werden die meisten (möglichen) Startups gar nicht erst gegründet.

Alleine die Anforderungen an Kapital und Risikobereitschaft filtern bereits einen Großteil der idealen Gründer-Kandidaten aus.

Außerdem: Opportunitäts-Kosten

In Deutschland stehen sehr guten, motivierten und engagierten Menschen eine ganze Menge an sehr guten Karriere-Möglichkeiten offen. Deshalb entscheiden sich viele davon gegen das Risiko und die Anstrengungen ein Startup zu gründen.

Insbesondere gut vernetzte Personen finden leicht alternative weniger riskante Möglichkeiten (viel) Geld zu verdienen.

Alternative? „Startup as a Job“

Wir benötigen daher eine Möglichkeit in einem Startup mitzuarbeiten ohne das immense Risiko tragen zu müssen. Das kann nur erreicht werden, indem wir „Startups“ besser geplant gründen.

Besser ausgestattet mit Kapital von Risikokapitalgebern. Und ausgestattet mit Fachleuten, die die unterschiedlichen Anforderungen abdecken.

Frontend und Backend: Code sharing!

Zumindest für die Kommunikation per REST benötigen Frontend (TypeScript) und Backend (Kotlin/Java) die selben Klassen.

Es ist jetzt super nervig, wenn man den selben Code für die Requests und Responses für TypeScript und z.B. Kotlin schreiben muss.

Außerdem macht es das Leben sehr schwer – weil Änderungen immer an zwei Stellen gleichzeitig durchgeführt werden müssen.

Die Lösung: Code Generierung

Bei uns ist das Backend „führend“. D.h. wir erstellen Klassen in Kotlin und generieren daraus automatisch den Frontend-Code.
Damit ist sichergestellt, dass eine Änderung/Erweiterung an einer Stelle ausreichend ist.

Im Build wird automatisch neu generiert, so dass uns der TypeScript-Compiler auf Fehler/Probleme hinweist.

Technisch? Kotlin -> OpenApi -> Orval

Wir generieren aus unseren Kotlin-Klassen und Ktor-Endpoints vollautomatisch ein openapi.json-File.

Aus diesem openapi.json werden dann von Orval zur Compile-Time die TypeScript-Klassen generiert.

Zusammenfassung

  • Die Responses/Requests und alle zugehörigen Klassen werden nur einmal geschrieben (Kotlin).
  • die Endpoints werden im Code dokumentiert
  • automatisch wird daraus ein openapi.json erstellt
  • daraus wiederum wird TypeScript-Code generiert (inklusive des Boilerplate-Codes für die Kommunikation)
  • der TypeScript-Kompiler stellt sicher, dass wir inkompatible Änderungen auf Frontend-Seite entdecken

Warum wir (fast) nur unseren Tech-Stack verwenden. Oder: Wer alles kann, kann nix!

Die Wahl des richtigen Technologie Stack ist eine der wichtigsten Entscheidungen in der Softwareentwicklung. Wir haben im Laufe der Jahre „unseren“ Stack entdeckt und perfektioniert:

  • Frontend: TypeScript + React
  • Backend: Kotlin/Ktor oder Java/Spring Boot
  • Datenbank: MongoDB (seltener relationale Datenbanken), S3 für Object-Storage
Unser Technologie Stack: Fokus auf Stabilität und Expertise.

Von diesem Technologie Stack weichen wir nur sehr selten und möglichst wenig ab. Und dafür gibt es gute Gründe!

Wer alles im Technologie Stack hat, kann nix richtig!

Die Software-Welt wird immer komplexer. Jeden Tag erscheinen neue Tools. Da überall auf dem Laufenden zu bleiben, ist unmöglich. Wir beschränken unseren Technologie Stack daher auf wenige Komponenten und lernen diese richtig. Wir sind hier wahre Experten.

Erfahrung ist teuer – wir haben den Preis schon bezahlt

Wir haben schon viele Fehler gemacht – und daraus gelernt. Wir kennen die Stolpersteine.
Positiv formuliert: Wir haben viel investiert, um heute mit unserem Setup schnell und sicher entwickeln zu können.

„Wie schwer kann React schon sein“?

„Hello World“ in React ist einfach. Aber eine komplexe Anwendung? Das ist eine Kunst!
Jeder Angular-Entwickler wird im ersten Versuch scheitern. Umgekehrt bilden wir uns nicht ein, komplexe Angular-Anwendungen schreiben zu können.

Der „beste“ Technologie Stack für den Job?

Lohnt es sich wirklich, immer das theoretisch „beste“ Tool zu suchen?
In fast allen Fällen gilt: Lieber ein „good enough“ Tool finden, welches vom Team bereits perfekt beherrscht wird.

Gibt es Projekte, in denen eine andere Datenbank als MongoDB geeigneter wäre? Sicher! Aber meistens sind die Vorteile marginal, während die Einarbeitungszeit in einen fremden Technologie Stack enorm ist.

Meistens kommt es sowieso anders

Früher oder später ändern sich die Anforderungen. Bei jeder Major Version neu zu evaluieren, um 5% Performance zu gewinnen, ist oft unwirtschaftlich.

Während andere noch evaluieren, wird bei Neckar IT schon entwickelt

Wir kennen unsere Technologien. Wir wissen sofort, ob unser Stack den Anforderungen gerecht wird. Es ist kein langwieriges Evaluieren notwendig. Wir legen sofort los – oder sagen ehrlich ab.

Ausnahmen und Grenzen

Manchmal setzen wir andere Datenbanken ein (z.B. SQL), wenn es zwingend nötig ist. Aber wir kennen unsere Grenzen. Wer uns engagiert, bucht 100%ige Experten für unseren Bereich.

Fazit: Teuer wird es am Ende

Architektur-Fehler zeigen sich oft erst spät. Deshalb gilt: Augen auf bei der Wahl der Experten und dem Technologie Stack. Niemand möchte Entwicklern eine teure Lektion in einer neuen Technologie finanzieren.