Facebook
Twitter
Google+
Kommentare
25

Horizontales und vertikales Ausrollen

Heute habe ich die Ehre mal wieder einen Gastautor zu begrüßen: MICH. Irgendwie hatte ich das Glück, dass so viele Artikel in den letzten Tagen von anderen Autoren verfasst wurden, dass es schon komisch für mich ist, wieder mal was zu verfassen. Zumindest so ein bisschen. Ich versuch mich also mal wieder im Bloggen, hat ja schon 500 mal geklappt, warum heute denn nicht wieder. Aber jetzt genug gestapelt, los zum Artikel, der eigentlich mehr ein Erfahrungsbericht ist.

Man kommt des Öfteren in die Situation mehrere Projekte, die relativ ähnlich gebaut sind parallel zu supporten. Meistens sind diese leider nur ähnlich und nicht identisch, man hat also nicht die Möglichkeit Änderungen 1:1 auszutauschen. Ist aber auch nicht schlimm, denn wir sind ja schon groß und können mit Herausforderungen umgehen. Gehen wir also mal von auch, wir haben vier sehr ähnliche Applikationen. Eine davon hat so viel Traffic, dass man als nächstes eine neue Caching-Schicht einführen sollte. Die anderen drei Seiten benötigen das eigentlich nicht, auch wenn die Seiten damit schneller laufen würden, aber konkrete Beschwerden kamen bis jetzt nicht auf.

Was ich mit horizontalen Ausrollen bezeichne, ist folgendes: Ich programmiere eine Caching-Schicht für die erste Seite und versuche dann möglichst zeitnahe alle vier Seiten darauf zu heben. Vertikal wäre es einfach, wenn man es nur für die erste Seite umsetzt und es dann auch dabei belässt. Argumente für die erste Variante liegen nahe, ich bin im Thema drinnen und die anderen Seiten profitieren ja auch von der Erweiterung. Der Umbau der übrigens drei Seiten geht also um einiges schneller wenn ich es sofort mache, als wenn ich mich später wieder reindenken muss. Auf dem Papier klingt das alles sehr stimmig.

Jetzt kommt die Erfahrung. Genau wir beim Programmieren gibt es hier das YAGNI-Prinzip (You ain’t gonna need it). Wenn die anderen Seiten es im Moment nicht brauchen, dann muss man da auch nichts machen. Man bringt nur neue Komplexität und eine neue Schicht in das Projekt. Ich nenne solche Änderungen gerne Projekt-42, man hat die Antwort auf eine Frage, die man nie gestellt hat. Besonders schlimm wird es wenn man den eigentlichen Projektmitarbeitern von außen auferlegt, dass sie doch jetzt bitte das neue Caching aufsetzen sollen. Diese Änderungen werden, sobald sie irgendwelche Probleme machen, immer als besonders nervig angesehen und als Auftraggeber hat man immer die A-Karte.

Aus meiner Erfahrung heraus würde ich sagen, dass man vertikales Ausrollen bevorzugen sollte. Wenn eine der anderen Seiten danach irgendwann auch mal in Performance-Probleme kommen sollte, dann kann man immer noch das gelernte und bereits entwickelte einsetzen. Wichtig ist dabei nur, dass man auf das existierende aufsetzt und nicht auf die Idee kommt es jetzt ganz anders zu machen. Auch wenn man jetzt eine bessere Idee kommt, dann immer so bauen, dass auch die erste Seite profitieren kann.

Über den Autor

Nils Langner

Nils Langner ist der Gründer von "the web hates me" und auch der Hauptautor. Im wahren Leben leitet er das Qualitätsmanagementteam im Gruner+Jahr-Digitalbereich und ist somit für Seiten wie stern.de, eltern.de und gala.de aus Qualitätssicht verantwortlich. Nils schreibt seit den Anfängen von phphatesme, welches er ebenfalls gegründet hat, nicht nur für diverse Blogs, sondern auch für Fachmagazine, wie das PHP Magazin, die t3n, die c't oder die iX. Nebenbei ist er noch ein gern gesehener Sprecher auf Konferenzen. Herr Langner schreibt die Texte über sich gerne in der dritten Form.
Kommentare

Ein Kommentar

  1. Ich glaube auch, dass das vertikale ausrollen die bessere Variante ist. Wie gesagt, mach nur das, was du gerade brauchst.

    Der letzte Abschnitt stimmt mich nachdenklich. Ich denke in der Praxis ist es häufig ein Problem zwischen verschiedenen Produkten bzw. Teams einen konsens zu schaffen. Häufig wird dann alles doppelt entwickelt, wenn die 2 Teams oder Projekte nicht zeitgleich etwas umsetzen.

    Hast Du da eine Idee, wie man das lösen könnte?

    Reply
  2. Ich sehe das etwas anders.

    Man könnte es doch erstmal für Projekt 1 erstellen und einpflegen. Danach kriegt man das Feedback zurück, ob es wie gewünscht funtkioniert, ob es ev. Bugs oder neue Vorschläge gibt. Nachdem man dass dann aktualisiert hat, KANN (MUSS MAN NICHT!!) es auch in die Systeme einspielen, sobald mal etwas Zeit da ist.

    Reply
  3. Wie heißt es so schön: Der Teufel steckt im Detail.

    Ähnliche Projekte sind halt nur, ehm, ähnlich? Habe ich ein Feature welches in P 1 wunderbar funktioniert, und benötigt wird, super. Will ich das Gleiche in ähnlichem P 2 integrieren, obwohl es vielleicht gerade (noch) nicht benötigt wird, so werde ich mit 50% Wahrscheinlichkeit auf die Nase fallen. Wobei auf die Nase fallen, vielleicht nicht so schlimm sein mag, wie es sich anhört. Ist so meine Erfahrung. Wenn ich allerdings sowieso auf Wiederverwertbarkeit achte, hält sich der Aufwand der Implementierung in ähnlichem Projekt allerdings in Grenzen.

    Reply
  4. Angenommen, die 4 Seiten basieren auf der gleichen Codebasis, die nur um individuelle Anpassungen erweitert wurde, dann würde eine Änderung in der Codebasis mE ein Deployment aller 4 Seiten erfordern. Gerne gestaffelt, aber sicher nicht vertikal.
    Meine Begründung ist hier die Wartbarkeit. Wenn man immer wieder solche Änderungen durchführt wird man in absehbarer Zeit 4 eigene Systeme haben, mit komplett eigenen Features und dann weiß irgendwann keiner mehr bei welchem System was wie funktioniert. Auch wird zu diesem Zeitpunkt eine Verbesserung in der Basis kein einfaches Deployment mehr erlauben und Verbesserungen oder Refactorings müssen 4fach durchgeführt werden.

    Reply
  5. Naja die Zeit „plant“ man halt ein.
    Sagt bloss, bei euch sind 100% eurer Arbeitszeit verplant?!
    Falls ja, ist das IMO nicht wirklich optimal und dann gerät man ja erst unter Druck wenn etwas unvorhergesehenes passiert.. (Wobei ich hier vermutlich nicht mitreden darf, da ich seit Jahren keinen Bürojob mehr hatte)

    Für mich macht es einen Unterschied, ob ich es in den nächsten 3 Wochen „geplant“ implementiere, oder aber der Kunde (bzw das Projekt da es nicht mehr optimal rennt und man schnellstmöglich die Cacheschicht braucht..) mir dann einen „unnötigen“ Zeitdruck aufsetzt.

    Wie man hier rausliest, kommt es natürlich auch auf die Priorität an, aber wieso soll man sich das Leben unnötig schwer machen und 3 Projekte auf „ähnlicher“ Codebasis haben (vermutlich alle mit dem gleichen/ähnlichen Framework sogar) aber unterschiedlichen Features.
    Kommt es da nicht oft zu Verwechslungen? (zB das man sich einbildet, dass man die Cacheschicht ja sowieso schon implementiert hat…???)

    Wenn ich schon ein Cachesystem erstelle und es mit Projekt1 erfolgreich getestet habe, ein paar Verbesserungen implementiert wurden und ich nun Projekt1 aktualisiere, kann ich mir doch gleich die Zeit nehmen und ein „roll out“ für alle Projekte durchführen.

    Reply
  6. Das Problem ist doch, die Arbeit an Kundenprojekt 2 bis 4 bezahlt einem von denen keiner. Denn sie brauchen es nicht. Deshalb wird denke ich im Projektgeschäft selten überhaupt auf horizontales Ausrollen gesetzt.

    Anders ist die Situation wenn man selber X-Portale auf ähnlicher Basis betreibt. Dann lohnt sich das rüberziehen sicher. Man kann dann ja auch die neuen Features im Dev-Branch belassen (sofern man einen hat). Denn nach 6 Monaten ein größeres Feature rüberzuziehen ist verdammt aufwendig und fehleranfällig und wehe wenn der entsprechende Mitarbeiter nicht mehr in der Firma ist 😉

    Reply
  7. Das Problem ist, wenn der Featurestand der Systeme 1-4 auseinander läuft, steigen die Support- und Wartungskosten, sowie der Aufwand, falls dann doch einmal alle Systeme angehoben werden müssen schnell ins Unermessliche.

    Das kann die Entwicklung eines Gesamtproduktes, welches allen Kundenprojekten zugrunde liegt, auf Dauer immer langsamer machen, bis sie so unrentabel wird, dass das Projekt gescheitert ist und komplett neu geschrieben werden muss.

    Vor dieser Situation stehen einige Systeme bei uns gerade. Diese Kosten zahlt dann erst recht kein Kunde.

    Deswegen habe ich vor Jahren genau für dieses Problem eine Pluginschnittstelle geschrieben.

    Write once – use many. 1x ein gut gekapseltes Plugin schreiben. 1x ohne Änderung auf 10 Systemen ausrollen (Checkout via SVN). Kunden informieren: „Ihr könnt jetzt auch Foo ,kostet Bar“.
    Über eine Konfigurationsoberfläche lassen diese sich ein- und ausschalten.

    Damit bleiben alle Systeme dauerhaft gleich und unterscheiden sich nur noch in der Konfiguration und Anzahl der installierten Plugins.
    3 Kunden zahlen für 10 Systeme: aber wir haben trotzdem nur 1x Wartungs- und Supportkosten (nicht 10x plus Overhead).

    Reply
  8. Im Idealfall sollte es doch so sein, das man zu jedem Projekt ein Testsystem hat. Weiterhin sollte nach Möglichkeit alles auf einer gemeinsamen Basis aufbauen, wie Norbert Bartels schon sagte. Wenn man dann weiterhin die gemeinsame Basis in einem Versionierungssystem hat und dieses je Projekt via Exportscript entsprechend auschecken kann, so sollte es relativ einfach sein, die gemeinsame Basis bei allen Projekten auf einen annähernd gleichen Stand zu halten (zumindest jeweils nach einem solchen Export je System). Die individuellen Teile je Projekt sollten davon unberührt bleiben und die gemeinsame Basis via definierter Schnittstelle ansprechen, die auch nach einem Update den gleichen Dienst tun sollte (was man nach Möglichkeit mit Unittests sicherstellen sollte). Ob dies so ist, sollte man in dem erwähnten Testsystem sicherstellen und dies dann in das Livesystem übnernehmen.
    Bietet die Anpassung an der Basis neue Funktionen (z.B. Caching) kann man dann je Projekt entscheiden, ob man diese neue Funktion in den projektspezifischen Erweiterungen nutzt oder nicht. Somit hätte man einen Mix aus beidem, die Basis wird auf dem neusten Stand gehalten und die projektspezifischen Erweiterungen können nachgezogen werden, wenn es notwendig wird.
    Dieses Schema setzen wir seit Beginn des Jahres bei neuen Projekten um (wobei die Basis leider erst teilweise mit Unittests ausgestattet ist). Das Problem, das wir vorher hatten (die Basis wird permanent weiterentwickelt und die neuste Version bei jedem neuen Projekt eingesetzt), hat dazu geführt, dass jedes Projekt eine andere Version der Basis hatte und wir bei älteren Projekten häufig auf neue Funktionen der Basis verzichten mussten, da diese nicht immer einfach upzudaten war.
    Die Erfahrung bisher zeigt, dass, auch wenn es noch in den Kinderschuhen steckt, jedes Projekt sehr einfach auf dem neuesten Stand gehalten werden kann und somit jedes Projekt immer auf die neuesten Funktionen zurückgreifen kann. Ob diese neuen Funktionen eingesetzt werden, kann man dann je Projekt entscheiden, wenn es nötig wird, wie z.B. ob man das Caching schon einsetzen möchte oder nicht (in der Basis sind die Funktionen dafür auf jeden Fall schonmal vorhanden).

    Reply
  9. Ich bin der Meinung, dass man das individuell entscheiden muss. Bei einem sehr großen Projekt habe ich eine sehr flexible Struktur – alles basiert auf Interfaces, sauberer Trennung verschiedener Systeme. Die Anwendung ist für ein Projekt gedacht, allerdings möchte ich sie auch in anderen Projekten einsetzen, die etwas kleiner sind. Bei diesen kleinen Projekten bräuchte ich aber beispielsweise keine i18n oder Benutzergruppen / -rechte, aber das kann ich vernachlässigen, da es nicht stört. Andere Dinge könnte ich auch einfach abkoppeln. Aber dadurch, dass ich die selbe Anwendung sozusagen mehrfach verwende und stets parallel Änderungen durchführe, ist es möglich, dass ich in allen Projekten auf dem selben Stand bin und keine Probleme mit Einarbeitungszeiten habe.
    Das funktioniert aber nur für ähnlich skalierbare Projekte .. alles andere wäre vermutlich überflüssig. Ich sehe den Vorteil einer parallelen Wartung darin, dass sich diese Anwendungen oft nur in ihrer Konfiguration und ihrem Datenbestand und Aussehen unterscheiden – im Kern sind sie aber ein und das selbe Projekt. Nachteil ist eben, dass man oft unnötige Dinge im System hat, die zu Lasten der Performance gehen (für mich nicht mehr sehr relevant) oder umständlichere Umsetzungen neuer Features erfordern.

    my 2 cents

    Reply
  10. Ich sehe es auch so, dass man das nur in em Projekt ausrollen sollte für das man das Feature geplant hat.

    Weil erstmal klingt es toll, hey alle Seiten verwenden Caching, alle werden schneller. Wenn es aber bisher keine Probleme gab freut sich sicher auch niemand wirklich dolle weil es keinen großen Unterschied gibt. Im Gegenteil wenn es Probleme gibt sind alle Kunden sauer, die Mitarbeiter genervt und langfristig hat man mehr Komplexität in den anderen Projekten, weil zum Beispiel Daten aus dem Cache kommen und nicht aktuell sind und man wieder Umwege fürs Debugging baut… Am Ende viel Zeit und Nerven für ein Feature was keiner brauchte.

    Leider ist das manchmal an höhere Etagen schwer zu kommunizieren, dass solche „Schattenanforderungen“ („das könnte man mal brauchen“) immer gefährlich sind. Vor allem wenn man es dann mal braucht sind die Anforderungen wieder anders als man es dachte und baut doch nochmal dran rum…

    Reply
  11. Ich glaube nicht, dass die Systeme auseinanderlaufen, nur weil System A eine Caching-Schicht drinnen hat und System B nicht. Und warum sollte es den Wartungsaufwand erhöhen? Das Grundsystem bleibt doch das gleiche und wenn B das Caching braucht, dann bekommt es das von A.
    Dem Satz „write once, use many“ würden wir damit auch nicht widersprechen.

    Reply
  12. Ich stimme Alex in diesem Punkt voll zu:
    „Das Problem ist doch, die Arbeit an Kundenprojekt 2 bis 4 bezahlt einem von denen keiner.“

    Mein Gedankengang kommt Sven schon sehr nahe..
    # Die denkweise der Abteilungsleitung und höher…
    1 Denken sie wie ein Entwickler und erkennen dort einen Sinn drin
    2 Denken sie mehr nach dem Motto was ich sehen kann hat auch eine Bedeutsamkeit.

    Ist 2 der Fall: Dann kommt kein Verständnis auf und es gibt sogar noch Stress.
    Man beschäftigt sich mit einer Aufgabe, wo für sie diese bereits erledigt ist…
    (Produkt 2-4 wäre für sie wieder ein neues Projekt, somit wäre der Zeitaufwand für sie verständlich)

    Auf dem Papier ist alles immer nur Zeit/Geld.
    Da für sie das Projekt ja bereits abgeschlossen ist, ist ein unnötiger Kostenfaktor entstanden und somit erkennen sie dort keinen Sinn drin…

    Wird aus Produkt 2-4 ein Projekt (was nicht der Fall sein muss) ist man später ja sogar zufrieden, da es relativ fix erledigt ist.

    Es gibt halt Fälle wo die eigene Denkweise logischer ist, aber nicht positive Auswirkungen hat…

    Hier hatte www also die Bedeutsamkeit: Was, Wer und Wie…
    Was muss man erledigen.
    Wer gibt mir diese Aufgabe.
    Wie ist seine denkweise.

    Reply
  13. Vertikales Ausrollen.
    Warum?
    Ich hatte hier noch nirgends den kaufmännischen Aspekt gelesen: Wer bezahlt denn die zusätzliche Arbeit für das horizontale Ausrollen für „Projekt-42“ (der Ausdruck gefällt mir). Selbst bei internen Projekten fällt dadurch Mehraufwand an, der sich irgendwie rentieren muss.

    Wenn man jetzt ganz auf business denkt, könnte man sogar sagen:

    Horizontales Ausrollen? Klar! Wenn es der Kunde bezahlt. Ah, kann man wieder verkaufen. Kunde – du brauchst das! Ausrollen und Rechnung stellen….

    😉

    Reply
  14. @Nils Du denkst da nicht weit genug. Das Caching wird am Ende durch X verschiedene Entwickler eingebaut. Aber natürlich macht es jeder ein bisschen anders und fummelt nochmal im Quelltext herum. Zusätzlich hat es auch Abhängigkeiten. Und es bleibt nicht beim Caching. Heute dieses Modul, morgen jenes Feature – immer schön kopiert und dann an den Kunden angepasst.

    Am Ende hat Kunde A das Caching in Version 1, Kunde B in Version 1.1 mit kundenspezifischen Änderungen die inkompatibel sind zu A. Kunde C hat noch 0.8. Kunde D hat ein anderes, inkompatibles Caching, dass ein anderer Entwickler geschrieben hat.
    Für Kunde D kann das Caching gar nicht installiert werden, weil dies die Bibliothek A in Version X benötigt, diese aber nicht installiert werden kann, da der Kunde eine Bibliothek B in Version Y installiert hat, die sich just mit der erforderlichen Bibliothek A nicht verträgt.

    Jetzt haben wir pro System aber noch bis zu 20 Subsysteme pro Kunde, die auch unterschiedlich sind. Macht über den Daumen gepeilt etwa 50 Systeme.

    Stell dir vor, du hast 50 Systeme auf denen eigentlich das gleiche Produkt läuft, aber innerhalb des Produktes jeder Kleinkram, bis hin zum Caching, unterschiedlich ist. Und genau dann bemerkt Kunde A einen Fehler in seinem Produkt.
    Da kann man nicht mehr mal schnell einen Bug fixen. Stattdessen wird der Bug nur für Kunde A gefixt und bei den Kunden B und C bleibt der Bug drin, solange bis die sich auch darüber beschweren. Wenn das passiert wird nicht etwa ein Update installiert, sondern der Bug wird in den inzwischen unterschiedlichen Versionen der Caching-Schicht für jeden Kunden nochmal gefixt, mit identischem Entwicklungs- und Wartungsaufwand.

    Ergebnis: Enwicklungsabteilung überlastet, Weiterentwicklung wird immer langsamer, Supportabteilung ist sauer, Kunde ist sauer und in der Kasse herrscht Ebbe, weil die Kosten für den Fix keiner der Kunden bezahlt. Bugs gehen immer zu Lasten des Herstellers.

    Hätte man dagegen alle Systeme von Anfang an auf einem einheitlichen Stand gehalten, dann würde der Fehler bei den Kunden B und C gar nicht erst auftreten nachdem man ihn für Kunde A behoben hat, weil B und C die gleiche, korrigierte Version wie A einsetzen. Das mag am Anfang die Entwicklungskosten um 20-30% erhöhen, es spart aber schon bei 3 Kunden 40-66% wieder ein.

    Wir haben aber nicht 3 Systeme, sondern 50. You get the picture.

    Momentan beschäftigen wir 2/3 der Entwickler mit der Wartung der Altsysteme, Tendenz steigend. In der Mehrzahl sind das Kosten auf denen sitzen bleiben. Jegliche Neuentwicklung wird stark verlangsamt und in Summe deutlich teurer. Das wird auf Dauer existenzbedrohend.

    Hätten wir das von Anfang an anders gemacht, müssten wir nur 10% der Leute für die Pflege einsetzen, würde schneller neue Produkte entwickeln, die der Kunde auch bezahlt. Dann gäbe es entspanntes Arbeiten und Jahresendprämie statt verstrichene Auslieferungstermine, Überstunden und Urlaubssperre.

    Reply
  15. @Tom: Meine Aussage ist, wenn ein System kein Caching braucht, dann baue es nicht ein! Damit reduziere ich die Komplexität und den Wartungsaufwand. Wenn ich Caching benötige, dann nehme eine standardisierte Komponente dazu.

    Die Probleme, die du beschreibst sind anderer Natur und sind natürlich auch so richtig, wie du es sagst, haben aber nur unmittelbar mit der Aussage zu tun.

    Reply
  16. Ich habe den Eindruck ihr vermischt Probleme auf mehreren Ebenen – Framework und Applikation 🙂

    Low-Level: Framework
    Wir haben vor einigen Jahren kurzzeitig versucht das horizontale Ausrollen auf unser Framework anzuwenden und bei neuen Releases die Kundenprojekte die auf dem Framework basieren upzugraden. Defacto hat das hinten und vorn nicht funktioniert sobald es größere Änderungen am Framework gab. Das Integrieren und Testen war extrem zeitaufwändig und gezahlt hat das verständlicherweise kein Kunde – es gibt ja keinen direkten Mehrwert. Ein echtes Problem war der Verzicht auf das Synchronisieren der Framework-Version bislang nicht. Klar Bugfixes müssen über verschd. Frameworkversionen nachgezogen werden, das hält sich bislang aber sehr in Grenzen. Es ist einfacher 3 Frameworkversionen zu prüfen/bugfixen als 10 Projekte nachzuziehen und intensiv zu testen.

    High-Level: Applikation
    Tom`s Standpunkt ist klar, korrekt und nachvollziehbar. Tom bezieht sich das Ausrollen auf eine komplette Software die bei Kunden in verschiedenen Konfigurationen vorliegt. Es gibt die unterschiedlichsten Techniken um eine konfigurierbare Software zu erreichen (DI, Plugin-Konzept, Software-Generatoren,…). Für den Fall kann ich das Paradigma der generativen Programmierung[1] empfehlen da es genau für den Fall ausgelegt wurde. Kernthema des ganzen ist die Entwicklung eines einzigen Software-Generators der basierend auf einer Konfiguration (DSL = domänenspezifische Sprache) Software in verschd. Varianten generiert. Im Gegensatz zu plugin-basierten Systemen entfällt der Overhead zur Laufzeit um zu Prüfen ob Plugins oder Module existent sind oder nicht. Zudem gibt es quasi nur eine Quelle, den Software-Generator. Also gibt es auch nur eine Implementierung des Cachings, aber eventuell mit minimalen Anpassungen (basierend auf der Kundenkonfiguration).

    [1] http://generative-programming.org/

    Reply
  17. @Nils: Im Grunde ist die generative Programmierung genau das was du willst. Wenn dein Kunde/Projekt kein Caching benötigt generiert der Software-Generator die Klassen nicht in deine Ziel-Anwendung. Die Anwendung ist zu jederzeit auf den Anwendungszweck hin optimiert.

    Reply
  18. @Stephan Danke, genau das was ich sagen wollte 🙂

    Beide Konzepte kann man übrigens auch mischen: Statischer Generator + Live-Generierung. Das Problem das Scannens geänderter Konfigurationen hatte ich dabei auch. In der Praxis ließ sich das beheben, indem nur noch on-demand die Konfiguration aktualisiert wird.

    Zum Verständnis: Wir arbeiten mit Software Product Families und unterscheiden zwischen Systemkern, Produktlinien und Kundenprodukt. Das Ergebnis ist eine Anwendungssuite.

    Jedes Produkt besteht aus Kern + Produkt 1..n + kundenspez. Features 1..m. Wobei Features den Quellcode der Produkte nicht verändern dürfen.
    Im Grunde also hierarchische Schichten.
    Die Komponenten sind lose gekoppelt und kommunizieren nur über einen Proxy.

    Weil das so ist haben wir praktisch keine Abhängigkeiten und können Änderungen am Kernsystem und den Produkten fast ohne Risiko ausrollen. Systemkern + gewünschte Produkte linken wir über SVN-Externals in die Kundenprojekte. Danach kann der Admin über die Konfigurationsoberfläche pro Kundensystem entscheiden, wann und ob er ein Produkt mit welchen Features aktiviert.
    Anpassungen müssen am Code wir dazu in aller Regel nicht vornehmen. Falls doch ändern wir nicht den bestehenden Quellcode, sondern schreiben ein Feature für das Basisprodukt.

    Kann man im Grunde genommen mit Eclipse oder meinetwegen Mozilla vergleichen. Da gibt es auch eine Plattform, Produkte (wie Eclipse IDE oder Firefox), die wiederum durch Features/AddOns erweitert/geändert werden.

    Kein Mensch käme auf die Idee, Eclipse im Kern eine neue Schicht zu verpassen, diese aber nur zusammen mit der Java-IDE auszuliefern und mit PDT nicht. Auch sollte ein Plugin, dass mit der Java-IDE problemlos läuft unter PDT nicht erst laufen, nachdem man den Quellcode angepasst hat.
    Aus dieser Überlegung heraus macht Nils‘ Vorschlag für mich folglich auch gar keinen Sinn.

    An Third-Party-Libraries und das Patchen in Systeme ohne gemeinsamen Kern (White-Box-Implementierung) hatte ich dabei gar nicht gedacht. Dort sind Nils‘ Ausführungen natürlich wieder zutreffen.

    Reply
  19. @Tom: Interessanter Ansatz. Hätte nicht gedacht dass ähnliche Techniken heutzutage überhaupt eingesetzt werden. Wäre es nicht effizienter wenn ihr statt des hierarchischen Ansatzes den Quellcode komplett aus einer Source heraus generieren würdet?

    So ganz trifft deine Beschreibung nicht den Inhalt den ich meinte. In unserem Fall bekommt der Kunde ein final konfiguriertes System vorgesetzt welches nur die Komponenten (Dateien, Klassen, Methoden, …) enthält die er tatsächlich für seine Konfiguration benötigt. Und wir können problemlos aufgrund einer bestimmten Konfiguration Abläufe im Basisframework bzw. Applikationsframework anpassen wenn wir wollen – ob das sinnvoll ist sei einmal dahingestellt… 🙂

    Reply
  20. @Stephan Wenn ich dich recht verstanden habe, sehe ich bei deinem Ansatz für uns ein Problem: wenn wir das im Deployment komplett generieren, müssen wir stets neu generieren und komplett neu ausrollen falls sich etwas ändert.
    Da fällt es schwer, nur einen Teil auszurollen.

    Ich habe dich jetzt so verstanden, dass ihr quasi statt Dinge wie eine Abhängigkeit auszuprogrammieren und zum Beispiel eine Dependency Injection im Code vorzusehen, den Quellcode inklusive Abhängigkeiten generiert.

    Eine bloße Rekonfiguration reicht bei uns zur Zeit nicht aus. Unsere Kunden sind Paragraphenreiter und geben sogar Zugriffsrechte nach den Werten bestimmter Spalten einzelner Tabellen. Auch sieht ein Großteil der Tabellen pro Kunde völlig anders aus, weil jeder Kunde andere Geschäftsprozesse und somit andere Daten hat.

    Es gibt sogar Kunden, da hat innerhalb eines Produktes jede Abteilung andere Prozesse, andere Tabellen, andere Ausgangsdaten. Die weigern sich sogar die Teile des gleichen Objektes zu pflegen, welche von anderen Abteilungen vorgeschlagen wurden. Selbst wenn dort eigentlich harte Constraints festgeschrieben sind.

    Außerdem weiß der Kunde in der Regel nicht, was er eigentlich will. Da kommt nach der Auslieferung ständig noch jemand und will etwas geändert haben. Die Kollegen sind jetzt in einem Projekt schon das 4. oder 5. Mal bei der Nachmigration von Daten und Einfügen/Löschen/Verschieben von Tabellenspalten, welche der Kunde vorher vergessen, oder aber es sich anders überlegt hat.

    Das heißt wir benötigen ein Change-Management und die Möglichkeit zum inkrementellen Ausrollen von Updates auf dem Zielsystem. Der Quellcode muss hochflexibel sind und wir müssen in der Lage sein Ressourcen wie zum Beispiel Spalten in Tabellen live im System zu ändern, ohne dass uns die Applikation um die Ohren fliegt.

    Reply
  21. @Tom: Teilweise ausrollen ist kein Problem. Wir kennen die Konfiguration der Kunden und wissen welche Quellcode-Bruchstücke den einzelnen Merkmalen entsprechen. So lässt sich bei Updates genau vorhersagen welche Kunden von Updates betroffen sind. Klar, für jeden Kunden muss dann separat ein Generierungsvorgang durchlaufen.

    Im Grunde machen wir beides. Dependency Injection ist bei uns seit 2006 ein sehr großes Thema. Dennoch lässt sich durch den Software-Generator das Zusammenstöpseln automatisiert erledigen bzw. manche Variabilitäten lassen sich einfacher in einer weiteren Abstraktionsschicht lösen. Features ziehen sich ja in der Regel von der UI über die Business-Logik bis hin zur DB. D.h. ein einfaches Zusammenstöpseln von Dependencies genügt nicht.

    Ich kann nur grob erahnen was ihr durchmacht würde aber vermuten dass Migrationsarbeiten rein technisch mit Hilfe des Generators kein Problem sein sollten da du weißt woher du kommst und wohin du willst. Das Diff für die DB und den Code sollte dementsprechend relativ einfach ermittelbar sein.

    Klingt alles sehr spannend und ich frage mich wie ihr sicherstellt dass die Applikation das tut was sie soll wenn alles so hochdynamisch abläuft 🙂

    Reply
  22. @Stephan Eigentlich stellen wir es dadurch sicher, dass wir sehr stark entkoppeln. Ich versuche den Leuten abzugewöhnen komplexe Anwendungen aus einem Guss zu bauen. Ein Feature erfüllt immer nur 1 Zweck und 1 Produkt hat viele Features.

    Soll heißen, wenn ein Feature zum Beispiel einen Blogeintrag speichert, dann darf es nicht auch noch eine E-Mail an alle Abonnenten verschicken. Letzteres muss in einem anderen Feature erledigt werden.
    Der Quellcode wird entsprechend schlank. Selten mehr als 100 Zeilen um einen spezifischen Zweck zu erfüllen. Meistens reichen sogar 20: 12 davon sind Kommentare 😉

    Im Endeffekt haben wir damit sehr gut im Griff, dass das Programm tut was es soll: einfach weil es per se kleiner ist.

    Klingt auf jeden Fall auch spannend wie ihr das umsetzt. Ich habe mich nie getraut den Generator soweit aufzubohren.
    Aktuell erzeugen wir Skeletons für die Features: mit PHP, XLIFF (OASIS Standard für Übersetzungen), XML-Beschreibung der Datenbank, AJAX-PHP-Bridge und Smarty-Templates.
    Eine menügesteuerte Installationsroutine, bei der man die Pakete ankreuzen kann die installiert werden sollen, haben wir auch. Doch die ist nicht unbedingt auf dem neuesten Stand.

    Mit einem Generator die gesamte Anwendung auszurollen oder Updates einzuspielen erfordert sicher schon noch etwas mehr Vorausplanung.

    Wie habt ihr das mit dem Ausführen der Installationsschritte im Deployment eigentlich gelöst? Mit einem Skript a la Ant? Das Deployment ist wie ich finde eine spannende Sache! 🙂

    Reply
  23. @Tom: Das Deployment wird im Moment semi-automatisch via Phing ausgeführt. Wir arbeiten noch daran die Installation komplett automatisch ausführen was natürlich im Falle eines Rollbacks je nach Anwendungsfall kompliziert werden kann.

    Reply

Leave a Comment.

Link erfolgreich vorgeschlagen.

Vielen Dank, dass du einen Link vorgeschlagen hast. Wir werden ihn sobald wie möglich prüfen. Schließen