Was tun mit einer C ++ - Quelldatei mit 11000 Zeilen?

229

Wir haben also diese riesige Quelldatei (ist 11000 Zeilen groß?) Mainmodule.cpp in unserem Projekt und jedes Mal, wenn ich sie berühren muss, schaudere ich.

Da diese Datei so zentral und groß ist, sammelt sie immer mehr Code an, und ich kann mir keinen guten Weg vorstellen, um sie tatsächlich schrumpfen zu lassen.

Die Datei wird in mehreren (> 10) Wartungsversionen unseres Produkts verwendet und aktiv geändert. Daher ist es sehr schwierig, sie zu überarbeiten. Wenn ich es "einfach", sagen wir zunächst einmal, in 3 Dateien aufteilen würde, würde das Zusammenführen von Änderungen aus Wartungsversionen zu einem Albtraum. Und auch wenn Sie eine Datei mit einem so langen und umfangreichen Verlauf aufteilen, SCCwird es plötzlich viel schwieriger , alte Änderungen im Verlauf zu verfolgen und zu überprüfen .

Die Datei enthält im Wesentlichen die "Hauptklasse" (interne interne Arbeitsverteilung und -koordination) unseres Programms. Jedes Mal, wenn ein Feature hinzugefügt wird, wirkt sich dies auch auf diese Datei aus und jedes Mal, wenn sie wächst. :-(

Was würden Sie in dieser Situation tun? Haben Sie Ideen, wie Sie neue Funktionen in eine separate Quelldatei verschieben können, ohne den SCCWorkflow zu beeinträchtigen?

(Hinweis zu den Tools: Wir verwenden C ++ mit Visual Studio; Wir verwenden AccuRevals, SCCaber ich denke, die Art von SCCspielt hier keine Rolle. Wir verwenden Araxis Mergeden tatsächlichen Vergleich und das Zusammenführen von Dateien.)

Martin Ba
quelle
15
@ BoltClock: Eigentlich wird Vim es ziemlich schnell öffnen.
Am
58
69305 Zeilen und Zählen. Eine Datei in unserer Anwendung, in die mein Kollege den größten Teil seines Codes ablegt. Ich konnte nicht widerstehen, dies hier zu veröffentlichen. Ich habe niemanden in meiner Firma, dem ich dies melden könnte.
Agnel Kurian
204
Ich verstehe es nicht Wie kann der Kommentar "Kündigen Sie diesen Job" so viele positive Stimmen bekommen? Einige Leute scheinen in einem Märchenland zu leben, in dem alle Projekte von Grund auf neu geschrieben wurden und / oder 100% agil, TDD, ... verwenden (setzen Sie hier eines Ihrer Schlagworte).
Stefan
39
@Stefan: Bei einer ähnlichen Codebasis habe ich genau das getan. Ich hatte keine Lust, 95% meiner Zeit damit zu verbringen, in einer 10 Jahre alten Codebasis um den Rohstoff herumzuarbeiten, und 5% schrieben tatsächlich Code. Es war tatsächlich unmöglich , einige Aspekte des Systems zu testen (und ich meine nicht Unit-Test, ich meine , den Code tatsächlich ausführen, um zu sehen, ob er funktioniert). Ich habe meine 6-monatige Probezeit nicht überstanden, ich hatte es satt, Schlachten zu verlieren und Code zu schreiben, zu dem ich nicht stehen konnte.
Binary Worrier
50
In Bezug auf den Verlaufsverfolgungsaspekt beim Teilen der Datei: Verwenden Sie den Kopierbefehl Ihres Versionskontrollsystems, um die gesamte Datei so oft zu kopieren, wie Sie möchten, und entfernen Sie dann den gesamten Code von jeder der Kopien, die Sie nicht möchten in dieser Datei. Dadurch bleibt der Gesamtverlauf erhalten, da jede der geteilten Dateien ihren Verlauf durch den Split zurückverfolgen kann (was wie eine riesige Löschung des größten Teils des Inhalts der Datei aussieht).
Rmeador

Antworten:

86
  1. Suchen Sie in der Datei nach Code, der relativ stabil ist (sich nicht schnell ändert und zwischen den Zweigen nicht sehr unterschiedlich ist) und als unabhängige Einheit fungieren kann. Verschieben Sie dies in eine eigene Datei und in alle Zweige in eine eigene Klasse. Da es stabil ist, führt dies nicht zu (vielen) "umständlichen" Zusammenführungen, die auf eine andere Datei angewendet werden müssen als die, für die sie ursprünglich erstellt wurden, wenn Sie die Änderung von einem Zweig zu einem anderen zusammenführen. Wiederholen.

  2. Suchen Sie in der Datei nach Code, der im Grunde nur für eine kleine Anzahl von Zweigen gilt und für sich allein stehen kann. Es spielt keine Rolle, ob es sich schnell ändert oder nicht, aufgrund der geringen Anzahl von Zweigen. Verschieben Sie dies in eigene Klassen und Dateien. Wiederholen.

Wir haben also den Code entfernt, der überall gleich ist, und den Code, der für bestimmte Zweige spezifisch ist.

Dadurch erhalten Sie einen Kern von schlecht verwaltetem Code - er wird überall benötigt, ist jedoch in jedem Zweig unterschiedlich (und / oder ändert sich ständig, sodass einige Zweige hinter anderen ausgeführt werden), und dennoch befinden Sie sich in einer einzigen Datei erfolglos versucht, zwischen Zweigen zusammenzuführen. Hör auf damit. Verzweigen Sie die Datei dauerhaft , möglicherweise indem Sie sie in jedem Zweig umbenennen. Es ist nicht mehr "main", sondern "main for configuration X". OK, Sie verlieren also die Möglichkeit, dieselbe Änderung durch Zusammenführen auf mehrere Zweige anzuwenden. Dies ist jedoch in jedem Fall der Kern des Codes, in dem das Zusammenführen nicht sehr gut funktioniert. Wenn Sie die Zusammenführungen ohnehin manuell verwalten müssen, um Konflikte zu lösen, ist es kein Verlust, sie manuell unabhängig auf jeden Zweig anzuwenden.

Ich denke, Sie können zu Unrecht sagen, dass die Art des SCC keine Rolle spielt, da beispielsweise die Zusammenführungsfähigkeiten von git wahrscheinlich besser sind als das von Ihnen verwendete Zusammenführungswerkzeug. Das Kernproblem "Zusammenführen ist schwierig" tritt also zu unterschiedlichen Zeiten für unterschiedliche SCCs auf. Es ist jedoch unwahrscheinlich, dass Sie SCCs ändern können, sodass das Problem wahrscheinlich irrelevant ist.

Steve Jessop
quelle
Was das Zusammenführen betrifft: Ich habe mir GIT und SVN und Perforce angesehen und möchte Ihnen sagen, dass nichts, was ich irgendwo gesehen habe, AccuRev + Araxis für das, was wir tun, übertrifft. :-) (Obwohl GIT dies kann [ stackoverflow.com/questions/1728922/… ] und AccuRev nicht - jeder muss selbst entscheiden, ob dies Teil der Zusammenführung oder der Verlaufsanalyse ist.)
Martin Ba
Fair genug - vielleicht haben Sie bereits das beste verfügbare Werkzeug. Die Fähigkeit von Git, eine Änderung, die in Datei A in Zweig X aufgetreten ist, in Datei B in Zweig Y zusammenzuführen, sollte das Teilen von verzweigten Dateien erleichtern, aber vermutlich hat das von Ihnen verwendete System die gewünschten Vorteile. Wie auch immer, ich schlage nicht vor, dass Sie zu Git wechseln, sondern nur sagen, dass SCC hier einen Unterschied macht, aber trotzdem stimme ich Ihnen zu, dass dies abgezinst werden kann :-)
Steve Jessop
129

Das Zusammenführen wird kein so großer Albtraum sein, wie es sein wird, wenn Sie in Zukunft 30000 LOC-Dateien erhalten. So:

  1. Fügen Sie dieser Datei keinen weiteren Code mehr hinzu.
  2. Teile es auf.

Wenn Sie nicht können Codierung während Refactoring Prozesses stoppen, könnten Sie diese große Datei verlassen wie für eine Weile zumindest ohne mehr Code , um es fügt hinzu: da es eine „Hauptklasse“ enthält könnte man von ihm erben und vererbten Klasse halten ( es) mit überladenen Funktionen in mehreren neuen kleinen und gut gestalteten Dateien.

Kirill V. Lyadvinsky
quelle
@Martin: Zum Glück haben Sie Ihre Datei hier nicht eingefügt, daher habe ich keine Ahnung von ihrer Struktur. Aber die allgemeine Idee ist, es in logische Teile aufzuteilen. Solche logischen Teile können Gruppen von Funktionen aus Ihrer "Hauptklasse" enthalten oder Sie können sie in mehrere Hilfsklassen aufteilen.
Kirill V. Lyadvinsky
3
Bei 10 Wartungsversionen und vielen aktiven Entwicklern ist es unwahrscheinlich, dass die Datei lange genug eingefroren werden kann.
Kobi
9
@ Martin, Sie haben ein paar GOF Muster , die den Trick tun würde, eine einzige Fassade , die die Funktionen des mainmodule.cpp abbildet, alternativ (Ich habe unten empfohlen) schaffen eine Reihe von Befehlsklassen , dass jede Karte auf eine Funktion / Funktion von mainmodule.app. (Ich habe dies in meiner Antwort erweitert.)
ocodo
2
Ja, stimme vollkommen zu, irgendwann musst du aufhören, Code hinzuzufügen, oder irgendwann wird es 30.000, 40.000, 50.000, Kaboom-Hauptmodul sein, nur fehlerhaft. :-)
Chris
67

Es hört sich für mich so an, als ob Sie hier einer Reihe von Code-Gerüchen ausgesetzt sind. Zunächst scheint die Hauptklasse gegen das Open / Closed-Prinzip zu verstoßen . Es klingt auch so, als würde es zu viele Verantwortlichkeiten übernehmen . Aus diesem Grund würde ich annehmen, dass der Code spröder ist, als er sein muss.

Obwohl ich Ihre Bedenken hinsichtlich der Rückverfolgbarkeit nach einem Refactoring verstehen kann, würde ich erwarten, dass diese Klasse nur schwer zu warten und zu verbessern ist und dass Änderungen, die Sie vornehmen, wahrscheinlich Nebenwirkungen verursachen. Ich würde annehmen, dass die Kosten für diese die Kosten für die Umgestaltung der Klasse überwiegen.

Da sich die Code-Gerüche mit der Zeit nur verschlechtern, überwiegen zumindest irgendwann die Kosten für diese die Kosten für das Refactoring. Aus Ihrer Beschreibung würde ich annehmen, dass Sie den Wendepunkt überschritten haben.

Das Refactoring sollte in kleinen Schritten erfolgen. Fügen Sie nach Möglichkeit automatisierte Tests hinzu, um das aktuelle Verhalten zu überprüfen, bevor Sie etwas umgestalten. Wählen Sie dann kleine Bereiche mit isolierten Funktionen aus und extrahieren Sie diese als Typen, um die Verantwortung zu delegieren.

Auf jeden Fall klingt es wie ein großes Projekt, also viel Glück :)

Brian Rasmussen
quelle
18
Es riecht viel: Es riecht, als wäre das Blob-Anti-Pattern in einem Haus ... en.wikipedia.org/wiki/God_object . Sein Lieblingsessen ist Spaghetti-Code: en.wikipedia.org/wiki/Spaghetti_code :-)
jdehaan
@jdehaan: Ich habe versucht, diplomatisch zu sein :)
Brian Rasmussen
+1 Auch von mir wage ich es nicht, selbst komplexen Code zu berühren, den ich ohne Tests geschrieben habe, um ihn abzudecken.
Danny Thomas
49

Die einzige Lösung, die ich mir jemals für solche Probleme vorgestellt habe, folgt. Der tatsächliche Gewinn durch das beschriebene Verfahren ist die Progressivität der Entwicklungen. Keine Revolutionen hier, sonst werden Sie sehr schnell in Schwierigkeiten geraten.

Fügen Sie eine neue cpp-Klasse über der ursprünglichen Hauptklasse ein. Im Moment würde es im Grunde alle Aufrufe an die aktuelle Hauptklasse umleiten, aber darauf abzielen, die API dieser neuen Klasse so klar und prägnant wie möglich zu gestalten.

Sobald dies geschehen ist, haben Sie die Möglichkeit, neue Funktionen in neuen Klassen hinzuzufügen.

Bestehende Funktionen müssen schrittweise in neue Klassen verschoben werden, da sie stabil genug sind. Sie werden die SCC-Hilfe für diesen Code verlieren, aber es kann nicht viel dagegen getan werden. Wählen Sie einfach das richtige Timing.

Ich weiß, dass dies nicht perfekt ist, obwohl ich hoffe, dass es helfen kann, und der Prozess muss an Ihre Bedürfnisse angepasst werden!

Zusätzliche Information

Beachten Sie, dass Git ein SCC ist, der Codeteile von einer Datei zur anderen verfolgen kann. Ich habe gute Dinge darüber gehört, daher könnte es helfen, während Sie Ihre Arbeit schrittweise verschieben.

Git basiert auf dem Begriff der Blobs, die, wenn ich das richtig verstehe, Teile von Codedateien darstellen. Bewegen Sie diese Teile in verschiedenen Dateien und Git findet sie, auch wenn Sie sie ändern. Abgesehen von dem Video von Linus Torvalds, das in den Kommentaren unten erwähnt wurde, konnte ich nichts klares darüber finden.

Benoît
quelle
Ein Hinweis darauf, wie GIT das macht / wie Sie es mit GIT machen, wäre sehr willkommen.
Martin Ba
@ Martin Git macht es automatisch.
Matthew
4
@Martin: Git macht es automatisch - weil es keine Dateien verfolgt, verfolgt es Inhalte. In Git ist es tatsächlich schwieriger, nur "den Verlauf der einen Datei abzurufen".
Arafangion
1
@Martin youtube.com/watch?v=4XpnKHJAok8 ist ein Vortrag, in dem Torvalds über Git spricht. Er erwähnt es später im Gespräch.
Matthew
6
@ Martin, schauen Sie sich diese Frage an: stackoverflow.com/questions/1728922/…
Benjol
30

Konfuzius sagt: "Der erste Schritt, um aus dem Loch herauszukommen, besteht darin, das Graben des Lochs zu beenden."

fdasfasdfdas
quelle
25

Lassen Sie mich raten: Zehn Kunden mit unterschiedlichen Funktionen und einem Vertriebsleiter, der die "Anpassung" fördert? Ich habe schon früher an solchen Produkten gearbeitet. Wir hatten im Wesentlichen das gleiche Problem.

Sie erkennen, dass eine riesige Datei ein Problem ist, aber noch mehr Probleme sind zehn Versionen, die Sie "aktuell" halten müssen. Das ist mehrfache Wartung. SCC kann das einfacher machen, aber es kann es nicht richtig machen.

Bevor Sie versuchen, die Datei in Teile zu zerlegen, müssen Sie die zehn Zweige wieder synchronisieren, damit Sie den gesamten Code auf einmal sehen und formen können. Sie können diesen Zweig einzeln ausführen und beide Zweige mit derselben Hauptcodedatei testen. Um das benutzerdefinierte Verhalten zu erzwingen, können Sie #ifdef und friends verwenden. Es ist jedoch besser, gewöhnliches if / else für definierte Konstanten zu verwenden. Auf diese Weise überprüft Ihr Compiler alle Typen und eliminiert höchstwahrscheinlich ohnehin "toten" Objektcode. (Möglicherweise möchten Sie jedoch die Warnung vor totem Code deaktivieren.)

Sobald es nur eine Version dieser Datei gibt, die implizit von allen Zweigen gemeinsam genutzt wird, ist es einfacher, mit herkömmlichen Refactoring-Methoden zu beginnen.

Die #ifdefs eignen sich hauptsächlich für Abschnitte, in denen der betroffene Code nur im Zusammenhang mit anderen Anpassungen pro Zweig sinnvoll ist. Man kann argumentieren, dass diese auch eine Chance für das gleiche Zweigverschmelzungsschema darstellen, aber nicht wild werden. Bitte jeweils ein kolossales Projekt.

Kurzfristig scheint die Datei zu wachsen. Das ist in Ordnung. Was Sie tun, ist Dinge zusammenzubringen, die zusammen sein müssen. Danach sehen Sie Bereiche, die unabhängig von der Version eindeutig identisch sind. Diese können in Ruhe gelassen oder nach Belieben überarbeitet werden. Andere Bereiche unterscheiden sich je nach Version deutlich. In diesem Fall haben Sie eine Reihe von Optionen. Eine Methode besteht darin, die Unterschiede an Strategieobjekte pro Version zu delegieren. Eine andere Möglichkeit besteht darin, Client-Versionen von einer gemeinsamen abstrakten Klasse abzuleiten. Aber keine dieser Transformationen ist möglich, solange Sie zehn "Entwicklungstipps" in verschiedenen Branchen haben.

Ian
quelle
2
Ich bin damit einverstanden, dass das Ziel darin bestehen sollte, eine Version der Software zu haben, aber wäre es nicht besser, Konfigurationsdateien (Laufzeit) zu verwenden und die Zeit nicht zu kompilieren
Esben Skov Pedersen
Oder sogar "Konfigurationsklassen" für den Build jedes Kunden.
tc.
Ich denke, dass die Konfiguration zur Kompilierungs- oder Laufzeitfunktion funktional irrelevant ist, aber ich möchte die Möglichkeiten nicht einschränken. Die Konfiguration zur Kompilierungszeit hat den Vorteil, dass der Client nicht mit einer Konfigurationsdatei herumhacken kann, um zusätzliche Funktionen zu aktivieren, da die gesamte Konfiguration im Quellbaum anstatt als bereitstellbarer "Textobjekt" -Code abgelegt wird. Die Kehrseite ist, dass Sie zu AlternateHardAndSoftLayers tendieren, wenn es Laufzeit ist.
Ian
22

Ich weiß nicht, ob dies Ihr Problem löst, aber ich denke, Sie möchten den Inhalt der Datei unabhängig voneinander in kleinere Dateien migrieren (zusammengefasst). Was ich auch bekomme ist, dass Sie ungefähr 10 verschiedene Versionen der Software haben und Sie müssen sie alle unterstützen, ohne die Dinge durcheinander zu bringen.

Erstens gibt es einfach keine Möglichkeit, dass dies einfach ist und sich in wenigen Minuten Brainstorming von selbst lösen wird. Die in Ihrer Datei verknüpften Funktionen sind für Ihre Anwendung von entscheidender Bedeutung. Wenn Sie sie einfach ausschneiden und in andere Dateien migrieren, wird Ihr Problem nicht behoben.

Ich denke, Sie haben nur folgende Möglichkeiten:

  1. Migrieren Sie nicht und bleiben Sie bei dem, was Sie haben. Kündigen Sie möglicherweise Ihren Job und beginnen Sie mit der Arbeit an seriöser Software mit gutem Design. Extreme Programmierung ist nicht immer die beste Lösung, wenn Sie an einem Langzeitprojekt mit genügend Geld arbeiten, um ein oder zwei Abstürze zu überstehen.

  2. Erarbeiten Sie ein Layout, wie Ihre Datei nach dem Aufteilen aussehen soll. Erstellen Sie die erforderlichen Dateien und integrieren Sie sie in Ihre Anwendung. Benennen Sie die Funktionen um oder überladen Sie sie, um einen zusätzlichen Parameter zu übernehmen (möglicherweise nur einen einfachen Booleschen Wert?). Wenn Sie an Ihrem Code arbeiten müssen, migrieren Sie die Funktionen, an denen Sie arbeiten müssen, in die neue Datei und ordnen Sie die Funktionsaufrufe der alten Funktionen den neuen Funktionen zu. Sie sollten Ihre Hauptdatei weiterhin auf diese Weise haben und weiterhin die Änderungen sehen können, die daran vorgenommen wurden, sobald es sich um eine bestimmte Funktion handelt, von der Sie genau wissen, wann sie ausgelagert wurde, und so weiter.

  3. Versuchen Sie, Ihre Mitarbeiter mit einem guten Kuchen davon zu überzeugen, dass der Workflow überbewertet ist und dass Sie einige Teile der Anwendung neu schreiben müssen, um ernsthafte Geschäfte zu machen.

Robin
quelle
19

Genau dieses Problem wird in einem der Kapitel des Buches "Effektiv mit Legacy-Code arbeiten" ( http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052 ) behandelt.

Patrick
quelle
informit.com/store/product.aspx?isbn=0131177052 ermöglicht es, das Inhaltsverzeichnis dieses Buches (und 2 Beispielkapitel) anzuzeigen . Wie lang ist Kapitel 20? (Nur um ein Gefühl dafür zu bekommen, wie nützlich es sein könnte.)
Martin Ba
17
Kapitel 20 ist 10.000 Zeilen lang, aber der Autor arbeitet daran, wie man es in verdauliche Stücke aufteilt ... 8)
Tony Delroy
1
Es sind ungefähr 23 Seiten, aber mit 14 Bildern. Ich denke, Sie sollten es bekommen, Sie werden sich viel sicherer fühlen, wenn Sie entscheiden, was Sie imho tun wollen.
Emile Vrijdags
Ein ausgezeichnetes Buch für das Problem, aber die darin enthaltenen Empfehlungen (und andere Empfehlungen in diesem Thread) haben alle eine gemeinsame Anforderung: Wenn Sie diese Datei für alle Ihre Zweige umgestalten möchten, können Sie dies nur einfrieren Datei für alle Zweige und nehmen Sie die ersten strukturellen Änderungen vor. Daran führt kein Weg vorbei. Das Buch beschreibt einen iterativen Ansatz zum sicheren Extrahieren von Unterklassen ohne automatische Refactoring-Unterstützung, indem doppelte Methoden erstellt und Aufrufe delegiert werden. All dies ist jedoch umstritten, wenn Sie die Dateien nicht ändern können.
Dan Bryant
2
@ Martin, das Buch ist ausgezeichnet, aber es hängt ziemlich stark von Test, Refactor und Testzyklus ab, was von Ihrem derzeitigen Standort aus ziemlich schwierig sein kann. Ich war in einer ähnlichen Situation und dieses Buch war das hilfreichste, das ich gefunden habe. Es hat gute Vorschläge für das hässliche Problem, das Sie haben. Aber wenn Sie kein Testgeschirr ins Bild bekommen, helfen Ihnen alle Refactoring-Vorschläge der Welt nicht weiter.
14

Ich denke, Sie sollten am besten eine Reihe von Befehlsklassen erstellen , die den API-Punkten der Datei mainmodule.cpp zugeordnet sind.

Sobald sie vorhanden sind, müssen Sie die vorhandene Codebasis umgestalten, um über die Befehlsklassen auf diese API-Punkte zugreifen zu können. Sobald dies erledigt ist, können Sie die Implementierung jedes Befehls in eine neue Klassenstruktur umgestalten.

Natürlich ist der Code in einer einzelnen Klasse von 11 KLOC wahrscheinlich stark gekoppelt und spröde, aber das Erstellen einzelner Befehlsklassen hilft viel mehr als jede andere Proxy- / Fassadenstrategie.

Ich beneide die Aufgabe nicht, aber mit der Zeit wird sich dieses Problem nur verschlimmern, wenn es nicht angegangen wird.

Aktualisieren

Ich würde vorschlagen, dass das Befehlsmuster einer Fassade vorzuziehen ist.

Das Verwalten / Organisieren vieler verschiedener Befehlsklassen über eine (relativ) monolithische Fassade ist vorzuziehen. Das Zuordnen einer einzelnen Fassade zu einer 11 KLOC-Datei muss wahrscheinlich selbst in einige verschiedene Gruppen unterteilt werden.

Warum sollte man sich die Mühe machen, diese Fassadengruppen herauszufinden? Mit dem Befehlsmuster können Sie diese kleinen Klassen organisch gruppieren und organisieren, sodass Sie viel mehr Flexibilität haben.

Natürlich sind beide Optionen besser als die einzelne 11 KLOC- und wachsende Datei.

Slomojo
quelle
+1 eine Alternative zu der von mir vorgeschlagenen Lösung mit der gleichen Idee: Ändern Sie die API, um das große Problem in kleine zu trennen.
Benoît
13

Ein wichtiger Rat: Mischen Sie kein Refactoring und keine Bugfixes. Was Sie wollen, ist eine Version Ihres Programms, die mit der vorherigen Version identisch ist , außer dass der Quellcode anders ist.

Eine Möglichkeit könnte darin bestehen, die am wenigsten große Funktion / das am wenigsten große Teil in eine eigene Datei aufzuteilen und dann entweder mit einem Header einzuschließen (wodurch main.cpp in eine Liste von #includes umgewandelt wird, was an sich nach Code riecht * Ich bin es nicht ein C ++ Guru zwar), aber zumindest ist es jetzt in Dateien aufgeteilt).

Sie können dann versuchen, alle Wartungsversionen auf die "neue" main.cpp oder eine andere Struktur umzustellen. Nochmals: Keine weiteren Änderungen oder Bugfixes, da das Verfolgen dieser Änderungen verdammt verwirrend ist.

Eine andere Sache: So sehr Sie sich wünschen, das Ganze auf einmal zu überarbeiten, können Sie mehr abbeißen, als Sie kauen können. Vielleicht wählen Sie einfach ein oder zwei "Teile" aus, nehmen Sie sie in alle Releases auf, und erhöhen Sie dann den Wert für Ihren Kunden (schließlich bietet Refactoring keinen direkten Mehrwert, sodass die Kosten gerechtfertigt sein müssen) und wählen Sie dann einen anderen aus ein oder zwei Teile.

Offensichtlich erfordert dies etwas Disziplin im Team, um die geteilten Dateien tatsächlich zu verwenden und nicht nur ständig neue Inhalte zur main.cpp hinzuzufügen, sondern auch hier ist der Versuch, einen massiven Refactor durchzuführen, möglicherweise nicht die beste Vorgehensweise.

Michael Stum
quelle
1
+1 für das Ausklammern und #einschließen wieder ein. Wenn Sie dies für alle 10 Zweige getan hätten (ein bisschen Arbeit dort, aber überschaubar), hätten Sie immer noch das andere Problem, Änderungen in allen Ihren Zweigen zu veröffentlichen, aber dieses Problem würde es nicht. ' t haben (notwendigerweise) erweitert. Ist es hässlich Ja, das ist es immer noch, aber es könnte ein wenig Rationalität in das Problem bringen. Nachdem ich mehrere Jahre mit der Wartung und Instandhaltung eines wirklich sehr großen Produkts verbracht habe, weiß ich, dass die Wartung sehr schmerzhaft ist. Lernen Sie zumindest daraus und dienen Sie anderen als warnende Geschichte.
Jay
10

Rofl, das erinnert mich an meinen alten Job. Es scheint, dass sich vor meinem Beitritt alles in einer riesigen Datei befand (auch C ++). Dann haben sie es (an völlig zufälligen Stellen mit Includes) in ungefähr drei (immer noch riesige Dateien) aufgeteilt. Die Qualität dieser Software war, wie zu erwarten, schrecklich. Das Projekt belief sich auf rund 40.000 LOC. (enthält fast keine Kommentare, aber viel doppelten Code)

Am Ende habe ich das Projekt komplett neu geschrieben. Ich begann damit, den schlimmsten Teil des Projekts von Grund auf neu zu erstellen. Natürlich hatte ich eine mögliche (kleine) Schnittstelle zwischen diesem neuen Teil und dem Rest im Sinn. Dann habe ich diesen Teil in das alte Projekt eingefügt. Ich habe den alten Code nicht überarbeitet, um die erforderliche Schnittstelle zu erstellen, sondern ihn nur ersetzt. Dann machte ich kleine Schritte von dort und schrieb den alten Code neu.

Ich muss sagen, dass dies ungefähr ein halbes Jahr gedauert hat und es in dieser Zeit keine Entwicklung der alten Codebasis neben Bugfixes gab.


bearbeiten:

Die Größe blieb bei etwa 40.000 LOC, aber die neue Anwendung enthielt in ihrer ursprünglichen Version viel mehr Funktionen und vermutlich weniger Fehler als die 8 Jahre alte Software. Ein Grund für das Umschreiben war auch, dass wir die neuen Funktionen brauchten und es fast unmöglich war, sie in den alten Code einzuführen.

Die Software war für ein eingebettetes System, einen Etikettendrucker.

Ein weiterer Punkt, den ich hinzufügen sollte, ist, dass das Projekt theoretisch C ++ war. Aber es war überhaupt nicht OO, es hätte C sein können. Die neue Version war objektorientiert.

Zikzystar
quelle
9
Jedes Mal, wenn ich im Thema "Refactoring" "von Grund auf" höre, töte ich ein Kätzchen!
Kugel
Ich war in einer sehr ähnlich klingenden Situation, obwohl die Hauptprogrammschleife, mit der ich mich auseinandersetzen musste, nur ~ 9000 LOC war. Und das war schon schlimm genug.
AndyUK
8

OK, zum größten Teil ist das Umschreiben der API von Produktionscode zunächst eine schlechte Idee. Zwei Dinge müssen passieren.

Erstens muss Ihr Team tatsächlich entscheiden, ob der Code für die aktuelle Produktionsversion dieser Datei eingefroren werden soll.

Zweitens müssen Sie diese Produktionsversion verwenden und einen Zweig erstellen, der die Builds mithilfe von Vorverarbeitungsanweisungen verwaltet, um die große Datei aufzuteilen. Das Aufteilen der Kompilierung mithilfe von JUST-Präprozessoranweisungen (#ifdefs, #includes, #endifs) ist einfacher als das Neukodieren der API. Es ist definitiv einfacher für Ihre SLAs und den laufenden Support.

Hier können Sie einfach Funktionen ausschneiden, die sich auf ein bestimmtes Subsystem innerhalb der Klasse beziehen, und sie in eine Datei mit dem Namen mainloop_foostuff.cpp einfügen und an der richtigen Stelle in mainloop.cpp einfügen.

ODER

Ein zeitaufwändigerer, aber robusterer Weg wäre es, eine interne Abhängigkeitsstruktur mit doppelter Indirektion zu entwickeln, wie die Dinge aufgenommen werden. Auf diese Weise können Sie die Dinge aufteilen und sich trotzdem um Co-Abhängigkeiten kümmern. Beachten Sie, dass dieser Ansatz eine Positionscodierung erfordert und daher mit entsprechenden Kommentaren gekoppelt werden sollte.

Dieser Ansatz würde Komponenten umfassen, die basierend auf der zu kompilierenden Variante verwendet werden.

Die Grundstruktur besteht darin, dass Ihre mainclass.cpp nach einem Anweisungsblock wie der folgenden eine neue Datei mit dem Namen MainClassComponents.cpp enthält:

#if VARIANT == 1
#  define Uses_Component_1
#  define Uses_Component_2
#elif VARIANT == 2
#  define Uses_Component_1
#  define Uses_Component_3
#  define Uses_Component_6
...

#endif

#include "MainClassComponents.cpp"

Die Primärstruktur der Datei MainClassComponents.cpp dient dazu, Abhängigkeiten innerhalb der Unterkomponenten wie folgt zu ermitteln:

#ifndef _MainClassComponents_cpp
#define _MainClassComponents_cpp

/* dependencies declarations */

#if defined(Activate_Component_1) 
#define _REQUIRES_COMPONENT_1
#define _REQUIRES_COMPONENT_3 /* you also need component 3 for component 1 */
#endif

#if defined(Activate_Component_2)
#define _REQUIRES_COMPONENT_2
#define _REQUIRES_COMPONENT_15 /* you also need component 15 for this component  */
#endif

/* later on in the header */

#ifdef _REQUIRES_COMPONENT_1
#include "component_1.cpp"
#endif

#ifdef _REQUIRES_COMPONENT_2
#include "component_2.cpp"
#endif

#ifdef _REQUIRES_COMPONENT_3
#include "component_3.cpp"
#endif


#endif /* _MainClassComponents_h  */

Und jetzt erstellen Sie für jede Komponente eine component_xx.cpp-Datei.

Natürlich verwende ich Zahlen, aber Sie sollten etwas logischeres verwenden, das auf Ihrem Code basiert.

Mit dem Präprozessor können Sie Dinge aufteilen, ohne sich um API-Änderungen kümmern zu müssen, was in der Produktion ein Albtraum ist.

Sobald Sie die Produktion abgeschlossen haben, können Sie tatsächlich an der Neugestaltung arbeiten.

Elfenkönig
quelle
Dies sieht aus wie die Arbeiten, aber anfangs schmerzhaften Ergebnisse der Erfahrung.
JBRWilkinson
Tatsächlich ist es eine Technik, die in Borland C ++ - Compilern verwendet wurde, um den Pascal-Stil zu emulieren. Verwendet für die Verwaltung von Header-Dateien. Vor allem, wenn sie den ersten Port ihres textbasierten Fenstersystems ausgeführt haben.
Elf - König
8

Nun, ich verstehe deinen Schmerz :) Ich war auch in einigen solchen Projekten und es ist nicht schön. Darauf gibt es keine einfache Antwort.

Ein Ansatz, der für Sie möglicherweise funktioniert, besteht darin, in allen Funktionen sichere Schutzfunktionen hinzuzufügen, dh Argumente und Vor- / Nachbedingungen in Methoden zu überprüfen und schließlich alle Komponententests hinzuzufügen, um die aktuelle Funktionalität der Quellen zu erfassen. Sobald Sie dies haben, sind Sie besser in der Lage, den Code neu zu faktorisieren, da Behauptungen und Fehler auftauchen, die Sie benachrichtigen, wenn Sie etwas vergessen haben.

Manchmal kann es jedoch vorkommen, dass Refactoring mehr Schmerzen als Nutzen bringt. Dann ist es möglicherweise besser, das ursprüngliche Projekt einfach in einem Pseudowartungszustand zu belassen, von vorne zu beginnen und dann schrittweise die Funktionalität des Tieres hinzuzufügen.

Klatschfalle
quelle
4

Sie sollten sich nicht mit der Reduzierung der Dateigröße befassen, sondern mit der Reduzierung der Klassengröße. Es kommt fast auf dasselbe an, aber Sie sehen das Problem aus einem anderen Blickwinkel (wie @Brian Rasmussen vorschlägt , scheint Ihre Klasse zu viele Aufgaben zu haben).

Björn Pollex
quelle
Wie immer möchte ich eine Erklärung für die Ablehnung erhalten.
Björn Pollex
4

Was Sie haben, ist ein klassisches Beispiel für ein bekanntes Design-Antimuster namens Blob . Nehmen Sie sich etwas Zeit, um den Artikel zu lesen, auf den ich hier verweise, und vielleicht finden Sie etwas Nützliches. Wenn dieses Projekt so groß ist, wie es aussieht, sollten Sie ein Design in Betracht ziehen, um zu verhindern, dass Code entsteht, den Sie nicht kontrollieren können.

David Conde
quelle
4

Dies ist keine Antwort auf das große Problem, sondern eine theoretische Lösung für ein bestimmtes Stück davon:

  • Finden Sie heraus, wo Sie die große Datei in Unterdateien aufteilen möchten. Fügen Sie an jedem dieser Punkte Kommentare in einem speziellen Format ein.

  • Schreiben Sie ein ziemlich triviales Skript, das die Datei an diesen Stellen in Unterdateien aufteilt. (Möglicherweise sind in den speziellen Kommentaren Dateinamen eingebettet, die das Skript als Anweisungen zum Teilen verwenden kann.) Die Kommentare sollten im Rahmen der Aufteilung beibehalten werden.

  • Führen Sie das Skript aus. Löschen Sie die Originaldatei.

  • Wenn Sie aus einem Zweig zusammenführen müssen, erstellen Sie zuerst die große Datei neu, indem Sie die Teile wieder zusammenfügen, die Zusammenführung durchführen und sie dann erneut aufteilen.

Wenn Sie den SCC-Dateiverlauf beibehalten möchten, sollten Sie Ihrem Quellcodeverwaltungssystem am besten mitteilen, dass es sich bei den einzelnen Stückdateien um Kopien des Originals handelt. Dann wird der Verlauf der Abschnitte beibehalten, die in dieser Datei gespeichert wurden, obwohl natürlich auch aufgezeichnet wird, dass große Teile "gelöscht" wurden.

Brooks Moses
quelle
4

Eine Möglichkeit, es ohne allzu große Gefahr aufzuteilen, wäre ein historischer Blick auf alle Linienänderungen. Gibt es bestimmte Funktionen, die stabiler sind als andere? Hot Spots der Veränderung, wenn Sie so wollen.

Wenn eine Zeile in einigen Jahren nicht geändert wurde, können Sie sie wahrscheinlich ohne allzu große Sorgen in eine andere Datei verschieben. Ich würde einen Blick auf die Quelle werfen, die mit der letzten Revision versehen ist, die eine bestimmte Zeile berührt hat, und prüfen, ob es Funktionen gibt, die Sie herausziehen könnten.

Paul Rubel
quelle
Ich denke, andere haben ähnliche Dinge vorgeschlagen. Dies ist kurz und auf den Punkt und ich denke, dies könnte ein gültiger Ausgangspunkt für das ursprüngliche Problem sein.
Martin Ba
3

Wow, hört sich toll an. Ich denke, es ist einen Versuch wert, Ihrem Chef zu erklären, dass Sie viel Zeit brauchen, um das Biest umzugestalten. Wenn er nicht einverstanden ist, ist das Beenden eine Option.

Was ich jedenfalls vorschlage, ist, die gesamte Implementierung wegzuwerfen und sie in neue Module zu gruppieren. Nennen wir diese "globalen Dienste". Das "Hauptmodul" würde nur an diese Dienste weiterleiten, und JEDER neue Code, den Sie schreiben, verwendet sie anstelle des "Hauptmoduls". Dies sollte in angemessener Zeit möglich sein (da es sich hauptsächlich um Kopieren und Einfügen handelt). Sie können vorhandenen Code nicht beschädigen und können jeweils eine Wartungsversion ausführen. Und wenn Sie noch Zeit haben, können Sie alle alten abhängigen Module neu gestalten, um auch die globalen Dienste zu nutzen.

back2dos
quelle
3

Mein Mitgefühl - in meinem vorherigen Job bin ich auf eine ähnliche Situation mit einer Datei gestoßen, die um ein Vielfaches größer war als die, mit der Sie sich befassen müssen. Lösung war:

  1. Schreiben Sie Code, um die Funktion in dem betreffenden Programm ausführlich zu testen. Klingt so, als hätten Sie das noch nicht in der Hand ...
  2. Identifizieren Sie einen Code, der in eine Hilfs- / Dienstprogrammklasse abstrahiert werden kann. Muss nicht groß sein, nur etwas, das nicht wirklich Teil Ihrer Hauptklasse ist.
  3. Refaktorieren Sie den in 2. identifizierten Code in eine separate Klasse.
  4. Führen Sie Ihre Tests erneut aus, um sicherzustellen, dass nichts kaputt gegangen ist.
  5. Wenn Sie Zeit haben, gehen Sie zu 2. und wiederholen Sie den Vorgang nach Bedarf, um den Code verwaltbar zu machen.

Die Klassen, die Sie in Schritt 3 erstellen, werden wahrscheinlich mehr Code enthalten, der für ihre neu gelöschte Funktion geeignet ist.

Ich könnte auch hinzufügen:

0: kaufe das Buch von Michael Feathers von über die Arbeit mit Legacy-Code

Leider ist diese Art von Arbeit nur allzu häufig, aber meiner Erfahrung nach ist es von großem Wert, funktionierenden, aber schrecklichen Code inkrementell weniger schrecklich zu machen, während er weiter funktioniert.

Steve Townsend
quelle
2

Überlegen Sie, wie Sie die gesamte Anwendung sinnvoller umschreiben können. Schreiben Sie vielleicht einen kleinen Teil davon als Prototyp um, um zu sehen, ob Ihre Idee machbar ist.

Wenn Sie eine praktikable Lösung gefunden haben, überarbeiten Sie die Anwendung entsprechend.

Wenn alle Versuche, eine rationalere Architektur zu erstellen, fehlschlagen, wissen Sie zumindest, dass die Lösung wahrscheinlich darin besteht, die Funktionalität des Programms neu zu definieren.

Wallyk
quelle
+1 - schreibe es in deiner eigenen Zeit neu, sonst könnte jemand seinen Dummy ausspucken.
Jon Black
2

Meine 0,05 Eurocent:

Entwerfen Sie das gesamte Durcheinander neu und teilen Sie es unter Berücksichtigung der technischen und geschäftlichen Anforderungen in Subsysteme auf (= viele parallele Wartungspfade mit möglicherweise unterschiedlicher Codebasis für jeden, es besteht offensichtlich ein Bedarf an hoher Modifizierbarkeit usw.).

Analysieren Sie bei der Aufteilung in Subsysteme die Orte, die sich am meisten geändert haben, und trennen Sie diese von den unveränderlichen Teilen. Dies sollte Ihnen die Problemstellen zeigen. Trennen Sie die sich am meisten ändernden Teile in ihre eigenen Module (z. B. DLL), sodass die Modul-API intakt bleibt und Sie BC nicht ständig unterbrechen müssen. Auf diese Weise können Sie bei Bedarf verschiedene Versionen des Moduls für verschiedene Wartungszweige bereitstellen, während der Kern unverändert bleibt.

Das Redesign muss wahrscheinlich ein separates Projekt sein. Der Versuch, es mit einem sich bewegenden Ziel zu tun, funktioniert nicht.

Was den Quellcodeverlauf betrifft, meine Meinung: Vergiss ihn für den neuen Code. Bewahren Sie den Verlauf jedoch irgendwo auf, damit Sie ihn bei Bedarf überprüfen können. Ich wette, Sie werden es nach dem Anfang nicht mehr so ​​oft brauchen.

Sie müssen höchstwahrscheinlich ein Management-Buy-In für dieses Projekt erhalten. Sie können vielleicht mit einer schnelleren Entwicklungszeit, weniger Fehlern, einer einfacheren Wartung und weniger allgemeinem Chaos argumentieren. Etwas in der Art von "Proaktiv die Zukunftssicherheit und Wartbarkeit unserer kritischen Software-Assets ermöglichen" :)

So würde ich zumindest anfangen, das Problem anzugehen.

Aufreizend
quelle
2

Fügen Sie zunächst Kommentare hinzu. In Bezug darauf, wo Funktionen aufgerufen werden und ob Sie Dinge bewegen können. Dies kann Dinge in Bewegung bringen. Sie müssen wirklich beurteilen, wie spröde die Codebasis ist. Verschieben Sie dann gemeinsame Funktionen. Kleine Änderungen auf einmal.

Jesper Smith
quelle
2

Ein weiteres Buch, das Sie vielleicht interessant / hilfreich finden, ist Refactoring .

Derek
quelle
2

Ich finde es nützlich (und ich mache es jetzt, obwohl nicht in der Größenordnung, in der Sie sich befinden), Methoden als Klassen zu extrahieren (Methodenobjekt-Refactoring). Die Methoden, die sich in Ihren verschiedenen Versionen unterscheiden, werden zu verschiedenen Klassen, die in eine gemeinsame Basis eingefügt werden können, um das unterschiedliche Verhalten bereitzustellen, das Sie benötigen.

Channing Walton
quelle
2

Ich fand diesen Satz der interessanteste Teil Ihres Beitrags:

> Die Datei wird in mehreren (> 10) Wartungsversionen unseres Produkts verwendet und aktiv geändert. Daher ist es sehr schwierig, sie zu überarbeiten

Zunächst würde ich empfehlen, dass Sie ein Quellcodeverwaltungssystem für die Entwicklung dieser 10+ Wartungsversionen verwenden, das die Verzweigung unterstützt.

Zweitens würde ich zehn Zweige erstellen (einen für jede Ihrer Wartungsversionen).

Ich kann dich schon zusammenzucken fühlen! Aber entweder funktioniert Ihre Quellcodeverwaltung aufgrund fehlender Funktionen nicht für Ihre Situation oder sie wird nicht richtig verwendet.

Nun zu der Branche, an der Sie arbeiten - überarbeiten Sie sie nach Belieben, in der Gewissheit, dass Sie die anderen neun Branchen Ihres Produkts nicht verärgern werden.

Ich wäre ein bisschen besorgt, dass Sie so viel in Ihrer main () -Funktion haben.

In allen Projekten, die ich schreibe, würde ich main () verwenden, um nur die Initialisierung von Kernobjekten durchzuführen - wie ein Simulations- oder Anwendungsobjekt - in diesen Klassen sollte die eigentliche Arbeit fortgesetzt werden.

Ich würde auch ein Anwendungsprotokollierungsobjekt in main für die globale Verwendung im gesamten Programm initialisieren.

Schließlich füge ich im Wesentlichen auch Leckerkennungscode in Präprozessorblöcken hinzu, der sicherstellt, dass er nur in DEBUG-Builds aktiviert ist. Dies ist alles, was ich zu main () hinzufügen würde. Main () sollte kurz sein!

Du sagst das

> Die Datei enthält im Wesentlichen die "Hauptklasse" (interne interne Arbeitsverteilung und -koordination) unseres Programms

Es hört sich so an, als könnten diese beiden Aufgaben in zwei separate Objekte aufgeteilt werden - einen Koordinator und einen Arbeitsverteiler.

Wenn Sie diese aufteilen, können Sie Ihren "SCC-Workflow" durcheinander bringen, aber es scheint, als würde die strikte Einhaltung Ihres SCC-Workflows zu Problemen bei der Softwarewartung führen. Lassen Sie es jetzt fallen und schauen Sie nicht zurück, denn sobald Sie es repariert haben, werden Sie anfangen, ruhig zu schlafen.

Wenn Sie nicht in der Lage sind, die Entscheidung zu treffen, kämpfen Sie mit Ihrem Manager darum - Ihre Anwendung muss überarbeitet werden - und das nach den Geräuschen! Nimm kein Nein als Antwort!

user206705
quelle
Soweit ich weiß, besteht das Problem darin, dass Sie keine Patches mehr zwischen den Versionen tragen können, wenn Sie in die Kugel und den Refactor beißen. SCC ist möglicherweise perfekt eingerichtet.
Peterchen
@ Peterchen - genau das Problem. SCCs werden auf Dateiebene zusammengeführt. (3-Wege-Zusammenführung) Wenn Sie Code zwischen Dateien verschieben, müssen Sie damit beginnen, geänderte Codeblöcke manuell von einer Datei in eine andere zu fummeln. (Die GIT-Funktion, die jemand anderes in einem anderen Kommentar erwähnt hat, ist nur gut für die Geschichte, nicht zum Zusammenführen, soweit ich das beurteilen kann)
Martin Ba
2

Wie Sie es beschrieben haben, besteht das Hauptproblem darin, Pre-Split und Post-Split zu unterscheiden und Fehlerbehebungen usw. zusammenzuführen. Es wird nicht so lange dauern, ein Skript in Perl, Ruby usw. fest zu codieren, um den größten Teil des Rauschens aus dem Unterschied zwischen Pre-Split und Post-Split herauszuholen. Tun Sie, was im Umgang mit Geräuschen am einfachsten ist:

  • Entfernen Sie bestimmte Zeilen vor / während der Verkettung (z. B. Schutzvorrichtungen einschließen).
  • Entfernen Sie bei Bedarf andere Elemente aus der Diff-Ausgabe

Sie können es sogar so machen, dass beim Einchecken die Verkettung ausgeführt wird und Sie etwas vorbereitet haben, das sich von den Einzeldateiversionen unterscheidet.

Tony D.
quelle
2
  1. Berühren Sie diese Datei und den Code nie wieder!
  2. Treat ist wie etwas, an dem man festhält. Schreiben Sie Adapter für die dort codierte Funktionalität.
  3. Schreiben Sie neuen Code in verschiedenen Einheiten und sprechen Sie nur mit Adaptern, die die Funktionalität des Monsters kapseln.
  4. ... wenn nur eine der oben genannten Möglichkeiten nicht möglich ist, kündigen Sie den Job und besorgen Sie sich eine neue.
paul_71
quelle
2
+/- 0 - im Ernst, wo leben Sie Menschen, denen Sie empfehlen würden, einen Job aufgrund eines solchen technischen Details zu kündigen?
Martin Ba
1

"Die Datei enthält im Wesentlichen die" Hauptklasse "(interne interne Arbeitsverteilung und -koordination) unseres Programms. Jedes Mal, wenn ein Feature hinzugefügt wird, wirkt sich dies auch auf diese Datei aus und jedes Mal, wenn sie wächst."

Wenn dieser große Schalter (von dem ich glaube, dass er vorhanden ist) zum Hauptwartungsproblem wird, können Sie ihn so umgestalten, dass er das Wörterbuch und das Befehlsmuster verwendet und die gesamte Schaltlogik aus dem vorhandenen Code in den Loader entfernt, der diese Karte auffüllt, dh:

    // declaration
    std::map<ID, ICommand*> dispatchTable;
    ...

    // populating using some loader
    dispatchTable[id] = concreteCommand;

    ...
    // using
    dispatchTable[id]->Execute();
Grozz
quelle
2
Nein, es gibt eigentlich keinen großen Schalter. Der Satz ist nur der nächste, den ich zur Beschreibung dieses Chaos kommen kann :)
Martin Ba
1

Ich denke, der einfachste Weg, den Verlauf der Quelle beim Teilen einer Datei zu verfolgen, wäre ungefähr so:

  1. Erstellen Sie Kopien des ursprünglichen Quellcodes mit den von Ihrem SCM-System bereitgestellten kopiererhaltenden Kopierbefehlen. Sie müssen wahrscheinlich an dieser Stelle einreichen, aber es ist noch nicht erforderlich, Ihrem Build-System die neuen Dateien mitzuteilen, sodass dies in Ordnung sein sollte.
  2. Löschen Sie den Code aus diesen Kopien. Das sollte die Geschichte für die Linien, die Sie behalten, nicht brechen.
Christopher Creutzig
quelle
"Verwenden Sie alle geschichtserhaltenden Kopierbefehle, die Ihr SCM-System bereitstellt" ... eine schlechte Sache, die es nicht bietet
Martin Ba
Schade. Das allein klingt nach einem guten Grund, auf etwas Moderneres umzusteigen. :-)
Christopher Creutzig
1

Ich denke, was ich in dieser Situation tun würde, ist die Kugel und:

  1. Finden Sie heraus, wie ich die Datei aufteilen wollte (basierend auf der aktuellen Entwicklungsversion)
  2. Setzen Sie eine Verwaltungssperre für die Datei ("Niemand berührt mainmodule.cpp nach 17 Uhr Freitag !!!"
  3. Verbringen Sie Ihr langes Wochenende damit, diese Änderung auf die> 10 Wartungsversionen (von der ältesten zur neuesten) bis einschließlich der aktuellen Version anzuwenden.
  4. Löschen Sie mainmodule.cpp aus allen unterstützten Versionen der Software. Es ist ein neues Zeitalter - es gibt keine mainmodule.cpp mehr.
  5. Überzeugen Sie das Management davon, dass Sie nicht mehr als eine Wartungsversion der Software unterstützen sollten (zumindest ohne einen großen $$$ Support-Vertrag). Wenn jeder Ihrer Kunden seine eigene Version hat ... yeeeeeshhhh. Ich würde Compiler-Direktiven hinzufügen, anstatt zu versuchen, mehr als 10 Gabeln zu verwalten.

Das Verfolgen alter Änderungen an der Datei wird einfach durch Ihren ersten Check-in-Kommentar gelöst, der so etwas wie "Von mainmodule.cpp trennen" sagt. Wenn Sie zu etwas Neuem zurückkehren müssen, werden sich die meisten Menschen an die Änderung erinnern. In zwei Jahren werden sie anhand des Kommentars darüber informiert, wo sie suchen müssen. Wie wertvoll wird es natürlich sein, mehr als zwei Jahre zurück zu gehen, um zu sehen, wer den Code geändert hat und warum?

BIBD
quelle