Connect with us

Entwicklung & Code

Legacy-Code Schritt für Schritt aktualisieren mit der Mikado-Methode


close notice

This article is also available in
English.

It was translated with technical assistance and editorially reviewed before publication.

Altsysteme strahlen eine gewisse Faszination aus, denn sie bieten ihre ganz eigenen, spannenden Herausforderungen. Die können viel interessanter als das Zusammenstecken moderner Frameworks zu einer Anwendung auf der grünen Wiese sein. Entwicklerinnen und Entwickler führen aber nur ungern Änderungen an Altsystemen aus, denn es ist oft nicht klar, was dabei kaputtgeht.

Weiterlesen nach der Anzeige


Falk Sippach

Falk Sippach

Falk Sippach ist Softwarearchitekt, Berater und Trainer bei der embarc Software Consulting GmbH. Er engagiert sich in der Java-Community und teilt sein Wissen in Artikeln, Blogbeiträgen und Vorträgen.

Viele Legacy-Systeme sind technologisch auf dem Stand von vor über zehn Jahren und damit nicht mehr up to date. Es fällt schwer, geeignete Mitarbeiter für die Wartung und Weiterentwicklung zu finden. Regelmäßige Aktualisierungen der eingesetzten Bibliotheken und Frameworks helfen leider nur wenig. Der Quellcode, der in den letzten Jahren schon durch so viele Hände gegangen ist, wird dadurch nicht besser.

Die größten Schwierigkeiten bestehen im fehlenden Wissen über die interne Struktur, über die Entscheidungen, die zu Beginn des Projekts getroffen wurden, und über die unklaren Auswirkungen der Änderungen. Leider lernen die meisten Entwickler in der Ausbildung oder im Studium häufig nur, wie sie neue Systeme erstellen und nicht, wie sie bestehende Software pflegen oder weiterentwickeln können.

Das Refactoring ist dabei ein Mittel zur Struktur- und Designverbesserung. Und in komplexen Umfeldern bietet die Mikado-Methode zusätzliche Unterstützung. In den Worten von Ola Ellnestam und Daniel Brolund, die die Methode erfunden haben: „Die Mikado-Methode ist ein strukturierter Weg, um signifikante Änderungen an komplexem Code vorzunehmen … für Änderungen an einem System, das zu groß ist, um es erst zu analysieren und dann zu bearbeiten. Das betrifft grundsätzlich jedes produktive System in der Welt“ (Ola Ellnestam, Daniel Brolund: The Mikado Method; Manning 2014).

Beim Editieren an einer Legacy-Code-Basis können Entwicklerinnen und Entwickler schon mal ins Schwitzen kommen. Insbesondere dann, wenn die Änderungen außer Kontrolle geraten und sie nach mehreren Stunden Arbeit wieder ganz am Anfang stehen. Es fällt leichter, wenn es eine ausführliche Dokumentation oder wenigstens automatisierte Tests gibt. Aber häufig steckt die gesamte Wahrheit nur im Quelltext und Tests sucht das Entwicklerteam vergeblich.

Weiterlesen nach der Anzeige

Hier unterstützt die Mikado-Methode dank ihres strukturierten Vorgehens. Das Refactoring von komplexem Code läuft ähnlich ab, wie das Umstellen von Möbeln in einer Wohnung. Wenn sich die alte Polstergarnitur aus Platzgründen (Tür wäre verstellt) nicht einfach durch das neu angeschaffte Dreisitzer-Sofa austauschen lässt, dann müssen größere Umbaumaßnahmen erfolgen (siehe Abbildung 1).


Beim Verrücken von Möbeln ergeben sich oft ähnliche Probleme wie beim Refactoring von Software (Abb. 1).,

Beim Verrücken von Möbeln ergeben sich oft ähnliche Probleme wie beim Refactoring von Software (Abb. 1).,

Beim Verrücken von Möbeln ergeben sich oft ähnliche Probleme wie beim Refactoring von Software (Abb. 1).

Der Esstisch muss für das neue Sofa weichen. Er soll an den Platz der alten Polstergarnitur rücken, die aber erst mal entfernt werden muss. Genau wie in der Programmierung kann man sich hier schnell im Chaos verlieren. Wenn die Bewohner einfach das neue Sofa ins Zimmer stellen, ist möglicherweise gar kein Platz mehr, um die Möbel umzustellen.

Darum werden die Schritte zunächst nur theoretisch durchdacht und die Hindernisse (zum Beispiel beim Verrücken des Esstischs) jeweils festgehalten. Im nächsten Schritt werden die Bewohner versuchen, das gerade entdeckte Problem zu beseitigen. Dabei stehen aber häufig weitere Hindernisse im Weg, wie in diesem Fall die alte Polstergarnitur. Diese muss zunächst aus der Wohnung entfernt werden. Dann kann der Esstisch umziehen und schließlich das neue Sofa endlich an den geplanten Platz gestellt werden.

Refactoring ist ein Hilfsmittel zur Verbesserung des Softwaredesigns. Bereits vor über zwanzig Jahren hat Martin Fowler in seinem Buch folgende Definition dazu festgehalten: „Refactoring (Substantiv): Eine Änderung an der internen Struktur einer Software, um sie verständlicher zu machen und kostengünstiger ändern zu können, ohne ihr sichtbares Verhalten zu ändern“ (Martin Fowler; Refactoring: Improving the Design of Existing Code; Addison Wesley 2019).

Wer den Quellcode restrukturieren will, wird dazu letztlich eine Serie von Refactorings anwenden. Dabei handelt es sich um sogenannte verhaltenserhaltende Code-Transformationen: An der fachlichen Logik ändert sich nichts. Das Ziel ist eine verständlichere interne Struktur, die zukünftige Änderungen erleichtert und die Testbarkeit erhöht. Damit das Refactoring gut funktioniert, braucht es einige Verbündete.

Dazu zählen schnelle Compile- und Build-Zeiten, vorhandene automatisierte Unit- beziehungsweise Integrationstests sowie eine Versionsverwaltung, um Änderungen jederzeit leicht rückgängig machen zu können. Zudem kann der Einsatz von Werkzeugen bei der sicheren und zügigen Durchführung unterstützen. Viele moderne Entwicklungsumgebungen wie Eclipse oder IntelliJ bringen ein komfortables Tool-Set mit.

Durch das Refactoring sichern Entwickler bestehende Investitionen in ihrer Software ab, verhindern den Designverfall und erhöhen die Lesbarkeit, Änderbarkeit sowie Testbarkeit. Sie können gegebenenfalls gut versteckte Fehler finden, Wissen durch aktives Lernen am Code transferieren, und sie helfen auch den nachfolgenden Entwicklern, die oft vergessene Nutzer des Quellcodes sind. Ganz nach dem Motto von Martin Golding: „Schreibe deinen Code so, als wäre der Typ, der ihn verstehen muss, ein Psychopath mit einer Axt, der weiß, wo du wohnst.“

Die Arbeitsweise basiert dabei auf zwei grundlegenden Prinzipien: erstens den Programmcode nicht kaputt machen und zweitens jeweils nur eine Sache bearbeiten. Bei der Durchführung gehört daher viel Selbstdisziplin dazu. Entwickler sollen immer nur kleine Schritte machen und dann den Code kompilieren sowie die Tests ausführen. Anschließend beginnt es wieder von vorn.

Wichtig ist auch Kent Becks Metapher der zwei Hüte. Man kann immer nur einen Hut auf den Kopf setzen. Daher sollte beim Refactoring kein inhaltlicher Code geändert, also keine neuen Features eingebaut oder Bugs repariert werden.

Angewendet werden können Refactorings bei unterschiedlichen Gelegenheiten: zum Beispiel regelmäßig täglich eine halbe Stunde, damit der Code gesund bleibt. Oder wenn Entwickler den Quellcode nicht (mehr) verstehen und ihn beim Lesen aktiv kennenlernen möchten. Außerdem bietet sich das Refactoring bei geplanten Änderungen an, zum Beispiel vor Erweiterungen beziehungsweise Bugfixes. Oder einfach während eines Code-Reviews, wenn Unstimmigkeiten im Code auffallen. Getreu dem Motto der Boyscout Rule von Robert C. Martin: „Den Platz immer sauberer hinterlassen, als Du ihn vorgefunden hast.“ (Kevlin Henney: 97 Things Every Programmer Should Know; O’Reilly 2010)

Wirklich fertig werden Entwickler übrigens nie. Jedes Refactoring hat möglicherweise ein Gegen-Refactoring. Denn es gibt keine wirklichen Sackgassen und es lässt sich ewig weitermachen. Von daher ist es sehr sinnvoll, Grenzen zu setzen. Entwickler könnten aufhören, wenn der zu analysierende Code verstanden, ein gesetztes Zeitlimit erreicht ist oder der Chef beziehungsweise der Kunde mit einem anderen Auftrag um die Ecke kommt.

Refactoring scheint eigentlich ganz einfach zu sein, aber doch auch wieder nicht. Das Vorgehen – möglichst kleine Schritte und das ständige Kompilieren beziehungsweise Ausführen der Tests – gerät schnell in Vergessenheit. Was bei einem überschaubaren Kontext noch gut funktioniert, fällt bei komplexen Softwaresystemen deutlich schwerer. Gemäß der Mikado-Methode lassen sich rasch blockierende Elemente ermitteln und analysieren. Diese Hindernisse werden dann aber zunächst nur notiert und erst nach der gesamten Analyse Schritt für Schritt entfernt. In den Worten von Ellnestam und Brolund: „Die Mikado-Methode hilft dabei, am Business Value orientierte Verbesserungen über mehrere Iterationen und Arbeitsschritte zu visualisieren, zu planen und umzusetzen, ohne dass die Codebasis während des Prozesses jemals kaputtgeht“ (Ola Ellnestam, Daniel Brolund: The Mikado Method; Manning 2014).

Die Vorgehensweise ist dabei relativ einfach (siehe Abbildung 2). Zunächst legen die Entwickler das Ziel fest (1). Das ist der Startpunkt der anstehenden Änderungen und gleichzeitig auch das Erfolgskriterium für das Ende. Anschließend beginnt das Team damit, erste Lösungsansätze auszuprobieren (2). Bei diesen Experimenten darf es den Code aktiv ändern, um Hypothesen zu prüfen und deren Auswirkungen zu beobachten. Hindernisse visualisieren die Testerinnen und Tester transparent für alle im Mikado-Graph (3). Die durch die Experimente eingeführten Änderungen machen sie dann direkt wieder rückgängig (4), zum Beispiel durch einen git reset. Anschließend geht es bei Schritt 2 weiter und das Vorgehen wiederholt sich so lange, bis keine Hindernisse mehr im Weg stehen.

Abbildung 2 zeigt den Gesamtablauf der Mikado-Methode. Durch den wiederkehrenden Zyklus Ausprobieren-Visualisieren-Rückgängigmachen (2) baut sich Stück für Stück der Mikado-Graph auf. Dieser Graph enthält viele wertvolle Informationen über die Struktur des Systems. Die Erstellung kann jederzeit unterbrochen und später fortgesetzt werden. Die Code-Basis ist dabei immer in einem lauffähigen Zustand, denn alle durchgeführten Experimente werden ja nach dem Dokumentieren wieder rückgängig gemacht. Sobald alle Hindernisse auf dem Weg zum festgelegten Ziel analysiert sind, wird der Graph rückwärts abgespult. Alle dokumentierten Änderungen werden der Reihe nach ausgeführt (3), bis das ursprüngliche Ziel erreicht ist (4).


Gesamtablauf: Nach dem Ablauf der Zyklen setzt das Team alle Änderungen in umgekehrter Reihenfolge um (Abb. 2).,

Gesamtablauf: Nach dem Ablauf der Zyklen setzt das Team alle Änderungen in umgekehrter Reihenfolge um (Abb. 2).,

Gesamtablauf: Nach dem Ablauf der Zyklen setzt das Team alle Änderungen in umgekehrter Reihenfolge um. Der rechte, bunte Teil der Grafik zeigt das nachfolgende Beispiel (Abb. 2).



Source link

Entwicklung & Code

Programmiersprache Python: Performante Algorithmen entwickeln und optimieren


Plant man eine Reise durch mehrere Städte und will die kürzeste Route finden, greift man auf Algorithmen zurück, eine wohldefinierte Abfolge deterministischer Operationen. Dieser Artikel begleitet den Entwicklungsprozess eines Algorithmus, der kürzeste Wege zwischen Städten findet. Er zeigt Schritt für Schritt den Weg von der ersten Skizze über Tests und Visualisierung mit Matplotlib und NetworkX bis zur Optimierung durch geeignete Datenstrukturen. So entsteht ein Programm, das nicht nur funktional korrekt arbeitet, sondern auch performant ist.

Weiterlesen nach der Anzeige


Michael Inden

Michael Inden

Michael Inden ist Java- und Python-Enthusiast mit über zwanzig Jahren Berufserfahrung. Derzeit ist er als Head of Development tätig, spricht auf Konferenzen und schreibt Fachbücher über Java und Python.

Ziel ist, in einem Straßennetz diejenigen Wege zu finden, die Städte am kürzesten verbinden. Zur Modellierung kann man Graphen verwenden. In Abbildung 1 repräsentieren Kreise mit Beschriftung die Städte und die Verbindungslinien mit Zahlen entsprechen Wegen mit Distanzen.


Ein Graph visualisiert die Abstände von Städten; die Zahlen stehen für die Entfernungen (Abb. 1).

Ein Graph visualisiert die Abstände von Städten; die Zahlen stehen für die Entfernungen (Abb. 1).

Ein Graph visualisiert die Abstände von Städten; die Zahlen stehen für die Entfernungen (Abb. 1).

Für diese vereinfachte Karte soll der kürzeste Weg von A nach D gefunden werden. Während man bei wenigen Städten und Verbindungen alle Möglichkeiten ausprobieren kann, wird der Ansatz aufwendiger, je mehr Städte und Verbindungen existieren. Folgende Verbindungen sind möglich, wobei 13 die schlechteste ist und 6 die beste:


A -> B -> C -> D => 5 + 1 + 7 = 13
A -> C -> B -> D => 2 + 1 + 3 = 6
A -> C -> D => 2 + 7 = 9
A -> B -> D => 5 + 3 = 8


Für eine gute Bedienbarkeit von Programmen ist relevant, wie schnell sich Berechnungen und Operationen ausführen lassen. Das gilt vor allem bei großen Datenmengen. Die O-Notation erlaubt es, Algorithmen zu klassifizieren und das Wachstum der Laufzeit (oder des Speicherbedarfs) eines Algorithmus zu beschreiben, wenn die Eingabemenge größer wird. Somit sind Effekte vorhersagbar, etwa wenn eine Liste nicht mehr 10 oder 20, sondern 100.000 und mehr Daten enthält.

Weiterlesen nach der Anzeige

Die O-Notation hilft, die Laufzeit von Operationen einzuschätzen. Sie ordnet Algorithmen und Funktionen in Komplexitätsklassen ein. Bei O(n³) wächst die Anzahl der Schritte mit der dritten Potenz der Eingabemenge. Bei 100 Eingabedaten ergibt sich ein Aufwand von 1003 für die Berechnung, also 1.000.000 Schritte. Je niedriger die Komplexitätsklasse ist, desto besser. Weitere Klassen zeigt die Tabelle „O-Notation mit in Komplexitätsklassen eingeteilten Algorithmen“, Abbildung 2 visualisiert die Effekte.

O-Notation mit in Komplexitätsklassen eingeteilten Algorithmen
Notation Bedeutung, Wachstum Beispiel
O(1) konstant Zugriff auf ein Listenelement
O(log n) logarithmisch Binärsuche
O(n) linear einfache Schleife über alle Elemente
O(n log n) linear-logarithmisch effiziente Sortieralgorithmen (etwa Mergesort)
O(n²) quadratisch zweifach verschachtelte Schleife
O(n3) kubisch dreifach verschachtelte Schleife


Die Graphen zeigen die Anzahl der Operationen in Abhängigkeit von der Eingabegröße (Abb. 2).

Die Graphen zeigen die Anzahl der Operationen in Abhängigkeit von der Eingabegröße (Abb. 2).

Die Graphen zeigen die Anzahl der Operationen in Abhängigkeit von der Eingabegröße (Abb. 2).

Eine Klassifikation mit der O-Notation ist insbesondere wichtig, um Laufzeiten unabhängig von Hardwareausstattung, Implementierungsdetails und gewählten Programmiersprachen bezüglich ihrer Skalierungseigenschaften zu vergleichen.

Für eine vereinfachte Einschätzung betrachtet man bei der Bewertung nur den dominierenden Term, da bei großen Eingabegrößen kleine Konstanten oder niedrigere Terme und Faktoren vernachlässigbar sind. In der Formel n3 + 4n² + 3n + 7 folgt durch die Vereinfachungen die Laufzeitklasse O(n3).

Ein systematisches Vorgehen ist selbst für kleinere Programme und vor allem bei komplexen Softwareprojekten der Schlüssel zu funktionalem, wartbarem und performantem Code.

1. Problem verstehen und analysieren

  • klären, welches Problem zu lösen ist, und es in Teilaufgaben zerlegen;
  • prüfen, ob es bereits bewährte Lösungen für Teilaufgaben gibt, beispielsweise Binärsuche für performante Suchen in sortierten Datenbeständen, Dijkstra-Algorithmus für kürzeste Wege;
  • Eingabe- und Ausgabedaten definieren;
  • Randbedingungen und Sonderfälle berücksichtigen.

2. Planen und eine Grobstruktur entwickeln

  • Problem in Teilaufgaben zerlegen;
  • Abläufe in natürlicher Sprache formulieren oder skizzieren;
  • geeignete Datenstrukturen wählen (Listen, Dictionaries, Heaps).

3. Implementierung

  • Sourcecode in klar getrennte Funktionen oder Klassen gliedern;
  • auf Lesbarkeit und Verständlichkeit achten, aussagekräftige Namen und (falls sinnvoll) ergänzende Kommentare verwenden;
  • vorhandene Bibliotheken nutzen, um Entwicklungszeit zu sparen (etwa Matplotlib zur Visualisierung).

4. Testen (Dry-Run- und Unit-Tests)

  • Funktionsweise ausprobieren;
  • Unit-Tests schreiben, um die Funktionsweise zu prüfen und Rand- und Sonderfälle abzudecken.

5. Performance messen

  • Messungen mit kleinen, mittleren und großen Datenbeständen ausführen, etwa mit 100, 10.000 und 1.000.000 Datensätzen;
  • Engpässe identifizieren – sie zeigen sich allerdings meist erst bei sehr großen Datenbeständen.

6. Optimieren

Wurden in Schritt 5 Schwachstellen aufgedeckt, sollte man die Umsetzung und die gewählten Algorithmen für Teilprobleme nochmals genauer anschauen.

  • O-Notation verwenden, um die Komplexität formal zu bewerten: Was läuft in Schleifen? Wie und wo erfolgt eine Suche – linear oder mit Binärsuche? Für verschiedene Aktionen kann das Laufzeiten von O(1), O(log n) oder O(n) bedeuten.
  • besser geeignete Algorithmen oder effizientere Datenstrukturen einsetzen.

Implementierung und Test miteinander verweben

In der Praxis laufen die Schritte 3 und 4 nicht immer unabhängig voneinander. Wenn sich die Ergebnisse gut vorhersagen lassen, bietet es sich an, mit dem Erstellen von Testfällen zu starten. Manchmal braucht es aber erst einmal eine Idee und einen Prototyp der Implementierung. Gerade bei größeren Programmierprojekten ergeben sich weitere Anforderungen während der Implementierungs- und Testphase.

Der folgende Ablauf hat sich in der Praxis bewährt und lässt sich auch beim Entwickeln eines Algorithmus anwenden.



Source link

Weiterlesen

Entwicklung & Code

GitLab 18.8: Duo Agent Platform jetzt allgemein verfügbar


Mit GitLab 18.8 stellt GitLab die Duo Agent Platform allgemein zur Verfügung. Sie soll Unternehmen dabei unterstützen, KI-Agenten für Planung, Entwicklung, Absicherung und Auslieferung von Software koordiniert einzusetzen.

Weiterlesen nach der Anzeige

GitLab reagiert damit auf ein bekanntes Problem beim Einsatz von KI in der Softwareentwicklung: KI-Tools steigern zwar die Produktivität einzelner Entwicklerinnen und Entwickler, verlieren diesen Effekt aber oft auf Teamebene. Die Duo Agent Platform orchestriert KI-Agenten deshalb innerhalb eines einheitlichen Systems und nutzt einen gemeinsamen Projektkontext aus Issues, Merge Requests, Pipelines und Security-Findings.

Lesen Sie auch

Die Plattform kombiniert konversationelle KI, spezialisierte Agenten und automatisierte Workflows. Ein zentraler Baustein ist der Agentic Chat, der in der GitLab-Oberfläche und in verschiedenen Entwicklungsumgebungen zur Verfügung steht. Er unterstützt beim Erstellen von Code, bei der Analyse und Fehlerbehebung, bei Tests und Dokumentation auf Basis des aktuellen Projektkontexts.

Der Planner Agent ist nun ebenso allgemein verfügbar und soll Produktmanager in GitLab bei der Arbeit mit Work Items unterstützen. Er kann unter anderem beim Analysieren von Backlogs, beim Priorisieren (z. B. mit RICE oder MoSCoW) und beim Aufbereiten von Planungsinformationen helfen.

Weiterlesen nach der Anzeige

Mit dem AI Catalog können Teams Agenten und Workflows organisationsweit bereitstellen und teilen. Vorgefertigte Agenten übernehmen typische Aufgaben wie Planung oder Sicherheitsanalyse. Flows automatisieren wiederkehrende Abläufe, etwa das Erstellen von Merge Requests, die Anpassung von CI/CD-Pipelines oder die Analyse fehlgeschlagener Builds.

Auch der GitLab Duo Security Analyst Agent ist mit GitLab 18.8 aus der Beta-Phase in die allgemeine Verfügbarkeit übergegangen. Er ermöglicht es, Schwachstellen per natürlicher Sprache im GitLab Duo Agentic Chat zu verwalten, und ist dort standardmäßig ohne zusätzliche Einrichtung verfügbar.

Die Duo Agent Platform ist auf GitLab.com und in GitLab Self-Managed verfügbar, GitLab Dedicated soll folgen. Transparenz- und Governance-Funktionen unterstützen den Unternehmenseinsatz. Die Abrechnung erfolgt nutzungsabhängig über GitLab Credits aus einem gemeinsamen Pool. Weitere Informationen finden sich im entsprechenden Blogbeitrag.


(mdo)



Source link

Weiterlesen

Entwicklung & Code

software-architektur.tv: Spec-Driven Development mit Simon Martinelli


In dieser Episode spricht Ralf D. Müller mit Simon Martinelli über den AI Unified Process (AIUP), einen agilen und iterativen Entwicklungsansatz, der Requirements ins Zentrum stellt – nicht den Code. Martinelli zeigt, wie man mit AIUP moderne Software entwickelt, bei der Anforderungen, Spezifikationen, Code und Tests gemeinsam durch kurze Iterationen wachsen, während KI als Konsistenz-Engine dient.

Weiterlesen nach der Anzeige

Das Duo diskutiert die zentrale Frage: Braucht es perfekte, deterministische Spezifikationen für KI-Code-Generierung? Simon Martinelli argumentiert, dass das der falsche Ansatz ist. Stattdessen ermöglicht AIUP iterative Verbesserung: Requirements treiben die Entwicklung, Spezifikationen werden detaillierter und Tests schützen das Systemverhalten, während der generierte Code sich gemeinsam mit allem anderen weiterentwickelt.

Lisa Maria Schäfer malt dieses Mal keine Sketchnotes.

Die Ausstrahlung findet am Freitag, 16. Januar 2026 live ab 13:00 Uhr statt. Die Folge steht im Anschluss als Aufzeichnung bereit. Während des Livestreams können Interessierte Fragen via Twitch-Chat, YouTube-Chat oder anonym über das Formular auf der Videocast-Seite einbringen.

software-architektur.tv ist ein Videocast von Eberhard Wolff, Blogger sowie Podcaster auf iX und bekannter Softwarearchitekt, der als Head of Architecture bei SWAGLab arbeitet. Zum Team gehören außerdem Lisa Maria Schäfer (Socreatory) und Ralf D. Müller (DB Systel). Seit Juni 2020 sind über 250 Folgen entstanden, die unterschiedliche Bereiche der Softwarearchitektur beleuchten – mal mit Gästen, mal Wolff, Schäfer oder Müller solo. Seit mittlerweile mehr als zwei Jahren bindet iX (heise Developer) die über YouTube gestreamten Episoden im Online-Channel ein, sodass Zuschauer dem Videocast aus den Heise Medien heraus folgen können.

Weitere Informationen zu den Folgen finden sich auf der Videocast-Seite.

Weiterlesen nach der Anzeige


(mdo)



Source link

Weiterlesen

Beliebt