Ich empfehle ein sehr gutes Buch zu diesem Thema: Effizientes Arbeiten mit altem Code von Michael Feathers. Ich fand es sehr nützlich für ähnliche Altprojekte.
Das Hauptproblem bei Legacy-Code ist, dass es keine "Standard"-Methode für Unit-Tests gibt :-( Der "Standard" Test Driven Development wurde für neue Projekte erfunden, bei denen man mit dem Schreiben von Code - und Unit-Tests - von Grund auf anfängt, so dass man seine Unit-Test-Suite vom ersten Tag an zusammen mit dem Code wachsen lassen kann und alle (oder die meisten) Teile des Codes immer abgedeckt bleiben.
Die Realität sieht jedoch so aus, dass die meisten Projekte im wirklichen Leben Legacy-Code ohne einen einzigen Unit-Test beinhalten (Feathers' Definition von Legacy-Code ist "Code ohne Unit-Tests"). Dieses Buch ist voll von nützlichen Ratschlägen, was zu tun ist, wenn Sie Code anfassen müssen, den Sie kaum verstehen, oder eine Monstermethode von 1000 Zeilen ändern und sicherstellen müssen, dass zumindest Ihre Änderung ordnungsgemäß unitgetestet wird. In solchen Fällen ist es in der Regel sehr schwierig, Unit-Tests zu schreiben, weil der Code nicht für das Testen konzipiert wurde. Also muss man ihn oft umstrukturieren, um ihn testbar zu machen, aber ohne Unit-Tests ist das natürlich riskant... Dennoch gibt es Wege aus solchen Fallen, und dieses Buch zeigt sie.
Im Allgemeinen sollten Sie nicht mit dem Ziel beginnen, die gesamte Codebasis abzudecken (es sei denn, Sie haben einen Projektleiter, der bereit ist, zu akzeptieren, dass Sie in den nächsten Monaten - oder Jahren ;-) keine neuen Funktionen entwickeln werden. Sie haben nur eine begrenzte Zeit, um den größtmöglichen Nutzen aus Ihren Unit-Tests zu ziehen. Daher müssen Sie sich zuerst auf die kritischsten Teile des Codes konzentrieren. Dies sind in der Regel die Teile, die am häufigsten geändert werden und/oder in denen die meisten Fehler gefunden werden (natürlich gibt es da einen Zusammenhang). Vielleicht wissen Sie auch schon im Voraus, dass eine bevorstehende Funktion die Erweiterung eines bestimmten Teils des Codes erfordert, so dass Sie den Weg dafür bereiten können, indem Sie Unit-Tests dafür erstellen. Auf diese Weise entstehen mit der Zeit kleine "Sicherheitsinseln" innerhalb des Codes, die immer besser mit Unit-Tests abgedeckt werden. Wartung und Refactoring sind an diesen Stellen einfacher, und je mehr Unit-Tests Sie hinzufügen, desto größer werden die Inseln...
Beachten Sie, dass diese "sicheren Inseln" anfangs kein sehr "systematisches" Muster des Auftretens aufweisen. Die kritischsten, am häufigsten geänderten Teile des Codes sind in der Regel ziemlich zufällig verteilt. Erst in einem viel späteren Stadium, wenn die unitgetesteten Inseln zu wachsen beginnen und zusammenwachsen, lohnt es sich, ein bestimmtes Modul systematischer abzudecken. Wenn Sie z. B. sehen, dass in diesem bestimmten Modul die Codeabdeckung durch Unit-Tests auf über 60 % angewachsen ist, können Sie beschließen, es durchzugehen und auch für die übrigen Codeteile Tests hinzuzufügen.