5. Juli 2021

Legacy Code – Das Erbe das keiner will

Heute besprechen Alex und Matthias „Legacy Code“. Falls du an einem solchen System gerade arbeitest und viele Schmerzen damit hast, kann diese Folge diese vielleicht etwas lindern. Aber auch wenn du zu den wenigen glücklichen gehörst, die noch nie in einem solchen Software-System zurecht kommen mussten, kannst du viele wertvolle Informationen rausziehen und bist auf die erste unausweichliche Begegnung mit Legacy-Code etwas besser vorbereitet.
Macht der Craft
Macht der Craft
Legacy Code - Das Erbe das keiner will
/

Alex: [00:00:03.36] Hallo und herzlich willkommen zu eine neue Episode von der Macht der Craft.

Matthias: [00:00:07.80] Hier sind Matthias.

Alex: [00:00:08.97] Und Alex.

Matthias: [00:00:10.29] Heute wollen wir über Legacy Code sprechen. Du erfährst heute, was das ist und wie du damit umgehen kannst,

Alex: [00:00:16.89] Egal ob du bereits Kontakt mit Legacy Code hattest ist oder nicht. Hier erhältst du hilfreiche Tipps, um die Arbeit damit leichter zu machen.

Matthias: [00:00:25.17] Wir orientieren uns dabei an den Vorschlägen, die Michael Feathers in seinem Buch „Effektives Arbeiten mit Legacy Code“ veröffentlicht hat und streuen hier und da dann unsere eigenen Ideen und Erfahrungen mit ein.

Alex: [00:00:36.54] Wir wünschen dir viel Spaß und viele Learnings bei der Folge.

Off: [00:00:41.37] Die Macht der Craft arbeitet für dich.

Alex: [00:00:46.00] Aber wir haben das Begriff verwendet, ohne zu sagen, was eigentlich Legacy Code ist. Da hausieren sehr viele unterschiedliche Definitionen davon. Was würdest du sagen, Matthias was Legacy Code ist?

Matthias: [00:01:03.49] Also ich bewege mich relativ nah an Michael Feathers’ Interpretation. Er beschreibt das Ganze in erster Linie mit fehlenden Tests tatsächlich. Also wenn man nun ein Software System hat, das eine hinreichende Komplexität und Größe hat, um nicht komplett erfasst zu werden. Und es ist ziemlich schnell der Fall und ich habe keine Tests, die mir die Möglichkeit geben, die Richtigkeit dieses Software Systems zu gewährleisten. Dann spreche ich von Legacy Code. Also das ist auch so meine Definition.

Alex: [00:01:41.50] Ich habe da unterschiedliche Definitionen gehört. Ich habe z.B. mal gehört Legacy Code ist jegliche alte Code. Alles was wir schon mal irgendwo gemacht haben, alles ist Legacy Code. Meiner Meinung nach mag das stimmen, muss aber nicht sein. Die Tatsache, dass der Code alt ist, macht es nicht per se schwierig, es zu verstehen oder kompliziert zu ändern.

Matthias: [00:02:07.03] Genau. Also ich würde darauf entgegnen jeglicher Legacy Code ist relativ alter, also älterer Code, aber nicht jeder ältere Code ist Legacy Code.

Alex: [00:02:18.61] Es lässt sich nicht so so so leicht sagen. Also die Definition ist zwar in der eine Richtung richtig, aber da fehlt was. Also es ist nicht genug zu sagen, dass Legacy Code einfach alte Code sei. Dann gibt es andere Menschen, die gehen ein Stückchen weiter und sagen Legacy Code ist Code von jemanden anders, die wir übernehmen müssen.

Matthias: [00:02:40.33] Ja.

Alex: [00:02:40.33] Zumindest teilweise stimmt das irgendwie. Aber das ist auch, meiner Meinung nach, zu kurz gedacht. Das Problem mit diesem Code ist, dass wir es nicht gut kennen und es eventuell schwer fällt, ihn zu verstehen, wie es funktionieren und wie er die Sachen macht, die es macht. Und wie können wir da anpassen. Und auch mit Code, die ich selber geschrieben habe, nach ein paar Monate. Es fühlt sich so an, als ob der Code von jemand anders geschrieben wäre. Auch der Code muss nicht zwingend fremd sein, um um Legacy zu sein oder als Legacy betrachtet werden zu können.

Matthias: [00:03:20.75] Nee, klar. Also es kann meiner Meinung nach sehr schnell gehen, dass Code zu Legacy Code in Anführungszeichen wird, weil naja, auch relativ neuer Code, wenn ich den schreib und ich teste den nicht, dann lasse ich den ein halbes Jahr liegen und soll dann nach einem halben Jahr über irgendeine Detail-Änderung daran vornehmen. Ohne Tests kann es mitunter relativ schwierig werden. Und dann habe ich dasselbe Problem mit meinem eigenen Code, der ein halbes Jahr alt ist. Also gebe ich dir absolut recht.

Alex: [00:03:53.42] Deswegen ist für mich die Definition, hat auch ein Körnchen von Wahrheit, aber es ist auch nicht das Gelbe vom Ei, sagen wir man so?

Matthias: [00:04:02.48] Ja, ich glaube, es ist tatsächlich sehr schwierig, da eine allumfassende Definition zu finden.

Alex: [00:04:08.75] Ich habe aber noch mehr.

Matthias: [00:04:10.11] Na dann lass mal hören.

Alex: [00:04:12.14] Die dritte hast du schon erwähnt, ist das Thema Code ohne Tests. Es ist das, was Michael Feathers in seinem Buch als Definition vorschlägt. Er meint, dass jegliche Code ohne Tests ist Legacy. Dem sei es so, weil wenn keine Test da sind, wir uns schwertun, den Code zu verstehen und vor allem zu verändern. Wir haben keine kein Sicherheitsnetz, um Änderung sicher machen zu können. Und tatsächlich ist das so! Aber, da hab ich auch ein Aber. Ob Tests da sind oder nicht, macht den Code zwar verständlicher, aber das heißt nicht, dass Code, dass keine Tests hat, nicht verstanden werden kann. Relativ leicht und schnell, ne. Es hängt von der Komplexität, von der Größe des Projekts. Und es gibt sehr viele Variablen. Ich war schon einmal konfrontiert mit Code, die zwar keine Tests hatte, aber der war recht gut geschrieben. Es war richtig klar, es war nicht so groß und so war es relativ leicht zu verstehen, was die tun, so dass man Änderungen vornehmen kann, ohne vorherige Tests zu haben. Ob das Legacy ist oder schon nicht Legacy ist, sei dahingestellt. Aber ja, da fehlt mir auch ein bisschen was und ich würde das vielleicht ein bisschen aus eine andere Perspektive sehen, also nicht aus die technische Perspektive, was ist technisch gesehen Legacy Code, sondern eine viel persönlichere Ebene. Und zwar, alle drei Definitionen, die wir bis jetzt gehört haben, haben irgendwie – die eine besser, die andere nicht so gut -. Ein Kern der Wahrheit, was wir als Legacy Code verstehen können. Und alle drei haben meiner Meinung nach irgendetwas gemeinsam, nämlich die Schwierigkeiten, den Code zu verstehen

Matthias: [00:06:12.78] Oder zu verändern,

Alex: [00:06:14.49] Um ihn zu verändern, muss ich ihm erst einmal verstehen, um es Sicher ändern zu können. Deswegen ist für mich das Kernproblem Code, die ich nicht verstehe. Ja, und das kann sein, weil er alt ist. Das kann sein, weil er keine Tests hat. Es kann sein, was auch immer. Aber wenn ich Schiss habe, den Code zu ändern, weil ich gar nicht weiß, was er macht oder wie der funktioniert. Das ist für mich Legacy. Das ist, was mir Probleme bereitet. Ich verstehe das nicht, deswegen kann ich nicht ordentlich ändern oder ich bin nicht sicher, ob ich es richtig geändert habe. Wir stehen vor Legacy Code, wenn wir uns unsicher sind, ob wir in der Lage sind, den Code korrekt und ohne ungewollte Nebeneffekte zu verändern. Was hältst du davon?

Matthias: [00:07:01.17] Finde ich gut. Ich persönlich bin zwar nach wie vor, solange ich Tests hab, wäre es für mich also auch wenn ich den Code jetzt nicht auf Anhieb verstehe, dann möglich weitere Tests hinzuzufügen, um Dinge herauszufinden und sowas. Also eher so dieses explorative, die explorative Seite von Tests. Also ich kann Tests auch nutzen, um Annahmen, die ich über den Code, den ich nicht zu hundert Prozent verstanden habe, kann ich auch Tests nutzen, um die diese Annahmen zu prüfen. Und das fällt mir natürlich leichter, wenn schon Tests vorhanden sind und somit auf das Test Framework z.B. schon eingerichtet ist und sowas. Aber grundsätzlich gebe ich dir recht. Die Essenz ist man, wenn man den Code nicht verstehen, durchdringen kann, dann hat man auf jeden Fall Legacy Code. Gebe ich dir absolut.

Alex: [00:07:54.75] Natürlich helfen die Annahmen von alle andere Definitionen da mit rein, ne. Je neuer der Code ist, desto wahrscheinlicher ist es, dass es mit Technologien und Methoden gespickt ist, die wir auch jetzt verwenden. Mein Code verstehe ich meistens eher besser als der Code von jemand anders. Wenn da Tests sind. Es ist deutlich einfacher zu verstehen, wie der Code funktioniert. Natürlich fällt das alles im Gewicht, aber das grundsätzliche Problem ist eben wie gesagt meiner Meinung nach, dass ich den Code nicht verstehe. Um den Code zu verstehen, um diese Legacy Code in den Griff zu bekommen. Das ist nicht immer leicht und wir schauen uns später noch ein paar Tipps an, um Code mit Tests abzudecken, ne. Wie können wir Tests in einen Code reinbringen, die keine Tests hat oder die weniger Tests hat? Welche Probleme haben wir mit Legacy gut? Was denkst du?

Matthias: [00:08:50.41] Ja, Legacy Code, die Probleme, die ich damit immer wieder habe, ist das, wenn ich Anpassungen an Legacy Systemen gemacht habe, oft Dinge kaputt gegangen sind, mit denen man einfach nicht gerechnet hat. Also, es ist schwer verständlich. Man kann nur schwer Anpassungen machen. Wir haben keine Tests, womit wir sicherstellen könnten, dass das System nach unseren Änderungen noch das Gleiche macht wie vorher. Und ja, ganz häufig ist es dann auch noch der Fall, dass die Leute, die dieses Legacy System entwickelt haben, nicht mehr in dem Projekt sind, die Firma verlassen haben. Also es fehlt auch jede Menge Know how. Das heißt, man muss sich in unbekanntem Code einarbeiten und das ist mitunter sehr, sehr schwierig. Und ich finde je älter so eine Codebasis ist und je ungepflegter, desto schwieriger wird es dann tatsächlich auch.

Alex: [00:09:49.87] Du hast einen Auftrag bekommen. Es besteht darin eine bestehende Codebasis, irgendwelche Änderungen und Anpassungen zu machen. Was ist das erste, was mir in den Sinn kommt, wenn ich sowas höre? Wir werden Zeit brauchen, um uns mit der alten Codebasis bekannt zu machen, um zu versuchen zu verstehen, damit wir was ändern können, ohne irgendwas kaputt zu machen. Das heißt, das Problem besteht für mich erst einmal darin, zu verstehen, was der Code, die schon da ist, macht.

Matthias: [00:10:25.91] Genau.

Alex: [00:10:27.68] Wir haben bis jetzt ein paar Mal schon das Begriff Legacy Code verwendet. Es hat oft einen negativen Beigeschmack. Wahrscheinlich, weil wir immer so viel Ärger damit haben und so viel Arbeit damit haben. Okay, wir müssen alter Code anpassen, alter Code verändern. Aber warum müssen wir das? Ich kenne Leute, die sagen: „Ja, da ist ein Programm, der ist alt, das versteht eh keine Sau, das schreiben wir jetzt neu!“. Ist das eine gute Idee? Ist das eine schlechte Idee?

Matthias: [00:10:54.18] Ich sage mal, es kommt auf den den Umfang dieses Systems an, also ich meine Legacy Systeme haben meistens auch noch so die, – Wenn wir jetzt wirklich über die richtigen großen Enterprise Legacy Systeme reden – die haben wir natürlich unfassbar viele Entwickler-Stunden im Bauch. Also da ist ja unfassbar viel Know how drinnen und unfassbar viel passiert. Und wenn das alles neu geschrieben werden muss, hat man natürlich das Problem, dass überhaupt erst einmal alles wieder abbilden zu können. Und auch dafür brauche ich natürlich erst mal wieder eigentlich die Grundlage. Also was soll das System denn eigentlich machen? Und das ist ja auch eine der Sachen, die wir gerade in der Definition hatten. Wenn ich den Code nicht verstehe, hab ich einen Legacy System. Das heißt, ich kann unter Umständen das System nicht wieder so von der Funktionalität nachbauen, wie es jetzt gerade ist.

Alex: [00:11:50.71] Ok. Ich finde, du hast zwei wichtige, sehr wichtige Sachen erwähnt. Das erste ist, und das fließt mal wieder ein bisschen in der Definition, was Legacy Code ist. Du hast vom produktiven Code geredet. Und ich finde, das ist wichtig, damit es Legacy Code tatsächlich ist, muss jetzt produktiven Code sein. Es muss ein Code sein, die irgendwo läuft, die Geld für das Unternehmen bringt. Ja, dann ist es ein Legacy Bestandteil. Wenn der Code nicht produktiv ist, was sollte es für einen Grund geben den zu verändern? In meinen Augen gar keine.

Matthias: [00:12:27.60] Vollkommen Recht, wenn dann nur zur Übung, um Refactorings zu üben.

Alex: [00:12:31.92] Ja gut, aber das hat mit produktiven Arbeiten dann eher nichts zu tun, sondern eher mit üben, ne?, wie du sagst. Der zweite Aspekt, die ich betrachten wollte, war eine Neuschreibung. Wie du gesagt hast, musst du alle Features wieder reinbringen, auf irgendeine Art und Weise. Ja, ich muss sie nicht gleich programmieren, aber die Features muss man im Großen und Ganzen alle da sein.

Matthias: [00:12:57.90] Sagen wir mal so, wenn ich das gleiche in das System rein gebe, will ich das gleiche rausbekommen danach.

Alex: [00:13:03.42] Genau.

Matthias: [00:13:03.99] Ganz grob gesagt.

Alex: [00:13:06.03] Und das ist in ein großes komplexes System öfters mal mit viel mehr Arbeit und Mühe verbunden, als einfach den alten Code anzupassen und weiterzuentwickeln. So, deswegen sind Neuschreibungen nicht immer die Lösung aller Probleme. Also Greenfield ist schön, weil du ganz schön arbeiten und andere Terminologie verwenden und und und. Aber es ist nicht nur mit gute Sachen verbunden, sondern auch mit der eine oder andere Problemchen. Es kann sein, dass durch diese neue Implementierung von Funktionalitäten, die vorher einwandfrei funktioniert haben, jetzt hier neue Fehler reinbauen.

Matthias: [00:13:43.68] Ja, genau.

Alex: [00:13:45.74] Aber welche Gründe haben wir, Software zu ändern? Eine hast du genannt. Ganz wichtig Fehlerkorrekturen, Sicherheit-Patches, was auch immer. Also alles was Fehlerkorrekturen betrifft. Ja, die andere hast du auch noch erwähnt. Eine zweite. Die wäre meiner Meinung nach neue Funktionalität hinzufügen. Also entweder will ich neue Funktionalität hinzufügen oder will ich Fehlerkorrekturen vornehmen. Öfters ist aber schwer zu unterscheiden, ob eine Änderung neue Funktionalität ist oder eine Fehlerkorrektur. Stell dir vor, wir haben eine Anwendungen und wir haben ein Logo und der Logo ist blau. Und jetzt will irgendjemand, dass der Logo grün ist? Ist das neue Funktionalität oder ist das ein Bug?

Matthias: [00:14:41.32] Ja kommt drauf an, wer wer dafür zahlt

Alex: [00:14:45.53] (lacht)

Matthias: [00:14:46.41] Nee, also aus meiner Warte kommt es darauf an, wenn die Anforderung mal war, dass das Logo blau sein soll und sich diese Anforderung ändert, würde ich eher von einer Anpassung aufgrund von einem Change in den Requirements argumentieren. Weil ein Fehler ist es letztendlich nicht gewesen. Also wenn mir von Anfang an gesagt worden wäre, ich hätte das grün machen sollen. Hab’s aber blau gemacht, weil ich blau viel cooler find. Und wenn man das jetzt aus der Sicht ich wäre jetzt bei einem Dienstleister, der das für den Kunden macht, dann würde der zurecht sagen Das bezahle ich dir nicht, diese Änderung.

Alex: [00:15:24.49] Stell dir vor, ich wäre jetzt der Kunde und ich sage dir, das ist ein Fehler.

Matthias: [00:15:27.70] Dann, wie gesagt, dann kommt es für mich darauf an, ob es vorher aufs Spiel von Anfang an gesagt worden ist, dass ich es hätte gleich grün machen sollen und habe es einfach nicht gemacht. Oder es hat am Anfang geheißen Nee, blau, es gefällt uns auch super, mach mal blau.

Alex: [00:15:44.53] Es hat keiner was gesagt.

Matthias: [00:15:45.85] Es hat keiner was gesagt?

Alex: [00:15:47.57] Ne, aber ich bin der Kunde. Ist es augenfällig, dass es grün sein muss?

Matthias: [00:15:52.77] (lach) Es ist doch klar (lach).

Alex: [00:15:55.42] Natürlich, ist das klar.

Matthias: [00:15:56.74] Ja, das wäre tatsächlich eine Diskussion, die ich jemand anderes überlassen würde. (beide lachen)

Alex: [00:16:03.13] Also, was ich zeigen wollte, ist, was, für mich wäre, um ehrlich zu sein eine neue Funktionalität.

Matthias: [00:16:13.34] Also Fehler wäre es in meinen Augen nicht.

Alex: [00:16:15.43] Keine technische Funktionalität. Aber der Kunden kann es aber als ein Fehler empfinden. Und deswegen ist es manchmal echt schwer Grenzen zu setzen. Was aber wichtig ist, egal ob wir es als neue Funktionalität oder als Bug definiert, Ja?, gehen wir weg davon, dass ist völlig egal, ob wir neue Funktionalität oder einen Bug ändern wollen. Das, was wir auf jeden Fall machen, ist, das Verhalten der Anwendungen anzupassen. Wir verändern das Verhalten der Software. Bist du damit happy?

Matthias: [00:16:48.58] Ja, es passt für mich.

Alex: [00:16:53.05] Der eine Typ von Änderungen, die wir am Software machen, ist in meinen Augen, angelehnt an Michael Feathers, sind Änderungen, die wir durchführen, um das Verhalten der Anwendung zu verändern.

Matthias: [00:17:07.18] Genau. Entweder bereits Bestehendes verändern oder Neues hinzufügen.

Alex: [00:17:11.83] Genau. Es gibt aber andere Gründe, warum wir Code ändern. Fällt dir da was ein?

Matthias: [00:17:20.68] Naja, Optimierungen, also Performance-Verbesserungen sind ja auch letztendlich Anpassungen am Code notwendig, die mitunter auch ein sehr tiefes Verständnis des Codes erfordern. Weil Code, den ich nicht verstehe, kann ich nicht optimieren, egal auf welche Weise. Genau. Ansonsten Man kann grundsätzlich die Struktur des Codes ändern, ohne das Verhalten anzupassen. Ja sind letztlich Änderungen, die keine Funktionalität hinzufügen.

Alex: [00:17:50.12] Also die andere, wie du wohl gesagt hast, sind Optimierungen entweder am Code, an der Struktur des Codes oder Optimierungen an der Performanz oder irgendwelche anderen nicht funktionalen Anforderungen. Die beide haben aber auch etwas gemeinsam, nämlich die dürfen das Verhalten der Software nicht verändern. Also wir haben im Endeffekt nur zwei Arten von Änderungen die, die das Verhalten ändern und die, die das Verhalten nicht ändern dürfen. Ne?

Matthias: [00:18:21.36] ja. Genau.

Alex: [00:18:23.79] Neue Funktionalität und Fehler bedeuten Verhaltensänderung, Design und Ressourcen Optimierung bedeuten keine Verhaltensänderung. Soweit so gut.

Matthias: [00:18:35.94] Soweit so gut, ja.

Alex: [00:18:38.73] Wie kann ich gewährleisten und sicherstellen, dass, wenn ich Änderungen machen, die das Verhalten nicht verändern oder nicht verändern dürfen, die es auch nicht tun.

Matthias: [00:18:50.79] Noch fällt mir mal wieder nur Tests ein.

Alex: [00:18:55.11] Code zu verändern, ohne das Verhalten zu ändern, hat einen Namen. Wie heißt das?

Matthias: [00:18:59.58] Refactoring.

Alex: [00:19:00.51] Genau Refactoring. Refactoring ist das, was wir tun, wenn wir die Anwendung verbessern oder verändern. Aber die Funktionalität, das Verhalten der Anwendung nicht verändern. Dann refaktorisieren, gibt’s ein deutsches Wort dafür?

Matthias: [00:19:17.39] Ja, ich höre immer wieder, also ich persönlich sage refaktorieren. Aber ich höre auch immer wieder refaktorisieren. Ich habe mir ehrlich gesagt nicht sicher. Aber es ist okay. Eventuell ist beides korrekt. Eventuell liege ich vollkommen falsch. Ich weiß es tatsächlich nicht.

Alex: [00:19:32.37] Gemeint ist Refactoring, dass das englische Begriff Refactoring.

Alex: [00:19:38.09] Also bei jeder Änderung, die wir durchführen, müssen wir zwei Sachen sicherstellen. Das erste ist, dass die Änderungen, die wir machen, korrekt implementiert sind, und das zweite ist das bereits vorhandene Verhalten nicht geändert wurde.

Matthias: [00:19:55.07] Genau.

Alex: [00:19:55.91] Also, die zwei Sachen mussten wir sicherstellen. Immer, bei jeder Änderung. Dabei ist es wichtig, folgendes zu wissen und zu verstehen: Was muss geändert werden? Wie weiß ich, dass die Änderungen korrekt funktionieren? Und wie weiß ich, dass die Änderungen nichts anders kaputt gemacht haben? So, diese drei Fragen sind für mich die drei Kernfragen bei jeder Änderung, die wir durchführen. Und die Antwort auf diese Fragen ist das, was uns hilft die Angst von dieser Legacy Code zu verlieren, zumindest weniger Angst davor haben zu müssen.

Matthias: [00:20:30.05] Zu lindern. Ganz weg wird sie wahrscheinlich nie gehen,

Alex: [00:20:35.09] Ja aber das hilft auch, wenn ich weiß, wie ich diese Fragen beantworten kann. Da bin ich deutlich sicherer

Matthias: [00:20:42.41] Von den Fragen, die jetzt aufgeworfen das finde ich auch tatsächlich die schwierigste zu beantworten: Das Sicherstellen, dass man an keiner anderen Stelle im System was verändert, ohne es zu wollen. Das ist rein theoretisch nur möglich, wenn alles unter Tests ist, zu einem gewissen Grad und das ist tatsächlich glaube ich das Schwerste zu erreichen. Diese Antwort immer mit „Ja, kann ich sicher sein“ beantworten zu können. Vor allem mal eben mit einem der gesündesten.

Alex: [00:21:16.58] Wir wollen unser Code verbessern oder neue Funktionalität hinzufügen. Wie können wir das machen? Soll das der Code auch besser wird, im Sinne von einfacher zu ändern, einfacher damit zu arbeiten, besser verständlich und so weiter. Ich würde jetzt mal gerne ein paar Tipps einfach mal loswerden für die Arbeit mit Legacy Code. Wenn ich mir Legacy Code vornehme. Wie groß sind die Änderungen, die ich durchführen soll?

Matthias: [00:21:45.11] Möglichst klein? Meiner Ansicht nach. Also ich sollte es möglichst vermeiden, viele Bereiche anfassen zu müssen. Also ich sollte möglichst genau die Stelle lokalisieren, die notwendig ist, angepasst zu werden, um die gewünschte Änderung zu erreichen. Und dann in erster Linie würde ich sagen Verständnis aufbauen. Also versuchen zu verstehen, was in dem Bereich passiert, welche andere Teile des Systems sind betroffen. Das Grundverständnis aufbauen.

Alex: [00:22:23.66] Ich bin voll bei dir. Klein anfangen. Kleine Änderungen vornehmen. Wenn wir den Code nicht verstehen, wenn wir einfach Angst haben, den Code zu ändern. Das ist verständlich, aber es ist kein Grund, es nicht zu tun. Fang an immer mit irgendetwas Kleines, dass du das Verständnis für den Code, wie du gesagt hast, aufbauen kannst, ne?. Ändere den Code inkrementell und ganz ganz wichtig: Refactoring ist ein ganz wichtiger Bestandteil der Aufgabe. Ich sag immer: „Refactoring ist ein Freund“.

Matthias: [00:22:56.63] Ein Fehler, den ich früher auch häufig gemacht habe, wenn es darum ging, solche Systeme anzupassen. War das nicht häufig genug funktionierende Zwischenstände committed habe. Dann, wenn ich dann in Probleme gelaufen bin, also dass ich irgendwas so kaputt gemacht hat, das es einfach nicht mehr zu retten war, weil ich mich vollkommen in diese Code Hölle verstrickt habe. Und dann hatte ich keinen Stand mehr zu dem zurück konnte. Dann hieß es Naja, nochmal von vorne anfangen eigentlich. Deswegen so ein kleiner Tipp zwischendurch häufig committen. Vor allem wenn was funktioniert hat. Also nur eine kleine Anpassung gemacht hat. Das System verhält sich wie erwartet. Kann man einfach mal committen, weil zu dem Stand kann man dann auf jeden Fall jederzeit zurück. Hilft auf jeden Fall

Alex: [00:23:40.28] Ein weiteres Thema, der mich bewegt, obwohl ich der erste bin. Die da es öfters beherzigen dürfte, ist nicht schimpfen. verurteile nicht. Unbekannt bedeutet nicht schlecht. Also was haben Sie vor einem *piep* *piep* *piep* programmiert? Hilft nicht. Befreit manchmal die Seele aber hilft nicht, dass wir nicht verstehen, was die gemacht haben, bedeutet eben nicht, dass der Code das andere geschrieben haben, oder wir vorher geschriebener, schlecht ist, sondern dass du die Situation, in dem wir zu der Zeit waren oder die andere Kollegin zu der Zeit waren, war es durchaus möglich, dass es die beste Implementierung, die zu der Zeit machen konnten.

Matthias: [00:24:24.46] Genau. Zumindest nach besten Wissen und Gewissen. Also davon gehe ich z.B. immer aus. Ich will niemandem unterstellen, dass er mit Absicht unwartbaren Code produziert, aber das ist halt häufig auch ein, wie soll man sagen, Problem, dass man es zu einem gewissen Zeitpunkt nicht besser weiß. Also ich hab schon Softwares Systeme gesehen, die wurden halt von Studenten in ihrer Studienzeit für Unternehmen geschrieben und die sind jahrelang gelaufen und werden dann irgendwann an eine Agentur übergeben. Und natürlich die Agentur stellt fest, dass der 18 jährige oder 19 jährige Kerl vielleicht nicht die schönsten Konstrukte drauf hatte und alles mit Tests abgedeckt hat, sondern es ist halt ja, wie sagt man so schön, Spaghetti Code gewesen. Aber er hat über Jahre getan, was er sollte und hat Geld produziert. Und dann finde ich es ein Unding halt, über, darüber herzuziehen, was da so gemacht hast. Und so hab ich alles auch schon erlebt.

Alex: [00:25:22.00] Also ich sage, was ich mir auch zu Herzen nehmen muss, gebe ich zu, ist nicht schimpfen. Die Personen, die den Code geschrieben haben, geh einfach davon aus, dass sie es so gut gemacht haben, wie sie zu der Zeit konnten, weil du weißt nicht, in welcher Situation sie sich befanden und deswegen. Also es bringt erst einmal nichts und es ist erst mal nicht besonders respektvoll, was da rauskommt.

Matthias: [00:25:44.18] Na ja, genau. Und wie gesagt das man will ja auch nicht, dass über einen selbst so geredet wird und jeder hat mal angefangen und jeder hat mal Fehler gemacht. Die eine oder andere hatte halt das Pech, das Produkt über Jahre sehr, sehr große Summen an Geld produziert hat und deswegen wichtig geworden ist für ein Unternehmen und weiterentwickelt werden soll. Was ein Student, der vor 5 Jahren irgendwie das Ding entwickelt hat, aber natürlich nicht mehr daran arbeitet, auch nicht leisten kann. Da sollten wir alle ein bisschen Professionalität wahren und einfach schauen, dass man dann aus so einem System einfach dann trotzdem das Beste rausholen kann.

Alex: [00:26:20.95] Eine andere Sache. Ein weiterer Punkt, die mir immer sehr hilft, ist nicht alleine zu arbeiten. Also arbeite immer mit irgendjemanden zusammen. Heißt für mich entweder Pair Programming ,Mobs. Das hilft tatsächlich ungemein, um das Code zu verstehen, weil vielleicht verbrenne ich mich aber mein Pair jetzt erklären: „Öh Alex, halt mal an, du verrennst dich gerade, das ist nicht so, das macht das und das“. Also in Pair oder in Mopp sogar wenn nichts anders geht nur mit Code Reviews zu arbeiten, sodass wir immer diese mindestens 4 Augen Prinzip bewahren und diese Austausch zwischen mehrere Entwicklern bei der Änderung passiert, ne?. Nicht, dass wir drei Monate später erstmal, sich irgendjemand das anschaut und sagt: „Ja, du hast einen Fehler gemacht“. Hilf mir ja viel besser wenn… Er kann mir ja das gleiche sagen, aber wenn es mir gleich sagt bin ich das machen kann man es gleich richtig machen?

Matthias: [00:27:17.43] Ja, das stimmt.

Alex: [00:27:19.57] Mehrere Köpfe sehen mehr und vor allem, auch sehr, sehr, sehr wichtig, das Wissen, wie der Code funktionieren, verteilt sich auch auf mehrere Köpfe und das ist nicht nur bei mir oder bei derjenige Person, die gerade das implementiert, sondern bei zwei oder mehrere Personen, die gemeinsam das Problem lösen.

Matthias: [00:27:38.43] Ja, stimme ich dir absolut zu. Das hilft auf jeden Fall.

Alex: [00:27:43.12] Was hilft mir auch total viel, wenn ich mit Legacy Code arbeite? Namen. Und zwar ordentliche Namen!

Matthias: [00:27:53.68] Genau. Und manchmal muss man die einführen. In so ein Legacy-System.

Alex: [00:27:57.88] Ja, natürlich. Aber eine der grundlegende Probleme beim beim Lesen von Code, die ich zumindest habe, ist zu verstehen, was irgendwelche Variablen oder irgendwelche Funktionen, Methoden, Klassen blabla, was sie eigentlich machen, was sie eigentlich zu bedeuten haben, in den Code, na? Deswegen gute Namen für diese Klassen, für diese Funktionen, für diese Variablen zu finden ist für die Person, die es danach lesen muss, einfach ein Segen. Transparent machen und explizit sagen, worum es geht. Aussagekräftige Namen finden. Dadurch wird zumindest mir die Arbeit deutlich erleichtert. Ich weiß nicht, wie es bei dir ist.

Matthias: [00:28:36.70] Nein, absolut. Das wäre auch mein Tipp. So wie man anfangen sollte. Definitiv schauen, dass man die Lesbarkeit, Verständlichkeit von dem Code erhöhen kann. Einfach auch wirklich durch Namensänderungen. Weil es liest sich dann einfach ganz anders. Was ich auch sonst sehr gerne mache und was man mitunter in Legacy Systeme ja auch häufig sieht, sind sehr komplexe IF-Verschachtelungen und sowas. Was ich relativ gerne mache ist zum Beispiel so IF-Bedingungen, in entsprechende Methoden-Namen einfach auslagern, um einfach mir nicht permanent den Kopf darüber zerbrechen zu müssen, was mache ich jetzt dieses IF hier und das IF-ELSE dann da unten, das können auch temporäre Refactorings sein, sowas, einfach nur um die Verständlichkeit von solchen komplexen Abläufen besser zu machen. Finde ich das ein super Werkzeug

Alex: [00:29:29.50] Und das ist meiner Meinung nach auch ein guter Anfangspunkt, um Änderungen an den Code zu bringen. So Refactorings zu verwenden, um diesen Namen zu verbessern.

Matthias: [00:29:40.96] Genau, ja.

Alex: [00:29:41.86] Es ist eine sehr kleine, sehr lokale, normalerweise, Änderung, die aber dir hilft deutlich das Lesen des Codes und das Verstehen des Codes erleichtern. Deswegen guter Anfangspunkt

Matthias: [00:29:53.71] Genau und relativ gefahrlos auch tatsächlich, wenn keine Tests da sind machbar.

Alex: [00:29:59.77] Was ich sonst noch gerne sehe ist, bin ich eine bestimmte Programmiersprache gerade verwende, völlig egal welche, C#, java, Python, Wurst. Jede Sprache hat bestimmte Konventionen und es hilft auch den Code zu lesen und den Code zu verstehen. Wenn dieser Code, diese Konventionen folgt, ist, sich auf diese Konventionen halten oder im Unternehmen, wenn es gewisse unternehmensweite Entwicklungskonventionen gibt, sich daran zu halten. Damit es einfacher für alle ist, den Code zu lesen, den Code zu verstehen und so auch einfacher Änderungen durchführen zu können.

Alex: [00:30:40.60] Finde ich auch eine eine super Sache. Clean Code, natürlich. Und all dessen Regeln und Konventionen helfen unheimlich strukturiert den Code zu schaffen, die leichter zu lesen ist und vor allem aber, was meiner Meinung nach das A und O der ganzen Geschichte ist. Tests. Tests, weil sie helfen dir für alle Probleme, die du hast. Erinnerst du dich an die drei Antworten, die wir oben hatten? Von den drei Antworten helfen dir Tests mindestens bei zwei davon.

Matthias: [00:31:15.16] Genau.

Alex: [00:31:15.82] Wenn nicht auch für alle drei

Matthias: [00:31:18.49] Wie gesagt, die dritte. Also die, wo ich ja vorhin auch schon gemeint hab, die ist halt einfach schwierig zu erreichen, weil es halt erfordern würde, dass ein Großteil des Systems unter Tests ist. Aber da gibt es auch Möglichkeiten, die sind halt trotzdem auch kein super dichtes Netz, sondern man versucht halt dann irgendwie die Business Cases mit Tests abzudecken, also da, wo der meiste Benefit von der Anwendung liegt. Das ich das mit Tests über das Gesamtsystem sicherstellen kann. Und dann kann ich diese Frage natürlich auch zu großen Teilen dann sicher beantworten, ja.

Alex: [00:31:54.82] Für mich ist die Großartigkeit von diese Tests ist Erstens, dass sie helfen, dass sie mir helfen zu verstehen, wie die Anwendung funktioniert. Und wenn ich es verstanden habe oder zumindest einen Teil verstanden habe, sind die Tests ja da. Und wenn ich was ändere, um was kaputt mache. Die Tests merken sofort, ob der Verhalten sich geändert hat. Also ich kann besser verstehen, was ich ändern kann und wie ich ändern kann und baue mir mit der Zeit ein gewisses Sicherheitsnetz an Tests, wo ich nachprüfen kann, ob ich Verhalten geändert habe oder nicht. Das ist verdammt wertvoll.

Matthias: [00:32:32.65] Genau, weil das soll mich erst auffallen, wenn der Code in Produktion ist.

Alex: [00:32:37.75] Okay, dann wollen wir vielleicht über Feedback reden, und zwar Feedback bei Änderungen. Wann brauchen wir Feedback bei Änderungen?

Matthias: [00:32:49.09] Ja eigentlich unverzüglich sobald ich eine Änderung durchführe, will ich Feedback haben, ob die Probleme macht oder nicht.

Alex: [00:32:57.19] Genau und zwar Feedback in dem Zusammenhang: ich will wissen, ob die Änderungen, die wir machen, das tun, was sie sollen und nichts anderes kaputt machen. Zwei Fragen oben. Je schnell ich den Feedback bekomme, desto besser. Kennst du Plug and Play?

Matthias: [00:33:13.03] Plug and Play von

Alex: [00:33:14.86] Ja, von Computern.

Matthias: [00:33:15.79] Ja klar, das kenne ich.

Alex: [00:33:17.50] Dann gibt’s der Änderungsverfahren, hat Feathers erst mal geschrieben, die heißt „Edit and Pray“, ja?. Und den wollen wir nicht. Den wollen wir nicht haben. Wir wollen nicht ändern und hoffen, dass alles geht, sondern wir wollen ändern und eine gewisse Sicherheit haben, dass …

Matthias: [00:33:33.34] Das es Gut genug ist.

Alex: [00:33:34.66] Genau, dass es das tut, was tun soll. Dafür, was könntest du dir vorstellen, welche Art von Tests?

Matthias: [00:33:42.33] In meinem grundsätzlich das Problem, dass wir ja mit Legacy Code dann häufig hat ist, dass der Code schwer Testbar ist, weil er halt einfach auch ohne Tests entstanden ist und wenn man sich so ein bisschen mit Tests auseinandergesetzt hat, und es mal ein bisschen selbst gemacht hat. Dann stellt man fest, dass wenn man Code mit Tests entwickelt, dann entsteht ganz anderer Code, als wenn ich einfach nur runter hacke und die Lösung fertig machen. Also meine Erfahrung ist tatsächlich, wenn ich versuche Nachhinein Tests reinzubringen, dann kann es unglaublich schwer sein und häufig erfordert es tatsächlich, dass man Code umstrukturiert, damit er überhaupt testbar wird. Das ist mit einer der größten Herausforderungen dabei.

Alex: [00:34:22.38] Ja, stimme ich dir zu. Ich kenne aussagen, „Du darfst nicht ändern, solange du keine Tests hast“. Es gibt Situationen, in denen du was ändern musst, damit du überhaupt Tests reinkriegen. Weil sonst geht’s halt nicht. Du kriegst die Tests gar nicht rein.

Matthias: [00:34:39.03] Genau. Und da muss man, meines Erachtens halt, sich echt Gedanken machen, wie man das machen kann, quasi minimal invasiv Operationen an dem Code durchzuführen, ohne Sachen kaputt zu machen. Also das ist aber auch nicht trivial und es kann auch schief gehen. Da muss man meines Erachtens irgendwo durch. Weil sonst hast du halt die Gefahr, dass es schiefgeht, die ganze Zeit.

Alex: [00:35:03.36] Ja. Also für schnelles Feedback, ja?, um schnelles Feedback zu bekommen ist meiner Meinung nach das, was wir aufbauen können oder aufbauen sollen, ein sogenanntes Sicherheitsnetz an Tests, also unsere ganze Anwendungen oder zumindest der Bereich, wo wir gerade Änderungen machen, so unter Test zu setzen, dass wir sicherstellen können, dass die Änderungen, die wir machen, das tun, was sie tun sollen und aber auch Regressionsprüfungen machen können. Also, wir die Tests zu jeder Zeit laufen lassen können, um zu checken, dass der Verhalten sich nicht geändert hat. Es sind Tests auf sehr low level Ebene, die sich darum kümmern Funktionen / Klassen zu testen. Sehr viele, sehr kleine Tests, die mir erlauben, in eine sehr kurze Zeit zu sehen, ob der Verhalten, meine Anwendung sich verändert hat. Feedback in Minuten und nicht in Stunden oder Tage. Und dafür gibt es für mich die Königsdisziplin. Die heißen Unit-Test. Unit-testen alles was möglich ist. Tests auf Funktionsebene, auf Klassenebene oder auf Methodenebenen, auf kleinen Korn. Wobei der Begriff Unit-Test sich sehr in Anführungszeichen verändert hat. Ich habe manchmal das Gefühl, alle Tests, die von Entwickler geschrieben werden, kriegen der Stempel Unit-Tests manchmal und das ist nicht so.

Alex: [00:36:35.94] Damit meine ich, da gibt es einen anderen Begriff, der heißt Mikro-Tests. Es sind Tests, die wirklich keine Abhängigkeit oder nicht die Anwendung als ganze prüfen, sondern die sich um die kleinere Einheiten der Anwendung kümmern: Funktionen, Klassen, Methoden und so weiter und so fort. Wenn wir einen größeren Korn nehmen und ein Problem auftritt, es ist schwieriger rauszufinden, wo das Problem liegt, weil wir viel mehr Code, wo der Fehler passieren könnte. Die Ausführung der Tests dauert auch länger, weil je mehr Code wir ausführen müssen, desto länger dauert die Ausführung an sich und die Tests werden komplexer und größer, weil ich eben nicht drumherum kommen werde, mich um irgendwelche Abhängigkeiten kümmern zu müssen. Wichtig, ganz, ganz wichtig ist Unit-Tests müssen schnell ausgeführt werden können und uns helfen, schnell den Fehler zu finden. Also Test, die mir sagen, es ist ein Fehler da, sind Gut und Nutzvoll, aber wenn ich eine Woche brauche, um den Fehler zu finden. Tja, Unit-Test wie gesagt, behandeln keine Abhängigkeiten, keine Datenbank, kein File-System, kein Netzwerk, keine Konfigurationsdateien.

Matthias: [00:37:53.94] Ja, aber an den Punkt zu kommen ist manchmal gar nicht so leicht. Dass man, dass man Klasse so isoliert testen kann.

Alex: [00:37:59.82] Es kann sein, dass wir mit großer Tests anfangen müssen, weil es nicht anders geht, weil wir so viele Abhängigkeiten haben und diese Tests sind natürlich auch wertvoll, keine Frage, ne?. Ich will nicht sagen, wir sollen sie nicht machen. Die sind wichtig und die helfen uns auf dem Weg. Aber das Ziel wäre, dieses Sicherheitsnetz aufzubauen. Wo wir mit Unit- oder Mikro-Tests, also diese ganz kleinen Tests, so viel von unserer Funktionalität vorab testen, dass wir innerhalb von Sekunden wissen können: „Hoppala, ich habe etwas kaputt gemacht“.

Matthias: [00:38:34.21] Ja.

Alex: [00:38:35.59] Jetzt kommen wir aber tatsächlich zu dem Punkt, wie du schon erwähnt hast. Code unter Test zu bekommen ist nicht so leicht.

Matthias: [00:38:42.14] Ja, warum nicht? Also ich habe früher häufiger Unterhaltungen in der Hinsicht geführt. Warum für ein Code, den ich reviewen sollte, keine Tests da waren und habe dann immer wieder gehört so: „Ja, den Code kann man nicht testen. Der ist nicht testbar!“. Und ich habe mir immer gedacht: „Aber warum nicht?“ Das ist ja irgendwie komisch und … Also ich kann nicht genau beschreiben, warum das so ist, weil wenn ich einfach mit dem Test anfangen würde. Erstmal ein Test schreibt und da beschreibt, wie soll sich dann mein Code verhalten? Ist ja naturgegeben, dass der Code Testbar ist, weil ne „Muss ja“. Aber andersherum gesehen, mache ich mir halt, also wenn ich einfach erst den Code schreibe, mache ich mir halt kaum Gedanken über Testbarkeit und stellt dann halt im Nachhinein fest, wenn ich dann einen Test schreiben will, dass es irgendwie schwer ist, dafür Test zu schreiben. Und das führt dann zu der Annahme, dass der Code aus irgendeinem Grund nicht testbar sei oder das Problem nicht testbar sei oder wie auch immer. Und ja, es ist tatsächlich schon schwierig, also man muss relativ viel an seinem Code anpassen, unter Umständen, damit es testbar wird. Und das fängt an mit so Geschichten eben wie: ja, wird mal direkt auf die Datenbank zugegriffen aus der Klasse oder es werden irgendwelche Abhängigkeiten rein geholt, die natürlich im Test dann auch da sein müssen. Und gerade die Datenbank ist ja zum Beispiel in Unit-Tests ein KO-Kriterium. Also es kann nicht sein, dass ich um Unit-Tests ausführen zu können, eine MySql-Datenbank bei mir laufen haben muss oder sowas. Also wenn ich jetzt in dem Legacy System, in dem ich bin so eine Herausforderung habe, werde ich nicht drum herum kommen, diese Abhängigkeiten irgendwie aufzulösen, also Abstraktionen einzuziehen und solche Geschichten. Das ist halt mitunter bisschen schwieriger.

Alex: [00:40:46.87] Okay, halten wir fest: 1. Problem: Abhängigkeiten machen das Testen in Isolation schwer.

Matthias: [00:40:53.77] Absolut ja!

Alex: [00:40:54.46] Manchmal unmöglich. Also es ist einer der wichtige Aspekte, die wir beherzigen müssen, wenn wir mit Legacy Code arbeiten, diese Abhängigkeiten aufzubrechen. Wie kann ich Code unter Test bekommen? Problem Numero Uno: Abhängigkeiten. Haben wir, Check!

Matthias: [00:41:15.95] Wenn ich vorher noch nie z. B. mit Tests zu tun hatte, werde ich zusätzlich noch die Herausforderung haben, überhaupt das Gesamtsystem testbar zu bekommen. Also, meine jetzt hat vor allem so was wie ein Test-Framework einbinden, dass es richtig konfiguriert ist, das grundsätzlich funktioniert. Das kann schon eine Herausforderung sein. Ja, und dann brauche ich natürlich auch irgendwelche Mechanismen, die diese Tests ausführen. Also ich kann das natürlich selbst lokal machen. Also auf meinem Rechner, auf dem ich programmiere, kann ich natürlich die Tests ausführen und sehe es dann auch sofort. Aber grundsätzlich sollte es dann nochmal irgendwie so einen zentralen Prozess geben. Also CI Prozess z. B. der sich dann da auch nochmal drum kümmert automatisiert, also das ich quasi nicht darauf angewiesen bin, dass jeder beteiligte Entwickler immer daran denkt, vorher die Tests auszuführen. Also da hat man eine ganze Reihe Herausforderungen, glaub ich.

Alex: [00:42:16.15] Eins was ich noch erwähnen möchte, ist beim … Wenn man anfängt zu arbeiten mit Legacy Code, ne, und diese Dependency aufzubrechen und irgendwie Tests zu schreiben, was man alles da nicht alles machen muss. Es passiert oft und es muss uns allen klar sein, dass der Code erstmal schlimmer aussieht als vorher, dass wir so Änderungen durchführen, die nicht wirklich dazu führen, dass der Code besser wird. Aber das muss nur ein Zwischenschritt sein, z.B. um Abhängigkeiten abzubrechen, wenn wir die gebrochen haben und wir die Abhängigkeit nicht mehr machen. Er muss wir uns auch darum kümmern, dass der Code wieder ordentlich, sauber, schön, funktional aussieht und nicht alles überall verteilt ist. Aber ich finde es eine wichtige Sache, die man auch erwähnen muss, weil ich hab auch mal von Menschen gehört: „Ich mach die Änderungen und breche die Abhängigkeit und Dings, damit wir überhaupt erst reinkommen und am Ende sieht das schrecklich aus“. Und dann ist die Sache nicht zu Ende gedacht. Das bedeutet du bist noch nicht fertig, du musst nicht jetzt aufhören, sondern mache die notwendigen Refactorings, damit die Sache ordentlich aussieht. Dafür ist das Refactoring da.

Matthias: [00:43:37.98] Genau. Die Tests sind ja eigentlich nur die Grundlage dafür, dass ich das System in größerem Umfang ändern kann. Und das steht mir natürlich auch frei. Wenn ein System oder ein Teil des Systems unter Test steht, kann ich diesen Teil relativ bedenkenlos refaktorieren und schöner machen. Definitiv, ja.

Alex: [00:44:00.52] Unser sehr geehrter Herr Feathers hat einen Algorithmus in Anführungszeichen vorgeschlagen, um mit Legacy Code zu arbeiten.

Matthias: [00:44:10.55] Naja, der Algorithmus, der besteht aus fünf Schritten, die er in seinem Buch beschreibt, fängt quasi damit an, dass man die Stellen identifiziert, an denen ich überhaupt eine Änderung vornehmen will. Das hatten wir ja vorhin auch schon mal erwähnt. Wenn ich die Stelle, an der ich rein muss, um eine Änderung machen zu können, identifiziert habe, dann muss ich schauen: „Gibt es eine Möglichkeit, irgendwie das unter Tests zu bekommen?“ Das ist so der zweite Schritt. Im dritten Schritt werde ich die Voraussetzungen dann schaffen, um eben Tests einführen zu können und da kommt eben der Schritt die Dependencies aufzubrechen. Und als nächstes im vierten Schritt würde ich dann eben Test schreiben. Das ist dann hab ich Tests für diesen Teil, den ich ändern möchte und im letzten Schritt heißt es dann: „Mach deine Änderungen und Refaktoriere“. Das sind die fünf Schritte, wie er sie ausführt.

Alex: [00:45:11.28] Ok dann gehen wir die noch kurz mal durch. Schritt Nummer 1: Stelle fest, wo müssen die Änderungen gemacht werden. Ja?. Wie würdest du das bewerkstelligen?

Matthias: [00:45:24.27] Naja, also zuallererst würde ich mir natürlich anschauen, die Anforderungen, die ich habe. Also was soll denn an dem System geändert werden? Und je nachdem, wie vertraut ich dann mit dem System schon bin, hab ich entweder schon so eine grobe Idee an, in welchem Bereich der Anwendung sich das befinden wird und würde dann dort im Prinzip eine manuelle Suche gehen. Also mir den Quellcode anschauen. Methoden Namen. Man kann zusätzlich wenn man will … Kann, man z. B. Es kommt natürlich darauf an, was für ein System man da vor sich hat, aber es vielleicht sogar auch programmatisch ansprechen und so Sachen herausfinden. Was ich auch schon gesehen habe ist mit Loggern zu arbeiten, um ein bisschen mehr Verständnis da zu erlangen. Ja genau.

Alex: [00:46:19.73] Der zweite Punkt war: „Wo kannst du deine Tests ansiedeln? Was muss getestet werden? Welche Nebeneffekte können auftreten?“ Und auch da würde mich deine Einschätzung interessieren.

Matthias: [00:46:33.98] Ja, ich glaube, es ist immer sehr Use Case abhängig. Also jetzt mal angenommen man, man kann im ersten Schritt halt eine einzelne Klasse identifizieren, die angepasst werden müsste. Dann wäre mein Vorgehen, dass ich mich mit dieser Klasse erst mal vertraut machen würde, also bevor ich irgendetwas anderes machen und schauen würde, ob ich den Code verstehe. Und wenn ich tatsächlich den Code verstehe, allein durchs Durchlesen sozusagen, dann kann man da relativ straight forward wahrscheinlich schon auch einen Test dafür schreiben. Also nee, ich würde ja nicht gleich schreiben, aber ich wüsste zumindest: „Ich kann diese eine Klasse isoliert betrachten“. Wenn ich den Code nicht verstehe. Und wenn ich mir auch nicht sicher bin, dass wirklich nur diese Klasse betroffen ist und vielleicht auch andere, dann muss man halt ja noch ein bisschen weiter forschen. Also das ist mitunter so ein bisschen Archäologie Arbeit glaub ich an der Stelle. Und ja, am Ende kommt es immer wieder drauf zurück glaube ich, dass man sich vertraut macht mit der Codebasis und versucht den Code bestmöglich nachzuvollziehen und zu verstehen. Auch da finde ich, kann man keine klare Antwort geben, weil das so wenn es so einfach wäre, dann könnte ich es automatisieren. Und ich glaube das tatsächlich so diese Arbeit mit Legacy Code eines der größten Herausforderungen in unserem Berufsstand ist.

Alex: [00:48:04.01] Finde ich auch, eine schwierige Herausforderung. Jetzt ist der dritte Schritt. Abhängigkeiten brechen. Heißt, wenn unsere Klassen oder der Bereich, wo wir jetzt ändern müssen, Abhängigkeiten jeglicher Art hat. Was wir versuchen, ist, diese Abhängigkeiten aufzubrechen. Das Abhängigkeit-Problem merkst du ziemlich schnell, weil du keine Test schreiben kannst, ohne die halbe Anwendung hoch zu ziehen.

Matthias: [00:48:35.51] Ja, genau.

Alex: [00:48:36.57] Das ist relativ schnell klar, dass du Abhängigkeiten hast? Ja, oder check…

Matthias: [00:48:40.80] Manchmal auch tatsächlich gar nicht so offensichtlich, also mir es auch schon passiert. Ich war, war in so einer Klasse und habe mir gedacht: „Ja, der braucht ja nur ein Service oder so. Es ist ja ganz einfach“. Aber dann stellst du fest, dass der Service, der du dir da rein holst, der holt noch einen Service, der braucht die Datenbank und der braucht dieses und jenes und wie du es dann schon grad beschrieben hast. Dann hast du das halbe System am laufen, um eine Klasse zu testen.

Alex: [00:49:04.56] Das wollen wir nicht. Deswegen versucht man diese Abhängigkeit irgendwie zu brechen. Da gibt es viele unterschiedliche Möglichkeiten, das zu tun. Unterschiedliche Ideen. Feathers schreibt in dem Buch eine ganze Menge davon. Aber dann gehen zu so Schritt 4 und da fangen wir Tests zu schreiben. Und zwar Tests, die prüfen das Verhalten, die bereits da ist. Also wir haben ja noch gar nicht angefangen mit der Änderung. Ja, wir checken nun erstmal, dass wir Tests schreiben, die das aktuelle Verhalten abprüfen, damit wir mit Sicherheit wissen, was der Code macht. Was passiert jetzt, bevor wir was ändern? Und der letzte Schritt ist dann tatsächlich die Änderungen vorzunehmen. Tests hinzufügen, wo es notwendig ist. Refaktorisieren oder Refaktorieren und dann ist er Käse gegessen, dann sind wir durch. Wenn alle Tests dann Grün sind, dann wissen wir: die Funktionalität von vorher ist immer noch da. Die Erweiterung oder die Änderungen, die wir gemacht haben, die auch mit Tests abgedeckt ist, tut auch das, was tun soll. Happy Coder, ne?

Matthias: [00:50:14.37] Hapy Coder, ja,

Alex: [00:50:15.07] Happy Coder. Alles geklappt, alles wunderbar. Und dieser Algorithmus immer wieder, immer wieder anwenden, sodass wir weiterkommen und unsere Änderungen durchführen können. Was mir aufgefallen ist: Testen. Überall gibt es Tests. Es geht um Tests. Sehr viel Test. Aber wie zum Geier soll ich das Zeug testen? Um mit Abhängigkeiten umgehen zu können, sind z.B. Fakes oder Mocks ein gutes Modell. Auch das Konzept von Seams, die Feathers einfügt, ist ein interessantes Konzept. Was verwendest du, Matthias, wenn du testest?

Matthias: [00:50:58.59] Also in erster Linie Mocks, aber bedeutet nicht, dass ich da nicht auch mal ein Spy oder einen Fake mit dabei ist. Also ich arbeite tatsächlich in erste Linie mit Mocks, um Abhängigkeiten, wie man so schön sagt wegzumocken.

Alex: [00:51:14.79] Kannst du erklären was Mocks sind?

Matthias: [00:51:16.74] Naja, Mocks sind im Prinzip ja, wie soll man sagen. Einfach sie tun so, als wären sie diese Abhängigkeit und aber ich brauche die Abhängigkeit nicht wirklich, also soll heißen: Ich habe jetzt z.B. eine Datenbank-Zugriff, den ich wegmocken will. Dann könnte ich halt diese Datenbank mit einem Mock-Objekt und dieser Mock hätte dann dieselben Methoden, die, die Original Datenbank-Klasse hätte. Mit dem Unterschied, dass ich den Mock sagen kann, was er tun soll, wenn die Methode aufgerufen wird. Also es wird dann nicht die echte Datenbank angesprochen, sondern ich sag dem Mock dann zum Beispiel, wenn ein ‚getUserFromDb‘ mit dem und dem String als Parameter kommt, dann gib mir doch einfach einen neuen User mit dem und dem Benutzernamen zurück.

Alex: [00:52:07.44] Kannst du eine Abgrenzung zu Fakes?

Matthias: [00:52:09.99] Also Fakes sind für mich jetzt in meine Vorstellung oder so wie ich sie kenne wirklich selbstgeschriebene Fake Implementierungen, wo man das, was ich gerade beschrieben habe in dieser Klasse von dem Fake definieren würde und sagt also da gäbe es dann eine Fake Klasse, die eben auch die Methode ‚getUserFromDb‘ hätte und da wäre einfach fix hard codiert drinnen: Return New User mit Vorname XY. Das wäre für mich ein Fake. Und im Unterschied dazu bei dem Mock hab ich diese ja ich sag mal Konfiguration des Mocks, also was er zurückgeben soll, wenn dieses und jenes aufgerufen wird, hab ich halt im Test. Also für mich persönlich besser, weil ich das näher an dem Stück Code dran habe, für das es relevant ist. Ich muss nicht extra in der Fake-Klasse reingehen, um zu gucken warum kommt denn da jetzt wird dieser User zurück, bin ich das aufrufen?

Alex: [00:53:12.52] Also Fakes sind eigene Implementierungen, die diese Abhängigkeit für den Test ersetzen. Und Mock ist ein anderen Konstrukt, in dem mir auch ein Stellvertreter für diese, für diese Abhängigkeit, für diese Datenbank Zugriff verwenden, die wir aber nicht selber implementieren, in dem Sinne, sondern sagen wir bauen uns eine Mock Instanz von dieser Abhängigkeit und sagen diese Abhängigkeit wie sich zu verhalten hat.

Matthias: [00:53:45.79] Genau. Und je nachdem welches Mock-Framework ich verwende, hab ich dann halt ein bisschen eine unterschiedliche Syntax, wie man das erreicht. Aber das ist es im Prinzip.

Alex: [00:53:56.35] Okay,

Matthias: [00:53:57.01] Da, die Grundvoraussetzung ist halt da, aber leider auch wieder. Ich muss halt ein Verständnis haben, wie sich diese Service, den ich mocken will, in Realität verhält. Also da muss ich trotzdem so ein gewisses Verständnis haben, aber ich kann halt dann, wenn in der Klasse, in der ich mich gerade befinde, die ich testen möchte. Ist halt das Schöne. Ich kann in jedem Test genau sagen, wie die Abhängigkeiten sich eben verhalten soll. In diesem speziellen Fall. Und wie ist dann mein erwartetes Verhalten für meine Klasse, die ich gerade testen will? Und das finde ich persönlich ganz schön.

Alex: [00:54:32.14] Kannst du uns irgendwas über die Seams, in die Feathers mal in dem Buch erwähnt, erzählen? Was ist der Sinn? Was ist ein Seam? Welche Arten?

Matthias: [00:54:42.20] Also so wie ich es verstanden habe, beschreibt er mit „Siams“ oder „Seams“, Seams, Stellen im Code, die eben solche Punkte beschreiben, wo Dependencies aufgebrochen werden können, z.B. der eben beschriebene Datenbank-Service, wäre so ein Seam, wo man, wenn man das im Code sieht, also nur so einen fremden Codebasis zum Legacy System, das ich gerade versuche zu erkunden, dann wäre halt das so eine, so eine Stelle, wo ich das Gefühl hätte: „Ja, dass das ist so ein Ding, das kann man ganz gut aufbrechen“. Also so verstehe ich die Seams,

Alex: [00:55:22.81] Was man in die objektorientierte Programmierung öfters verwendet, sind diese sogenannte Objekt-Seams, indem man durch unterschiedliche objektorientierte Techniken, diese Abhängigkeiten versucht zu lösen. Da ist z.B. Interface Definitionen ist eine super Methode, um Abhängigkeiten aufzubrechen. Anstatt eine Abhängigkeit zur Klasse habe ich eine Abhängigkeit zu den Interfaces und den Interface kann ich in den Test durch ein Fake ersetzen, durch eine Fake-Implementierung von dieser Interface. Ganz klassisch, aber auch Klasse-Ableitungen und Hierarchien sinn ein Thema, Polymorphismus, durch Polymorphismus können wir auch das Verhalten einer Klasse für ein Test überschreiben und solche Sachen. Also da gibt es sehr, sehr viele unterschiedliche Möglichkeiten, das anzugehen, was ich nur sagen möchte: „Man ist nicht allein gelassen“. Man hat Möglichkeiten, diese Abhängigkeiten tatsächlich zu brechen und den Code so zu hinterlassen, dass es Testbar ist, dass das tut, was es tun soll und so weiter und so fort.

Matthias: [00:56:29.49] Ja, genau.

Alex: [00:56:32.25] Wir haben jetzt über, über sehr viele Sachen gesprochen, wie man testen kann: Fakes, Mocks, Refactorings, Test. Zigtausend Sachen, die man machen soll, tun, kann. Aber wie? Wie machen wir das? Welche Helfer haben wir dabei?. Da ist mir ganz wichtig: „Kenne deine Tools“.

Matthias: [00:56:54.93] Kann ich nur zustimmen, ja.

Alex: [00:56:57.09] Z.b. Man kann per Hand Refaktorisieren. Das ist keine Frage, man kann es machen. Es ist mehr oder minder Mühselig, aber man kann es machen. Aber warum sollte ich es machen, wenn meine IDE oder Plugins, die in meine IDE zur Verfügung stehen, diese Möglichkeiten anbieten, um diese Refactorings zu automatisieren? Also eine ganz wichtige, ein ganz wichtiger Aspekt. Ein ganz wichtigen Tool, die ich andauernd verwende, sind die Refactoring Tools von der IDE, die ich verwende. Und die muss man kennen, damit muss man sich vertraut machen. Kannst du was dazu erzählen?

Matthias: [00:57:42.98] Naja, also Refactoring ist, also gerade des Refactoring mit der IDE ist Goldwert, weil letztendlich die IDE weiß ganz genau, welche Gültigkeitsbereich hat die Variable oder die Methode, die ich gerade umbenennen möchte und sie weiß einfach, an welchen Stellen das mitgeändert werden muss. Es wird automatisch in Tests mitgeändert. Also das sind so viele Sachen, die ich nicht händisch machen wollen würde, weil sie auch einfach so viel Gefahr bergen, etwas falsch zu machen. Und man glaubt gar nicht, was für böse Bugs man sich mit dem Scope von Variablen einfangen kann. Genau deswegen also ja, diese Tools sind Goldwert.

Alex: [00:58:27.41] Sind sie wirklich Nutzt sie. Verwendet die!

Matthias: [00:58:30.71] Genau. Nutzt sie häufig. Also es ist auch in unserer täglichen Arbeit und wir sind gerade nicht an Legacy Systeme, aber wir restrukturieren hundert Mal pro Woche wahrscheinlich: irgendwelche Variablennamen Klassennamen hier und da. Und das ist ja das Schöne. Wenn man dieses Sicherheitsnetz aus Tests hat, dann kann man das ja bedenkenlos auch machen. Also gerade solche Geschichten wie Namensänderungen sind da überhaupt kein Problem mehr. Und auch funktionales Refactoring ist überhaupt kein Problem mehr. Deswegen ja: Macht es!

Alex: [00:59:09.83] Und es gibt zwei Sachen, die ich total hilfreich finde. Das sind Frameworks, zwar Mock-Frameworks und Test-Frameworks. Das sind abgekapselte, funktionale Implementierung für die Verwendung entweder von Mocks oder von Tests und die helfen auch ungemein. Durch den Unit Test Framework hab ich eine eindeutige Vorgehensweise, wie Tests ausgeführt werden, wie ich die Tests schreiben kann, in welcher Notation oder welche Form und Art die Tests, Testnamen, wie auch immer haben müssen. Da gibt es viele unterschiedliche. Aber Einen zu verwenden, ist für mich eine wichtige Sache. Ich möchte mich nicht in jedem Projekt damit beschäftigen müssen, mir die Funktionen zu schreiben und bereitzustellen, damit ich Tests schreiben kann, damit ich Tests ausführen kann, damit ich Test prüfen kann, ob die funktionieren oder nicht funktionieren. Das will ich nicht jedes Mal machen. Deswegen nehme ich ein Framework, die mir das alles anbietet, binde ich sie mein Projekt ein und dann kann ich diese Framework verwenden, um meine Test zu schreiben und das gleiche mit dem Mock-Framework. Das jedes Mal selber zu implementieren ist Verschwendung. Es gibt supertolle Mock-Frameworks da draußen, nutzt die einfach.

Matthias: [01:00:31.00] Genau und probiert doch ruhig mal rum, weil es gibt da unterschiedlichste Geschmacksrichtungen und das eine fühlt sich vielleicht besser als das andere.

Alex: [01:00:41.11] Aber das nutzen von automatisierte Refactoring, von Mocking-Frameworks, von Unit-Test-Frameworks. Es gibt uns einen Rahmen, in dem wir arbeiten können, um uns wirklich an das konzentrieren können, worum es geht, nämlich unsere Änderungen abzusichern, ein Sicherheitsnetz aufzubauen. Sichergehen, dass wir keine Fehler einbauen bzw. so sichergehen wie möglich, ne?. Natürlich kann man es nicht 100 Prozent ausschließen, kann man nicht, aber so sicher machen wie möglich. Das ist es, was uns all diese Tools nee, wie gesagt automatisch Refactoring, Mock-Frameworks, Test-Frameworks. Es ist für mich das, was uns am besten hilft bei der Arbeit.

Matthias: [01:01:23.17] Einen Hinweis hätte ich dazu noch und zwar, falls es das erste Mal sein sollte, dass ihr mit Mock- oder Test-Frameworks zu tun habt und ihr, eure Aufgabe ist es, das in einen Legacy System einzubringen. Ich würde es erstmal auf einem kleinen Greenfield Ding ausprobieren, damit man überhaupt ein Gefühl für das Ganze bekommt, weil mitunter, wie ich vorhin schon erwähnt habe, kann es auch schmerzhaft sein, diese Frameworks in so ein Legacy System zu bekommen.

Alex: [01:01:55.93] Gute Einwand. Nun, ich würde sagen, wir sind grob am Ende der Folgen, ne, angekommen.

Matthias: [01:02:02.52] Ja.

Alex: [01:02:03.56] Vielen Dank fürs Zuhören.

Matthias: [01:02:05.30] Ihr habt heute erfahren, was Legacy Code ist und wie ihr damit umgehen könnt.

Alex: [01:02:10.61] Auf unsere Website werden wir die Shownotes für diese Folge hinzufügen mit Links auf weiterführende Informationen über das Thema, damit ihr euch weiter informieren könnt, wenn ihr wollt.

Matthias: [01:02:22.30] Ja, und beim nächsten Mal, ja, wenig überraschend beschäftigen wir uns wieder mit einer Frage, die sich mit Qualität und Tests auseinandersetzt, und zwar lautet die Frage: „Müssen Entwickler testen“ und wir wollen das ein bisschen erörtern …

Alex: [01:02:36.58] und wenn ja, wie viele.

Matthias: [01:02:37.81] Genau. Und warum das so ist. Genau. Also dann,

Alex: [01:02:42.31] Wenn du den Podcast unterstützen möchtest, teile ihn mit anderen Menschen, die daran interessiert sein könnten. Poste darüber in sozialen Medien oder bewerte uns. Um nichts zu verpassen, kannst du natürlich auch den Podcast abonnieren.

Matthias: [01:02:55.87] Nochmals danke fürs Zuhören. Und wir hören uns dann in drei Wochen.

Off: [01:03:01.18] Die Macht der Craft arbeitet für dich.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.