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?
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.
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?
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:
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.
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.
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.
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.
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:
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.
Willkommen zu einem weiteren Abenteuer in der Welt der Softwareentwicklung! Heute tauchen wir in die mystische Sphäre des sagenumwobenen „Fullstack-Entwicklers“ ein. Ja, du hast richtig gehört – jener geheimnisvolle Zauberer, der gleichzeitig die Macht des Frontends und des Backends beherrscht. Aber gibt es ihn wirklich, oder ist er nur ein Mythos? Eine moderne Version des Einhorns, das wir alle jagen, aber niemals wirklich finden?
Was ist ein „Fullstack-Entwickler“?
Bevor wir uns auf die Suche nach dem Einhorn begeben, klären wir doch erst einmal, was mit „Fullstack-Entwickler“ überhaupt gemeint ist. Theoretisch handelt es sich um jemanden, der sowohl das Frontend – also das, was der Benutzer sieht und womit er interagiert – als auch das Backend – das, was im Hintergrund abläuft und die Daten verwaltet – beherrscht. Diese Entwickler kennen sich sowohl mit HTML, CSS und JavaScript aus als auch mit Server-Architekturen, Datenbanken und beherrscht mehrere Programmiersprachen Java und TypeScript.
Die Realität: Ein universelles Genie oder doch nur Wunschdenken?
Nun zur großen Frage: Gibt es den Fullstack-Entwickler wirklich? In der Theorie klingt das alles wunderbar. Ein Entwickler, der alles kann – das ist der Traum eines jeden Unternehmens! Wer würde nicht gerne jemanden einstellen, der sowohl die hübsche Benutzeroberfläche zaubern als auch die komplexe Serverlogik im Hintergrund orchestrieren kann?
Aber Moment mal! In der Realität stellt sich schnell heraus, dass die Anforderungen an einen Fullstack-Entwickler oftmals in den Bereich des Unmöglichen fallen. Die Technologien in beiden Bereichen entwickeln sich so rasant weiter, dass es schwer ist, in allen Feldern stets auf dem neuesten Stand zu bleiben. Selbst die besten Entwickler müssen sich spezialisieren, um in einem Bereich wirklich herausragend zu sein.
Der Mythos vom „Alleskönner“
Der Gedanke des „Fullstack-Entwicklers“ ist daher oft eher ein Marketing-Gag oder Wunschdenken als Realität. Klar, es gibt Entwickler, die sowohl ein solides Grundverständnis von Frontend- als auch Backend-Technologien haben. Doch ein tiefes, umfassendes Wissen in beiden Bereichen? Das ist schon schwieriger.
Was Unternehmen eigentlich suchen, sind Entwickler mit einer „Fullstack-Mentalität“: Menschen, die bereit sind, über den Tellerrand ihres Fachgebiets hinauszuschauen, die sich in neue Technologien einarbeiten und flexibel zwischen verschiedenen Aufgaben wechseln können. Aber das ist ein großer Unterschied zu dem, was oft erwartet wird.
Die Suche nach dem Einhorn
Hier sind wir also, auf der Suche nach dem Einhorn, dem „Fullstack-Entwickler“. Wir durchforsten Jobbörsen, schicken LinkedIn-Nachrichten, interviewen Kandidaten… und stoßen immer wieder auf dieselbe Erkenntnis: Ein echter „Fullstack-Entwickler“ ist so selten wie ein Einhorn. Die meisten Entwickler haben ihre Stärken und Leidenschaften entweder im Frontend oder im Backend, und das ist auch gut so!
Die Moral der Geschichte? Vielleicht sollten wir aufhören, nach Einhörnern zu suchen, und stattdessen lernen, die einzigartigen Fähigkeiten jedes Entwicklers zu schätzen. Teams sind dann am erfolgreichsten, wenn sie aus Spezialisten bestehen, die zusammenarbeiten und sich gegenseitig ergänzen.
Fazit: Ein Hoch auf die (teamfähigen!) Spezialisten
Statt den Mythos des Fullstack-Entwicklers weiter zu glorifizieren, sollten wir uns vielleicht auf das konzentrieren, was wirklich wichtig ist: Zusammenarbeit, kontinuierliches Lernen und die Bereitschaft, über den Tellerrand hinauszuschauen. Jeder Entwickler bringt seine eigenen Fähigkeiten und Erfahrungen mit ein – und genau das macht ein starkes Team aus.
Also, das nächste Mal, wenn du auf der Suche nach einem „Fullstack-Entwickler“ bist, frag dich lieber: Will ich wirklich ein Einhorn? Oder wäre ein Team aus talentierten, spezialisierten Entwicklern, die sich gegenseitig ergänzen, nicht viel wertvoller?
Das Einhorn mag zwar ein schöner Mythos sein, aber wahre Magie entsteht durch Zusammenarbeit und gemeinsame Leidenschaft. Und das ist kein Mythos.
Kotlin hat sich aus verschiedenen Programmiersprachen das Beste zusammengeklaut. So erlaubt die Kombination von Sealed Interfaces und when sehr schönen und klaren Code. Der gleichzeitig auch noch sehr gut vom Compiler auf Korrektheit überprüft wird!
// Stub function to simulate fetching a response
private fun getResponse(): Response = TODO()
fun main() {
// Call a method with a relaxed return type.
// This ensures the compiler treats 'response' as 'Response' rather than a specific subtype, avoiding compile-time warnings.
val response: Response = getResponse()
// Using 'when' with all concrete types. Skipping one type results in a compile error due to exhaustiveness checking.
when (response) {
is Response.Success.Success1 -> println("S1")
is Response.Success.Success2 -> println("S2")
is Response.Failure.Failure1 -> println("F1")
is Response.Failure.Failure2 -> println("F2")
}
// Handling multiple types in one code block using commas.
when (response) {
is Response.Success.Success1,
is Response.Success.Success2,
is Response.Failure.Failure1,
-> println("combined S1, S2, F1") // Note the trailing comma for readability
is Response.Failure.Failure2 -> println("F2")
}
// Handling all failure cases in one block by using the common interface 'Failure'
when (response) {
is Response.Success.Success1 -> println("S1")
is Response.Success.Success2 -> println("S2")
is Response.Failure -> println("Failure case") // This will match Failure1 and Failure2 both
}
// Handling Success and Failure cases together by their common interfaces
when (response) {
is Response.Success -> println("Success case") // This will match Success1 and Success2
is Response.Failure -> println("Failure case") // This will match Failure1 and Failure2
}
}
// Define a sealed interface 'Response' which can be either a Success or a Failure
sealed interface Response {
// Define a nested sealed interface 'Success' under 'Response'
sealed interface Success : Response {
// Define concrete data classes for Success1 and Success2 which implement the Success interface
data class Success1(val foo: String) : Success
data class Success2(val bar: String) : Success
}
// Define a nested sealed interface 'Failure' under 'Response'
sealed interface Failure : Response {
// Define concrete data classes for Failure1 and Failure2 which implement the Failure interface
data class Failure1(val foo: String) : Failure
data class Failure2(val foo: String) : Failure
}
}
Vorteile / Features
Sealed Interface-Hierarchie
Das Response-Interface ist sealed, was bedeutet, dass alle möglichen Unterklassen zur Compile-Time bekannt sind. Dadurch ist der when-Ausdruck exhaustiv – wir können nichts vergessen!
Unterschiedliche „Genaugigkeiten“ beim Machting
Durch die Typ-Hierarchie innerhalb der Sealed Interface können wir jetzt unterschiedliche Genauigkeiten matchen. Wir können z.B. alles Failure oder Success-Cases gemeinsam abhandeln. Der Compiler übernimmt die Checks und fügt „Smart Casts“ hinzu.
Diese Beispiele zeigen, wie mächtig und flexibel sealed Interfaces und der when-Ausdruck in Kotlin sind, um komplexe Typ-Hierarchien zu handhaben und die Kompilierzeit-Sicherheit zu gewährleisten.
Zustimmung verwalten
Um dir ein optimales Erlebnis zu bieten, verwenden wir Technologien wie Cookies, um Geräteinformationen zu speichern und/oder darauf zuzugreifen. Wenn du diesen Technologien zustimmst, können wir Daten wie das Surfverhalten oder eindeutige IDs auf dieser Website verarbeiten. Wenn du deine Zustimmung nicht erteilst oder zurückziehst, können bestimmte Merkmale und Funktionen beeinträchtigt werden.
Funktional
Immer aktiv
Die technische Speicherung oder der Zugang ist unbedingt erforderlich für den rechtmäßigen Zweck, die Nutzung eines bestimmten Dienstes zu ermöglichen, der vom Teilnehmer oder Nutzer ausdrücklich gewünscht wird, oder für den alleinigen Zweck, die Übertragung einer Nachricht über ein elektronisches Kommunikationsnetz durchzuführen.
Vorlieben
Die technische Speicherung oder der Zugriff ist für den rechtmäßigen Zweck der Speicherung von Präferenzen erforderlich, die nicht vom Abonnenten oder Benutzer angefordert wurden.
Statistiken
Die technische Speicherung oder der Zugriff, der ausschließlich zu statistischen Zwecken erfolgt.Die technische Speicherung oder der Zugriff, der ausschließlich zu anonymen statistischen Zwecken verwendet wird. Ohne eine Vorladung, die freiwillige Zustimmung deines Internetdienstanbieters oder zusätzliche Aufzeichnungen von Dritten können die zu diesem Zweck gespeicherten oder abgerufenen Informationen allein in der Regel nicht dazu verwendet werden, dich zu identifizieren.
Marketing
Die technische Speicherung oder der Zugriff ist erforderlich, um Nutzerprofile zu erstellen, um Werbung zu versenden oder um den Nutzer auf einer Website oder über mehrere Websites hinweg zu ähnlichen Marketingzwecken zu verfolgen.