Sub-Komponenten für Sources und deren Verwaltung

Sub-Komponenten für Sources und deren Verwaltung

Inhalt


Topic:.SubCmpn.

Last changed: 2019-10-01

Der Artikel beschreibt Gedanken zur Zusammenstellung einer Applikation aus Komponenten, betrifft Verzeichnisordnung der Sources.


1 Die einfache Herangehensweise

Topic:.SubCmpn..

Nehmen wir an, eine Applikation A bestehend aus Sources a1..an nutzt ein etabliertes System in einer bestimmten Version, nennen wir es Q, und läuft unter einem Betriebssystem, kurz W. Dann sieht die Ordnung der Sources wie folgt aus:

+-W auf einem bestimmten Platz im Filesystem, global auffindbar
+-Q auf einem anderen Platz im Filesystem
+-+
  +-A Applikation mit seinen Sources irgendwo verschiebbar lokalisiert.

Für A gibt es ein git-Archiv. Die Komponenten Q und W sind per Version und Install wohl definiert. Für Q gilt: Im Tool selbst, in Q enthalten, sind die passende Lib- und Inc-Pfade gesetzt, auch für den Zugriff auf W.


2 Aufteilung der Applikation

Topic:.SubCmpn..

Nehmen wir an, im Verlauf der Entwicklung teilen sich die Sources der gesamten Applikation auf, mehrere Bearbeiter, abtrennbare Subkomponenten, die entweder zum Schluss zu einer Gesamtapplikation zusammengefasst werden (statisch gelinkt, dll, eingebundene Sub-Komponenten) oder sogar unabhängige Teile eines Ganzen sind.

Mit Beibehaltung des Prinzips sieht das dann wie folgt aus.

+-W auf einem bestimmten Platz im Filesystem, global auffindbar
+-Q auf einem anderen Platz im Filesystem
+-+
  +-A Applikation mit seinen Sources irgendwo verschiebbar lokalisiert.
  |
  +-+-B Teilapplikation von anderem Bearbeiter, woanders lokalisiert
  |
  +-+-C Jede Teilapplikation hat ihren Workspace-File-Bereich,

Die Komponenten der Applikation sind möglicherweise auf verschiedenen PCs lokalisiert, da verschiedene Bearbeiter. Zum Zusammenfassen oder zur Kontrolle werden sie vom Maintainer auf seinem PC passend nebeneinander gestellt.

Es gibt damit mehrere git-Archive für A, B und C. Für den Datenaustausch müssen bestimmte Headerfiles identische bzw. zueinander passende Definitionen aufweisen. Das wird hier verdrängt, es wird Hand-abgestimmt. Möglicherweise werden für die Teilkomponenten der Applikation auch verschiedene Programmiersprachen verwendet, so dass sich von daher die Interfaces nur auf Word-Papier-Ebene abstimmen lassen.


3 Definition eines gemeinsamen Interfaces, Nutzung von gemeinsamen Subkomponenten

Topic:.SubCmpn..

Würde man jetzt gemeinsame Interface-Beschreibungsquellen I mit eigenem git-Archiv (Headerfiles in C/++ ) definieren, so sähe dies bei verteilten Komponenten etwa wie folgt aus:

+-W auf einem bestimmten Platz im Filesystem, global auffindbar
+-Q auf einem anderen Platz im Filesystem
+-+
  +-A Teilapplikation mit seinen Sources irgendwo verschiebbar lokalisiert.
  | +-I als Sub-git
  |
  +-+-B Teilapplikation von anderem Bearbeiter, woanders lokalisiert
  |   +-I als Sub-git
  |
  +-+-C Jede Teilapplikation hat ihren Workspace-File-Bereich,
      +-I als Sub-git

Im Verlauf der Entwicklung könnten sich dann noch gemeinsame Subkomponenten etablieren, nennen wir sie E und S. Diese gemeinsam als Quellen und/oder Libraries genutzten Komponenten sind aber nicht wie Q und W mit Universalcharakter betrachtbar. An diesen wird zwar seltener, aber auch gearbeitet wird. Dann ergibt sich in Fortsetzung des Geankens folgendes Bild:

+-W auf einem bestimmten Platz im Filesystem, global auffindbar
+-Q auf einem anderen Platz im Filesystem
+-+
  +-A Teilapplikation mit seinen Sources irgendwo verschiebbar lokalisiert.
  | +-I als Sub-git
  | +-E als Sub-git
  | +-S als Sub-git
  |
  +-+-B Teilapplikation von anderem Bearbeiter, woanders lokalisiert
  |   +-I als Sub-git
  |   +-S als Sub-git
  |
  +-+-C Jede Teilapplikation hat ihren Workspace-File-Bereich,
      +-I als Sub-git
      +-E als Sub-git

In dieser Konstellation gibt es ein Problem zu beachten: Die Interfacefiles I sollten eigentlich immer von allen Beteiligten in der gleichen Version verwendet werden. Erfahrungsgemäß wird aber auch hier in Details von den einzelnen Bearbeitern nachgebessert, so dass kleine Versionsunterschiede existieren könnten, die aber nicht auffällig sind. Bei einem gemeinsamen git zeigen sich diese in Seitenzweigen bzw. verschieden verwendeten Versionen. Die Abstimmung der Interfaces sollte aber jeweils mit einem Integrationstest erfolgen.

Anders bei den Komponenten E und S, die nicht notwendigerweise identisch sein müssen da es lediglich synergetisch gemeinsam verwendete Quellen ohne Interfacecharakter sind. Hier wird eine Tendenz bestehen, da ausgetestet und bekannt, in der einzelnen Komponente lieber auf dem bekannten Stand stehenzubleiben und eben nicht jeweils den neuesten Stand zu nutzen. Man wird nach einiger Zeit des Auseinanderdriftens dann eher Workarrounds in E und S schreiben als den Weg des Angleichs zu gehen. Das verkompliziert die Lage aber weiterhin. An einem Projektende wird man nicht den Detailteufel heraufbeschwören und jetzt auch noch angleichen.

Würde man in einem Integrationstest alle Komponenten in einem Workingtree zusammenfassen, und die Detailunterschiede in I, E und S übersehen, dann geht das gar nicht:

+-W auf einem bestimmten Platz im Filesystem, global auffindbar
+-Q auf einem anderen Platz im Filesystem
+-+
  +-+
    +-A Teilapplikation mit seinen Sources irgendwo verschiebbar lokalisiert.
    +-B Teilapplikation von anderem Bearbeiter, woanders lokalisiert
    +-C Alle Teilapplikationen zusammengeschoben in einem Workspace-File-Bereich,
    | +-I als Sub-git
    | +-E als Sub-git
    | +-S als Sub-git

Nunmehr würden die Subkomponenten auf die gleiche Version gezwungen, sie waren aber vormals ggf. nicht in der gleichen Version.

Folglich würde für die Integration getrennte Workspaces auf einem PC des Maintainer notwendig sein:

+-W auf einem bestimmten Platz im Filesystem, global auffindbar
+-Q auf einem anderen Platz im Filesystem
+-+
  +-+
    +-A Teilapplikation mit seinen Sources irgendwo verschiebbar lokalisiert.
    | +-I als Sub-git
    | +-E als Sub-git
    | +-S als Sub-git
    |
    +-B Teilapplikation von anderem Bearbeiter, woanders lokalisiert
    | +-I als Sub-git
    | +-S als Sub-git
    |
    +-C Jede Teilapplikation hat ihren Workspace-File-Bereich,
      +-I als Sub-git
      +-E als Sub-git

Wenn für die Integration nun im Interface nachgebessert wird, dann muss die Arbeit an mehreren Stellen synchronisiert werden. Dabei kann schonmal ein Handlungsfehler passieren, da man nicht bei jeder kleinen Änderung während Entwicklung und Test über git gehen will. Der Angleich der Komponenten E und S erfolgt hier ebenfalls nicht, es wird weiter mit verschiedenen Versionen und Workarrounds gearbeitet.


4 Subkomponenten parallel stellen

Topic:.SubCmpn..

Eine Verbesserung hinsichtlich Abgleich der Subkomponenten bringt die Parallelstellung:

+-W auf einem bestimmten Platz im Filesystem, global auffindbar
+-Q auf einem anderen Platz im Filesystem
+-+
  +-+
    +-A Teilapplikation mit seinen Sources irgendwo verschiebbar lokalisiert.
    +-B Teilapplikation von anderem Bearbeiter, woanders lokalisiert
    +-C Alle Teilapplikationen zusammengeschoben in einem Workspace-File-Bereich,
    +-I mit eigenem git
    +-E mit eigenem git
    +-S mit eigenem git

Dies ist der Baum im gemeinsamen Workingtree des Maintainers. Bei dem Einzelbearbeiter würde das ggf. wie folgt aussehen:

+-W auf einem bestimmten Platz im Filesystem, global auffindbar
+-Q auf einem anderen Platz im Filesystem
+-+
  +-+
    +-A Teilapplikation mit seinen Sources irgendwo verschiebbar lokalisiert.
    +-B Teilapplikation von anderem Bearbeiter, woanders lokalisiert
    +-I mit eigenem git
    +-E mit eigenem git
    +-S mit eigenem git
    +-TS Test für S
    +-TE Test für E
    +-TA Spezialtest für A zusätzlich

Die Komponente C wird gar nicht bearbeitet, fehlt also. Die Komponente B sei nur vorhanden, weil Sie beim Test mit läuft, wird aber selbst nicht bearbeitet. Zusätzlich sind hier aber bestimmte Testprojekte TS TE und auch TA vorhanden, die für die Integration keine Rolle spielen aber dem Komponententest dienen.

Werden in A Quellen aus E benötigt, dann ist der Path dorthin relativ mit ../ schreibbar. Dabei darf A beispielsweise tiefer angeordnet sein, aus anderen Gründen, so dass ../../.. zu schreiben ist. Die Anordnung von E relativ zu A muss festgelegt sein. Gleiches gilt für die anderen Subkomponenten, hier I und S.

Für die Bearbeitung von E mit TE spielt die Anordnung von A B C etc. keine Rolle. Es sollte die Dependency-Regel gelten: E ist eigenständig, A braucht E, S und I usw. Dies hilft für eigenständige Bearbeitung und Wiederverwendung in anderen Projekten, möglicherweise auch getrennter Verkauf oder Open-Source-Stellung einer Subkomponente.

Es fällt hier der Zwang (!) auf, sowohl beim Maintainer als auch beim Bearbeiter genau die gleiche Version für die Subkomponenten E und S verwenden zu müssen. Beim Bearbeiter ist das noch moderat, solange nur bestimmte Komponenten im Fokus sind, kann passend zur eigenen Bearbeitung auch an E und S geändert werden noch ohne Abstimmung; Die Abstimmung muss erst mit der Integration beim Maintainer erfolgen, dann aber gezwungenermaßen.

Die im Strukturbild enthaltenen Testprojekte sind eigentlich eigenständig, von einem anderen Bearbeiter:

+-W auf einem bestimmten Platz im Filesystem, global auffindbar
+-+
  +-+
    +-E mit eigenem git, Test-Workingdir
    +-TE Test für E (mit eigenem git)

Sie sind nur deshalb bei dem einen oder anderen Bearbeiter eben mit integriert, weil der Bearbeiter selbst die Tests durchführen will, sei es als Kontrolle oder auch nur, um über den Test bestimmte Funktionsweisen dokumentiert zu sehen. Letzteres ist die wichtige Intension.

Warum ist der Zwang zur Abstimmung der gemeinsamen Subkomponeten I, E und S wichtig? Bei I mag diese Frage als selbstverständlich beantwortet werden. Es handelt sich um das gemeinsame Interface. Wenn aus Sicht der Zusammenarbeit B und C ein Detail geändert wird, das für A unwesentlich ist, dann sollte aber nicht darauf verzichtet werden, die Änderung bei A ebenfalls zu etablieren. Wenn aufgrund verschiedener Einstellungen oder Entwicklergewohnheiten Dinge verschieden formuliert werden müssen (etwa ein #include eines Lieblingsheaders), dann darf dies nicht sein. Es bringt nicht wirklich Vorteile sondern stiftet Chaos. Bei Interfaces ist das unbedingt abzustimmen.

Warum aber auch bei Zulieferteilen abstimmen? Nehmen wir an, in E sind neue Features enthalten die über TE getestet sind, aber nicht bei A, B oder C benötigt werden. Dazu gekommen sind aber Korrekturen, die bei A notwendig, bei B wünschenswert und bei C nicht notwendig und sogar störend sind. Wird das so beibehalten, dann ist ein Versionssalat die Folge. Das betrifft im Beispiel auch S oder weitere Subkomponenten D, ... Wenn dann weitere Korrekturen hinzukommen, die bei C und B notwendig sind, C arbeitet aber notwendigerweise mit einer anderen Version, dann wird dieser Versionssalat von mal zu mal schlimmer. Wenn dann die Sub-Kompontenen für die Endzusammenstellung des Produktes als getestet deklariert werden sollen, dann ufert dies aus. Es sind verschiedene TE-Version 3..28 und weitere notwendig, abgesehen vom Testaufwand.

Es ist konsequenter und einfacher, bei einer Änderung in E aus der Intension A heraus dies in A lokal zu bearbeiten, danach oder zeitgleich im zugehörigen Testprojekt TExy zu testen, dies entsprechend zu versionieren und zu dokumentieren, danach für B und C zu etablieren und dort schauen, ob es nicht erwartete Nebeneffekte gibt. Gibt es diese, sind diese ebenfalls nachzubessern. Möglicherweise stellt sich die Bearbeitungsintension in E aus Sicht A auch als falsch heraus. Insgesamt ergibt sich damit eine wirklich verbesserte und synergiewirksame Komponente E, die auch in anderen Projekten wiederverwendbar ist. Man lernt beim schnellen Ändern einer gemeinsamen Subkomponente hinzu und wird die nächste Änderung erst nach Absprache und Abstimmung durchführen.


5 Zusammenfassendes Archiv für die parallel stehenden Subkomponenten

Topic:.SubCmpn..

Für jede zu bearbeitende Teilkompontente muss gelten: Einfaches Auschecken eines Archivs, und die Dinge müssen laufen. Wenn es mehr als ...4 Subkomponenten gibt, deren Version manuell nachgeschaut und abgestimmt werden muss, dann sind Detailfehler wieder nicht ausgeschlossen.

Betrachtet man das Workingdir einer zu bearbeitenden Kompontente:

+-W auf einem bestimmten Platz im Filesystem, global auffindbar
+-Q auf einem anderen Platz im Filesystem
+-+
  +-a   a ist Komponentenverwaltungs-Git-Archiv
    +-A Teilapplikation mit seinen Sources irgendwo verschiebbar lokalisiert.
    +-I mit eigenem git
    +-E mit eigenem git
    +-S mit eigenem git

dann ist mit dem git-Archiv a, das A, I, E und S als Subarchiv verwaltet, alles außer W und Q gegeben. Die Subkomponenten I E und S sind dort wie die Hauptkomponente A in der Version festgehalten.

Für den Integrationstest bzw. beim Maintainer sieht das wie folgt aus:

+-W auf einem bestimmten Platz im Filesystem, global auffindbar
+-Q auf einem anderen Platz im Filesystem
+-+
  +-a
    b
    c
    te
    ta
    +-A Teilapplikation mit seinen Sources irgendwo verschiebbar lokalisiert.
    +-I mit eigenem git
    +-E mit eigenem git
    +-S mit eigenem git
    +-I mit eigenem git
    +-E mit eigenem git
    +-S mit eigenem git
    +-TE Test für E
    +-TA Spezialtest für A zusätzlich

Der Maintainer muss, wenn alles in einem Workingdir zusammengefasst werden soll, selbstverständlich in a b c te ta kontrollieren, ob alle Subkomponenten die gleiche Version haben. Ist dies nicht der Fall dann muss in der einzelnen Bearbeitung nachgebessert werden, also upgedated bezüglich der Subkomponenten. Es lässt sich für einen Test auch beispielsweise b in einer vorigen Version auschecken, weil der Bearbeiter für b selbst schon weiter ist. Der Integrationstest erfolgt damit für b in der Vorversion (als Beispiel).

Die Git-Archive für die Zusammenfassung der Komponeten stehen auf einem entsprechenden Parent-Directory: Die einzelnen Subkomponenten sind in Sub-Dirs angeordnet. Dabei kann eine Tiefe von >1 aus anderen Systematisierungsgründen durchaus vorkommen. Die Tiefe sollte aber überschaubar sein und muss insbesondere in dem zusammenfassenden Git-Archiv so bezeichnet sein. Die Anordnung der Subkomponenenten im Filebaum ist also zusammenhängend abzustimmen.