Una biblioteca comune è una buona idea?


16

Ho sempre pensato che una "biblioteca comune" fosse una buona idea. Con ciò intendo una libreria che contiene le funzionalità comuni che sono spesso necessarie a poche applicazioni diverse. Ne risulta una minore duplicazione / ridondanza del codice.

Di recente ho letto un articolo (non riesco a trovarlo ora) che diceva che in realtà questa è una cattiva idea e sono arrivato al punto di dire che era un "anti-pattern"

Mentre ci sono aspetti positivi di questo approccio. Il controllo delle versioni e la gestione delle modifiche significano test di regressione per la suite di app che utilizzano questa libreria.

Sono un po 'bloccato in una carreggiata per il mio nuovo progetto (Golang). La deduplicazione del codice è stata martellata in me nel corso degli anni, ma mi sento come se dovessi provarlo questa volta.

Mentre scrivo, sto cominciando a pensare che questo approccio "common lib" sia il risultato della scrematura dell'architettura? Forse il mio design ha bisogno di più pensiero?

Interessato a sentire pensieri.


2
I miei 2 centesimi ... Se devi apportare modifiche all'API comune se uno del sistema lo utilizza ha bisogno di quel cambiamento, allora
quell'API

1
Trovo che ci sia un compromesso fondamentale tra duplicazione del codice e accoppiamento. La tua domanda ne è un ottimo esempio. L'equilibrio tra i due dipenderà probabilmente dall'ambiente in cui verrà eseguito il tuo codice.
Joel Cornett

La maggior parte degli sviluppatori C che conosco hanno una raccolta di utility che chiamano "toolbox". Tuttavia, di solito non viene raccolto in una singola libreria comprensibile. È più "scegli e scegli".
Mark Benningfield,

2
Qualcuno chiama Apache e risolve questa follia. Apache commons
Laiv

Risposte:


21

Le biblioteche e il riutilizzo sono assolutamente una buona cosa. Hanno un lato negativo gigante, che è che se non gestiti con cura, diventano l'equivalente del cassetto della tua cucina che contiene tutte le probabilità e le estremità che non vanno da nessun'altra parte.

L'ho visto in azione quando sono diventato responsabile delle prime porte del codice di un'intera unità di business (per lo più nuovo per me) su sistemi a 64 bit e facendo una revisione completa della nostra build e del packaging, molti dei quali erano stati fatti a mano e talvolta non molto bene. * Tendevamo a costruire ciò che spedivamo da una pila di applicazioni, in cui il cliente diceva: "Vorrei un sistema che fa A, B, D e F, oltre a cose M e N che non hai ancora fatto e colla leggermente diversa che li integra tutti. " Ciò che aveva in comune era una libreria di cianfrusaglie che, nel corso di un paio di decenni, aveva accumulato tutto ciò che la gente pensava potesse essere riutilizzabile. Per farla breve, una frazione del codice nella libreria non era. Stavamo spendendo molto tempo prezioso per costruire e mantenere quelle dipendenze solo per l'installazione della libreria comune, non perché ne avevamo davvero bisogno.

La morale è che le biblioteche devono essere trattate come classi e non sovraccaricate di troppe responsabilità. Non mettere il tuo parser JSON nella stessa libreria con le tue funzioni di algebra lineare anche se ogni programma che stai scrivendo usa entrambi.

Mantenerli discreti ha molti vantaggi, il più grande dei quali è che costringe i vostri sviluppatori e confezionatori a elaborare una contabilità dettagliata di ciò di cui i loro prodotti hanno effettivamente bisogno invece di includere solo il cassetto spazzatura e il bagaglio che ne deriva. Quando si configura un sistema utilizzando i pacchetti creati, le dipendenze a grana fine assicurano che vengano installate solo le parti necessarie. Anche se trascuri il tuo repository e continui a compilare la roba vecchia e croccante, nulla di ciò che non è più in uso perde in ciò che spedisci.

Ci sono, naturalmente, eccezioni come quella libcche racchiudono molte funzioni in una libreria. Questo è uno dei casi in cui i vantaggi di farlo in questo modo possono essere spiegati invece di ascoltare ciecamente lo zelante in fondo al corridoio che insiste sul fatto che qualsiasi altro modo diverso da X è sempre una cattiva pratica.


* Nel processo, ho scoperto un binario che era stato passato in giro e non era stato ricompilato da zero in sei anni.

** Non c'è niente di sbagliato nel codice vecchio di decenni. Avevamo un certo numero di algoritmi critici che erano stati così ben dimostrati che saremmo stati degli sciocchi a riscriverli esclusivamente nell'interesse della modernità.


1
La mia regola empirica è che, se hai intenzione di nominare la tua biblioteca qualcosa di simile a "comune", sei diretto a guai.
Karl Bielefeldt,

9

Imbarazzantemente ho introdotto una libreria "comune", così chiamata, in un ambiente di squadra un paio di decenni fa. All'epoca non capivo davvero le dinamiche di ciò che sarebbe potuto accadere in una squadra scarsamente coordinata nel giro di pochi mesi.

Quando l'ho presentato, ho pensato di chiarire e documentare anche che è per cose che tutti concorderemmo che troviamo utile su base giornaliera, che è destinato ad essere una biblioteca minimalista e che la biblioteca non dovrebbe dipendere da nient'altro oltre al libreria standard in modo che sia il più semplice possibile da implementare in nuovi progetti. All'epoca pensavo che fosse la nostra piccola estensione alla libreria standard per cose che, nel nostro particolare dominio, abbiamo trovato utili su base giornaliera.

E è iniziato abbastanza bene. Abbiamo iniziato con una libreria matematica ( common/math*) di routine che tutti usavamo quotidianamente, poiché lavoravamo in computer grafica che era spesso pesante sull'algebra lineare. E dal momento che spesso interagivamo con il codice C, abbiamo concordato alcune utili funzioni di utilità come quelle find_index, a differenzastd::findin C ++, restituirebbe un indice a un elemento trovato in una sequenza anziché in un iteratore che imitava il modo in cui funzionavano le nostre funzioni C - cose di questo tipo - un po 'eclettico ma minimalista e ampiamente usato abbastanza da rimanere familiare e pratico per tutti e la familiarità istantanea è un criterio estremamente importante per come la vedo nel tentativo di creare qualsiasi cosa che sia "comune" o "standard" poiché se è veramente "comune", dovrebbe avere quella qualità familiare su di esso a causa della sua ampia adozione e uso quotidiano.

Ma nel tempo le intenzioni di progettazione della biblioteca mi sono sfuggite di mano quando le persone hanno iniziato ad aggiungere cose che usavano personalmente che pensavano potessero essere utili a qualcun altro, solo per trovare nessun altro che lo usasse. E in seguito qualcuno ha iniziato ad aggiungere funzioni che dipendevano da OpenGL per le routine comuni relative a GL. Successivamente abbiamo adottato Qt e le persone hanno iniziato ad aggiungere codice che dipendeva da Qt, quindi già la libreria comune dipendeva da due librerie esterne. A un certo punto qualcuno ha aggiunto routine di shader comuni che dipendevano dalla nostra libreria di shader specifica per l'applicazione, e a quel punto non si poteva nemmeno distribuirlo in un nuovo progetto senza introdurre Qt, OGL e la nostra libreria di shader e la scrittura specifiche dell'applicazione uno script di compilazione non banale per il tuo progetto. Così si è trasformato in questo pasticcio eclettico e interdipendente.

Ma ho anche scoperto discutendo cosa dovrebbe e non dovrebbe andare in questa libreria che ciò che è considerato "comune" può facilmente trasformarsi in un'idea molto soggettiva se non si imposta una regola molto rigida che ciò che è "comune" è ciò che tutti tendono a trovare utile su base giornaliera. Qualsiasi allentamento degli standard e degrada rapidamente dalle cose che tutti trovano utili su base giornaliera a qualcosa che un singolo sviluppatore trova utile che potrebbe avere la possibilità di essere utile per qualcun altro, e a quel punto la libreria si degrada in un disordine eclettico molto velocemente .

Inoltre, quando raggiungi quel punto, alcuni sviluppatori possono iniziare ad aggiungere cose per il semplice motivo che non amano il linguaggio di programmazione. Potrebbe non piacere la sintassi di un ciclo for o una chiamata di funzione, a quel punto la libreria sta iniziando a riempirsi di cose che stanno solo combattendo la sintassi fondamentale del linguaggio, sostituendo un paio di righe di codice semplice che non è proprio duplicazione di qualsiasi logica fino a una singola riga concisa di codice esotico familiare solo allo sviluppatore che ha introdotto tale abbreviazione. Quindi uno sviluppatore del genere potrebbe iniziare ad aggiungere più funzionalità alla libreria comune implementata utilizzando tali shorthands, a quel punto sezioni significative della biblioteca comune si intrecciano con queste esotiche scorciatoie che potrebbero sembrare belle e intuitive per lo sviluppatore che le ha presentate ma brutte, estranee e difficili da capire per tutti gli altri. E a quel punto penso che tu sappia che ogni speranza di fare qualcosa di veramente "comune" è persa, dal momento che "comune" e "non familiare" sono idee polari opposte.

Quindi ci sono tutti i tipi di lattine di worm lì, almeno in un ambiente di squadra vagamente coordinato, con una biblioteca con ambizioni tanto ampie e generalizzate come solo "roba di uso comune". E mentre il problema di fondo potrebbe essere stato il coordinamento allentato sopra ogni altra cosa, almeno più librerie intese a servire uno scopo più singolare, come una libreria destinata a fornire routine matematiche e nient'altro, probabilmente non si degraderebbero in modo significativo in termini di progettare la purezza e le dipendenze come una biblioteca "comune". Quindi, a posteriori, penso che sarebbe molto meglio sbagliare dal lato delle biblioteche che hanno intenzioni di progettazione molto più chiare. Ho anche scoperto nel corso degli anni che restringere lo scopo e restringere l'applicabilità sono idee radicalmente diverse.

Inoltre, devo ammettere che almeno un po 'poco pratico e la cura forse un po' troppo dell'estetica, ma il modo in cui tendo a percepire la mia idea della qualità di una biblioteca (e forse anche la "bellezza") è giudicato più dal suo legame più debole che è più forte, in un modo simile che se mi hai presentato il cibo più appetitoso del mondo ma, sullo stesso piatto, metti lì qualcosa che marcisce e che ha un cattivo odore, tendo a voler rifiutare l'intero piatto. E se sei come me al riguardo e fai qualcosa che invita ogni sorta di aggiunta come qualcosa chiamato "comune", potresti ritrovarti a guardare quella piastra analogica con qualcosa che marcisce sul lato. Allo stesso modo, penso che sia buono se una biblioteca è organizzata, denominata e documentata in modo tale da non farlo t invitano sempre più aggiunte nel tempo. E ciò può valere anche per le tue creazioni personali, dal momento che ho sicuramente creato alcune cose marce qua e là, e "si guasta" molto meno se non viene aggiunto al piatto più grande. Separare le cose in librerie piccole e molto singolari ha anche la tendenza a disaccoppiare meglio il codice, anche se solo per la pura virtù che diventa molto meno conveniente iniziare ad accoppiare tutto.

La deduplicazione del codice è stata martellata in me nel corso degli anni ma mi sento come se dovessi provarla questa volta.

Quello che potrei suggerire nel tuo caso è iniziare a semplificare la deduplicazione del codice. Non sto dicendo di copiare e incollare grossi frammenti di codice mal testato, soggetto a errori o qualcosa del genere, o duplicare enormi quantità di codice non banale che ha una discreta probabilità di richiedere cambiamenti in futuro.

Ma soprattutto se sei della mentalità per creare una libreria "comune", per la quale presumo che il tuo desiderio sia quello di creare qualcosa di ampiamente applicabile, altamente riutilizzabile e forse idealmente qualcosa che trovi utile oggi come fai tra un decennio da oggi , quindi a volte potresti persino aver bisogno o desiderare una duplicazione per ottenere questa qualità sfuggente. Perché la duplicazione potrebbe effettivamente fungere da meccanismo di disaccoppiamento. È come se desideri separare un lettore video da un lettore MP3, quindi devi almeno duplicare alcune cose come batterie e dischi rigidi. Non possono condividere queste cose o sono indivisibilmente accoppiati e non possono essere usati indipendentemente l'uno dall'altro, e a quel punto le persone potrebbero non essere più interessate al dispositivo se tutto ciò che vogliono fare è riprodurre MP3. Ma qualche tempo dopo aver diviso questi due dispositivi, potresti scoprire che il lettore MP3 può beneficiare di un design della batteria diverso o di un disco rigido più piccolo rispetto al lettore video, a quel punto non stai più duplicando nulla; ciò che inizialmente era iniziato come duplicazione per consentire a questo dispositivo interdipendente di dividersi in due dispositivi separati e indipendenti, in seguito si sarebbe potuto produrre progetti e implementazioni che non sono più ridondanti.

Vale la pena considerare le cose dal punto di vista di quello che usa una biblioteca. Vorresti davvero usareuna libreria che minimizza la duplicazione del codice? È probabile che non lo farai perché uno che lo farà dipenderà naturalmente da altre librerie. E quelle altre librerie potrebbero dipendere da altre librerie per evitare di duplicare il loro codice, e così via, fino a quando potresti aver bisogno di importare / collegare 50 librerie diverse solo per ottenere alcune funzionalità di base come caricare e riprodurre un file audio, e questo diventa molto ingombrante . Nel frattempo, se una simile libreria audio ha deliberatamente scelto di duplicare alcune cose qua e là per raggiungere la sua indipendenza, diventa molto più facile da usare in nuovi progetti e le probabilità sono che non debba essere aggiornato quasi altrettanto spesso da quando ha vinto ' Deve cambiare a seguito di una modifica delle sue librerie esterne dipendenti, che potrebbe tentare di soddisfare uno scopo molto più generalizzato di quello di cui la libreria audio ha bisogno.

Quindi a volte vale la pena scegliere deliberatamente di duplicare un po '(consapevolmente, mai per pigrizia - in realtà per diligenza) al fine di disaccoppiare una libreria e renderla indipendente perché, attraverso tale indipendenza, raggiunge una gamma più ampia di applicabilità pratica e stabilità uniforme (niente più accoppiamenti afferenti). Se vuoi progettare le librerie più riutilizzabili possibili che ti dureranno da un progetto al successivo e nel corso degli anni, oltre a restringere al minimo il suo ambito, suggerirei davvero di considerare di duplicare un po 'qui. E naturalmente scrivi test unitari e assicurati che sia veramente accuratamente testato e affidabile in quello che sta facendo. Questo è solo per le biblioteche che vuoi davvero impiegare del tempo per generalizzare a un punto che va ben oltre un singolo progetto.


3

Esistono tre diverse categorie di funzioni che potresti considerare di inserire nelle librerie:

  1. Cose che vale la pena riutilizzare per tutti.
  2. Tutto ciò che vale la pena riutilizzare per la tua organizzazione.
  3. Roba che non vale la pena riutilizzare.

La prima categoria è qualcosa per cui dovrebbe esistere una libreria standard , ma per qualche motivo nessuno si è preoccupato di crearne una (o qualcuno l'ha fatto? Hai cercato a fondo?). In tal caso, considera di rendere la tua libreria open source. Condividere il tuo lavoro non aiuta solo gli altri, ti aiuta anche perché riceverai segnalazioni di bug e patch da altri utenti. Quando dubiti che qualcuno possa contribuire alla tua biblioteca, potresti avere a che fare con funzionalità che sono in realtà categorie 2 o 3.

La seconda categoria sono cose di cui hai bisogno ancora e ancora, ma nessun altro al mondo ne ha bisogno. Ad esempio, l'implementazione dell'oscuro protocollo di rete per comunicare con il tuo sistema di back-end sviluppato internamente. In tal caso potrebbe essere logico inserire tali elementi in una libreria interna per migliorare la velocità di sviluppo di nuove applicazioni. Assicurati solo che non venga influenzato troppo dal creep di funzionalità e inizi a contenere elementi che rientrano effettivamente nelle categorie 1 o 3. Inoltre, i consigli di Blrfl in merito alla modularizzazione sono molto buoni: non creare una libreria monolitica Conor Corp.. Crea più librerie separate per funzionalità separate.

La categoria tre è una funzionalità che è così banale da implementare che non vale la pena spostarla in una libreria o dove non sei sicuro di averne più bisogno esattamente in quel modulo in un'altra applicazione. Questa funzionalità dovrebbe rimanere parte dell'applicazione per cui è stata sviluppata. In caso di dubbio, probabilmente appartiene a questa categoria.


1

Quasi tutte le lingue hanno una libreria comune / standard, quindi questa è ampiamente riconosciuta come una buona idea. L'uso di librerie di terze parti in parte per vari compiti piuttosto che reinventare la ruota è generalmente considerato una buona idea, anche se il costo / beneficio e la qualità della biblioteca dovrebbero ovviamente essere valutati in ogni caso.

Esistono poi le librerie di "utilità comuni" utilizzate da un singolo sviluppatore o istituzione in progetti altrimenti non correlati. Questo è il tipo di libreria che potrebbe essere considerata un anti-pattern. Nel caso che ho visto, queste librerie replicano solo le funzionalità delle librerie standard o delle librerie di terze parti più conosciute in un modo non standard e mal documentato.


these libraries just replicate functionality from standard librariesquesto non è del tutto negativo, in javascript hai aggiunto librerie che già implementano cose esistenti per aggiungere supporto su vecchi motori js, hai anche librerie di supporto Android per vecchi sdk ecc.
svarog

@svarog: Stai pensando a "polyfill" che emulano la funzionalità in nuovi standard per motori più vecchi che non la supportano in modo nativo? Non vedo alcun motivo per scriverli da soli, dal momento che ci sono librerie open source ben note disponibili per questi scopi.
JacquesB,

0

La maggior parte delle librerie condivise tra i team causa più problemi di quanti ne risolvano. "La strada per l'inferno è lastricata di buone intenzioni."

Le eccezioni sono le librerie che soddisfano la maggior parte di quanto segue:

  • Assicurare finanziamenti per la manutenzione a lungo termine
  • Avere un team / comunità di supporto dedicato
  • Avere un bugtracker
  • Avere ampia copertura di prova
  • Hanno un unico scopo ben definito
  • Non hanno dipendenze stesse (sia in fase di compilazione che in fase di esecuzione), diverse dalle librerie standard o quasi standard
  • Distingue chiaramente tra api pubbliche e interni
  • Avere un canale di comunicazione e processo per tutti / molti utenti per concordare nuove funzionalità e versioni

All'interno di società tipiche (non di avvio), quasi nessuna delle condizioni di cui sopra è presente per le librerie condivise tra i team. Questo perché la maggior parte dei team aziendali sono pagati per consegnare prodotti, non biblioteche. Alcune aziende hanno strategie di condivisione di successo, come il monorepo di Google, ma questo comporta investimenti molto elevati nella costruzione e nell'infrastruttura di test.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.