2009-03-17 5 views
24

Ich habe ein Projekt geerbt, bei dem die Klassendiagramme einem Spinnennetz auf einem Teller Spaghetti ähneln. Ich habe in den letzten zwei Monaten über 300 Unit-Tests geschrieben, um mir ein Sicherheitsnetz zu geben, das die Haupt-Executable abdeckt.Legacy Code Nightmare

Ich habe meine Bibliothek von agilen Entwicklungs Büchern in greifbarer Nähe zu einem bestimmten Zeitpunkt:

  • Effektives Arbeiten mit Legacy Code
  • Refactoring
  • Code Complete
  • Agile Prinzipien Muster und Praktiken in C#
  • usw.

Das Problem ist, alles, was ich berühren, etwas anderes zu brechen scheint. Die UI-Klassen enthalten Geschäftslogik und Datenbankcode. Es bestehen gegenseitige Abhängigkeiten zwischen einer Reihe von Klassen. Es gibt ein paar Gott-Klassen, die jedes Mal brechen, wenn ich eine der anderen Klassen ändere. Es gibt auch eine mutierte Singleton/Utility-Klasse mit etwa halb Instanz-Methoden und halb statischen Methoden (obwohl ironischerweise die statischen Methoden auf der Instanz beruhen und die Instanz-Methoden nicht).

Meine Vorgänger dachten sogar, dass es klug wäre, alle Datensätze rückwärts zu verwenden. Jedes Datenbankupdate wird als Parameter in einer gespeicherten Prozedur direkt an den Datenbankserver gesendet. Anschließend werden die Datensätze manuell aktualisiert, sodass die Benutzeroberfläche die neuesten Änderungen anzeigt.

Ich bin manchmal versucht zu glauben, dass sie eine Form der schwachen Verschleierung entweder für die Sicherheit des Arbeitsplatzes oder als letzten Abschied vor der Übergabe des Codes benutzt haben.

Gibt es irgendwelche guten Mittel, um dieses Durcheinander zu entwirren? Die Bücher, die ich habe, sind hilfreich, scheinen aber nur die Hälfte der Szenarien zu erfassen, in die ich hineingeraten bin.

+10

arbeitest du an meinem alten ort? ;) – geocoin

Antwort

23

Es klingt, als ob Sie es in der richtigen Weise angehen.

  • -Test
  • Umgestalten
  • -Test wieder

Leider kann dies ein langsamer und langwieriger Prozess sein. Es gibt wirklich keinen Ersatz dafür, zu erforschen und zu verstehen, was der Code zu erreichen versucht.

Ein Buch, das ich empfehlen kann (wenn Sie es nicht bereits unter "etc." abgelegt haben) ist Refactoring to Patterns. Es richtet sich an Menschen, die in Ihrer exakten Situation sind.

+0

Refactoring to Patterns Link ist jetzt kaputt - ich nehme an, es ist von Joshua Kerievsky von Addison Wesley im August 2004 veröffentlicht. – VictorySaber

+1

@VictorySaber Das ist das richtige Buch, aber der Link scheint für mich arbeiten. –

17

Ich arbeite in einer ähnlichen Situation.

Wenn es nicht ein kleines Programm, sondern ein großes Unternehmen Projekt dann ist es:

a) ist es zu spät
b) über die Möglichkeiten einer einzelnen Person zu fixieren a)
c) zu versuchen, kann nur durch ein vollständiges Umschreiben der Sachen behoben werden, die nicht in Frage kommen

Refactoring kann in vielen Fällen nur in Ihrer privaten Zeit auf Ihr persönliches Risiko versucht werden. Wenn Sie nicht ein explizites Mandat bekommen, es als Teil Ihrer täglichen Arbeit zu tun, dann werden Sie wahrscheinlich nicht einmal dafür Kredit bekommen. Man könnte sogar dafür kritisiert werden, "sinnlos Zeit mit etwas zu verschwenden, das schon lange perfekt funktioniert".

Fahren Sie einfach fort, es so zu hacken, wie es zuvor gehackt wurde, erhalten Sie Ihren Gehaltsscheck und so weiter. Wenn Sie völlig frustriert sind oder das System nicht mehr hackbar ist, suchen Sie einen anderen Job.

EDIT: Immer wenn ich versuche, die Frage nach der wahren Architektur anzugehen und die Dinge richtig zu machen, bekomme ich normalerweise LOL direkt von verantwortlichen Managern, die sagen: "Mir ist alles egal Architektur "(versuchte Übersetzung aus dem Deutschen). Ich habe persönlich eine sehr schlechte Komponente an den Punkt der Nicht-Hackbarkeit gebracht, während ich natürlich bereits Monate im Voraus Warnungen gegeben habe. Sie mussten dann einige versprochene Features an Kunden stornieren, weil es nicht mehr machbar war. Niemand berührt es mehr ...

+1

Hey schau mal - meine Biografie von 2005 bis 2008! –

+1

So ist das System überhaupt erst entstanden. –

+0

Nun, das ist buchstäblich ihr Geschäft. Wenn sie wissen, dass sie ohne Refaktor-Arbeit nicht mehr sicher modifiziert werden können, und sie das nicht wollen, ist das ihre Entscheidung. –

1

Meistens klingt das ziemlich schlecht. Aber ich verstehe nicht, diesen Teil:

Meine Vorgänger dachte sogar, es wäre klug sein, alle die Datensätze verwenden zurück. Jede Datenbankaktualisierung wird direkt an den Datenbankserver als Parameter in einer gespeicherten Prozedur, dann die Datensätze werden manuell aktualisiert, so die Benutzeroberfläche wird die neuesten Änderungen angezeigt.

Das klingt ziemlich nah an eine Art, wie ich häufig Dinge schreibe. Was ist daran falsch? Was ist der richtige Weg?

+1

Ich denke, er meint, dass sie die Datensätze nicht verbunden haben, um die Datenbank-Updates zu machen und stattdessen Ad-hoc-SQL in die Präsentationsebene geschrieben, um sie zu behandeln. Sie haben am Ende entweder doppelten Code oder eine "Datenbankaktualisierung" -Klasse, die das tut, wofür das Dataset zu verdrahten ist. –

+0

Ich habe es nicht geschafft, das Paradies der Code-freien Datenbindung in nicht-trivialen CRUD-Apps zu erreichen. Es gibt immer einige Geschäftsregeln, die ich nicht adäquat ausdrücken konnte, ohne etwas kaputt zu schreiben und Code zu schreiben. Aber ich könnte es falsch machen. – recursive

+0

Eigentlich meinte ich mit dieser Aussage, dass alle Datensätze nach jeder CRUD-Operation gelöscht und neu gefüllt wurden. Dies führte zu einer exponentiellen Verlangsamung bei jedem Datensatz, der zu einer Tabelle hinzugefügt wurde. Bei 100 Datensätzen dauerte eine CRUD-Operation einige Nanosekunden. Mit 10000 Aufzeichnungen dauerte jede Operation, egal wie trivial sie war, mehr als eine Sekunde. –

4

Ich habe (einmal) über Code kommen, die so wahnsinnig war verwirrt, dass ich es nicht mit beheben könnte ein funktionelles Duplikat in einer angemessenen Zeitspanne. Das war jedoch ein spezieller Fall, da es ein Parser war und ich keine Ahnung hatte, wie viele Clients einige der Fehler, die sie hatten, "benutzen" konnten. Hunderte von fehlerhaften "funktionierenden" Quelldateien zu rendern war keine gute Option.

Die meiste Zeit ist es unmittelbar machbar, nur entmutigend. Lesen Sie dieses Refactoring-Buch durch.

Ich fange im Allgemeinen an, schlechten Code zu reparieren, indem ich die Dinge ein wenig umschalte (ohne den Implementierungscode mehr als nötig zu ändern), so dass Module und Klassen zumindest einigermaßen kohärent sind.

Wenn das erledigt ist, können Sie Ihre kohärentere Klasse nehmen und ihre Eingeweide neu schreiben, um die exakt gleiche Art und Weise auszuführen, aber dieses Mal mit vernünftigem Code. Dies ist der knifflige Teil des Managements, da sie im Allgemeinen nicht gerne hören, dass es Wochen dauert, etwas zu programmieren und zu debuggen, das sich genau gleich verhält (wenn alles gut geht).

Während dieses Prozesses garantiere ich Ihnen, dass Sie Tonnen von Bugs entdecken werden und Dummheiten entwerfen. Es ist in Ordnung, während der Umcodierung triviale Fehler zu beheben, aber ansonsten solche Dinge für später zu lassen.

Sobald dies mit ein paar Klassen getan wird, werden Sie beginnen zu sehen, wo Dinge besser modularisiert werden können, etc., etc. Es wird einfacher sein, solche Änderungen vorzunehmen, ohne damit zusammenhängende Dinge zu beeinflussen, weil der Code jetzt mehr ist Modular, und Sie kennen es wahrscheinlich gründlich.

1

Kein Buch kann alle möglichen Szenarien abdecken. Es hängt auch davon ab, was Sie von dem Projekt erwarten und ob es irgendeine externe Spezifikation gibt.

  • Wenn Sie nur gelegentlich kleine Änderungen vornehmen müssen, tun Sie einfach diese und fangen Sie nicht an zu refaktorieren.
  • Wenn es eine Spezifikation (oder Sie können jemanden, der es zu schreiben zu bekommen), eine komplett neu geschrieben prüfen, ob es
  • durch die vorhersehbare Menge an Änderungen des Projekts gerechtfertigt werden kann
  • Wenn „die Umsetzung ist die Spezifikation“ und da sind viele Änderungen geplant, dann wird man ziemlich abgespritzt. Schreiben Sie LOTS von unit tests und starten Sie das Refactoring in kleinen Schritten.
  • Eigentlich werden Komponententests von unschätzbarem Wert sein, egal was Sie tun (wenn Sie sie an eine Schnittstelle schreiben können, die sich nicht viel mit Refactorings oder einem Neuschreiben ändern wird).

    1

    Wenn Ihre Refactorings Code brechen, insbesondere Code, der nicht verwandt zu sein scheint, dann versuchen Sie zu viel zu tun.

    Ich empfehle ein First-Pass-Refactoring, wo alles, was Sie tun, ExtractMethod ist: Das Ziel ist einfach, jeden Schritt im Code zu nennen, ohne irgendwelche Konsolidierungsversuche.

    Danach, über Abhängigkeiten zu brechen, ersetzen Singletons, Konsolidierung.

    8

    Ich habe diesen Job schon einmal gemacht. Ich verbrachte etwas mehr als zwei Jahre mit einem Vermächtnis, das sehr ähnlich ist. Es dauerte zwei von uns über ein Jahr, nur um alles zu stabilisieren (es ist immer noch kaputt, aber es ist besser).

    Das erste - holen Sie sich eine Ausnahmeprotokollierung in die App, wenn sie noch nicht existiert. Wir haben FogBugz verwendet und es hat ungefähr einen Monat gedauert, bis Berichte in unsere App integriert wurden. Es war nicht sofort perfekt, aber es meldete automatisch Fehler. Es ist normalerweise ziemlich sicher, try-catch-Blöcke in all Ihren Ereignissen zu implementieren, und das deckt die meisten Ihrer Fehler ab.

    Von dort beheben Sie die Fehler, die zuerst kommen. Dann kämpfe gegen die kleinen Schlachten, besonders gegen die Bugs. Wenn Sie einen Fehler beheben, der sich unerwartet auf etwas anderes auswirkt, refaktorieren Sie diesen Block so, dass er vom Rest des Codes entkoppelt ist.

    Es wird einige extreme Maßnahmen erfordern, um eine große, kritische-zu-Unternehmen-erfolgreiche Anwendung neu zu schreiben, egal wie schlecht sie ist.Selbst wenn Sie die Erlaubnis dazu erhalten, werden Sie zu viel Zeit damit verbringen, die Legacy-Anwendung zu unterstützen, um überhaupt Fortschritte beim Neuschreiben zu erzielen. Wenn Sie viele kleine Refactorings machen, werden schließlich entweder die großen nicht so groß sein oder Sie werden wirklich gute Basisklassen für Ihre Neufassung haben.

    Eine Sache, die daraus zu entfernen ist, dass es eine großartige Erfahrung ist. Es wird frustrierend sein, aber Sie werden viel lernen.

    +2

    Dies ist ein toller Kommentar. Ich würde jedoch nicht zustimmen, dass Sie viel lernen werden. Sie werden wahrscheinlich nur lernen, wie Sie etwas falsch machen können. Je nachdem, wie widerstandsfähig du bist, wirst du entweder etwas daraus lernen oder einfach nur degradieren und vergessen, wie man die Dinge richtig macht. – User

    +0

    Eine schlechte Implementierung ist eine gute Lernerfahrung. Es braucht ein hochkarätiges Individuum, um nicht zu degradieren. Ich würde jedoch sagen, dass es Leute waren, die vergessen haben, wie man Dinge richtig macht, die einen zu solchen Situationen bringen. –

    +1

    "Eine Sache zu entfernen ist, dass es eine großartige Erfahrung ist. Es wird frustrierend sein, aber Sie werden viel lernen." - Ich muss mich immer an diese Worte erinnern, wenn ich schreien will ... danke. –

    1

    Wenn Ihre Refactorings etwas kaputt machen, dann bedeutet das, dass Sie keine ausreichende Unit-Testabdeckung haben - da die Unit-Tests zuerst hätten unterbrochen werden sollen. Ich empfehle Ihnen, eine bessere Unit-Test-Abdeckung zu erhalten, nachdem Sie sich an der richtigen Stelle eingeloggt haben.

    Dann empfehle ich Ihnen, kleine Refactorings zuerst - Extract-Methode, um große Methoden in verständliche Stücke zu brechen; Führen Sie Variable ein, um einige Duplizierungen innerhalb einer Methode zu entfernen. Vielleicht führen Sie Parameter ein, wenn Sie eine Duplizierung zwischen den von Ihren Anrufern und dem Angerufenen verwendeten Variablen finden.

    Und führen Sie die Unit-Test-Suite nach jedem Refactoring oder Satz von Refactorings. Ich würde sagen, führen Sie sie alle, bis Sie das Vertrauen gewonnen haben, welche Tests jedes Mal erneut ausgeführt werden müssen.

    +0

    Es hört sich an, als würde er die Brüche erkennen, es ist einfach schwer, etwas zu tun, ohne etwas anderes zu kaputt zu machen. –

    +0

    Wenn ja, dann entschuldige ich mich. Es klang wie Gebrauch und QA fand die Brüche, wenn es die Einheitstests sind, die dies tun sollten. –

    0

    Viel Glück, das ist der schwierige Teil eines Entwicklers.

    Ich denke, Ihr Ansatz ist gut, aber Sie müssen sich auf die Bereitstellung von Geschäftswert konzentrieren (Anzahl der Komponententests ist kein Maß für den geschäftlichen Wert, aber es kann Ihnen einen Hinweis geben, ob Sie auf oder außerhalb der Spur sind). Es ist wichtig, die Verhaltensweisen zu identifizieren, die geändert, priorisiert und auf die wichtigsten konzentriert werden müssen.

    Der andere Ratschlag ist, bescheiden zu bleiben. Verstehen Sie, dass wenn Sie unter realen Terminen etwas so Großes geschrieben haben und jemand anderes Ihren Code gesehen hat, würden sie wahrscheinlich auch Probleme damit haben, es zu verstehen. Es gibt eine Fähigkeit, sauberen Code zu schreiben, und es gibt eine wichtigere Fähigkeit, mit dem Code anderer Leute umzugehen.

    Der letzte Ratschlag ist zu versuchen, den Rest Ihres Teams zu nutzen. Frühere Mitglieder können Informationen über das System, das Sie lernen können, kennen. Außerdem können sie helfen, Verhaltensweisen zu testen. Ich weiß, dass das Ideal ist, automatisierte Tests zu haben, aber wenn jemand helfen kann, indem er Sachen für Sie überprüft, manuell erwägt, ihre Hilfe zu erhalten.

    0

    Ich mag besonders das Diagramm in Code Complete, in dem Sie mit nur Legacy-Code, ein Rechteck der fuzzy grauen Textur beginnen. Wenn Sie dann etwas davon ersetzen, haben Sie unten ein graues Grau, oben ein durchgehendes Weiß und eine gezackte Linie, die die Schnittstelle zwischen den beiden darstellt.

    Das heißt, alles ist entweder 'böse alte Sachen' oder 'nette neue Sachen'. Eine Seite der Linie oder die andere.

    Die Linie ist gezackt, weil Sie verschiedene Teile des Systems mit unterschiedlichen Raten migrieren.

    Während Sie arbeiten, fällt die gezackte Linie allmählich ab, bis Sie mehr Weiß als Grau und schließlich nur Grau haben.

    Natürlich macht das die Einzelheiten für Sie nicht einfacher. Aber es gibt Ihnen ein Modell, mit dem Sie Ihren Fortschritt überwachen können. Zu jeder Zeit sollten Sie ein klares Verständnis davon haben, wo die Linie ist: welche Bits sind neu, welche alt sind und wie die zwei Seiten kommunizieren.

    0

    Sie könnten den folgenden Beitrag nützlich finden: http://refactoringin.net/?p=36

    Wie es in der Post gesagt wird, nicht verwerfen eine vollständige Überschreiben leicht das. Wenn es überhaupt möglich ist, versuchen Sie auch, ganze Layer oder Tiers durch eine Lösung von Drittanbietern wie zum Beispiel ORM für Persistenz oder mit neuem Code zu ersetzen. Aber am wichtigsten ist, versuchen Sie die Logik (Problemdomäne) hinter dem Code zu verstehen.

    +0

    Die Verbindung ist (effektiv) unterbrochen (die Domäne existiert nicht mehr wirklich). –

    0

    Sie könnten einen Teil davon extrahieren und dann umgestalten, um die Abhängigkeiten aufzubrechen und Schichten in verschiedene Module, Bibliotheken, Baugruppen und Verzeichnisse zu isolieren. Dann injizieren Sie die gereinigten Teile erneut in die Anwendung mit einer strangler application Strategie. Aufschäumen, abspülen, wiederholen.