Ich habe immer gedacht, dass eine "gemeinsame Bibliothek" eine gute Idee ist. Damit meine ich eine Bibliothek, die die allgemeine Funktionalität enthält, die häufig von einigen verschiedenen Anwendungen benötigt wird. Dies führt zu weniger Code-Duplizierung / -Redundanz.
Ich habe kürzlich einen Artikel gelesen (kann ihn jetzt nicht finden), der besagt, dass dies eigentlich eine schlechte Idee ist.
Zwar gibt es Vorteile für diesen Ansatz. Die Versionierung und Verwaltung von Änderungen bedeutet einen Regressionstest für die Suite von Apps, die diese Bibliothek verwenden.
Ich stecke für mein neues (Golang-) Projekt irgendwie in der Klemme. Die Code-Deduplizierung wurde über die Jahre in mich hineingehämmert, aber ich denke, ich sollte es diesmal versuchen.
Während ich dies schreibe, beginne ich zu denken, dass dieser "Common Lib" -Ansatz das Ergebnis von Überfliegen der Architektur ist? Vielleicht braucht mein Design mehr Nachdenken?
Interessiert Gedanken zu hören.
Antworten:
Bibliotheken und Wiederverwendung sind absolut eine gute Sache. Sie haben einen großen Nachteil: Wenn sie nicht sorgfältig verwaltet werden, werden sie zum Äquivalent der Schublade in Ihrer Küche, in der sich alle Kleinigkeiten befinden, die sonst nirgendwo zu finden sind.
Ich sah dies in Aktion, als ich für die ersten (für mich zumeist neuen) Ports des Codes eines gesamten Geschäftsbereichs für 64-Bit-Systeme verantwortlich wurde und eine vollständige Überarbeitung unserer Builds und Verpackungen durchführte, von denen eine Menge durchgeführt wurde von Hand und manchmal nicht sehr gut. * Wir haben das, was wir geliefert haben, in der Regel aus einem Stapel von Anwendungen aufgebaut, in denen der Kunde sagte: "Ich hätte gerne ein System, das A, B, D und F ausführt, plus Dinge M und N, die Sie noch nicht tun und etwas anderer Kleber, der sie alle integriert. " Allen gemeinsam war eine Junk-Drawer-Bibliothek, die über ein paar Jahrzehnte alle Dinge angesammelt hatte, von denen die Leute dachten, sie sollten wiederverwendbar sein. Um es kurz zu machen, ein Bruchteil des Codes in der Bibliothek war nicht. Wir haben viel wertvolle Zeit aufgewendet, um diese Abhängigkeiten aufzubauen und aufrechtzuerhalten, nur damit die gemeinsame Bibliothek installiert werden konnte, nicht weil wir sie tatsächlich brauchten.
Die Moral ist, dass Bibliotheken wie Klassen behandelt und nicht mit zu vielen Verantwortlichkeiten überlastet werden müssen. Stellen Sie Ihren JSON-Parser nicht mit Ihren linearen Algebra-Funktionen in dieselbe Bibliothek, auch wenn jedes Programm, das Sie schreiben, beides verwendet.
Ihre Diskretion bringt viele Vorteile mit sich. Der größte Vorteil besteht darin, dass Entwickler und Paketierer gezwungen sind, detailliert zu berechnen, was ihre eigenen Produkte tatsächlich benötigen, anstatt nur die Müllschublade und das dazugehörige Gepäck einzubeziehen. Wenn Sie ein System mit den erstellten Paketen einrichten, stellen die detaillierten Abhängigkeiten sicher, dass nur die erforderlichen Teile installiert werden. Selbst wenn Sie Ihr Repository vernachlässigen und das alte, krumme Zeug weiter kompilieren, dringt nichts, was nicht mehr verwendet wird, in das, was Sie versenden, ein.
Es gibt natürlich Ausnahmen
libc
, die eine ganze Reihe von Funktionen in eine Bibliothek packen. Dies ist einer der Fälle, in denen die Vorteile einer solchen Vorgehensweise begründet werden können, anstatt blindlings auf den Eiferer im Flur zu achten, der darauf besteht, dass jede andere Methode als X immer eine schlechte Praxis ist.* Dabei habe ich eine Binärdatei entdeckt, die herumgereicht und seit sechs Jahren nicht mehr von Grund auf neu kompiliert wurde.
** An jahrzehntelangem Code ist nichts auszusetzen. Wir hatten eine Reihe kritischer Algorithmen, die so gut bewiesen waren, dass wir uns getäuscht hätten, sie nur im Interesse der Moderne neu zu schreiben.
quelle
Peinlicherweise habe ich vor ein paar Jahrzehnten in einer Teamumgebung eine "gemeinsame" Bibliothek eingeführt, die als solche bezeichnet wird. Ich verstand damals nicht wirklich die Dynamik dessen, was in einem locker koordinierten Team in nur wenigen Monaten passieren konnte.
Als ich es einführte, dachte ich, ich hätte es klargestellt und auch dokumentiert, dass es für Dinge, über die wir uns alle einig sind, täglich nützlich ist, dass es eine minimalistische Bibliothek sein soll und dass die Bibliothek von nichts anderem als dem abhängen sollte Standardbibliothek, damit die Bereitstellung in neuen Projekten so einfach wie möglich ist. Ich dachte damals, dass es unsere eigene kleine Erweiterung der Standardbibliothek für Dinge ist, die wir in unserem speziellen Bereich täglich nützlich fanden.
Und es fing gut genug an. Wir begannen mit einer mathematischen Bibliothek (
common/math*
) von Routinen, die wir alle tagtäglich verwendeten, da wir in Computergrafiken arbeiteten, die oft schwer mit der linearen Algebra zu tun hatten. Und da wir oft mit C-Code interagierten, haben wir uns auf einige nützliche Utility-Funktionen geeinigt, die sich vonfind_index
denen unterscheidenstd::find
In C ++ würde anstelle eines Iterators, der die Funktionsweise unserer C-Funktionen nachahmt, ein Index für ein Element zurückgegeben, das sich in einer Sequenz befindet. Diese Dinge sind ein wenig eklektisch, aber minimalistisch und weit verbreitet genug, um jedem vertraut und praktisch zu bleiben , und sofortige Vertrautheit ist ein äußerst wichtiges Kriterium, da ich darin sehe, dass alles versucht wird, was "allgemein" oder "Standard" ist, denn wenn es wirklich "allgemein" ist, sollte es aufgrund seiner Breite die vertraute Qualität haben Annahme und tägliche Nutzung.Aber im Laufe der Zeit sind mir die Gestaltungsabsichten der Bibliothek entgangen, als die Leute begannen, Dinge hinzuzufügen, die sie persönlich benutzten, von denen sie lediglich dachten, dass sie jemand anderem von Nutzen sein könnten, nur um niemanden zu finden, der sie benutzt. Und später begann jemand, Funktionen hinzuzufügen, die von OpenGL für allgemeine GL-bezogene Routinen abhängig waren. Weiterhin haben wir Qt übernommen und die Leute haben angefangen, Code hinzuzufügen, der von Qt abhängt, so dass die gemeinsame Bibliothek bereits von zwei externen Bibliotheken abhängig war. Irgendwann fügte jemand allgemeine Shader-Routinen hinzu, die von unserer anwendungsspezifischen Shader-Bibliothek abhängig waren, und zu diesem Zeitpunkt konnte man sie nicht einmal in einem neuen Projekt bereitstellen, ohne Qt, OGL und unsere anwendungsspezifische Shader-Bibliothek einzubringen und zu schreiben Ein nicht triviales Build-Skript für Ihr Projekt. So wurde es zu diesem eklektischen, voneinander abhängigen Durcheinander.
Aber ich habe auch durch die Debatte darüber, was in diese Bibliothek aufgenommen werden sollte und was nicht, herausgefunden, dass das, was als "allgemein" angesehen wird, leicht zu einer sehr subjektiven Idee werden kann, wenn Sie keine sehr strenge Regel festlegen, die besagt, was "allgemein" ist Was jeder täglich für nützlich hält. Wenn die Standards gelockert werden und es schnell zu einer Verschlechterung kommt, von Dingen, die jeder täglich für nützlich hält, bis hin zu Dingen, die ein einzelner Entwickler für nützlich hält und die möglicherweise für einen anderen von Vorteil sind. In diesem Moment verwandelt sich die Bibliothek sehr schnell in ein eklektisches Durcheinander .
Wenn Sie diesen Punkt erreicht haben, können einige Entwickler Dinge hinzufügen, weil sie die Programmiersprache nicht mögen. Sie mögen möglicherweise nicht die Syntax einer for-Schleife oder eines Funktionsaufrufs. Ab diesem Zeitpunkt fängt die Bibliothek an, sich mit Dingen zu füllen, die nur der grundlegenden Syntax der Sprache widersprechen, und ersetzt ein paar Zeilen einfachen Codes, die nicht wirklich sind Duplizieren einer Logik bis zu einer einzigen knappen Zeile exotischen Codes, die nur dem Entwickler bekannt ist, der eine solche Abkürzung eingeführt hat. Dann könnte ein solcher Entwickler beginnen, der gemeinsamen Bibliothek, die unter Verwendung solcher Abkürzungen implementiert wurde, mehr Funktionalität hinzuzufügen. An diesem Punkt werden bedeutende Teile der allgemeinen Bibliothek mit diesen exotischen Abkürzungen verwoben, die dem Entwickler, der sie eingeführt hat, schön und intuitiv erscheinen, aber für alle anderen hässlich und fremd und schwer zu verstehen sind. Und an diesem Punkt denke ich, dass Sie wissen, dass jede Hoffnung, etwas wirklich "gemeinsam" zu machen, verloren geht, da "gemeinsam" und "unbekannt" gegensätzliche Vorstellungen sind.
Es gibt also alle möglichen Arten von Würmern, zumindest in einer locker koordinierten Teamumgebung, mit einer Bibliothek, deren Ambitionen so umfassend und verallgemeinerungsbedürftig sind wie nur "häufig verwendete Sachen". Und obwohl das zugrunde liegende Problem vor allem die lockere Koordination gewesen sein könnte, würden sich zumindest mehrere Bibliotheken, die einem eher singulären Zweck dienen sollen, wie eine Bibliothek, die mathematische Routinen und nichts anderes bereitstellen soll, wahrscheinlich nicht so stark verschlechtern Designreinheit und Abhängigkeiten als "gemeinsame" Bibliothek. Im Nachhinein denke ich, dass es viel besser wäre, sich auf die Seite von Bibliotheken zu setzen, die viel klarere Designabsichten haben. Ich habe im Laufe der Jahre auch festgestellt, dass enge Absichten und enge Anwendbarkeit radikal unterschiedliche Ideen sind.
Ich bin zugegebenermaßen ein bisschen unpraktisch und kümmere mich vielleicht ein bisschen zu sehr um die Ästhetik, aber die Art und Weise, wie ich meine Vorstellung von der Qualität einer Bibliothek (und vielleicht sogar von "Schönheit") wahrnehme, wird mehr nach ihrem schwächsten Glied beurteilt als nach Es ist am stärksten, ähnlich wie wenn Sie mir das appetitlichste Essen der Welt präsentieren, aber auf dem gleichen Teller etwas verrotten lassen, das wirklich schlecht riecht, dann möchte ich den gesamten Teller ablehnen. Und wenn Sie in dieser Hinsicht wie ich sind und etwas machen, das alle Arten von Ergänzungen als etwas "Gemeines" einlädt, werden Sie vielleicht auf diese analoge Platte stoßen, auf der etwas verrottet. Deswegen finde ich es auch gut, wenn eine Bibliothek so organisiert und benannt und dokumentiert ist, dass dies nicht der Fall ist. ' t im laufe der zeit immer mehr zusätze einladen. Und das kann sogar auf Ihre persönlichen Kreationen zutreffen, da ich hier und da mit Sicherheit ein paar faule Sachen erschaffen habe, die sich viel weniger "verfärben", wenn sie nicht auf den größten Teller gelegt werden. Das Aufteilen von Dingen in kleine, sehr singuläre Bibliotheken führt zu einer besseren Entkopplung von Code, wenn auch schon aufgrund der Tatsache, dass es weitaus unpraktischer ist, alles zu koppeln.
Was ich in Ihrem Fall vorschlagen könnte, ist, die Code-Deduplizierung zu vereinfachen. Ich sage nicht, große Schnipsel schlecht getesteten, fehleranfälligen Codes oder dergleichen zu kopieren und einzufügen oder große Mengen von nicht-trivialem Code zu duplizieren, der mit ziemlicher Wahrscheinlichkeit in Zukunft Änderungen erforderlich macht.
Aber vor allem, wenn Sie die Einstellung haben, eine "gemeinsame" Bibliothek zu erstellen. Ich gehe davon aus, dass es Ihr Wunsch ist, eine allgemein anwendbare, wiederverwendbare und möglicherweise ideale Bibliothek zu erstellen, die Sie heute genauso nützlich finden wie in einem Jahrzehnt danach Dann kann es manchmal sein, dass Sie eine Kopie benötigen oder möchten, um diese schwer fassbare Qualität zu erzielen. Denn die Vervielfältigung könnte tatsächlich als Entkopplungsmechanismus dienen. Wenn Sie einen Videoplayer von einem MP3-Player trennen möchten, müssen Sie zumindest einige Dinge wie Batterien und Festplatten duplizieren. Sie können diese Dinge nicht teilen oder sie sind untrennbar miteinander verbunden und können nicht unabhängig voneinander verwendet werden, und an diesem Punkt sind die Leute möglicherweise nicht mehr an dem Gerät interessiert, wenn sie nur MP3s abspielen möchten. Einige Zeit nachdem Sie diese beiden Geräte getrennt haben, werden Sie möglicherweise feststellen, dass der MP3-Player von einem anderen Akkudesign oder einer kleineren Festplatte als der Videoplayer profitiert. An diesem Punkt duplizieren Sie nichts mehr. Was anfangs als Duplikation begann, damit sich dieses voneinander abhängige Gerät in zwei separate, unabhängige Geräte aufteilen kann, kann sich später als Ergebnis von Designs und Implementierungen herausstellen, die nicht mehr redundant sind.
Es lohnt sich, die Dinge aus der Perspektive eines Bibliotheksbenutzers zu betrachten. Wollen Sie wirklich wollen , zu verwendeneine Bibliothek, die Code-Duplikationen minimiert? Wahrscheinlich werden Sie dies nicht tun, da dies natürlich von anderen Bibliotheken abhängt. Und diese anderen Bibliotheken sind möglicherweise von anderen Bibliotheken abhängig, um das Duplizieren ihres Codes zu vermeiden, und so weiter, bis Sie möglicherweise 50 verschiedene Bibliotheken importieren / verknüpfen müssen, um einige grundlegende Funktionen wie das Laden und Abspielen einer Audiodatei zu erhalten, was sehr unhandlich wird . Wenn eine solche Audiobibliothek absichtlich einige Dinge hier und da dupliziert, um ihre Unabhängigkeit zu erreichen, wird die Verwendung in neuen Projekten so viel einfacher, und es besteht die Möglichkeit, dass sie seit dem Gewinn nicht mehr so oft aktualisiert werden muss. ' Es ist nicht erforderlich, Änderungen an den abhängigen externen Bibliotheken vorzunehmen, die möglicherweise einen viel allgemeineren Zweck erfüllen als das, was die Audiobibliothek benötigt.
Manchmal lohnt es sich also, absichtlich ein wenig zu duplizieren (bewusst, nie aus Faulheit - eigentlich aus Fleiß), um eine Bibliothek zu entkoppeln und unabhängig zu machen, weil sie durch diese Unabhängigkeit ein breiteres Spektrum an praktischer Anwendbarkeit und Anwendbarkeit erreicht gleichmäßige Stabilität (keine afferenten Kupplungen mehr). Wenn Sie die bestmöglichen wiederverwendbaren Bibliotheken entwerfen möchten, die von einem Projekt zum nächsten und über die Jahre hinweg Bestand haben, dann schlage ich vor, hier ein wenig zu duplizieren, um den Umfang auf ein Minimum zu beschränken. Und natürlich schreiben Sie Unit-Tests und stellen Sie sicher, dass diese wirklich gründlich getestet und zuverlässig sind. Dies gilt nur für die Bibliotheken, für die Sie sich wirklich die Zeit nehmen möchten, sich auf einen Punkt zu verallgemeinern, der weit über ein einzelnes Projekt hinausgeht.
quelle
Es gibt drei verschiedene Kategorien von Funktionen, die Sie möglicherweise in Bibliotheken ablegen sollten:
Kategorie eins ist etwas, für das eine Standardbibliothek existieren sollte , aber aus irgendeinem Grund kam niemand darum herum, eine zu erstellen (oder hat jemand? Haben Sie gründlich gesucht?). In diesem Fall sollten Sie Ihre Bibliothek zu Open Source machen. Das Teilen Ihrer Arbeit hilft nicht nur anderen, sondern auch Ihnen, da Sie Fehlerberichte und Patches von anderen Benutzern erhalten. Wenn Sie bezweifeln, dass jemand zu Ihrer Bibliothek beitragen würde, haben Sie es möglicherweise mit Funktionen zu tun, die tatsächlich der Kategorie 2 oder 3 entsprechen.
Die zweite Kategorie sind Dinge, die Sie immer und immer wieder brauchen, aber niemand auf der Welt braucht sie. Zum Beispiel die Implementierung des obskuren Netzwerkprotokolls für die Kommunikation mit Ihrem hausintern entwickelten Backend-System. In diesem Fall ist es möglicherweise sinnvoll, diese Informationen in einer internen Bibliothek abzulegen, um die Entwicklungsgeschwindigkeit neuer Anwendungen zu verbessern. Stellen Sie nur sicher, dass es nicht zu sehr vom Kriechen der Features betroffen ist und Inhalte enthält, die tatsächlich in die Kategorien 1 oder 3 passen. Außerdem ist der Ratschlag von Blrfl zur Modularisierung sehr gut: Erstellen Sie keine einzige monolithische Conor Corp.-Bibliothek. Erstellen Sie mehrere separate Bibliotheken für separate Funktionen.
Kategorie drei ist eine Funktionalität, die entweder so einfach zu implementieren ist, dass es sich nicht lohnt, sie in eine Bibliothek zu verschieben, oder wenn Sie nicht sicher sind, ob Sie sie in genau dieser Form in einer anderen Anwendung jemals wieder benötigen werden. Diese Funktionalität sollte Teil der Anwendung bleiben, für die sie entwickelt wurde. Im Zweifelsfall gehört es wahrscheinlich in diese Kategorie.
quelle
Fast alle Sprachen haben eine gemeinsame / Standardbibliothek, daher wird dies allgemein als gute Idee anerkannt. Die Verwendung von Drittbibliotheken für verschiedene Aufgaben, anstatt das Rad neu zu erfinden, wird im Allgemeinen ebenfalls als eine gute Idee angesehen, obwohl Kosten / Nutzen und die Qualität der Bibliothek natürlich in jedem Fall bewertet werden sollten.
Dann gibt es die "Common Utilities" -Bibliotheken, die von einem einzelnen Entwickler oder einer Institution für ansonsten nicht verwandte Projekte verwendet werden. Dies ist die Art von Bibliothek , die könnte ein Anti-Muster in Betracht gezogen werden. In dem Fall, den ich gesehen habe, replizieren diese Bibliotheken nur Funktionen von Standardbibliotheken oder bekannteren Bibliotheken von Drittanbietern auf eine nicht standardmäßige und schlecht dokumentierte Weise.
quelle
these libraries just replicate functionality from standard libraries
Dies ist keine schlechte Sache, in Javascript haben Sie Bibliotheken hinzugefügt, die bereits vorhandene Dinge implementieren, um Unterstützung für alte JS-Engines hinzuzufügen. Sie haben auch Android-Unterstützungsbibliotheken für ältere SDKs usw.Die meisten Bibliotheken, die von Teams gemeinsam genutzt werden, verursachen mehr Probleme als sie lösen. "Der Weg zur Hölle ist mit guten Absichten gepflastert."
Ausnahmen sind Bibliotheken, die die meisten der folgenden Anforderungen erfüllen :
In typischen (Nicht-Startup-) Unternehmen gibt es fast keine der oben genannten Bedingungen für Bibliotheken, die von Teams gemeinsam genutzt werden. Das liegt daran, dass die meisten Unternehmensteams für die Lieferung von Produkten bezahlt werden, nicht für Bibliotheken. Einige Unternehmen haben erfolgreiche Freigabestrategien, wie zum Beispiel Googles Monorepo. Dies ist jedoch mit sehr hohen Investitionen in die Erstellung und Erprobung von Infrastruktur verbunden.
quelle