Wir haben mehrere Kunden mit unterschiedlichen Bedürfnissen. Obwohl unsere Software bis zu einem gewissen Grad modularisiert ist, ist es fast sicher, dass wir die Geschäftslogik jedes Moduls hier und da ein wenig für jeden Kunden anpassen müssen. Die Änderungen sind wahrscheinlich zu klein, um das Aufteilen des Moduls in ein bestimmtes (physisches) Modul für jeden Client zu rechtfertigen. Ich fürchte, Probleme mit dem Build, ein Chaos bei der Verknüpfung. Diese Änderungen sind jedoch zu umfangreich und zu umfangreich, um sie in einigen Konfigurationsdateien über Switches zu konfigurieren, da dies zu Problemen bei der Bereitstellung und möglicherweise zu zahlreichen Supportproblemen führen würde, insbesondere bei Admins vom Typ "Basteln".
Ich möchte, dass das Build-System mehrere Builds erstellt, einen für jeden Client, in denen Änderungen in einer speziellen Version des betreffenden einzelnen physischen Moduls enthalten sind. Ich habe also einige Fragen:
Würden Sie empfehlen, das Build-System mehrere Builds erstellen zu lassen? Wie soll ich die verschiedenen Anpassungen in der Quellcodeverwaltung speichern, insbesondere SVN?
#ifdef
für Sie?Antworten:
Was Sie brauchen , ist Feature Verzweigung -ähnliche Code Organisation. In Ihrem speziellen Szenario sollte dies als clientspezifische Trunk-Verzweigung bezeichnet werden, da Sie wahrscheinlich auch die Feature-Verzweigung verwenden, während Sie neue Inhalte entwickeln (oder Fehler beheben).
http://svnbook.red-bean.com/de/1.5/svn.branchmerge.commonpatterns.html
Die Idee ist, einen Code-Trunk mit den Zweigen der neuen Features zusammenzuführen. Ich meine alle nicht-kundenspezifischen Funktionen.
Dann haben Sie auch Ihre kundenspezifischen Filialen, in denen Sie auch die Filialen der gleichen Features nach Bedarf zusammenführen (Kirschernte, wenn Sie so wollen).
Diese Client-Zweige ähneln Feature-Zweigen, obwohl sie nicht vorübergehend oder kurzlebig sind. Sie bleiben über einen längeren Zeitraum erhalten und werden überwiegend nur zusammengeführt. Es sollte so wenig wie möglich kundenspezifische Zweigentwicklungen geben.
Kundenspezifische Zweige sind parallele Zweige zum Trunk und sind so lange aktiv wie der Trunk selbst und werden nirgendwo als Ganzes zusammengeführt.
quelle
Tun Sie dies nicht mit SCM-Zweigen. Machen Sie den allgemeinen Code zu einem separaten Projekt, das ein Bibliothek- oder Projektskelett-Artefakt erzeugt. Jedes Kundenprojekt ist ein separates Projekt, das dann von dem gemeinsamen als Abhängigkeit abhängt.
Dies ist am einfachsten, wenn Ihre allgemeine Basisanwendung ein Framework für die Abhängigkeitsinjektion wie Spring verwendet, sodass Sie problemlos verschiedene Ersatzvarianten von Objekten in jedes Kundenprojekt einfügen können, wenn benutzerdefinierte Features erforderlich sind. Auch wenn Sie noch kein DI-Framework haben, ist das Hinzufügen eines Frameworks und das Ausführen auf diese Weise möglicherweise die schmerzloseste Wahl.
quelle
Speichern Sie Software für alle Kunden in einer einzigen Filiale. Es ist nicht erforderlich, die für verschiedene Kunden vorgenommenen Änderungen zu ändern. Im Endeffekt möchten Sie, dass Ihre Software für alle Clients von bester Qualität ist, und die Fehlerbehebung in Ihrer Kerninfrastruktur sollte alle Benutzer betreffen, ohne unnötigen Aufwand beim Zusammenführen zu verursachen, der auch weitere Fehler verursachen kann.
Modularisieren Sie den allgemeinen Code und implementieren Sie den Code, der sich in verschiedenen Dateien unterscheidet, oder schützen Sie ihn mit verschiedenen Definitionen. Stellen Sie sicher, dass Ihr Build-System für jeden Client spezifische Ziele hat, wobei jedes Ziel eine Version kompiliert, die nur den Code für einen Client enthält. Wenn Ihr Code beispielsweise C ist, möchten Sie möglicherweise Funktionen für verschiedene Clients mit einem "
#ifdef
" oder einem anderen in Ihrer Sprache verfügbaren Mechanismus für das Build-Konfigurationsmanagement schützen und eine Reihe von Definitionen vorbereiten, die der Anzahl der Funktionen entsprechen, für die ein Client bezahlt hat.Wenn Sie
ifdef
s nicht mögen , verwenden Sie "Schnittstellen und Implementierungen", "Funktoren", "Objektdateien" oder welche Tools Ihre Sprache zum Speichern verschiedener Dinge an einem Ort bereitstellt.Wenn Sie Quellen an Ihre Kunden verteilen, sollten Sie dafür sorgen, dass Ihre Erstellungsskripten spezielle "Quellenverteilungsziele" haben. Sobald Sie ein solches Ziel aufrufen, erstellt es eine spezielle Version der Quellen Ihrer Software, kopiert sie in einen separaten Ordner, damit Sie sie versenden können, und kompiliert sie nicht.
quelle
Wie so viele gesagt haben: Berechnen Sie Ihren Code richtig und passen Sie ihn basierend auf dem allgemeinen Code an - dies wird die Wartbarkeit erheblich verbessern. Unabhängig davon, ob Sie eine objektorientierte Sprache / ein objektorientiertes System verwenden oder nicht, ist dies möglich (obwohl es in C etwas schwieriger ist als etwas, das wirklich objektorientiert ist). Dies ist genau die Art von Problem, bei deren Lösung Vererbung und Kapselung helfen!
quelle
Sehr vorsichtig
Feature Branching ist eine Option, aber ich finde es etwas schwer. Darüber hinaus werden tiefgreifende Änderungen erleichtert, die dazu führen können, dass Ihre Anwendung nicht mehr funktioniert, wenn Sie nicht die Kontrolle behalten. Im Idealfall möchten Sie die Anpassungen so weit wie möglich vorantreiben, um Ihre Kerncodebasis so allgemein und allgemein wie möglich zu halten.
Hier ist, wie ich es tun würde, obwohl ich nicht weiß, ob es auf Ihre Codebasis ohne große Modifikationen und Umformulierungen anwendbar ist. Ich hatte ein ähnliches Projekt, bei dem die Grundfunktionalität identisch war, aber jeder Kunde einen sehr spezifischen Satz von Funktionen benötigte. Ich habe eine Reihe von Modulen und Containern erstellt, die ich dann durch Konfiguration (à la IoC) zusammenstelle.
dann habe ich für jeden Kunden ein Projekt erstellt, das im Wesentlichen die Konfigurationen und das Build-Skript enthält, um eine vollständig konfigurierte Installation für seine Site zu erstellen. Gelegentlich platziere ich dort auch einige für diesen Kunden maßgeschneiderte Komponenten. Dies ist jedoch selten und wenn immer möglich, versuche ich, es in einer allgemeineren Form zu erstellen und nach unten zu verschieben, damit andere Projekte sie verwenden können.
Das Endergebnis ist, dass ich die Anpassungsstufe habe, die ich benötige. Ich habe angepasste Installationsskripte, so dass ich beim Aufrufen des Kundenstandorts nicht die ganze Zeit über das System pfeife und als zusätzlichen SEHR signifikanten Bonus bekomme Regressionstests erstellen zu können, die direkt mit dem Build verknüpft sind. Auf diese Weise kann ich immer dann, wenn ich einen kundenspezifischen Fehler erhalte, einen Test schreiben, der das System während der Bereitstellung bestätigt und somit TDD auch auf dieser Ebene durchführen kann.
Also in Kürze:
Bei ordnungsgemäßer Ausführung sollte Ihre Produktassembly bis auf einige Konfigurationsdateien alle enthalten.
Nachdem ich dies für eine Weile verwendet hatte, erstellte ich Metapakete, die die am häufigsten verwendeten oder wesentlichen Systeme als eine Kerneinheit zusammenfassten, und verwendete dieses Metapaket für Kundenassemblys. Nach ein paar Jahren hatte ich einen großen Werkzeugkasten, den ich sehr schnell zusammenbauen konnte, um Kundenlösungen zu erstellen. Ich bin gerade dabei, Spring Roo zu untersuchen und zu sehen, ob ich die Idee nicht ein bisschen weiter vorantreiben kann, in der Hoffnung, dass ich eines Tages mit dem Kunden in unserem ersten Interview einen ersten Entwurf des Systems erstellen kann Entwicklung ;-).
Hoffe das hat geholfen
quelle
IMO, sie können nicht zu klein sein. Wenn möglich, würde ich den kundenspezifischen Code mithilfe des Strategiemusters so ziemlich überall herausrechnen. Dies reduziert die Menge des Codes, der verzweigt werden muss, und verringert die Zusammenführung, die erforderlich ist, um den allgemeinen Code für alle Clients synchron zu halten. Es vereinfacht auch das Testen ... Sie können den allgemeinen Code mit Standardstrategien testen und die clientspezifischen Klassen separat testen.
Wenn Ihre Module so codiert sind, dass für die Verwendung von Richtlinie X1 in Modul A die Verwendung von Richtlinie X2 in Modul B erforderlich ist, sollten Sie über ein Refactoring nachdenken, damit X1 und X2 zu einer einzigen Richtlinienklasse kombiniert werden können.
quelle
Sie können Ihr SCM verwenden, um Zweigniederlassungen zu verwalten. Halten Sie den Master-Zweig von benutzerdefiniertem Clientcode sauber. Hauptentwicklung auf diesem Zweig tun. Pflegen Sie für jede angepasste Version der Anwendung separate Zweige. Jedes gute SCM-Tool eignet sich hervorragend zum Zusammenführen von Zweigen (Git fällt mir ein). Aktualisierungen im Hauptzweig sollten in den benutzerdefinierten Zweigen zusammengeführt werden, aber der kundenspezifische Code kann im eigenen Zweig bleiben.
Versuchen Sie jedoch, das System möglichst modular und konfigurierbar zu gestalten. Der Nachteil dieser benutzerdefinierten Zweige kann sein, dass sie sich zu weit vom Core / Master entfernen.
quelle
Wenn Sie in C schreiben, ist dies ein ziemlich hässlicher Weg.
Common Code (zB Einheit "frangulator.c")
Kundenspezifischer Code, kleine Teile, die nur für jeden Kunden verwendet werden.
Verwenden Sie im Code der Haupteinheit #ifdef und #include, um so etwas wie zu tun
Verwenden Sie dies immer wieder als Muster in allen Codeeinheiten, die eine kundenspezifische Anpassung erfordern.
Dies ist SEHR hässlich und führt zu einigen anderen Problemen, ist aber auch einfach, und Sie können clientspezifische Dateien relativ einfach miteinander vergleichen.
Dies bedeutet auch, dass alle kundenspezifischen Teile jederzeit deutlich sichtbar sind (jeweils in einer eigenen Datei), und dass eine eindeutige Beziehung zwischen der Hauptcodedatei und dem kundenspezifischen Teil der Datei besteht.
Wenn Sie wirklich schlau sind, können Sie Makefiles einrichten, um die richtige Client-Definition zu erstellen.
clienta machen
erstellt für client_a und "make clientb" erstellt für client_b und so weiter.
(und "make" ohne angegebenes Ziel kann eine Warnung oder eine Verwendungsbeschreibung ausgeben.)
Ich habe schon einmal eine ähnliche Idee verwendet, die Einrichtung dauert eine Weile, kann aber sehr effektiv sein. In meinem Fall hat ein Quellbaum ungefähr 120 verschiedene Produkte erstellt.
quelle
In git würde ich es so machen, dass ich einen Hauptzweig mit dem gesamten gemeinsamen Code und Zweigen für jeden Client habe. Wenn eine Änderung am Kerncode vorgenommen wird, setzen Sie einfach alle clientspezifischen Zweige auf dem Master neu auf, sodass Sie eine Reihe von sich bewegenden Patches für die Clients haben, die auf der aktuellen Baseline angewendet werden.
Wann immer Sie eine Änderung für einen Client vornehmen und einen Fehler bemerken, der in anderen Zweigen enthalten sein sollte, können Sie diesen Fehler entweder in den Master-Zweig oder in den anderen Zweigen auswählen, die den Fix benötigen (obwohl verschiedene Client-Zweige Code gemeinsam nutzen) Sie sollten wahrscheinlich beide einen gemeinsamen Zweig vom Master abzweigen lassen.
quelle