Coffee, Code and Consult

Organisatorische Herausforderungen beim Refactoring in Legacy Projekten

Cofinpro AG

Use Left/Right to seek, Home/End to jump to start or end. Hold shift to jump forward or backward.

0:00 | 30:38

Legacy-Code ist oft ein Albtraum: Alte Technologien wie Java 8 laufen am Ende aus, APIs werden nicht mehr unterstützt, und der Code ist kaum noch wartbar. Jan und Tristan berichten aus der Praxis: Wir haben ein Projekt seit 2006 und jetzt müssen wir ein Refactoring durchführen. Wie schaffen wir das ohne den Business-Flow zu stören?

In dieser Folge erfahrt ihr:

👉 Warum Refactoring oft keine Option, sondern Pflicht ist: Unterstützungsauslauf von Java-Versionen und API-Updates erzwingen oft Strukturänderungen.

👉 Strategien für das Refactoring: Wie man „Big-Bang-Migration" vermeidet und stattdessen schrittweise mit Facades und Adaptern vorgeht.

👉 Die Rolle von AI & LLMs: Wie KI beim Verstehen von „God Classes" hilft (aber Vorsicht ist geboten).

👉 Kundenkommunikation: Wie man dem Stakeholder die Kosten für Refactoring klar machen kann (und warum es Geld spart).

👉 Onboarding & Tests: Die Herausforderung neuer Entwickler in altem Code und die Notwendigkeit von Unit-Tests.


Wichtige Links & Ressourcen:

📚 Empfohlenes Buch: Working Efficiently with Legacy Code (https://www.oreilly.com/library/view/working-effectively-with/0131177052/)

🎙️ Vorfolge: Folge Dezember 2024 über technische Schulden (mit Ben & Michi Heck) – lohnt sich als Vorab-Info!


Timestamps:

0:07 Einführung in Refactoring

1:48 Herausforderungen im Legacy-Code

4:05 Technische Schulden und ihre Auswirkungen

7:38 Refactoring und Kundenkommunikation

10:27 Vertrauen und Kundenbeziehungen

13:09 Strategien zur Refactoring-Kontrolle

15:33 Der Einsatz von KI im Refactoring

19:30 Integration neuer Entwickler

25:21 Akzeptanzkriterien und Teamintegration

29:50 Abschluss und Ausblick


Folge uns auf unseren Social Media Kanälen und bleibe immer auf dem neuesten Stand:

📸 Instagram 🔗 LinkedIn 🎥 YouTube👍 Facebook

Oder besuche unsere offizielle Webseite: Cofinpro.de

Einschalten, zuhören und inspirieren lassen. Wir freuen uns auf dich! 🎧

Udo:

Herzlich willkommen zu Coffee Code & Consult. Heute wollen wir eine Folge aufnehmen über Refactoring. Und dazu haben wir uns zwei Expertengäste eingeladen. Einmal den Jan.

Jan:

Freut mich.

Udo:

Und einmal den Tristan.

Tristan:

Freut mich auch dabei zu sein.

Udo:

Und die Folge nimmt heute auf für euch Eurost, der Udo. Und bevor wir in die Folge einsteigen, würde ich euch bitten, euch einmal ganz kurz vorzustellen.

Jan:

Ich bin Jan, ich arbeite seit September 2024 bei der Coffin Pro und habe ungefähr Erfahrungen in der Softwareentwicklung seit 2020.

Tristan:

Ich bin der Tristan. Ich bin jetzt über fünf Jahre bei der Covenbro dabei, ist auch nach dem Studium die erste Firma gewesen, wo ich angefangen habe. Ich bin mittlerweile auch Senior Consultant und über das letzte Jahr und ein bisschen mehr explizit bin ich in einem Projekt, wo gerade Refactoring in einem großen Legacy Software einen Fokus hat. Und deswegen bin ich mit Jan, der im selben Projekt ist heute hier dabei, ein bisschen darüber Auskunft zu geben, was wir da so miterleben können und gestalten dürfen.

Udo:

Wunderbar. Und ich erlaube mir noch zwei Hinweise. Es gibt einerseits ein sehr, sehr schönes Buch zu dem Thema Refactoring als solches, Working Efficiently with Legacy Code. Die ISBN-Nummer und den Link dazu packe ich in die Show Notes. Und es gibt auch schon eine Podcast-Folge von uns aus dem Dezember 2024 über technische Schulden. Und das ist so ein bisschen die Vorfolge, wenn man so will, zu dieser Folge, weil dort werden im Prinzip die Fragen diskutiert. Was sind denn überhaupt technische Schulden und was ist denn überhaupt der Anlass für ein Refactoring? Und ja, genau, könnt ihr gerne nachhören. Lohnt sich immer noch mit Ben als Host und unserem Michi Heck, unserem Tech Director. Und ja, jetzt können wir eigentlich mal die Frage klären, wie seid ihr denn überhaupt zum Refactoring gekommen und welchen Anlass gab es da für euch?

Jan:

Ja, also bei uns im Projekt gibt es momentan mehrere Baustellen. Also erstmal muss man wahrscheinlich sagen, dass unser Projekt schon sehr alt ist. Es ist entwickelt worden seit 2006 und es benutzt auch noch sehr alte Technologien, also Jahre 8. Dementsprechend haben wir sehr viele Legacy-Stellen, die wenig angefasst wurden. Und man muss auch feststellen, dass früher der Fokus bei unserem Projekt, aber ich glaube, das gilt auch generell für viele Projekte, nicht so sehr darauf lag, Code wirklich sauber zu programmieren, sondern eher darauf, schnelle Features zu generieren, Dieser Fokus auf clean, sauberen Code, den gibt es so, würde ich sagen, seit den Anfang der 2000er, auch durch Chris Martin vor allen Dingen vorangetrieben. Und ja, da gab es das agile Manifest und später clean Code und seitdem ist mehr so der Fokus auch der Unternehmen darauf, dass der Code wirklich sauber und wartbar ist. Und wir sind jetzt gerade an der Stelle, dass wir oftmals darüber stolpern, dass wir sehr viel einen alten Code haben, den wir erstmal durchblicken müssen, der sehr viel Spaghetti-Code hat und wo wir schon sehen, dass die Entwicklung teilweise sehr ineffizient ist.

Tristan:

Was man zu dem Projekt auch sagen muss, über viele Jahre hinweg stellt sich die Frage, ob dieses Projekt nicht durch einen anderen Tech-Stack oder ein ganz anderes Projekt als solches, vielleicht sogar eine Plattform, ersetzt werden soll. Was natürlich auch häufig dazu führt, dass Refactoring und Qualitätsaufwände, die eigentlich betrieben werden müssten, zurückgestellt werden. Einfach, weil man nicht davon ausgeht, dass das Projekt noch eine lange Lebenszeit haben wird. Gleichzeitig ist dieses Projekt aber für das Umfeld, in dem wir unterwegs sind, sehr instrumental. Das heißt, dieses Projekt muss auf jeden Fall am Leben erhalten werden und auch laufen. Sonst funktioniert das ganze Unternehmen nicht. Und wir sind jetzt nach und nach in Situationen, wo dadurch, dass der Tech-Stack so alt wird, Support für unterschiedliche Technologien ausläuft. Java 8 ist zum Beispiel eine davon. Ende dieses Jahrzehnts wird man umsteigen müssen. Java 11 Support wird auch eingestellt. Das heißt, es ist wichtig, nach und nach zu migrieren, was oft auch mit Refactoring-Notwendigkeiten einhergeht. Und das ist einer der Gründe, auch warum wir überhaupt in dem Maße, wie wir es jetzt können und dürfen, Refactoring durchführen. Weil oft ist es ja durchaus auch ein Thema, dass ein Kunde die Notwendigkeit gar nicht sieht, warum man Kosten und Aufwände allokieren sollte für Refactoring in dem Maße. Und da haben wir viel zu tun. Wir haben jetzt aber auch gerade einfach die Chance, uns damit mehr und innerlicher zu beschäftigen.

Jan:

Was man vielleicht auch noch dazu sagen sollte, ist, dass jetzt bei uns auch schon auf Projektmanager-Ebene mehr und mehr der Fokus darauf gelegt wird, dass bei uns gar keine neuen technischen Schulden entstehen, sodass wir statische Code-Analyse zum Beispiel nutzen, die sehr restriktiv eingestellt ist. Und da wird auch dann akzeptiert, dass die Feature-Entwicklung dann halt ein bisschen länger dauert und dafür eben der Code so sauber wie möglich ist. Das Einzige, was vielleicht bei uns noch ein bisschen mehr, wo ein bisschen mehr technische Schulden entstehen, sind die Dokumentation.

Udo:

Ja, gut. Das kennt man ja aus allen Projekten. Also insofern häufen ja die meisten Projekte nicht nur technische Schulden im Sinne von schwierigen Abwägungen auf der Code-Basis an, sondern eben auch in der Dokumentation. Das heißt, ihr habt jetzt einen Faktor weniger, weil ihr das einfach in der Code-Basis dann nicht mehr so habt. Jetzt hast du zwei Sachen im Prinzip genannt, Tristan. Das eine ist irgendwie ein Upgrade der Plattform, der Sprachplattform. Und das andere ist ja im Prinzip das Aufräumen bzw. Das verständlicher machen, Wartbarkeit erhöhen der Code-Basis. Das sind für mich zwei verschiedene Aspekte letztendlich von Refactoring, weil das eine ist nur so ein implizites Refactoring. Man muss halt an die neue Sprachversion sozusagen sich anpassen. Das heißt unter anderem von 8 auf 11 heißt es ja dann auch noch das Austauschen mit Java X. Also das heißt, da hat man da noch ein bisschen was zu tun. Und während man das ohnehin tut, kann man dann eben auch noch sich überlegen, wie kann man die Struktur noch verbessern.

Jan:

Ja, jetzt zusätzlich bei uns im Projekt kommt noch zusätzlich was dazu, dass wir eine API haben, die offiziell nur auf Java 8 supportet wird. Es gibt ein API-Update, was zwei Major-Versionen über unserer aktuellen API ist und 0% kompatibel ist. Das heißt, es ist zwingend notwendig, dass wir auch diese updaten und da müssen wir dann schon mehr als nur bei einem Sprachupdate, weil man muss ja sagen, dass Java eigentlich immer hundertprozentig abwärtskompatibel ist, aber für dieses API-Update müssen wir auf jeden Fall komplett alle Klassen in unserem System anfassen, da wir auch sehr oft diese Zugriffe verstreut haben. Und da kann man dann zum Beispiel sagen, man hat gar keinen wirklichen Zeitverlust, wenn man jetzt diese Stellen schon mal zentralisiert. Das erfordert dann aber, dass man natürlich neuen Code schreibt und eben sich auch eine saubere Architektur ausdenkt, was ja auch schon zum Refactoring gehört. Und somit ist das in dem Fall bei einem Refactoring, was durchgeführt wird, weil der Kunde gerne neue technische Dinge implementiert haben will, natürlich dann einfacher dem Kunden zu vermitteln, ja, jetzt machen wir den Code auch sauberer, weil wir müssen eh alles anfassen. Und dadurch entsteht dann weniger Zeitverlust, als wenn man jetzt sagen würde einfach, ja, wir refactoren jetzt, weil wir nicht mehr entwickeln können. Das ist dem Kunden immer schwerer zu vermitteln, dass das hinterher die Effizienz höher ist, auch wenn das Bewusstsein heute dafür mehr und mehr da ist.

Udo:

Ja, und das verschafft euch natürlich an der Stelle auch einen Vorteil in der Argumentation, weil ich sage mal, das ist ja in dem Sinne dann ein erzwungenes Refactoring, was mit der Inkompatibilität zur alten API einhergeht. Also das führt ja quasi die API-Art Absurdum an. Nichtsdestotrotz gibt es das in ganz vielen Projekten immer wieder. Eine API soll ja eigentlich genau das sein. Die soll stabil sein, soll sozusagen die Abhängigkeiten verstecken und soll dir nur die Funktionen bereitstellen. Manchmal lässt sich es aber nicht verhindern. Das Internet bewegt sich sehr schnell, Technik bewegt sich sehr schnell. Und wenn man dann sowieso schon dran ist, kann man natürlich vieles machen. Wenn man das nicht hat, dann hast du immer das Problem bei der Argumentation, dass der Kunde natürlich die Frage zurückspielt und sagt, ja Moment, warum habt ihr denn in der Vergangenheit überhaupt so, ich sage jetzt mal ganz salopp, ein bisschen polemisch schlecht programmiert, damit ihr das jetzt aufräumen müsst. Also man ist technische Schulden eingegangen und auch darüber wird in der Folge, auf die ich gerade verwiesen habe, gesprochen. Ja, die Frage ist, ist man bewusst diese technischen Schulden angegangen und hat man sie dokumentiert, hat man sie transparent gemacht?

Jan:

Ich würde sagen, bei alten Projekten meistens in der Vergangenheit ist man die eher nicht bewusst eingegangen. Man hat meistens im Wasserfall per Festpreis die Preise berechnet und gearbeitet. Und dann ist es natürlich so, dass man ein festes Release-Datum hat, an dem Sachen fertig werden müssen. Und dann war es im Zweifelsfall einfach so, ja, wir müssen das Feature fertigstellen, egal wie der Code hinterher aussieht. Da wurde dann auch in den Festpreisen nicht mit eingerechnet, dass man jetzt alte Codestellen verbessert oder so. Das heißt, da ist man dann implizit eher die technischen Schulden eingegangen und das kann man dem Kunden auch mittlerweile oder den meisten Kunden, zumindest habe ich die Erfahrung gemacht, einfach vermitteln, weil die selber wissen, okay, früher haben wir so gearbeitet und die ganzen Sachen, um sauberen Code zu hinterlassen, die wurden erst nach und nach implementiert in dem Prozess.

Tristan:

Das ist auf jeden Fall ein Schiff, der auch auf einer gewissen Vertrauensbasis basiert, die man mit dem Kunden hat. Weil du es gerade schon angesprochen hast, das Projekt ursprünglich ist mal ein Wasserfall gewesen, auch mit einer Festpreisplanung und ist jetzt zu einem typischen agilen Modus gewechselt mit zweiwöchigen Sprints. Und das lässt vielmehr den Raum, dass man sich die Zeit nimmt, a. Keine neuen Schulden aufzubauen bei Entwicklungen, die man jetzt tätigt, Man aber auch den Rahmen hat oder einfach die Kapazität zu sagen, wir investieren größere Mengen für Refactoring-Aufwände. Hier hatten wir auch den Luxus, dass der Kunde diesen Impuls selbst gesteckt hat, weil klar war, die Version läuft irgendwann aus. Oft ist man ja als technischer Berater in der Bringschuld, aber auch in einer gewissen Problematik. Man sieht das Potenzial, auch das Problem. Man muss Refactoren, man muss sich um die technische Schuld kümmern, damit die sich nicht weiter anhäuft. Gleichzeitig sieht der Kunde das nicht ein. Hier haben wir den Vorteil, dass dieses Vertrauensverhältnis da ist und auch seit dem Switch von Maschafal zu einem emergieren Umfeld der Leistungserweis erbracht wurde, Dass man trotz dieses Switches und auch trotz, dass man Zeit in Refactoring investiert, das Vertrauen da ist. Es wird aber auch mit fachlichen Tickets und einer Menge produziert. Gleichzeitig räumen wir aber Schulden weg. Und das führt jetzt dazu, dass wir über viele Jahre hinweg, wurde ja schon erwähnt, das Projekt existiert seit 2006, so viel in die technische Restaurierung investieren können wie je zuvor. Und das erlaubt uns auch, Synergieeffekte zu erzielen mit Performance, Verbesserungen, die wir an unterschiedlichen Stellen haben oder auch Architekturproblemen, die existiert haben, seit langem schon, die durch Ausfälle in der Produktion hin und wieder führen oder zu anderen Problemen, die man manuell fixen muss, an denen wir jetzt vorbeikommen und wir diese ganze technische Infrastruktur verbessern, obwohl der eigentliche Plan nur war oder das eigentliche Ziel nur ist, die Version anzugleichen. Und solange mal eine gute Absprache ist zu den Extra-Wänden, die man bei Factory betreibt, um diesen Synergieeffekt zu bekommen, kommt man doch im Grunde auf ein gutes Ende und ein gutes Ergebnis.

Jan:

Ergänzend kann man vielleicht auch noch sagen, dass, falls der Kunde jetzt nicht so ein vertrauensvolles Verhältnis hat, wie er es jetzt bei uns hat, gibt es mittlerweile aber trotzdem schon relativ viele Arbeiten und Studien, die belegen, dass Code mit vielen technischen Schulden dem Entwickler auch viel Zeit kostet zusätzlich. Also es gibt zum Beispiel eine Studie von Adam Thornhill und Markus Borg, in der sie untersucht haben, dass im Schnitt in der Softwareentwicklung 23 bis 42 Prozent Entwicklungszeit nur durch technische Schulden überhaupt zustande kommen. Und da kann man dem Kunden natürlich auch vorrechnen, dass das ein hoher Effizienzverlust ist und dass es ihm vielleicht auch langfristig sehr viel Geld sparen kann, wenn er zusätzliche Ressourcen in das Refactoring investiert.

Udo:

Ja, das leuchtet absolut ein. Ich meine, es ist natürlich auch eine luxuriöse Situation, wenn man schon das Vertrauen gewonnen hat. Das ist immer gut. Jetzt stelle ich mir trotzdem vor, dass wenn ich als beispielsweise Product Owner in einem Projekt für die Kostenkontrolle genauso verantwortlich bin, dass ich natürlich mir die Frage stelle, wie kann ich das verhindern, dass es sozusagen ausufert, dass die Entwickler sich vielleicht verrennen oder dass Refactoring ein Fass ohne Boden wird, wie man so schön sagt. Welche Ansätze habt ihr da gefunden, eben solchen offensichtlichen Ansätzen wie beispielsweise eine Timebox zu finden? Also was habt ihr unternommen im Projekt, um das so ein bisschen einzudämmen?

Tristan:

Unser Projekt ist auf eine natürliche Art und Weise geschnitten in unterschiedliche Module. Das heißt, je mehr die Architektur an der Microservice-Architektur ähnelt, desto mehr ist man ja sowieso in unterschiedliche Projekte oder Kleinprojekte unterteilt und kann jedes von diesen einzelnen Effektern. Sondern was bei uns der Kernaufwand oder die Kernveränderung ist, die wir herbeiführen müssen, die API, die diese Software, die wir verwenden, bei uns mit sich bringt, die haben wir gekapselt bei uns in einem eigenen Service und dort haben wir unterschiedlich, wir nennen es Fassaden, die Funktionen übernehmen, die für unser Projekt relevant sind, aufgeteilt auf ihre unterschiedlichen Anwendungskategorien. Was wir im Moment tun, ist stückweise neue Schnittstellen zu erstellen, die erstmal in die alte Funktionalität verlinkt und verweist, um diese dann ordentlich ersetzen zu können durch bessere Implementationen. Das glaubt uns einmal, dass wir nicht in einer Big-Bang-Migration landen, wo plötzlich wie auf einen Schlag alles migrieren müssen und es nicht mehr funktioniert und wir diese Vorarbeit leisten, aber es auch recht klar abgesteckt ist, an welchen Stellen wollen wir ansetzen und den Effekt dann nicht alles verweht. Wir haben auch viele Situationen, weil der Code zu alt ist und die meisten Projekte werden hoffentlich Sona benutzen, dass dort Stellen sind mit Sona-Issues, wo wir Klassen gar nicht mehr anfassen, weil der Aufwand, der entsteht, einfach nur um Sona-konform hier weitermachen zu können, Jegliche Form von Refractoring-Sinnhaftigkeit springt. Und einfach nur dann, um eine Metrik zu bedienen, für eine Klasse, die seit 15 Jahren nicht mehr angefasst wurde, hat dann auch nicht mehr den Sinn. Aber wir fokussieren uns sehr explizit auf diese Fassaden, auf diese API-Aufrufe. Und dann gibt es mehrere Schichten Migrationsphasen, die wir da vorgehen.

Jan:

Genau und man kann auch die Erfahrung quasi aus einem Softwaremodulteil oder einer Fassade eben nutzen, um die weiteren Fassaden eben besser abschätzen zu können, wie viel Arbeit das ist. Also grundsätzlich kann man vielleicht sagen, es lohnt sich, das Refactoring in kleine Teile zu zerteilen und dann diese auszuführen und seine Schätzung, was für ein Aufwand das ist, eben danach anzupassen, wie diese Migration am Ende dann läuft. Das ist ja im Prinzip auch die Idee von agilen Softwareentwickeln, Inspec nennt das. Genau, man führt etwas aus und hinterher kann man seine Schätzungen daran anpassen, wie es gelaufen ist.

Udo:

Ganz oft, gerade bei Legacy-Code, hat man ja die Herausforderung, dass man irgendeiner, ich sage jetzt mal, God-Class begegnet, die eine Menge Funktionen enthält, eine Menge Verantwortlichkeiten in sich vereint aus einer dunklen Zeit, als wir noch nicht domain-driven unterwegs waren, sage ich jetzt mal. Und diese Klassen dann entsprechend irgendwie zu refactoren, das ist oftmals sehr schwierig, weil man gar nicht weiß, welche Nebenwirkungen man da produziert.

Jan:

Genau, also es gibt da mehrere Herangehensweisen. Problematisch ist oftmals, wenn selber das Team eine starke Fluktuation hat, die ursprünglichen Entwickler von bestimmten Code-Teilen von bestimmten Methoden gar nicht mehr im Team sind, dass man erstmal nachvollziehen muss, wie überhaupt die Business-Logik des Codes ist. Da passiert es auch leider oft, dass man selbst beim Kunden nachfragt und der muss auch erstmal analysieren, was wollen wir eigentlich genau von dem Code. Das hat bisher immer funktioniert. Eventuell gab es auch bekannte Fehler. Also diese Analyse der Funktionalität, die wäre zuerst mal das Erste, was man machen müsste bei eben so einer God-Klasse, God-Methode. Und da kann man mittlerweile auch ganz gut die Large Language Models als Code Assistant nutzen, die einem helfen, den Code eben besser zu verstehen und wirklich aufzuschlüsseln, was der Code macht in natürlicher Sprache, sodass man das eben auch dem Fachbereich präsentieren kann. Dabei muss man allerdings immer darauf achten, wenn man KI einsetzt, man sollte das nie blind tun. Man sollte immer darauf schauen, stimmt das, was die KI ausgegeben hat? Stimmt das, was der Code wirklich macht? Passt das dazu? Aber mittlerweile muss man sagen, gerade im letzten Jahr, seit GPT-5, Gemini 3 und natürlich Cloud Code, muss man eben sagen, dass da wirklich die... Die Präzision der Antworten gerade oder das Verständnis des Codes der KI, wenn ich es jetzt mal so nennen soll, dann sehr stark zugenommen hat.

Udo:

Ja, mittlerweile haben ja auch so gut wie alle Editoren, die ein bisschen was auf sich halten, IDEs, irgendeine Form von Integration. Meine Erfahrung war, es ist immer ein bisschen eine Herausforderung, gerade bei solchen Refactoring-Aufgaben stelle ich mir das ähnlich vor, den richtigen Kontext zu finden. Ich glaube, da kann man dann wahrscheinlich auch ein, zwei, drei Anläufe mal nehmen, bis man den so gefunden hat, bis er passt. Aber das gegeben und dass man weiß, was man ungefähr tut, ist das auf jeden Fall absolut ein Produktivitätsgewinn.

Jan:

Genau. Und dann, wenn man am Ende abgeklärt hat, wie soll eigentlich die Funktionalität aussehen, dann wäre eben der nächste Schritt, Unit-Tests zu erstellen, weil oftmals, muss man leider auch sagen, bei alten Code keine Unit-Tests vorhanden sind und das manuell oder maximal per Integrationstest eben getestet wurde. Aber es ist eben wichtig, möglichst präzise Unit-Tests zu haben und möglichst vollständige Unit-Tests zu haben, um hinterher nach dem Refactoring auch sicherzustellen, dass die Funktionalität am Ende gleichgeblieben ist. Weil was man natürlich am wenigsten will nach dem Refactoring ist, neue Bugs zu erzeugen. Was man allerdings auch oft findet, ist alte Fehler, alte Ineffizienzen, was natürlich dann auch das, Sorry, hier fehlt gerade ein Wort. Was natürlich auch das Verständnis beim Kunden für das Refactoring weiter erhöht, wenn man nach dem Refactoring effizienteren Code hat und eben auch alte Fehler, die vielleicht schon seit zehn Jahren existieren, die nie blockend waren, aber immer störend, behebt dadurch.

Udo:

Ja, verständlich. Und mit ein bisschen Glück ist der Seiteneffekt, dass die Fachseite ihre Software auch wieder etwas besser versteht als vorher, Wenn gerade ganz alte Anwendungen am Laufen sind, ich denke da an die ein oder andere Mainframe-Anwendung bei Banken, wo auch die Leute, die mal ehemals wussten, was die Software tat, an der einen oder anderen Stelle irgendwie gar nicht mehr im Unternehmen verfügbar sind, in Rente gegangen oder das Unternehmen gewechselt haben. Ja, wie sind eure Erfahrungen gewesen mit dem Ergebnis der KI? Wie habt ihr das dann wieder integriert? Das ist ja auch nicht ganz einfach. Also sprich, wenn die KI dann irgendwie Vorschläge macht, wie übernimmt man das dann?

Jan:

Ich würde sagen, man sollte nicht die KI wirklich den kompletten Codeblock erstellen lassen, sondern auch wirklich die Sachen immer in kleine Teile zerteilen, wirklich genau verstehen, was ist jetzt hier eigentlich das Ziel. Diese kleine Code-Blöcke, es ist ja sowieso sinnvoll, eine Riesengott-Methode, wie du sie genannt hast, einfach mal zu zerstückeln, in kleine Einzelteile zu zerteilen und dann eben Schritt für Schritt das zu implementieren, immer wieder sicherzustellen, dass die Funktionalität gegeben ist. Was ich vorhin, glaube ich, noch nicht gesagt habe, ist, dass man auch prima mit den LLMs eben Unit-Tests erzeugen kann. Also auch wieder nicht einfach nur Copy und Paste, sondern man guckt sich den Code an, versteht den. Es ist halt einfach ein riesen Zeitgewinn, ein riesen Effizienzgewinn, wenn man nicht von Null auf erstmal alle Tests schreiben muss, sondern wenn man schon mal eine gewisse Vorlage hat. Und ja, dann eben Schritt für Schritt vorgehen, alles in kleine Einzelteile verpacken und eben immer wieder die Funktionalität sicherstellen. Das würde ich sagen, ist das Wichtigste, wenn man mit KI arbeitet und falls man offensichtlich sieht, dass halluziniert wird oder die KI die Anforderungen nicht verstanden hat, dann resetet man die Anfrage und stellt einen neuen Prompt, optimiert den Prompt und irgendwann sieht man dann auch schon, ob das Verständnis wirklich da ist. Was in meiner Erfahrung auch oft hilft, ist, in den Prompt schon als Anforderung reinzuschreiben, wenn du dir irgendwo unsicher bist, stell Rückfragen. Überraschend gut klappt das dann meistens auch, wenn irgendwas unklar ist.

Tristan:

Ja, mit den Möglichkeiten, die auch immer weiterentwickelt werden, glaube ich, werden sich auch gerade solche Projekte noch mal fast schon revolutionieren, in der Art und Weise, wie man sie angehen kann. Der Prozess, den wir jetzt ein bisschen aufgemalt haben, der klassische, wir testen erstmal alles ab, damit wir beim Refactoring sicherstellen können, dass wir nichts kaputt machen und keine Funktionalität verlieren, die es mal gab. Gerade bei solchen alten Bestandssystemen. Zumindest bei uns ist das der Fall, ist natürlich, kommen wir nicht an die modernste KI ran. Wenn wir jetzt mal den Verlauf betrachtet, den wir so sehr gesehen können, von was geht gerade schon auf dem Markt, dann gehen wir schon weit über hinaus von, wir können mal Unit-Tests schreiben. Bei uns ist das noch viel Nebenklassen, Code-Klassen, die werden in den Kontext händisch gelegt und dann wird der Code analysiert. Eine komplette Agentive-Lösung, die man ja erstreben will, die auch dann viel mehr Möglichkeiten hat und viel mehr kann. Kommt man in diesen alten Legistik-Projekten noch nicht und dann hat man auch eine deutlich größere Menge an Code. Ist nochmal ein Schwung, wo wir auch hinwollen, wo wir jetzt auch noch nicht die Chance hatten, tiefere Erfahrungen mitzusammeln, dass gerade in solchen Projekten, wo wir sagen, was fehlt uns, Unit-Tests, Dokumentationen, Dinge, wo es um Verständnis geht, was die LOMs sehr gut können, aber auch um die hohe Masse an Produktionen von Unit-Tests und Dokumentationen, die dann nochmal einen sehr großen Aussprung bedeuten werden. Zugleich man natürlich auch sicherstellen muss, dass sie nur Tests sinnvolle Dinge testen. Das ist nicht immer sichergestellt. Und wenn wir dann an dem Punkt sind, dass das auch mehr geht, denke ich, gewinnt man da auch nochmal deutlich an Geschwindigkeit. In vielerlei Hinsichtgabe bei diesen Projekten ist aber auch Setup. Dieses moderne KI-Setup, was ja noch kommt, noch deutlich hinterher, bis zu dem, was wahrscheinlich jetzt schon geht, womit vielleicht gewisse oder zu einem gewissen Komplexitätsgrad Refactoring-Projekte sehr stark trivialisiert werden können. Auch jetzt schon.

Jan:

Auch da sieht man oft, es lohnt sich, so transparent wie möglich mit dem Kunden da auch umzugehen, immer zu präsentieren in Reviews, was man herausgefunden hat, wie einem die KI zum Beispiel auch geholfen hat, gerade wenn man transformieren will von einem einfachen Chatbot zu wirklich einer Agentive AI, um auch für den Kunden eine Motivation herzustellen, diese Plattform zu bieten. Weil das ist nämlich auch ganz wichtig. Oftmals steigt das Verständnis eben nur langsam für die Notwendigkeit. Man denkt, so eine API ist vielleicht teuer, vielleicht tut es auch ein Chatbot. Und da eben wirklich immer weiter zu motivieren, weiter zu zeigen, was für Effizienzgewinne das gibt, lohnt sich dann eben in der Praxis auch.

Udo:

Ja, verstehe. Jetzt ist ja Refactoring in der Regel auch ein relativ technisches Unterfangen. Ich nehme an, wahrscheinlich habt ihr auch die Storys dazu dann von Entwicklern schreiben lassen. Gehe ich stark von aus. Wie sind eure Erfahrungen, gerade wenn ihr jetzt neue Entwickler dazu bekommen habt und denen so ein Stück Code irgendwie zum Refactoring hinlegt, wie schreibt ihr da Akzeptanzkriterien oder wie stellt ihr sicher, sage ich jetzt mal, dass das Refactoring das Ergebnis bekommt, was ihr dann auch haben wollt? Weil ich sage jetzt mal, wenn ich neu in eine Code-Basis reinkomme, dann ist das wirklich wie eine neue Stadt kennenlernen. Ich steige irgendwo am Hauptbahnhof aus und dann versuche ich mich erstmal zu orientieren. Da gibt es irgendwie bestimmte Gebäude, die kann ich mir merken, weil die sind einfach sehr eindrucksvoll. Und dann gibt es aber ganz viele Straßen, da finde ich mich erst nach Wochen oder Monaten zurecht, weil ich einfach nicht weiß, die nicht wiedererkennen kann, irgendwie optisch oder was. Und so ist es ja an Code letztendlich auch. Wie stellt ihr sich ja, dass Leute, die neu reinkommen, sich da gut integrieren können? Das Schöne erstmal an Refactoring ist ja, das ist nicht mehr der Hauptbahnhof. Das fängt ja meistens bei irgendeiner Seitenstraße an, ohne dass man den Hauptbahnhof je gesehen hat. Ich würde sagen, das ist eine Challenge, mit der wir uns befassen oder länger befassen müssen. Wir haben in dem Projekt unter anderem auch Nearshoring verproben dürfen. Wir verwenden es jetzt auch. Und der Gedanke ist erstmal da, in einem großen fachlichen Kontext. Man will die meisten wissen, oder von einem Podcast, Kufenpro ist ja im Bankenumfeld unterwegs, das ist meistens mit viel Fachlichkeit verbunden. Gerade wenn man aus dem Nearschalleng-Kontext kommt, andere Sprache, andere Regulatorik, in die man damit hergeht, Projekt wird erstmal zum Einstieg sehr komplex sein. Dann eignet sich doch bestimmt so ein Migrations-Refactoring-Task, weil der ist ja erstmal sehr technisch. Ja, aber wenn ich das Umfeld kaum kenne, wie soll ich denn dann wirklich nachvollziehen können und testen können, auch an der deployten, lebenden Umgebung, nicht nur in Unit-Tests, dass meine Änderungen nichts kaputt gemacht haben, dass sie noch funktionieren. Und das bleibt nach wie vor eine Challenge, weil man A, zu sehr mit diesem Fokus, als kleiner Witz gemeint, aber wenn man zu sehr in diesem Refractoring einsteigt, ohne dass man hier den Hauptbahnhof gesehen hat, dann bleibt man sehr stark verirrt in der Gasse, die man irgendwo an der Seite hat. Man findet aber nie wirklich zu diesem Hauptbahnhof zurück, um mal ein ordentliches Stadtbild zu kriegen. Was wir gesehen haben, neue Entwickler, die zu sehr in diesem Task anfangen, zu sehr direkt im Refractoring drin sind, auch wenn man sich erwartet, Also das hat doch bestimmt einen schnellen Anlaufeffekt. Ja, vergleich zu anderen Tätigkeiten wahrscheinlich, aber die Integration in das Projekt, in das Umfeld bleibt ein bisschen aus, weil man zu sehr in diesem technischen Gefangenen ist. Und deswegen, was man durchaus schauen muss, ist, dass das von allein nicht der Fokus ist. Gleichzeitig müssen erfahrene Kollegen, die die Hoheit haben für das Projekt, dranbleiben, wie wir das schon zur KI hatten. Merch-Reviews müssen sehr akribisch und sehr durchdacht durchgeführt werden, viel Zeit investiert werden in was genau da passiert wird und auch die physischen oder die manuellen Retests, die man macht, wenn man dann Änderungen deployt. Und dadurch, dass Refactoring sich oft nicht auf eine Anwendung oder eine Funktion in der Plattform bezieht, die man dann explizit testen könnte, sondern gerade wir an API-Schrauben, die alle Anwendungen in irgendeiner Form beeinflusst, wir versuchen, dass man einen umfassenden Test so viel abdeckt wie möglich.

Tristan:

Das ist in der 100% Abdeckung gar nicht machbar. Und das ist einer der Herausforderungen, die uns immer noch begleitet.

Jan:

Ja, ich würde auch sagen, einen großen Fehler würde man machen, wenn man sagt zu neuen Kollegen, ja, verlockend ist es natürlich, gerade neue Kollegen dann oft in diese Refactoring-Aufgaben reinzusetzen oder in Umwandlung von Code allgemein, weil man sehr schnell dazu kommt, da produktiv zu werden. Aber genau wie Tristan es eben gesagt hat, es ist dann natürlich die Folge, dass die Leute das Große und Ganze nicht verstehen, sodass man einen gewissen Mix finden muss aus eben diesen Aufgaben, die sehr schnell produktiv sind und trotzdem sich die Zeit nehmen, auch in andere Themenbereiche, in neue Features oder so die neuen Kollegen eben einzuarbeiten.

Udo:

Ja, das sind jetzt zwei Punkte, die mir da auffallen. Zum einen hast du gesagt, man muss dann schon auch viel Zeit in den Merch Review investieren und ich glaube, das ist aber eine Botschaft, die man auch den Product Ownern und den Projektverantwortlichen dann auch wieder mitgeben muss. Also klar, wir sparen eine Menge Zeit und wir gewinnen eine Menge Produktivität durch die KI oder durch die Large Language Models, aber wir müssen halt eben die Kontrolle dafür mindestens genauso gut machen wie vorher und da sparen wir eben gar keine Zeit. Und das zweite Ding ist, ja, Unit-Tests wird man. Ohne fachlichen Kontext noch ganz gut schreiben können, Integrationstests zum Teil, aber bei End-to-End-Tests, da hört es dann auch relativ schnell dann schon auf, weil ich nicht verstehe, was die Anwendung per se eigentlich tut, im Normalfall. Also ich kann gar nicht den Happy-Pass von dem Bad-Pass richtig unterscheiden und habe dann natürlich Probleme, das sicherzustellen, dass die Anwendung noch tut, was sie soll.

Tristan:

Da haben wir auch zu der vorigen Frage, um die mal konkret zu beantworten, an sich keine, ich nenne es mal, gute Lösung, außer die wichtige Erkenntnis, die Zeit muss investiert werden, im Nachhinein um sich zu stellen, dass die Änderungen noch passen.

Udo:

Gut, Jan Tristan, habt ihr sonst noch etwas, was ihr zu dem Thema Refactoring loswerden wollt? Sonst würde ich sagen, vielen Dank für euren Besuch bei Coffee Code & Consult Und damit würde ich die Folge beenden.

Tristan:

Danke, dass du uns gehostet hast,

Jan:

Wie du hier sein darfst. Danke.

Udo:

Sehr gerne, sehr gerne. Und vielleicht können wir ja zu dem Thema, wie man neue Kollegen in Projekte reinbringt, nochmal irgendwann später sprechen. Und wie gesagt, ich packe die Verweise zu dem Buch und der alten Folge in die Shownotes. Und freue mich natürlich, wenn ihr uns Zuschriften schickt, wie eure Erfahrungen zu Refactoring in euren Projekten waren. In diesem Sinne, macht's gut, bis bald. Bis zum nächsten Mal. Tschüss.

Podcasts we love

Check out these other fine podcasts recommended by us, not an algorithm.