Die Kosten von Microservices (2020)
(robertovitillo.com)- Backends starten meist als Monolith, aber wenn mehrere Feature-Teams an derselben Codebasis arbeiten, kann die Kopplung steigen und die Änderungsgeschwindigkeit sinken
- Microservices teilen Services nach Geschäftsfunktionsbereichen auf, um unabhängige Deployments und die Autonomie kleiner Teams zu erreichen
- Werden Services zu klein, steigt die operative Last stärker als der Nutzen; der Name „micro“ kann die Beurteilung sogar eher trüben
- Eine vollständige Einführung bringt weitreichende Kosten mit sich: Standardisierung, Resource Provisioning, Absicherung von Remote Calls, CI/CD, Betrieb und eventuelle Konsistenz
- Meist ist es sicherer, erst im Monolithen genügend Erfahrung mit Grenzen zu sammeln und dann bei wachsenden Schmerzen schrittweise auszulagern; ein Microservices-first-Ansatz eignet sich, wenn Erfahrung und Plattformvorbereitung vorhanden sind
Grenzen, die sichtbar werden, wenn der Monolith wächst
- Das Backend moderner Single-Page-JavaScript-Anwendungen beginnt meist als ein einzelner zustandsloser Webservice, der eine RESTful-HTTP-API bereitstellt und eine relationale Datenbank nutzt
- Dieser Service besteht aus Komponenten oder Bibliotheken, die unterschiedliche Geschäftsfunktionen implementieren
- Je mehr Feature-Teams an derselben Codebasis mitarbeiten, desto stärker wird die Kopplung zwischen Komponenten, und die Arbeitsbereiche der Teams überschneiden sich leichter
- Die Produktivität sinkt, und es wird schwieriger, den Einflussbereich von Änderungen vorherzusagen
- Wird die Codebasis komplex genug, versteht niemand mehr das Gesamtsystem vollständig, sodass neue Features und Bugfixes mehr Zeit brauchen
- Selbst wenn das Backend in teambezogene Bibliotheken aufgeteilt ist, führen Änderungen an Bibliotheken letztlich dennoch zu einem Redeployment des Service
- Tritt ein Bug wie ein Memory Leak auf, kann der gesamte Service betroffen sein
- Wird ein fehlerhafter Build zurückgerollt, beeinflusst das nicht nur das Team, das den Bug verursacht hat, sondern das Tempo aller Teams
Autonomie durch Service-Grenzen
- Eine Möglichkeit, die Wachstumsschmerzen eines monolithischen Backends zu verringern, besteht darin, das Backend in eine Sammlung unabhängig deploybarer Services aufzuteilen
- Die Services kommunizieren über APIs, und APIs schaffen Grenzen, die schwerer zu durchbrechen sind als Komponentengrenzen innerhalb desselben Prozesses
- Diese Architektur wird als Microservices-Architektur bezeichnet, aber der Ausdruck
microkann irreführend sein- Services müssen nicht unbedingt klein sein
- Services mit zu wenig Funktionalität verursachen unter Umständen mehr operative Last als Nutzen
- Ein passenderer Name wäre vielleicht service-oriented architecture, aber auch dieser Begriff trägt Altlasten mit sich
Geschwindigkeit durch kleine Teams und unabhängige Services
- Wenn das Backend in Services entlang von Geschäftsfunktionen aufgeteilt wird, kann jeder Service von einem kleinen einzelnen Team entwickelt und betrieben werden
- Kleine Teams sind effektiver, weil die Kommunikationskosten mit der Teamgröße quadratisch steigen
- Wenn jedes Team seinen eigenen Release-Zeitplan festlegt und die Kontrolle über seine Codebasis hat, sinkt der Abstimmungsaufwand zwischen Teams und Entscheidungen werden schneller getroffen
- Ist die Codebasis eines Service klein, können Entwickler sie leichter verstehen, und auch die Einarbeitungszeit neuer Mitarbeiter sinkt
- Kleine Codebasen verringern Verlangsamungen in der IDE und helfen damit ebenfalls der Produktivität
- Dank starker Service-Grenzen müssen Entwickler beim Ändern eines Teils des Backends nicht das Ganze verstehen, sondern nur einen kleinen Ausschnitt
- Jeder Service kann unabhängig skaliert werden und einen zu seinen Anforderungen passenden Tech-Stack wählen
- API-Konsumenten müssen sich nicht darum kümmern, wie eine Funktion intern implementiert ist
- Neue Technologien lassen sich leichter ausprobieren und evaluieren, ohne andere Systembereiche zu beeinflussen
- Jeder Microservice kann ein eigenes Datenmodell und einen eigenen Datenspeicher haben
- Entwickler können das Schema ändern, ohne andere Services zu beeinträchtigen
Die tatsächlichen Zusatzkosten von Microservices
- Microservices fügen dem Gesamtsystem mehr bewegliche Teile hinzu, und diese Kosten sind nicht kostenlos
- Die Kosten einer vollständigen Einführung lohnen sich nur, wenn sie über Dutzende Entwicklungsteams hinweg verteilt abgeschrieben werden können
-
Entwicklererfahrung
- Es ist zwar möglich, pro Service unterschiedliche Sprachen, Bibliotheken und Datenspeicher zu verwenden, aber das kann die Wartung der Anwendung erschweren
- Sind die Software-Stacks vollständig unterschiedlich, fällt es Entwicklern schwerer, von einem Team in ein anderes zu wechseln
- Um gemeinsame Funktionen zu liefern, die jeder Service braucht, etwa Logging, müssen Bibliotheken für jede verwendete Sprache unterstützt werden
- Ein gewisses Maß an Standardisierung ist notwendig
- Teams, die dem empfohlenen Portfolio an Sprachen und Technologien folgen, kann man eine gute Developer Experience bieten und so den Einsatz bestimmter Technologien locker fördern
-
Resource Provisioning
- Um viele unabhängige Services zu unterstützen, müssen Server, Datenspeicher und andere allgemeine Ressourcen leicht erstellt werden können
- Man sollte nicht zulassen, dass jedes Team seine eigene Art der Ressourcenerstellung entwickelt
- Bereitgestellte Ressourcen müssen anschließend erneut konfiguriert werden; um das zu bewältigen, ist erhebliche Automatisierung nötig
-
Kommunikation
- Remote Calls sind teuer und führen neue Arten ein, auf die ein System scheitern kann
- Um Ausfälle abzufangen, braucht es Schutzmechanismen wie Timeouts, Retries und Circuit Breaker
- Um Performance-Verluste durch Netzwerkkommunikation zu reduzieren, sollte man auch asynchrone Verarbeitung und Batching einsetzen
- Diese Faktoren erhöhen die Systemkomplexität
- Auch Monolithen können wegen Remote-Client-Zugriffen und der Nutzung von Third-Party-APIs ähnliche Probleme haben, allerdings in kleinerem Ausmaß
-
Kontinuierliche Integration, Delivery und Deployment
- Kontinuierliche Integration sorgt dafür, dass Codeänderungen erst nach automatischen Builds und Tests in den Main-Branch gemergt werden
- Sobald Codeänderungen gemergt sind, sollten sie automatisch in eine produktionsähnliche Umgebung veröffentlicht und deployt werden
- In dieser Umgebung sollten Integrations- und End-to-End-Tests laufen, um sicherzustellen, dass ein Microservice keine anderen Services beschädigt, von denen er abhängt
- Das Testen einzelner Microservices ist nicht schwieriger als das Testen eines Monolithen, aber Integrationstests für alle Microservices zusammen sind deutlich schwieriger
- Wenn einzelne Services miteinander interagieren, können sehr subtile und unerwartete Verhaltensweisen auftreten
-
Betrieb
- Anders als beim Monolithen ist es deutlich teurer, für jedes zuständige Service-Team ein separates Ops-Team bereitzustellen
- Das Team, das einen Service entwickelt, übernimmt in der Regel auch dessen On-Call-Bereitschaft
- In jedem Sprint muss das Team entscheiden, ob Entwicklungsarbeit oder operative Last Vorrang hat, und daraus entsteht Reibung
- Auch das Debugging von Systemausfällen wird schwieriger
- Man kann nicht die gesamte Anwendung auf einer lokalen Maschine starten und sie im Debugger Schritt für Schritt durchlaufen
- Mehr bewegliche Teile bedeuten auch mehr mögliche Fehlerarten
- Gutes Logging und Monitoring werden auf allen Ebenen wichtiger
-
Eventuelle Konsistenz
- Wenn eine Anwendung in mehrere Services aufgeteilt wird, existiert das Datenmodell nicht mehr nur in einem einzigen Datenspeicher
- Datensätze in unterschiedlichen Datenspeichern atomar zu aktualisieren und starke Konsistenz zu garantieren, ist langsam, teuer und schwer umzusetzen
- Eine solche Architektur muss in der Regel eventuelle Konsistenz akzeptieren
Wann sollte man aufteilen?
- Wenn eine Anwendung in Services aufgeteilt wird, steigt die Komplexität des Gesamtsystems erheblich
- Im Allgemeinen ist es besser, mit einem Monolithen zu beginnen und erst dann aufzuteilen, wenn es dafür ausreichend gute Gründe gibt
- Die richtigen Grenzen zwischen Services zu finden, ist schwierig; daher ist es oft einfacher, innerhalb des Monolithen Grenzen zu verschieben und so den passenden Zuschnitt zu finden
- Ist der Monolith reif genug und werden die Wachstumsschmerzen größer, kann man nach und nach einzelne Microservices herauslösen
- Ein Microservices-first-Ansatz ist nur dann sinnvoll, wenn bereits Erfahrung vorhanden ist und entweder schon eine passende Plattform aufgebaut wurde oder die dafür nötige Aufbauzeit eingeplant ist
1 Kommentare
Hacker-News-Kommentare
Ich stimme dem Artikel im Großen und Ganzen zu, aber andersherum betrachtet gibt es auch Fälle, in denen die Kosten tragbar sind: Man kann alte Kompatibilitätsprobleme mit Third-Party-Abhängigkeiten in einen separaten Service auslagern und so schmerzhafte Refactorings oder eine eigene Neuimplementierung vermeiden; man kann verhindern, dass Deployment-Beschränkungen eines hochverfügbaren Kernsystems die Deployments anderer Systeme blockieren; und man kann Design-Zwänge isolieren, die große Kunden nicht ändern können
Das heißt nicht, dass es kostenlos ist, sondern dass es Kosten sind, die es wert sein können, um zu vermeiden, dass ein einfacher Monolith zu einem Müllhaufen wird, durch riskante Deployments ins Wanken gerät oder an einen Geschäftspartner mit einem noch schlechteren Tech-Stack gekettet bleibt
Sind Microservices nicht eher ein Ansatz, bei dem interne Komponenten grundsätzlich als Services statt als Bibliotheken oder Klassen gebaut werden?
Wenn man etwa einen Machine-Learning-Inferenzservice in Python, ein serverseitig gerendertes UI in Rails und einen Service mit viel I/O und Nebenläufigkeit in Go schreibt, kann der zusätzliche Aufwand, diese drei als getrennte Services aufzuteilen, gerechtfertigt sein
Ich mag Microservices, aber frühe Optimierung und frühe Skalierung können genauso schlimm sein wie technische Schulden, die entstehen, wenn man später in eine völlig andere Richtung optimieren muss
Der richtige Zeitpunkt, etwas in einen separaten Service auszulagern, ist dann erreicht, wenn ein Problem auftritt, das sich anders nicht mehr auf beherrschbare Weise lösen lässt
Architekturkomplexität zu erhöhen, um Grenzen zu erzwingen, kann keine Lösung für mangelnde organisatorische Disziplin sein, aber mittelgroße Tech-Unternehmen behandeln es ständig so. Wenn schwache Domänengrenzen Probleme verursachen, ist es gut möglich, dass das Herauslösen eines Services sie auch nicht wirklich löst
Zitat: https://www.oreilly.com/library/view/software-architects-han...
Auch CI/CD, das PRs blockiert, bis Tests grün sind, ist eine teure technische Lösung für ein Problem organisatorischer Disziplin
Alle erkennen an, dass es sinnvoll ist, die Auflösung von Domainnamen durch einen externen Service erledigen zu lassen, und kaum jemand integriert einen rekursiven DNS-Resolver samt Cache in einen Monolithen. Trotzdem wird diese alte Verantwortungstrennung nicht gut als Beispiel anerkannt
Das Problem scheint zu sein, dass gerade die „guten“ Mitarbeiter oft so motiviert sind, Wert zu zeigen, dass sie ins Overengineering abgleiten, und am Ende wird alles zur Hölle
Microservices
grug fragt sich, warum Großhirne sich das schwierigste Problem vornehmen, nämlich ein System korrekt zu zerlegen, und dann auch noch Netzwerkaufrufe hineinpacken
Für grug wirkt das sehr verwirrend
https://grugbrain.dev/#grug-on-microservices
„Wir wollen wegen SCALE_REASONS den Zugriff auf FOO_TABLE modernisieren und dafür von MySQL auf DynamoDB wechseln, aber 32 von 59 Teams greifen direkt auf FOO_TABLE zu oder rufen direkt private Methoden unserer Klasse auf. Diese Teams haben andere Prioritäten und können weder auf FOO_SERVICE umsteigen noch ihre Queries so anpassen, dass sie Sharding-Tabellen verwenden. Jetzt wird die Skalierung von FOO_TABLE zu einem mehrquartaligen Projekt, und wir müssen den Teams ermöglichen, sich langsam zu aktualisieren. In 1–2 Jahren können wir den alten Ansatz, der gerade brennt, vielleicht abschalten. Viel Spaß bis dahin mit On-Call“
Mit Microservices merkt das Team zwar, dass die Tabelle nicht skaliert, aber die Daten werden über eine API bereitgestellt. Im nächsten Sprint plant und führt es die Migration durch, und die API-Nutzer stellen nur fest, dass alles viel schneller geworden ist
Wenn man ein REPL in aktuellem Ruby öffnet, bekommt man in Bezug auf Autovervollständigung und Syntaxprüfung schon einen großen Teil dessen, was man von einer IDE für statisch typisierte Sprachen erwartet
Isolation sollte man nicht leichtfertig abtun; sie ist eine der mächtigsten Eigenschaften, die man in Software einbauen kann. Sie kann die Performance erhöhen, Fehlergrenzen schaffen und Sicherheitsgrenzen bereitstellen
Das ist dasselbe Grundkonzept wie beim Actor-Modell. Statt dass zwei Komponenten denselben Speicher gemeinsam nutzen und verändern, kommunizieren zwei isolierte Systeme wie Actoren oder Microservices nur über ein definiertes Protokoll miteinander
Ich denke, viele Menschen verstehen Modularität falsch. Modularität ist wichtig, aber ich bin zu der Überzeugung gelangt, dass es noch ein weiteres wichtiges Architekturprinzip gibt: das Prinzip des einzelnen Wirbels
In Softwaresystemen ist ein Wirbel eine Schleife im Datenfluss. Wenn man Daten irgendwohin sendet und das verarbeitete Ergebnis zurückbekommt oder auf irgendeine Weise von den Auswirkungen dieser Daten betroffen ist, gibt es einen Wirbel. Veränderliche Variablen sind ein Beispiel für einen sehr kleinen Wirbel
Das Prinzip des einzelnen Wirbels bedeutet, dass es in einem Softwaresystem idealerweise nur einen Wirbel geben sollte, oder anders gesagt, dass alle Komponenten wissen sollten, in welche Richtung sich ihr Wirbel dreht
Wenn man Module zusammenführt, die zwei Wirbel bilden, ist die Komposition einfach, wenn die Wirbelrichtung übereinstimmt; bei entgegengesetzter Richtung wird es knifflig, weil man entscheiden muss, wie die Richtung des neuen Wirbels aussehen soll. Deshalb ist es am besten, wenn alle Module dieselbe Wirbelrichtung haben und einen einzigen Wirbel bilden
Dieses Prinzip ist eine Verallgemeinerung von Ideen wie dem Flux-Muster, CQRS, Event Sourcing und Unveränderlichkeit
Oder in A passiert etwas Unerwartetes und dadurch kann C blockiert werden. Im Idealfall sollte der Parent nur seine Children aufrufen und nicht von seinem Parent abhängen; wenn das scheitert, ist auch die Architektur gescheitert
Wenn man sich ein Architekturdiagramm ansieht, besteht die halbe Arbeit darin zu erkennen, was asynchron oder synchron ist, was Push oder Pull ist und in welche Richtung und Reihenfolge der Fluss verläuft
Enterprise Service Bus und Message Queues wirken wie eine Lösung dafür, einen Wirbel dort zu erzeugen, wo die Richtungen auf beiden Seiten nicht zusammenpassen
Es gibt die Stelle, dass die „Anzahl der Libraries, die man unterstützen muss, um gemeinsame Funktionen wie Logging bereitzustellen, die alle Services brauchen, jeweils eine pro eingesetzter Sprache“ ein Problem sei
Das ist der größte Grund, warum wir Microservices aufgegeben haben. Mit den Tools von 2023, besonders in rein Cloud-nativen oder endlos skalierenden FaaS-Umgebungen, ist es völlige Verschwendung, dafür mentale Bandbreite aufzubringen. Das gilt umso mehr bei Kunden wie Banken und Finanzinstituten, die jede einzelne Third-Party-Abhängigkeit gnadenlos prüfen
Wir betreiben das Ganze jetzt als monolithisches .NET-Binary-Deployment, mit gzip etwa 250 MB groß, und es gibt keinerlei Anzeichen von Rissen. Wenn du bei SaaS-Deployments von 10–100 MB pedantische Sorgen hast wie „meine exe passt nicht mehr in L2“, kannst du beruhigt sein. Die Reise zu monolithischer Software hat noch nicht einmal richtig begonnen
Wenn man irgendwann in die Lage kommt, einen dieser Müllhaufen neu schreiben zu müssen, wäre es dann nicht viel einfacher, wenn alle Commits an einer Stelle liegen und global konsistent sind?
Microservices bedeuten nicht automatisch Anarchie und dass alle Teams handeln dürfen, ohne die Gesamtorganisation zu berücksichtigen. Es kann und sollte Regeln geben, um ein gewisses Maß an Konsistenz zwischen Teams zu wahren
Der beste Code ist Code, den man leicht wegwerfen und neu schreiben kann. Realistisch gesehen geht alter Code durch mehr Hände, wird dadurch schlechter und vor allem will ihn irgendwann niemand mehr warten
Befürworter von Microservices sagen, Änderungen oder Neuschreibungen seien einfacher, weil es klare Verträge dafür gibt, wie ein Service funktionieren soll, und weil die Codebasis dieses Services viel kleiner ist
Die Monolith-Seite sagt, Änderungen oder Neuschreibungen seien einfacher, weil alles ein großes Garnknäuel sei und man zum Ändern eines Fadens nur wissen müsse, an welchen Stellen dieser Faden mit anderen verflochten ist
Ich weiß nicht, wer recht hat. Für langjährige Mitarbeiter, die sich die Codebasis über Jahre hinweg unter dem Mikroskop angeschaut haben, ist ein Monolith wahrscheinlich besser; für alle anderen eher Microservices
Aus Sicht des Release-Managements wird der Schritt vom Monolithen zu Microservices oft aus den falschen Gründen gemacht
Der einzig wirklich legitime Grund für eine Änderung scheint ein Skalierbarkeitsproblem zu sein, etwa ein Performance-Engpass. Alles andere verlagert nur die Komplexität der Softwareentwicklung in die Systemwartung
Natürlich werden Entwickler es mögen, wenn die große Last abnimmt, sich mit anderen Teams abstimmen zu müssen. Aber klarzumachen, wann und wie eine Funktion implementiert, über Microservices hinweg korrekt getestet, in Produktion aktiviert und zentral gesteuert wird, wird viel schwieriger, wenn die Kommunikation zwischen den Entwicklungsteams nicht reif genug ist. Und genau diese Unreife ist oft der eigentliche Grund, warum man den Monolithen überhaupt aufspaltet
Umgekehrt ist es schmerzhaft, mit mehreren Microservices umzugehen, wenn es nur wenige Entwickler gibt und der Kontext klar abgegrenzt ist
Die Aussage „Alles andere verlagert nur die Komplexität der Softwareentwicklung in die Systemwartung“ klingt plausibel, solange die Software aktiv weiterentwickelt wird. Entwicklung ist teuer. Es kann sein, dass die Kosten für den Betrieb verteilter Systeme bei einem großen Team niedriger sind als die Kosten für die Entwicklung eines riesigen Monolithen; am Ende hängt es von der konkreten Situation ab
Wo ist denn der Artikel über die „Kosten des Monolithen“? Hier sieht man ihn nicht. Das liegt nur daran, dass alle auf dumme Weise Microservices umsetzen und deshalb nur dieses Problem sichtbar ist
Wenn alle auf dumme Weise Monolithen umgesetzt hätten, gäbe es massenhaft Artikel darüber, dass „Monolithen schlecht sind“
Die Leute verstehen nicht, dass beide Systeme am Ende dieselbe Menge an Problemen erzeugen. Es ist der Unterschied zwischen einem Elefanten, der die Arbeit erledigt, und 1000 Mäusen, die sie erledigen. Beide haben Probleme, nur unterschiedliche
Wenn man sich für eines von beiden entscheidet, verschwinden die Probleme nicht. In beiden Fällen entstehen Probleme, und man muss für die gewählte Lösung Antworten liefern. Wenn in einem Unternehmen die größte Hürde die Frage „Welche Architektur?“ ist, würde ich dort gerne arbeiten
Wenn ein Monolith gut zerlegt ist, worin unterscheidet er sich dann von Microservices, die am selben Ort deployed sind? Vermutlich nur in der Schnittstelle. Ein Funktionsaufruf wird zu RPC, und man akzeptiert etwas Overhead für Vorteile wie die getrennte Behandlung einzelner Komponenten, etwa für Patches
Worin unterscheiden sich verteilte Microservices von Microservices am selben Ort? Das Deployment ist komplexer, aber man kann Prozesse intelligenter auf besser geeigneter Hardware platzieren. Es gibt mehr Fehlermodi, dafür erhält man höhere Fehlertoleranz
Darauf gibt es keine Universallösung. Wenn man die Vorteile braucht, zahlt man die Kosten. Ich glaube, diese Debatte Microservices gegen Monolithen entsteht daraus, dass man ein Muster falsch angewandt hat, dass Werkzeuge fehlen, die das Leben leichter machen würden, oder meistens daraus, dass man 10 Jahre alte Software einfach weiter betreibt, ohne sie fortlaufend zu refaktorisieren und neu zu entwerfen, sodass sie unabhängig von der ursprünglichen Architektur unerquicklich zu bearbeiten geworden ist
„Ein Funktionsaufruf wird zu RPC“ klingt simpel, aber in dem Moment muss die Client-App plötzlich Ausführungspfade wie keinen Zugriff auf den DNS-Server, Fehler bei der Hostnamenauflösung, Host nicht erreichbar, Verbindung abgelehnt, Authentifizierung abgelehnt, nicht vertrauenswürdiges TLS-Zertifikat, Rate Limit überschritten, 301 Permanent Redirect, verzögerte Verfügbarkeit des Ergebnisses und Timeout behandeln
Selbst bei einer gut zerlegten App ist oft ein Redesign nötig, damit sie das robust abfangen kann: mit asynchronen Queues, Request-Rate-Limits, Ergebnis-Caches, Retry mit logarithmischem Backoff, konfigurierbaren Client-Credentials, Trust Stores und Ressourcen-URLs sowie Logging von Fehlern
Bei lokalen Funktionsaufrufen braucht man zusätzlich RPC-Funktionen und Parameter, um Daten bereitzustellen, die vorher einfach im Kontext vorhanden waren. Außerdem muss eine Monolith-UI dem Benutzer womöglich Netzwerklatenz und Ausfälle vermitteln, die es vorher gar nicht gab
Selbst der bestgemachte Monolith erfordert erheblichen Aufwand, um in Microservices refaktoriert zu werden
Kann dieser RPC-Aufruf sicher wiederholt werden? Mit exponentiellem Backoff? Funktioniert der API-Load-Balancer korrekt, dann braucht man dafür jetzt auch Monitoring. Wie macht man Tracing zwischen Microservices, jetzt braucht man plötzlich so etwas wie OpenTelemetry
Wie viel schwieriger ist Debugging mit Breakpoints in einer Microservices-Architektur? Wie rollt man eine Datenbanktransaktion zwischen zwei Microservices zurück? Ein einfacher Funktionsaufruf wird auf einmal viel komplexer
Immer wenn dieses Konzept verteidigt wird, lässt man die Fragmentierung außen vor, die durch die Forderung entsteht, dass Services sehr klein sein müssen. Konkreter gesagt: Wenn man einen Monolithen und einen Microservice hat, dann sind das noch keine Microservices. Der Begriff impliziert, dass alles in kleine Services zerlegt ist und es keinen Monolithen mehr gibt
Die meisten Argumente für Microservices brechen in sich zusammen, sobald man erkennt, dass es zwingend um „Micro“ gehen muss. Und wenn man diese Anforderung aufgibt, bleibt daran nichts Neues oder Tiefgehendes mehr übrig
Aus Betriebssicht ist „Das Deployment ist komplexer“ eine massive Untertreibung. Jedes Paket ist ein eigenständig zu verwaltendes Objekt mit eigenen Eigenschaften und Problemen
Ähnlich wie Frontend-Entwickler glauben, man könne ohne Frontend-Frameworks oder -Bibliotheken wie React/Vue/Svelte keine „moderne Web-App“ bauen, scheint heute auch die Vorstellung umzugehen, ein „Monolith“ sei ein furchteinflößender riesiger Teerklumpen, der als einzelne Instanz läuft und deshalb „nicht skaliert“ — das ist Unsinn
Ein weiterer Punkt ist, dass die gesamte Codebasis viel größer wird und die meisten Services aus 20 % Business-/Domänencode und 80 % Verarbeitung von Netzwerk-Nachrichtenversand und -empfang bestehen. Man kann das noch so gut verstecken, am Ende ist es da, und man muss das Netzwerk auf die eine oder andere Weise behandeln
Wie beim Frontend-Wahnsinn wird auch dieser Microservices-Kult erst enden, wenn die Wirtschaft zusammenbricht und kein Geld mehr da ist, um diese Türme von Babel des Untergangs zu finanzieren
Ergänzend: Es gibt durchaus Einsatzorte für Microservices. Innerhalb mancher Unternehmen ist das sinnvoll, weil der erzielte Wert die Kosten übersteigt
Es gibt zwar technische Vorteile wie unabhängige Skalierung pro Service, aber der größte Vorteil sind klare Ownership-Grenzen und die Vermeidung unnötig enger Kopplung
Wenn ein kleines Team ein einzelnes Produkt baut, gibt es die organisatorischen Probleme, die Microservices lösen, womöglich noch gar nicht, also braucht man sie vielleicht auch nicht. In diesem Fall sind Microservices vorzeitige Optimierung, und man zahlt Kosten, um Probleme zu lösen, die noch gar nicht existieren
Darüber hinaus ist Frontend-Komplexität komplett im Kopf: https://bower.sh/front-end-complexity
Der Text scheint davon auszugehen, dass die Entwicklungsorganisation völlig kaputt und außer Kontrolle ist. Man kann nicht festlegen, was während eines Sprints getan werden soll, schmuggelt heimlich Third-Party-Bibliotheken und unbekannte Sprachen ein und spielt stillschweigend inkompatible Änderungen in die Produktion aus
Wenn die Entwickler keine Idioten sind, scheint auch Microservices einfacher zu sein
Wenn man in einem Team aus klugen und fähigen Entwicklern arbeitet, bin ich sicher, dass Microservices einfacher sind. Wobei dann natürlich alles einfacher wäre