Entwicklung & Code
Deep Dive Spring Modulith Teil 1: Fachliche Module im Fokus
Mit Spring Modulith ist es möglich, Spring-Boot-Anwendungen in fachliche Module aufzuteilen, die über klare Schnittstellen verfügen, per Events kommunizieren und sich isoliert testen lassen. Die Architektur der Module lässt sich automatisiert überwachen und dokumentieren. Diese Artikelserie stellt Spring Modulith, dessen Version 2.0 im November 2025 erschien, in zwei Teilen vor. Der erste Teil zeigt die Grundlagen der Modul- und Event-basierten Architektur. Teil 2 wirft unter anderem einen Blick auf die Testmöglichkeiten der Module.
Weiterlesen nach der Anzeige

Nils Hartmann ist freiberuflicher Softwareentwickler und Coach mit den Schwerpunkten Java im Backend und React im Frontend, wozu er auch Workshops und Trainings gibt.
Von klassischen Architekturen zum modularen Monolithen
Spring Boot ist eine typische Wahl bei der Entwicklung von Java-basierten Anwendungen. Um den damit entwickelten Code auch langfristig beherrschbar zu halten, werden oft Architekturmuster wie Schichten-, Hexagonal- oder Onion-Architektur angewendet. Diese Muster teilen die Anwendung aber oft nur in eher technische Bestandteile auf, um beispielsweise eine Entkopplung von eingesetzten Datenbanken oder anderen externen Systemen zu ermöglichen. In der klassischen Layer-Architektur greifen beispielsweise Controller auf Services und Services auf Repositorys zu, nicht aber Repositorys auf Services oder Controller.
In der Hexagonal-Architektur werden einzelne Komponenten wie UI- und Persistenzschicht sowie der fachliche Anwendungskern über „Ports“ und „Adapter“ voneinander separiert. Das soll die Domain-Logik von technischen Details, wie der Anbindung an eine konkrete Datenbank, trennen. Außerdem sollen diese Architekturstile ermöglichen, dass einzelne Teile der Anwendung isoliert testbar und jederzeit austauschbar sind, etwa beim Wechsel der Datenbank. Spring Modulith geht einen anderen Weg.
Modulith ist ein Kofferwort aus „modularer Monolith“. Die Anwendung wird als Monolith entwickelt, ist aber intern nach streng fachlichen Modulen (auch Slices genannt) aufgeteilt. Der potenziell komplexe fachliche Kern einer Anwendung soll so in beherrschbare kleinere Einheiten zerteilt werden, die in sich abgeschlossen mit klaren Schnittstellen versehen sind und alles enthalten, was zur Umsetzung der jeweiligen Fachlichkeit nötig ist. Änderungen an fachlichen Anforderungen haben somit im besten Fall nur Auswirkungen auf genau ein Modul. Die Module einer Anwendung können nach den oben genannten Architekturstilen implementiert werden, müssen es aber nicht. Ähnlich wie bei Microservices kann auch hier pro Modul – jedenfalls in einem gewissen Rahmen – die jeweils passende Architektur gewählt werden.
Dieser Artikel stellt Spring Modulith anhand der Beispielanwendung „Plantify“ vor. Plantify bietet die Möglichkeit, dass Kunden ihre Pflanzen pflegen lassen. Dazu registrieren sie zunächst ihre Pflanzen. Plantify legt für jede Pflanze Pflegeaufgaben an, die dann ein Dienstleister abarbeitet. Für die durchgeführten Aufgaben schickt Plantify regelmäßig eine Rechnung an die Kunden. Die Kommunikation mit der Anwendung findet über eine HTTP-API statt. Der Sourcecode der Beispielanwendung ist auf GitHub zu finden.
(Bild: buraratn/123rf)

Bei der Online-Konferenz betterCode() Spring stehen am Vormittag sichere Anwendungen mit Spring Security und die Integration von KI mit Spring AI im Fokus. Der Nachmittag widmet sich Spring Boot und zeigt die Neuerungen von Version 4 im Zusammenspiel mit Java 25, Tipps zur Integration von Containern sowie in der Praxis bewährte Spring Boot Hacks.
Application Module mit Spring Modulith
Weiterlesen nach der Anzeige
Um Spring Modulith in der eigenen Anwendung zu verwenden, fügt man dessen Starter-Paket in der eigenen Maven- oder Gradle-Konfiguration hinzu. Das reicht schon aus, damit Spring Modulith die Packages in der Anwendung unterschiedlich klassifiziert und behandelt. Alle Packages, die sich direkt unterhalb des Root-Packages befinden (das ist das Package mit der Klasse SpringBootApplication), betrachtet Spring Modulith als „Application Module“. Technisch bleiben es zwar weiterhin normale Java-Packages, aber Spring Modulith legt der Verwendung dieser Application Modules einige Regeln auf. Zum Beispiel darf es zwischen den Application Modules nicht zu direkten oder indirekten zirkulären Abhängigkeiten kommen.
In der Plantify-Anwendung gibt es etwa direkt unterhalb des Root-Packages nh.plantify drei Packages plant, care und billing. Diese drei Packages interpretiert Spring Modulith als Application Modules. In der Anwendung greift das Application Module plant auf care zu (z. B. um die Pflegeaufgaben einer neuen Pflanze anzulegen). Außerdem greift care auf billing zu, um die Einrichtungsgebühr zu berechnen. Diese Abhängigkeiten sind erlaubt, denn sie zeigen nur in eine Richtung. Würde die Anwendung aber erweitert, sodass eine Klasse aus dem billing-Modul auf das plant-Modul zugreifen würde, ergäbe sich daraus eine zirkuläre Abhängigkeit. Diese erkennt und verbietet Spring Modulith, da diese Art der Abhängigkeit oft zu Problemen in der weiteren Entwicklung führt.
Das Einhalten der Regeln lässt sich mit Spring Modulith auf mehreren Wegen kontrollieren und sicherstellen. Zum einen kann Spring Modulith sie bei jedem Start der Anwendung prüfen und bei Verstößen Fehler ausgeben. Das kostet allerdings Zeit und findet im Entwicklungsprozess erst spät statt. Alternativ lässt sich ein JUnit-Test implementieren, der die Regel überprüft. Ein entsprechendes Beispiel findet sich in Listing 1.
class PlantifyModuleTest {
static ApplicationModules modules = ApplicationModules.of(PlantifyApplication.class);
@Test
void verifyModules() {
modules.verify();
}
@Test
void writeDocumentationSnippets() {
new Documenter(modules)
.writeModulesAsPlantUml();
}
}
Listing 1: Die Modulstruktur wird im JUnit-Test überprüft und visualisiert
Die Klasse ApplicationModules repräsentiert die erkannten Application Modules im übergebenen Paket. Die verify-Methode stellt innerhalb des Tests sicher, dass alle Regeln eingehalten werden. Bei Verstößen schlägt der Test mit einer ausführlichen Fehlermeldung fehl (siehe Abbildung 1).

Der Unit-Test deckt Regelverstöße (zirkuläre Abhängigkeiten) auf (Abb. 1).
Diese Tests werden mit allen anderen „normalen“ Tests einer Anwendung kontinuierlich ausgeführt, sodass Probleme schnell auffallen. Die ApplicationModules-Klasse kann außerdem die Modulstruktur als C4-Diagramm ausgeben, aus der auch die Art der Verwendungen eines Moduls hervorgeht (dazu später mehr). Die Visualisierung der Plantify-Module zu diesem Zeitpunkt ist in Abbildung 2 zu sehen.

Die initiale Modulstruktur, visualisiert von Spring Modulith (Abb. 2).
Interne Module
Neben dem Verbot zirkulärer Abhängigkeiten wendet Spring Modulith noch eine andere Regel von Haus aus an: Alle Unterpakete in einem Application Module gelten als „intern“. Klassen daraus dürfen zwar innerhalb des Application Module beliebig verwendet werden, allerdings ist der Zugriff darauf aus anderen Modulen verboten. Mit anderen Worten: Das Root-Package eines Application Module ist dessen öffentliche API. Nur die Public-Klassen stehen anderen Modulen zur Verfügung. Der oben gezeigte Test Case prüft auch die Einhaltung dieser Regel. Zusätzlich können IDEs wie IntelliJ, Eclipse oder VS Code mit den entsprechenden Plug-ins diese Regeln prüfen und direkt im Quellcode anzeigen. Abbildung 3 zeigt ein entsprechendes Beispiel in Eclipse mit den Spring Tools.

Die Verwendung interner Klassen zeigt Eclipse mit den Spring Tools als Fehler an (Abb. 3).
Hier wird aus dem CareTaskService, der sich im Modul care befindet, auf die Klasse InvoiceGenerator zugegriffen, die sich in dem Unterpaket invoice des billing-Moduls befindet. Ohne Spring Modulith wäre der Zugriff aus Java-Sicht regelkonform, da die Klasse InvoiceGenerator public ist, und somit von allen Packages aus verwendet werden darf.
Entwicklung & Code
Warum T-förmiges Wissen in Zeiten von KI wichtiger wird denn je
Die Softwarebranche erlebt gerade einen Umbruch, der viele Entwicklerinnen und Entwickler verunsichert. Tools wie Claude Code, Codex, Copilot und andere KI-gestützte Assistenten übernehmen Aufgaben, die noch vor wenigen Jahren als Kernkompetenz galten. Code schreiben, Fehler finden, Dokumentation erstellen: All das erledigen diese Werkzeuge inzwischen in beeindruckender Qualität. Die Frage, welche Fähigkeiten in einer solchen Welt noch gefragt sind, beschäftigt Einsteigerinnen und Einsteiger ebenso wie erfahrene Fachkräfte.
Weiterlesen nach der Anzeige

Golo Roden ist Gründer und CTO von the native web GmbH. Er beschäftigt sich mit der Konzeption und Entwicklung von Web- und Cloud-Anwendungen sowie -APIs, mit einem Schwerpunkt auf Event-getriebenen und Service-basierten verteilten Architekturen. Sein Leitsatz lautet, dass Softwareentwicklung kein Selbstzweck ist, sondern immer einer zugrundeliegenden Fachlichkeit folgen muss.
Doch diese aktuellen Diskussionen sind nur der jüngste Gipfel einer Entwicklung, die ich schon seit über zwanzig Jahren in der Branche beobachte, und meine Antwort auf diese Frage ist eindeutig (und seit jeher die gleiche): Der Schlüssel zu einer erfolgreichen Karriere in der Softwareentwicklung schlechthin war schon immer und ist weiterhin das sogenannte T-förmige Wissen. Doch was früher ein Vorteil war, wird heute zur Notwendigkeit. Wer in Zeiten von KI bestehen will, muss verstehen, was hinter den Werkzeugen steckt, die sie oder er täglich einsetzt. Oberflächliches Wissen, das gestern noch ausgereicht hat, um Aufgaben zu erledigen, genügt morgen nicht mehr.
Was T-förmiges Wissen bedeutet
Das Konzept des T-förmigen Wissens stammt ursprünglich aus dem Management und beschreibt ein Kompetenzprofil, das zwei Dimensionen vereint. Der vertikale Balken des T steht für tiefe Expertise in einem spezifischen Fachgebiet. Hier geht es um echte Meisterschaft, um ein Verständnis, das über die Oberfläche hinausreicht und auch die Randfälle, die Geschichte und die Designentscheidungen eines Themenfeldes umfasst. Der horizontale Balken repräsentiert ein breites Grundlagenwissen, das über die eigene Spezialisierung hinausreicht und es ermöglicht, Verbindungen herzustellen und in verschiedenen Kontexten handlungsfähig zu sein.
In der Softwareentwicklung bedeutet das konkret: Eine Entwicklerin oder ein Entwickler mit T-förmigem Profil beherrscht ein Themenfeld wirklich fundiert, sei es eine bestimmte Technologie, eine Architekturform oder einen methodischen Ansatz. Diese Person kann nicht nur Standardaufgaben lösen, sondern auch ungewöhnliche Probleme analysieren, Entscheidungen begründen und andere anleiten. Gleichzeitig verfügt sie über solides Wissen in angrenzenden Bereichen: Algorithmen, Datenstrukturen, Betriebssysteme, Netzwerke, Security und vieles mehr.
Das unterscheidet T-förmige Fachkräfte sowohl von reinen Spezialisten als auch von Generalisten. Spezialistinnen und Spezialisten kennen ihr Fachgebiet zwar in- und auswendig, können aber außerhalb davon kaum Zusammenhänge herstellen. Sie sind gefangen in ihrer Nische und hilflos, sobald ein Problem die Grenzen ihres Expertenwissens überschreitet. Generalisten wissen von allem ein wenig, erreichen aber nirgendwo eine Tiefe, die echten Mehrwert schafft. Sie können mitreden, aber keine schwierigen Probleme lösen. Das T-Profil kombiniert das Beste aus beiden Welten: tiefes Verständnis dort, wo es zählt, und genug Breite, um größere Zusammenhänge zu erkennen und interdisziplinär zu arbeiten.
Warum eine Programmiersprache reicht, wenn man sie wirklich beherrscht
Weiterlesen nach der Anzeige
In Stellenanzeigen und Diskussionen über Karrierechancen geht es oft um konkrete Technologien. Java oder C#? Python oder Go? React oder Angular? Diese Fragen führen regelmäßig zu leidenschaftlichen Debatten, doch sie lenken vom Wesentlichen ab. Sie suggerieren, dass die Wahl der richtigen Technologie über Erfolg und Misserfolg entscheidet. Das ist ein Irrtum.
Meine Erfahrung zeigt: Es ist nahezu irrelevant, welche Programmiersprache jemand als Erste wirklich beherrscht. Was zählt, ist die Tiefe des Verständnisses. Wer eine Sprache wirklich durchdrungen hat, wer ihre Paradigmen, ihre Stärken und Schwächen, ihre typischen Anwendungsmuster verstanden hat, kann andere Sprachen vergleichsweise schnell erlernen. Die Syntax mag sich unterscheiden, die zugrundeliegenden Konzepte bleiben oft ähnlich.
Objektorientierung funktioniert in Java, C#, Python und vielen anderen Sprachen nach denselben Grundprinzipien. Kapselung, Vererbung, Polymorphie: Diese Konzepte tragen verschiedene Namen und haben unterschiedliche syntaktische Ausprägungen, aber ihr Kern bleibt derselbe. Funktionale Konzepte wie Immutability, reine Funktionen oder Higher-Order-Functions finden sich in Haskell ebenso wie in JavaScript oder Kotlin. Wer diese Konzepte einmal verstanden hat, erkennt sie überall wieder. Der Wechsel von einer Sprache zur anderen wird dann zur Übersetzungsaufgabe, nicht zum Neulernen.
Die Programmiersprache ist ein Werkzeug, nicht das Ziel. Ein Handwerker, der sein Handwerk versteht, kann mit verschiedenen Werkzeugen arbeiten. Er muss nicht für jede Schraube einen neuen Beruf erlernen. Genauso verhält es sich in der Softwareentwicklung: Die Konzepte sind übertragbar, die konkrete Sprache ist austauschbar. Wer das begriffen hat, verliert die Angst vor neuen Technologien und gewinnt die Freiheit, das jeweils passende Werkzeug zu wählen.
Die Grundlagen, die niemand überspringen sollte
Neben der tiefen Beherrschung einer Programmiersprache gibt es einen Kanon an Grundlagenwissen, der für jede Softwareentwicklerin und jeden Softwareentwickler unverzichtbar ist. Dieser Kanon bildet den horizontalen Balken des T. Dabei geht es nicht darum, in jedem dieser Bereiche zum Experten zu werden. Das wäre weder realistisch noch notwendig. Es geht darum, die verschiedenen Abstraktionsebenen zu verstehen und konzeptionell einordnen zu können. Wer die Schichten kennt, auf denen die eigene Arbeit aufbaut, trifft bessere Entscheidungen und erkennt Probleme früher.
Algorithmen und Datenstrukturen bilden das Fundament. Wer nicht versteht, warum eine Hash-Map schneller ist als eine lineare Suche, oder wann ein Baum einer Liste vorzuziehen ist, wird dauerhaft suboptimale Entscheidungen treffen. Das gilt unabhängig davon, ob man diese Strukturen selbst implementiert oder fertige Bibliotheken verwendet. Denn auch die Auswahl der richtigen Bibliothek erfordert dieses Verständnis. Eine falsche Datenstruktur kann den Unterschied zwischen einer responsiven Anwendung und einem trägen System ausmachen.
Eng damit verbunden ist die Laufzeitkomplexität. Die O-Notation mag auf den ersten Blick akademisch wirken, doch sie entscheidet im Alltag darüber, ob eine Anwendung performant läuft oder bei größeren Datenmengen in die Knie geht. Wer einmal erlebt hat, wie eine harmlos aussehende, verschachtelte Schleife einen Server lahmlegt, vergisst diese Lektion nicht mehr. Der Unterschied zwischen O(n) und O(n^2) ist bei kleinen Datenmengen vernachlässigbar, bei großen Datenmengen jedoch fatal.
Auf einer tieferen Ebene liegt das Verständnis von Speicherverwaltung. Stack und Heap, Referenzen und Werte, Garbage Collection und manuelles Memory-Management: Diese Konzepte bestimmen, wie Programme mit Ressourcen umgehen. Auch wer in einer Hochsprache mit automatischer Speicherverwaltung arbeitet, profitiert davon, die darunterliegende Mechanik zu verstehen. Memory-Leaks, Performanceprobleme und schwer zu findende Bugs haben ihre Ursache oft in mangelndem Verständnis dieser Grundlagen. Wer weiß, was unter der Haube passiert, debuggt effizienter und schreibt robusteren Code.
Security ist ein weiteres Feld, das niemand ausklammern sollte. Die häufigsten Sicherheitslücken basieren auf Fehlern, die mit grundlegendem Wissen vermeidbar wären: SQL-Injection, Cross-Site-Scripting, fehlerhafte Authentifizierung. Diese Angriffsvektoren sind seit Jahrzehnten bekannt, und dennoch tauchen sie immer wieder auf. Sicherheit ist keine Aufgabe für Spezialistinnen und Spezialisten allein, sondern muss von allen mitgedacht werden, die Code schreiben. Ein Grundverständnis von Bedrohungsmodellen und Abwehrstrategien gehört zum Handwerkszeug.
Schließlich gehört auch Architekturwissen zum unverzichtbaren Repertoire. Wie strukturiert man eine Anwendung so, dass sie wartbar, erweiterbar und testbar bleibt? Welche Muster und Prinzipien haben sich bewährt? Wann ist ein Monolith die richtige Wahl, wann eine verteilte Architektur? Diese Fragen lassen sich nur beantworten, wenn man verschiedene Ansätze kennt und ihre Vor- und Nachteile einschätzen kann. Architekturentscheidungen haben langfristige Konsequenzen und sind oft schwer zu revidieren. Wer sie fundiert trifft, spart später viel Aufwand.
Domänenwissen und Kommunikation als Multiplikatoren
Technisches Wissen allein macht allerdings noch keine erfolgreiche Karriere. Mindestens zwei weitere Faktoren wirken als Multiplikatoren und unterscheiden durchschnittliche von herausragenden Entwicklerinnen und Entwicklern. Ohne diese Faktoren bleibt technische Kompetenz abstrakt und entfaltet nicht ihr volles Potenzial.
Der erste Faktor ist Domänenwissen. Software existiert nicht im luftleeren Raum. Sie löst Probleme in konkreten Anwendungsfeldern: Finanzwesen, Gesundheitswesen, Logistik, E-Commerce oder unzähligen anderen Bereichen. Wer die Fachlichkeit der eigenen Domäne versteht, wer mit Anwenderinnen und Anwendern auf Augenhöhe sprechen kann, wer die Geschäftsprozesse kennt und ihre Tücken durchschaut, bringt einen Mehrwert ein, den rein technische Expertise nicht bieten kann.
Domänenwissen ermöglicht es, die richtigen Fragen zu stellen. Es schützt davor, technisch brillante Lösungen für die falschen Probleme zu bauen. Es hilft dabei, Anforderungen kritisch zu hinterfragen und Alternativen vorzuschlagen, die den eigentlichen Bedarf besser treffen. Wer versteht, warum ein Geschäftsprozess so funktioniert, wie er funktioniert, kann ihn intelligenter digitalisieren. Diese Fähigkeit wird umso wertvoller, je mehr die reine Code-Produktion automatisiert wird.
Der zweite Faktor ist Kommunikation. Softwareentwicklung ist Teamarbeit. Ideen müssen vermittelt, Entscheidungen begründet, Konflikte gelöst werden. Wer komplexe technische Sachverhalte verständlich erklären kann, wer zuhört und nachfragt, wer Feedback geben und annehmen kann, wird zum wertvollen Bindeglied zwischen Technik und Fachlichkeit. In einer Welt, in der KI immer mehr Routineaufgaben übernimmt, werden genau diese menschlichen Fähigkeiten wichtiger, nicht unwichtiger.
Kommunikationsfähigkeit ist keine Soft-Skill-Nebensache. Sie entscheidet darüber, ob gute Ideen umgesetzt werden oder in Meetings versanden. Sie bestimmt, ob ein Team effektiv zusammenarbeitet oder aneinander vorbei entwickelt. Die beste technische Lösung nützt wenig, wenn sie niemand versteht oder akzeptiert.
Was KI an dieser Gleichung verändert
KI-gestützte Entwicklungswerkzeuge sind beeindruckend. Sie generieren funktionierenden Code aus natürlichsprachlichen Beschreibungen. Sie schlagen Vervollständigungen vor, die oft genau das treffen, was man ohnehin schreiben wollte. Sie finden Fehler, erklären komplexe Codepassagen und erstellen Dokumentation. Für Routineaufgaben sind sie bereits heute unverzichtbare Helfer.
Doch diese Fähigkeiten haben Grenzen, die sich bei genauerem Hinsehen offenbaren. KI-Tools sind gut darin, Muster zu erkennen und zu reproduzieren. Sie versagen dort, wo es um echtes Verständnis geht. Sie generieren Code, der syntaktisch korrekt ist, aber subtile logische Fehler enthält. Sie schlagen Lösungen vor, die funktionieren, aber nicht skalieren. Sie produzieren en passant Sicherheitslücken, weil sie den Kontext nicht verstehen. Ohne menschliche Überprüfung entsteht technische Schuld im Zeitraffer.
Hier zeigt sich der Wert von t-förmigem Wissen: Wer die Grundlagen versteht, kann KI-generierten Code bewerten. Wer Algorithmen kennt, erkennt ineffiziente Lösungen. Wer Security-Prinzipien verinnerlicht hat, sieht Sicherheitslücken, bevor sie zu Problemen werden. Wer Architekturerfahrung mitbringt, kann einschätzen, ob ein Vorschlag ins Gesamtbild passt oder nicht.
KI verschiebt die Anforderungen, aber sie eliminiert sie nicht. Routineaufgaben werden automatisiert, doch die Fähigkeit, diese Automatisierung sinnvoll einzusetzen, erfordert mehr Verständnis, nicht weniger. Der Programmierer der Zukunft wird weniger Zeit mit dem Schreiben von Boilerplate-Code verbringen, aber mehr Zeit mit dem Verstehen, Bewerten und Integrieren von Lösungen. Das erfordert fundiertes Wissen, das über Copy-Paste-Kompetenz hinausgeht.
Wer künftig nicht mehr gebraucht wird
Meine These ist klar: Wer nur einen einzelnen Aspekt aus der beschriebenen Kombination abdeckt, wird es in Zukunft schwer haben. Die Zeit der Spezialisten ohne Breite und der Generalisten ohne Tiefe neigt sich dem Ende zu.
Wer nur eine Programmiersprache kennt, aber weder Tiefe noch Breite mitbringt, ist austauschbar. Die Syntax einer Sprache beherrscht jedes KI-Tool aus dem Stegreif. Das Verständnis der dahinterliegenden Konzepte nicht. Wer Java schreiben kann, aber nicht erklären kann, warum ein bestimmtes Pattern in einem bestimmten Kontext sinnvoll ist, verliert gegenüber einer KI, die dasselbe schneller produziert.
Wer nur Grundlagenwissen hat, ohne es praktisch anwenden zu können, bleibt theoretisch. Wissen, das nie zur Anwendung kommt, verliert seinen Wert. Die Fähigkeit, Theorie in funktionierende Lösungen zu übersetzen, macht den Unterschied zwischen akademischem Interesse und beruflicher Kompetenz.
Wer nur Domänenwissen mitbringt, ohne technisches Verständnis, kann zwar Anforderungen formulieren, aber nicht einschätzen, was realistisch umsetzbar ist. Die Brücke zwischen Fachlichkeit und Technik kann nur schlagen, wer beide Seiten versteht. Andernfalls entstehen Spezifikationen, die an der Realität vorbeigehen.
Wer nur kommunizieren kann, aber weder technisch noch fachlich fundiert ist, wird schnell als oberflächlich wahrgenommen. Kommunikation ohne Substanz erzeugt keinen Mehrwert. Sie führt zu Meetings ohne Ergebnisse und Präsentationen ohne Tiefgang.
Die Zukunft gehört denen, die diese Fähigkeiten kombinieren. Das bedeutet nicht, dass jede Einzelperson in allem exzellent sein muss. Es bedeutet, dass ein solides Fundament in allen Bereichen notwendig ist, ergänzt durch tiefe Expertise in mindestens einem davon. Diese Kombination macht den Unterschied zwischen ersetz- und unverzichtbar.
Der eigentliche Karrierehebel ist Lernfähigkeit
T-förmiges Wissen ist kein Zustand, den man einmal erreicht und dann für immer hält. Technologien verändern sich. Domänen entwickeln sich weiter. Was heute tiefe Expertise darstellt, kann in zehn Jahren Basiswissen sein. Was heute als Grundlage gilt, wird möglicherweise durch neue Paradigmen abgelöst. Die einzige Konstante in unserer Branche ist der Wandel.
Der eigentliche Karrierehebel ist deshalb nicht das Wissen selbst, sondern die Fähigkeit, kontinuierlich zu lernen. Wer gelernt hat zu lernen, wer weiß, wie man sich neue Themenfelder erschließt, wer die Disziplin aufbringt, regelmäßig in die eigene Weiterbildung zu investieren, kann sich immer wieder neu aufstellen. Diese Fähigkeit ist wichtiger als jedes konkrete Wissen, denn sie ermöglicht es, konkretes Wissen immer wieder zu erneuern.
Diese Lernfähigkeit ist selbst eine Fähigkeit, die man entwickeln kann. Sie umfasst das Wissen darum, wie man effektiv lernt, welche Ressourcen zuverlässig sind, wie man Wichtiges von Unwichtigem unterscheidet. Sie erfordert die Bereitschaft, sich immer wieder auf Anfängerniveau zu begeben, Fehler zu machen und daraus zu lernen. Das ist unbequem, aber unvermeidlich.
KI-Tools können bei diesem Lernprozess unterstützen. Sie können Konzepte erklären, Beispiele generieren, Fragen beantworten. Doch die Entscheidung, was man lernt und warum, bleibt beim Menschen. Die Integration neuen Wissens in das eigene Verständnisgebäude ist eine kognitive Leistung, die keine KI abnehmen kann. Lernen bleibt Arbeit, auch wenn die Werkzeuge besser werden.
Wer heute in T-förmiges Wissen investiert, tut also mehr, als nur aktuelle Kompetenzlücken zu schließen. Sie oder er entwickelt die Grundlage für lebenslanges Lernen in einer Branche, deren einzige Konstante der Wandel ist. Und genau das macht den Unterschied zwischen einer Karriere, die von technologischen Umbrüchen bedroht wird, und einer, die von ihnen profitiert.
(rme)
Entwicklung & Code
Type Punning in C++: Der saubere Ansatz mit C++20

Andreas Fertig ist erfahrener C++-Trainer und Berater, der weltweit Präsenz- sowie Remote-Kurse anbietet. Er engagiert sich im C++-Standardisierungskomitee und spricht regelmäßig auf internationalen Konferenzen. Mit C++ Insights ( hat er ein international anerkanntes Tool entwickelt, das C++-Programmierenden hilft, C++ noch besser zu verstehen.
Der heutige Beitrag widmet sich der Typumwandlung via Type Punning in C++. Das habe ich jahrelang gemacht, als ich im Bereich Embedded-Software gearbeitet habe, und andere haben das schon lange vor mir gemacht. Laut Standard ist das zu 100 Prozent verboten. Trotzdem weiß ich, dass viele Embedded-Geräte mit Type Punning gebaut werden, obwohl es nicht nur verboten ist, sondern auch zu unbestimmtem Verhalten (Undefined Behavior, UB) führt.
Weiterlesen nach der Anzeige
Das undefinierte Verhalten bei Type Punning
Anhand des folgenden Codes möchte ich klären, was ich unter Type Punning verstehe und warum es sich dabei um undefiniertes Verhalten in C++ handelt.
float pi = 3.14f; // #A
// #B
uint32_t first = static_cast(pi);
// #C
uint32_t second = *reinterpret_cast(&pi);
Der Code soll die Bitdarstellung des float in #A in eine Ganzzahl umwandeln. Es geht nicht darum, den Wert 3,14 in eine Ganzzahl umzuwandeln, die 3 wäre. Das Ziel ist es, die Bitdarstellung (0x4048f5c3) zu erhalten.
Ich stelle mir zwei Versuche vor, die ich oft gesehen habe. Der erste in #B verwendet ein static_cast. Dieser Versuch lässt sich zwar kompilieren und ist zu 100 Prozent gültiges C++, aber das Ergebnis ist nicht das, was du suchst, da du die konvertierten Werte erhältst und am Ende die Zahl 3 in first hast.
Der zweite Versuch muss natürlich cleverer sein, und ich würde sagen, dass #C tatsächlich clever aussieht. Dieser Code holt sich zuerst die Adresse des float und nutzt dann reinterpret_cast, um den float-Zeiger in einen int-Zeiger umzuwandeln, und schließlich den frisch erhaltenen Zeiger auf einen int zu dereferenzieren. Du beugst einfach die Regeln von C++ (und übrigens auch von C) so weit, dass der Compiler die Umwandlung zulässt. Erfolg!
Weiterlesen nach der Anzeige
Nun ja – dass der Code kompiliert wird, ist nur der erste Schritt! Als Nächstes muss er verknüpft (linked) werden, was ebenfalls erfolgreich ist. Dann muss der Code das tun, was du geplant hast. Hier wird es knifflig. Du hast gerade Code geschrieben, der undefiniertes Verhalten enthält.
Indem du bei der Konvertierungssequenz zu clever warst, hast du dem Compiler die Möglichkeit gegeben, die Zuweisung zu second zu optimieren. Das hängt im Wesentlichen mit der Lebensdauer von Objekten und den entsprechenden Regeln zusammen. Grob gesagt wurde der int, den du in #C zuweist, aus Sicht des Compilers nie aktiv. Es gibt keinen Konstruktor und keine zulässige Konvertierungssequenz, die den Compiler auf den Beginn der Lebensdauer aufmerksam machen würde. Eine perfekte Gelegenheit für den Compiler, uns einige Anweisungen zu ersparen, indem er diese gesamte Zuweisung optimiert.
Ich habe Kunden, die aus diesem Grund die Optimierungsstufe nicht höher als -O1 einstellen.
C++20 zur Rettung
Als ich über den Teil „zur Rettung“ nachdachte, kam mir die Fernsehserie Baywatch in den Sinn, in der die Rettungsschwimmer mit roten Rettungsbojen (Safety Buoy) ins Wasser rannten.
Okay, zurück vom Strand ins Büro. C++20 hat eine Rettungsboje namens std::bit_cast für den oben beschriebenen Fall. Anstatt eine Menge Code zu schreiben, um den Compiler zur gewünschten Konvertierungssequenz zu verleiten, wende std::bit_cast auf die gleiche Weise an wie first static_cast. Dein Code sieht dann folgendermaßen aus:
const float pi = 3.14f;
const uint32_t pii = std::bit_cast(pi);
Wie du siehst, kommt std::bit_cast wirklich wie ein static_cast daher. Du gibst den Zieldatentyp in den spitzen Klammern und die Quellvariable oder den Quellwert als Argument an. Als Ergebnis erhältst du ein Objekt vom Zieldatentyp.
Intern nutzt std::bit_cast memcpy, um die Quellbits in den Zielpuffer zu kopieren, bevor dieser zurückgegeben wird. Das funktioniert, weil memcpy seit C++20 vom Standard als Element anerkannt ist, das die Lebensdauer eines Objekts startet.
Wenn du mit einer Situation wie der am Anfang des Beitrags konfrontiert bist, solltest du sicherheitshalber immer std::bit_cast bevorzugen, wenn du kannst.
(rme)
Entwicklung & Code
programmier.bar: Data-Aware Architecture mit Matthias Niehoff
In dieser Podcastfolge spricht die programmier.bar mit Matthias Niehoff, Head of Data und Principal Data Architect bei Codecentric, über das Konzept der Data-Aware Architecture. Dabei handelt es sich weniger um ein klar abgegrenztes Architektur-Pattern als um einen Denkansatz: Daten sollen von Beginn an systematisch in Architekturentscheidungen einbezogen werden, insbesondere mit Blick auf analytische Nutzung. Ziel ist es, Daten verlässlich, nachvollziehbar und strukturiert nutzbar zu machen, ohne die Anwendung selbst unnötig komplex zu gestalten.
Weiterlesen nach der Anzeige
Data-Aware Architecture in der Praxis
Matthias Niehoff beschreibt, wie sich Data-Aware Architecture in der Praxis umsetzen lässt. Dazu zählen unter anderem die frühzeitige Einbindung von Analytics-Teams, die Definition von Data Contracts sowie die Etablierung sogenannter Data Products als stabile Schnittstellen zwischen Entwicklungsteams und Datenkonsumenten. Diese sollen dokumentierte, validierbare Datenflüsse ermöglichen, die auch bei Änderungen in den zugrunde liegenden Systemen konsistent bleiben. Entscheidend sei dabei ein pragmatischer Ansatz: Weder große Plattformen noch zusätzliche Tools lösen das Grundproblem allein, vielmehr komme es auf klare Verantwortlichkeiten und Abstimmung zwischen Teams an.
Empfohlener redaktioneller Inhalt
Mit Ihrer Zustimmung wird hier ein externer Inhalt geladen.
Ein besonderer Fokus liegt auf dem Umgang mit Legacy-Systemen. Matthias Niehoff erläutert, wie bestehende Schnittstellen analysiert, Verantwortlichkeiten geklärt und Data Contracts schrittweise eingeführt werden können – auch in gewachsenen Systemlandschaften. Abschließend ordnet er die Rolle von KI im Kontext Data-Aware Architecture ein, etwa als Unterstützung bei semantischem Kontext-Engineering oder als direkter Konsument strukturierter Daten.
(Bild: RONY/Adobe Stock)

Die Online-Konferenz betterCode() Modern Architecture von iX und dpunkt.verlag am 25. März 2026 stellt aktuelle Konzepte der Softwarearchitektur vor wie Clean Architecture, Hexagonale Architektur oder Microservices. Design mit LLMs ist ebenso ein Thema wie Architektur für eine digitale Souveränität.
Die aktuelle Ausgabe des Podcasts steht auch im Blog der programmier.bar bereit: „Data-Aware Architecture mit Matthias Niehoff“. Fragen und Anregungen gerne per Mail oder via Mastodon, Bluesky, LinkedIn oder Instagram.
Weiterlesen nach der Anzeige
(mdo)
-
Entwicklung & Codevor 3 MonatenKommandozeile adé: Praktische, grafische Git-Verwaltung für den Mac
-
Künstliche Intelligenzvor 2 MonatenSchnelles Boot statt Bus und Bahn: Was sich von London und New York lernen lässt
-
Social Mediavor 1 WocheCommunity Management zwischen Reichweite und Verantwortung
-
Apps & Mobile Entwicklungvor 3 MonatenHuawei Mate 80 Pro Max: Tandem-OLED mit 8.000 cd/m² für das Flaggschiff-Smartphone
-
Apps & Mobile Entwicklungvor 3 MonatenFast 5 GB pro mm²: Sandisk und Kioxia kommen mit höchster Bitdichte zum ISSCC
-
Entwicklung & Codevor 2 MonatenKommentar: Anthropic verschenkt MCP – mit fragwürdigen Hintertüren
-
Datenschutz & Sicherheitvor 2 MonatenSyncthing‑Fork unter fremder Kontrolle? Community schluckt das nicht
-
Social Mediavor 2 MonatenDie meistgehörten Gastfolgen 2025 im Feed & Fudder Podcast – Social Media, Recruiting und Karriere-Insights
