it-swarm.dev

So verwalten Sie verschiedene, angepasste Versionen derselben Software für mehrere Clients

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 für jeden Kunden ein wenig anpassen müssen. Die Änderungen sind wahrscheinlich zu klein, um die Aufteilung des Moduls in ein eigenes (physisches) Modul für jeden Client zu rechtfertigen. Ich befürchte Probleme mit dem Build, ein Verknüpfungschaos. Diese Änderungen sind jedoch zu umfangreich und zu umfangreich, um sie durch Schalter in einer Konfigurationsdatei zu konfigurieren, da dies zu Problemen während der Bereitstellung und wahrscheinlich zu vielen Supportproblemen führen würde, insbesondere bei Administratoren vom Typ Bastler.

Ich möchte, dass das Build-System mehrere Builds erstellt, einen für jeden Client, wobei Änderungen in einer speziellen Version des 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?

48
Falcon

Was Sie brauchen, ist Feature-Verzweigung - wie Code-Organisation. In Ihrem speziellen Szenario sollte dies als Client-spezifische 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 neuer Features zusammenzuführen. Ich meine alle nicht kundenspezifischen Funktionen.

Dann haben Sie auch Ihre kundenspezifischen Zweige , in denen Sie auch die Zweige der gleichen Features nach Bedarf zusammenführen (Kirschernte, wenn Sie so wollen).

Diese Client-Zweige sehen Feature-Zweigen ähnlich, obwohl sie nicht vorübergehend oder kurzlebig sind. Sie bleiben über einen längeren Zeitraum erhalten und werden hauptsächlich nur zusammengeführt. Es sollte so wenig kundenspezifische Entwicklung von Feature-Zweigen wie möglich geben.

Client-spezifische Zweige sind parallele Zweige zum Trunk und sind so lange aktiv wie der Trunk selbst und werden nirgendwo zusammengeführt.

            feature1
            ———————————.
                        \
trunk                    \
================================================== · · ·
      \ client1            \
       `========================================== · · ·
        \ client2            \
         `======================================== · · ·
              \ client2-specific feature   /
               `——————————————————————————´
8
Robert Koritnik

Tun Sie dies nicht mit SCM-Zweigen. Machen Sie den allgemeinen Code zu einem separaten Projekt, das eine Bibliothek oder ein Projektskelett-Artefakt erzeugt. Jedes Kundenprojekt ist ein separates Projekt, das dann als Abhängigkeit vom gemeinsamen Projekt abhängt.

Dies ist am einfachsten, wenn Ihre gemeinsame Basis-App ein Abhängigkeitsinjektionsframework wie Spring verwendet, sodass Sie problemlos verschiedene Ersatzvarianten von Objekten in jedes Kundenprojekt einfügen können, wenn benutzerdefinierte Funktionen erforderlich sind. Selbst wenn Sie noch kein DI-Framework haben, kann es am wenigsten schmerzhaft sein, eines hinzuzufügen und auf diese Weise zu tun.

39
Alb

Speichern Sie Software für alle Kunden in einer einzigen Filiale. Es ist nicht erforderlich, die für verschiedene Kunden vorgenommenen Änderungen zu ändern. Am einfachsten ist es, wenn Sie möchten, dass Ihre Software für alle Clients von bester Qualität ist, und der Bugfix für Ihre Kerninfrastruktur sollte alle betreffen, ohne unnötigen Overhead beim Zusammenführen, was auch zu weiteren Fehlern führen kann.

Modularisieren Sie den gemeinsamen Code und implementieren Sie den Code, der sich in verschiedenen Dateien unterscheidet, oder schützen Sie ihn mit unterschiedlichen Definitionen. Stellen Sie sicher, dass Ihr Build-System spezifische Ziele für jeden Client hat, wobei jedes Ziel eine Version mit nur dem Code kompiliert, der sich auf einen Client bezieht. Wenn Ihr Code beispielsweise C ist, möchten Sie möglicherweise Funktionen für verschiedene Clients mit "#ifdef "oder welcher Mechanismus auch immer Ihre Sprache für das Build-Konfigurationsmanagement hat, und bereiten Sie eine Reihe von Definitionen vor, die der Anzahl der Funktionen entsprechen, für die ein Client bezahlt hat.

Wenn Sie ifdefs nicht mögen, verwenden Sie "Schnittstellen und Implementierungen", "Funktoren", "Objektdateien" oder andere Tools, die Ihre Sprache zum Speichern verschiedener Dinge an einem Ort bereitstellt.

Wenn Sie Quellen an Ihre Clients verteilen, sollten Sie dafür sorgen, dass Ihre Build-Skripte spezielle "Quellverteilungsziele" haben. Sobald Sie ein solches Ziel aufrufen, erstellt es eine spezielle Version von Quellen Ihrer Software, kopiert sie in einen separaten Ordner, damit Sie sie versenden können, und kompiliert sie nicht.

13
P Shved

Wie so viele gesagt haben: Faktorieren Sie Ihren Code richtig und passen Sie ihn basierend auf dem allgemeinen Code an - dies verbessert die Wartbarkeit erheblich. Unabhängig davon, ob Sie eine objektorientierte Sprache/ein objektorientiertes System verwenden oder nicht, ist dies möglich (obwohl dies in C etwas schwieriger ist als etwas, das wirklich objektorientiert ist). Dies ist genau die Art von Problem, die durch Vererbung und Kapselung gelöst werden kann!

8
Michael Trausch

Sehr vorsichtig

Feature Branching ist eine Option, aber ich finde es etwas schwer. Außerdem erleichtert es tiefgreifende Änderungen, die zu einem direkten Verzweigen Ihrer Anwendung führen können, wenn sie nicht unter Kontrolle gehalten werden. Idealerweise 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 machen würde, obwohl ich nicht weiß, ob es ohne große Modifikationen und Neufaktorisierungen auf Ihre Codebasis anwendbar ist. Ich hatte ein ähnliches Projekt, bei dem die Grundfunktionalität dieselbe war, aber jeder Kunde einen ganz bestimmten 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 seinen Standort zu erstellen. Gelegentlich platziere ich dort auch einige Komponenten, die speziell für diesen Kunden angefertigt wurden. Aber das ist selten und wann immer möglich versuche ich es in einer allgemeineren Form zu machen und es nach unten zu drücken, damit andere Projekte sie verwenden können.

Das Endergebnis ist, dass ich den Grad der Anpassung erhalten habe, den ich benötigt habe. Ich habe angepasste Installationsskripte erhalten, damit ich nicht so aussehe, als würde ich das System ständig tweeken, und als zusätzlichen SEHR bedeutenden Bonus bekomme ich um Regressionstests erstellen zu können, die direkt mit dem Build verknüpft sind. Auf diese Weise kann ich jedes Mal, wenn ich einen kundenspezifischen Fehler erhalte, einen Test schreiben, der das System bei der Bereitstellung bestätigt und somit auch auf dieser Ebene TDD ausführt.

also kurz gesagt:

  1. Stark modularisiertes System mit flacher Projektstruktur.
  2. Erstellen Sie ein Projekt für jedes Konfigurationsprofil (Kunde, obwohl mehrere ein Profil freigeben können)
  3. Stellen Sie die erforderlichen Funktionssätze als ein anderes Produkt zusammen und behandeln Sie es als solches.

Bei ordnungsgemäßer Ausführung sollte Ihre Produktbaugruppe alle bis auf einige Konfigurationsdateien enthalten.

Nachdem ich dies für eine Weile verwendet hatte, erstellte ich schließlich Metapakete, die die am häufigsten verwendeten oder wesentlichen Systeme als Kerneinheit zusammenstellen und dieses Metapaket für Kundenbaugruppen verwenden. Nach einigen Jahren hatte ich eine große Toolbox, die ich sehr schnell zusammenstellen konnte, um Kundenlösungen zu erstellen. Ich schaue gerade nach Spring Roo und sehe nach, ob ich die Idee nicht weiter vorantreiben kann, in der Hoffnung, dass ich eines Tages in unserem ersten Interview direkt mit dem Kunden einen ersten Entwurf des Systems erstellen kann ... I. Ich denke, man könnte es User Driven Development nennen ;-).

Hoffe das hat geholfen

5
Newtopian

Die Änderungen sind wahrscheinlich zu klein, um die Aufteilung des Moduls in ein eigenes (physisches) Modul für jeden Client zu rechtfertigen. Ich befürchte Probleme mit dem Build, ein Verknüpfungschaos.

IMO, sie können nicht zu klein sein. Wenn möglich, würde ich den kundenspezifischen Code fast überall anhand des Strategiemusters herausrechnen. Dies reduziert die Menge an Code, die verzweigt werden muss, und reduziert die Zusammenführung, die erforderlich ist, um den allgemeinen Code für alle Clients synchron zu halten. Dies 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 der Richtlinie X1 in Modul A die Verwendung der 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.

3
kevin cline

Wenn Sie in einfachem C schreiben, ist dies eine ziemlich hässliche Methode.

  • Gemeinsamer Code (zB Einheit "frangulator.c")

  • client-spezifischer Code, kleine Teile, die nur für jeden Client verwendet werden.

  • verwenden Sie im Code der Haupteinheit #ifdef und #include, um so etwas zu tun

 # Ifdef CLIENT = CLIENTA 
 # Include "frangulator_client_a.c" 
 # Endif 

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 kundenspezifische Dateien ganz einfach miteinander vergleichen.

Dies bedeutet auch, dass alle kundenspezifischen Teile jederzeit klar sichtbar sind (jeweils in einer eigenen Datei) und eine klare Beziehung zwischen der Hauptcodedatei und dem kundenspezifischen Teil der Datei besteht.

Wenn Sie wirklich schlau werden, können Sie Makefiles einrichten, um die richtige Client-Definition zu erstellen.

clienta machen

wird für client_a erstellt und "make clientb" wird für client_b erstellt und so weiter.

(und "make" ohne angegebenes Ziel kann eine Warnung oder eine Verwendungsbeschreibung ausgeben.)

Ich habe bereits 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.

1
quickly_now

Sie können Ihr SCM verwenden, um Zweige zu pflegen. Halten Sie den Hauptzweig makellos vom benutzerdefinierten Clientcode. Machen Sie die Hauptentwicklung in diesem Bereich. Pflegen Sie für jede angepasste Version der Anwendung separate Zweige. Jedes gute SCM-Tool eignet sich sehr gut zum Zusammenführen von Zweigen (Git fällt mir ein). Alle Aktualisierungen im Hauptzweig sollten in den benutzerdefinierten Zweigen zusammengeführt werden, aber der kundenspezifische Code kann in seinem eigenen Zweig verbleiben.


Versuchen Sie jedoch, wenn möglich, das System modular und konfigurierbar zu gestalten. Der Nachteil dieser benutzerdefinierten Zweige kann sein, dass sie sich zu weit vom Kern/Master entfernen.

1
Htbaa

In git würde ich es so machen, einen Hauptzweig mit dem gesamten gemeinsamen Code und Zweigen für jeden Client zu haben. Wenn eine Änderung am Kerncode vorgenommen wird, müssen nur alle clientspezifischen Zweige über dem Master neu aufgebaut werden, sodass Sie eine Reihe von verschiebbaren Patches für die Clients haben, die über 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 entweder in den Master oder in die anderen Zweige auswählen, die das Update benötigen (obwohl verschiedene Client-Zweige Code gemeinsam nutzen sollten Sie wahrscheinlich beide von einem gemeinsamen Zweig vom Master abzweigen lassen).

0
Cercerilla