Das große Projekt, an dem ich seit ein paar Jahren arbeite, ist die Steuerung (und alles) eines fortschrittlichen Geräts, das Herzstück seiner Firmware.
Das Gerät ist ziemlich fortschrittlich, mit mehr Funktionen, als ich aus dem Speicher ableiten könnte, und 98% von ihnen werden von dieser riesigen ausführbaren Datei verwaltet. Auf der einen Seite ist das Programm gut wartbar, im Inneren gut modularisiert, ordnungsgemäß dokumentiert, es gibt eine sinnvolle Trennung der Funktionen nach Verzeichnissen und Dateien und so weiter.
Aber am Ende wird alles zu einer Anwendung zusammengefasst, die alles erledigt, von der entfernten Datenbankkommunikation über die Bedienung des Touchscreens bis hin zur Verwaltung eines Dutzend verschiedener Kommunikationsprotokolle, Messungen, verschiedener Steuerungsalgorithmen, Videoerfassung, Sonnenaufgangszeit und Datum von Ostern (im Ernst, und das sind sie auch) benötigt für sehr ernste Zwecke!) ... Im Allgemeinen Dinge, die sehr eng miteinander verbunden sind, oft nur durch einige Daten, die zwischen einigen entfernten Modulen fließen.
Dies kann erfolgen, indem mehrere separate ausführbare Dateien miteinander kommunizieren, z. B. über Sockets, mit einem genaueren Zweck, und je nach Bedarf geladen / entladen werden. Kein konkreter Grund, warum es so gemacht wird.
In einer Hand funktioniert es und es funktioniert in Ordnung. Das Projekt ist einfacher, ohne den Build mehrerer Binärdateien beizubehalten. Die interne Struktur ist auch einfacher, wenn Sie einfach eine Methode aufrufen oder eine Variable lesen können, anstatt über Sockets oder Shared Memory zu sprechen.
Aber auf der anderen Seite macht mich die Größe, der Umfang dieses Dinges einfach fertig, es fühlt sich an, als würde ich die Titanic steuern. Mir wurde immer beigebracht, zu modularisieren, und alles in eine gigantische Datei zusammenzufassen, fühlt sich falsch an. Ein Problem, das ich kenne, ist ein schwerer Absturz eines (sogar unbedeutenden) Moduls, der alle abstürzt. Die Codequalität stellt jedoch sicher, dass dies in Release-Versionen nicht wirklich der Fall ist. Andernfalls stellen interne Trennung und defensive Programmierung sicher, dass dies größtenteils korrekt funktioniert, auch wenn die Hälfte der internen Module aus irgendeinem Grund normal ausfällt.
Welche anderen Gefahren habe ich übersehen? Warum kriecht mich das raus? Ist das nur irrationale Angst vor Unbekanntem? Ist es eine akzeptierte Praxis, ernsthafte Großprojekte auf diese Weise zu realisieren? Beruhigen Sie meine Befürchtungen oder geben Sie mir einen guten Grund, die Version 2.0 in mehrere kleinere Binärdateien umzuwandeln.
Antworten:
Mit Ausnahme des winzigen Kommentars am Ende (zweitens, Überwachung der CPU) könnten Sie meine Firma beschreiben. Ja, wir brauchen auch Ostern.
Nun, wir sind ein bisschen weiter. Wir haben die große ausführbare Datei aufgeteilt und versucht, Standardkomponenten für Standardbits zu verwenden. Nicht gerade die große Verbesserung, auf die Sie hoffen würden. Tatsächlich wird die Leistung selbst bei leistungsstärkerer Hardware zu einem großen Problem. Und die Wartungskosten sind nicht wirklich gesunken, da es Unmengen an Code gibt, mit dem Daten über schmale Schnittstellen serialisiert und synchronisiert werden können.
Die Lektion, die ich gelernt habe? Eine einzige ausführbare Datei ist eine bewährte Lösung für kleine Systeme, und wir verfügen über jahrzehntelange Erfahrung in der Verwaltung. Alle unsere Tools unterstützen dies von Haus aus. Modularität kann auch innerhalb einer einzelnen ausführbaren Datei erfolgen, und wenn Sie aus anderen Gründen Kompromisse bei der Modularität eingehen müssen, bleibt der Hack klein.
quelle
Sie haben es selbst gesagt - es ist durchaus wartbar, gut modularisiert ...
Meiner Meinung nach, und die meisten Mitglieder des Managements werden mir in dieser Frage zustimmen, sollten Änderungen niemals zum Wohle von Änderungen vorgenommen werden. Es ist nichts Falsches an diesem Programm (Ihrer Beschreibung), außer dass es nicht den neuesten Programmiertrends folgt (die in anderen Antworten erwähnt werden).
Ja, es ist ein größeres Programm ... viele zuvor waren es auch. Es ist auch gut dokumentiert. Alles, was Sie tun müssen, ist die interne Organisation zu studieren, und Sie werden die Logik darin erkennen. Ich bezweifle, dass alle vor Ihnen, die es geschrieben haben, inkompetent waren. Also, erforsche es ein wenig, lerne es ... danach behalte es so, wie es ist, bis ein solider Grund zum Umschreiben auftaucht. Ihr Vorgesetzter wird Ihnen für ein gutes Kosten-Nutzen-Verhältnis dankbar sein.
Es macht dir Angst, weil es groß ist - aber andererseits erwartet niemand von dir, dass du es in einer Woche auswendig lernst. Arbeiten Sie also Stück für Stück, bis Sie den Dreh raus haben. Am Ende werden Sie feststellen, dass es wahrscheinlich gut organisiert ist und wie es organisiert ist.
Würden Sie es umschreiben, würden Sie dasselbe erreichen, es aber gleichzeitig für alle ruinieren, die an die vorherige Organisation des Codes gewöhnt waren. Auf diese Weise müssen nur Sie "anpassen".
quelle
Ich fühle mich besser, wenn Sie sagen, Sie hätten alles in Unit-Tests verpackt. Wenn Sie dies nicht tun, sollten Sie eine Testsuite erstellen, bevor Sie überhaupt über eine Neuarchitektur der Anwendung nachdenken.
Wenn Sie über eine Testsuite verfügen, können Sie die Anwendung besser verstehen und erfahren, wenn eine Änderung in der Anwendung zu Problemen führt.
quelle
Wenn der Code mit geringer Kopplung und hoher Kohäsion gut modularisiert ist, wird er effektiv partitioniert. Der Kommunikationsaufwand sollte innerhalb eines Prozesses geringer sein als bei mehreren Prozessen.
Bei eingebetteten Systemen muss durch einen einzigen Prozess möglicherweise kein Betriebssystem implementiert werden, um alle Prozesse auszuführen, die Sie erstellen würden. Sie müssten auch ein System implementieren, um zu überprüfen, ob alle Prozesse ausgeführt werden, und sie nach Möglichkeit neu starten. Wenn es nicht möglich wäre, müssten Sie entsprechend reagieren.
Das Aufteilen des Codes in separate ausführbare Dateien würde auch die Gesamtgröße des Quellcodes erhöhen, da Sie den gesamten Client / Server-Code zwischen den Modulen implementieren müssten. Sie benötigen außerdem mehr Testfälle, um diesen Code zu verarbeiten, und Fälle, in denen der Serverprozess nicht vorhanden war. Große Projekte sind groß, da unnötige Komplexitäten vermieden werden, wie es scheint, um sie so klein wie möglich zu halten.
Testen ist hier eine Art roter Hering, da die Probleme mit oder ohne Testen immer noch da sind. Gute Tests mit beiden Entwurfswahlen sollten zweifellos angeregt werden. Ich gehe davon aus, dass das Testen mit dem monolithischen Code einfacher ist.
Bei Bedarf einen Absturz zu erzwingen, ist auch mit einer monolithischen ausführbaren Datei einfacher.
quelle
Wenn ich an einer so großen monolithischen Anwendung arbeiten würde, wären die Dinge, die mich aus der Ruhe bringen würden, mangelnde Verkapselung, geringe Kohäsion, hohe Kopplung und das Testen all dessen. Große Monolithen laden oft dazu ein, die richtige Architektur aufzugeben und den Abstieg in einen großen Schlammballen zu beginnen .
Sobald dies geschieht, wird das Testen zu einem Albtraum und Sie befinden sich auf einem guten Weg in die Veralterung.
Wenn Ihr Code jedoch gut strukturiert und gewartet ist und die Prinzipien der hohen Kohäsion, der geringen Kopplung und der ordnungsgemäßen Kapselung eingehalten werden, sehe ich wenig Grund, dies in kleinere Einheiten umzuwandeln.
Sie möchten jedoch gute Tests durchführen.
quelle
Idealerweise lautet die Antwort ja. Wenn Sie mehrere Binärdateien haben, können Sie diese möglicherweise stabiler machen.
... Sie haben jedoch auch erwähnt, dass der Code in der monolithischen Codebasis gut geschrieben und modularisiert ist, was bedeutet, dass Sie nicht mit einem großen Durcheinander, sondern nur mit einer großen Codebasis zu tun haben ...
Insgesamt würde ich sagen, dass das zutreffende Sprichwort lautet: "Lass das Perfekte nicht der Feind des Guten sein." Obwohl ich der Meinung bin, dass Sie zu Recht sagen, dass eine monolithische Codebasis schlecht ist, halte ich es auch nicht für die Mühe wert, sich darum zu kümmern.
Es wäre vernünftig, voranzukommen, indem Sie neue Codeteile in ihre eigenen Binärdateien einfügen, aber ein Zurückgehen und Umgestalten lohnt sich wahrscheinlich nicht.
quelle
Wenn Sie einen einzelnen Prozess verwenden, besteht die Möglichkeit, dass ein Joker-Zeiger von einem Ihrer Untermodule einen kritischen Teil des Speichers beschädigt (und das Ganze abstürzt).
Wenn Sie unterschiedliche Prozesse verwenden, verwenden Sie zumindest nicht denselben Heap- oder Aufrufstapel, und es ist möglicherweise auch einfacher, einen einzelnen Unterprozess neu zu starten.
Wenn Sie alles in mehrere Prozesse aufteilen, müssen Sie auch das Design bereinigen und vermeiden, dass jedes Modul die internen Methoden anderer Module verwendet.
Davon abgesehen ist es wahrscheinlich eine entmutigende Aufgabe.
Was Sie tun könnten, ist zu entscheiden, dass jedes neue Modul ein isolierter Prozess sein sollte.
quelle
Wenn ich in den Bereich komme, in dem das Projekt zu groß ist, um es auf einmal zu schaffen, erstelle ich ein Diagramm, um es an der Wand zu befestigen, in dem alle großen Abschnitte und ihre Zusammenhänge dargestellt sind. Wenn ich dann arbeite, kann ich auf das Modul verweisen, auf das ich mich konzentriere, und eine visuelle Erinnerung haben, wie es in das Ganze passt.
Es ist durchaus möglich, dass die Komplexität und die Gefahren der Wartung eines Build- und Versionsverwaltungssystems für mehrere, nicht verbundene Binärdateien Ihr Unbehagen bei einem so großen und monolithischen Projekt bei weitem überwiegen.
quelle