Czy wspólna biblioteka to dobry pomysł?

16

Zawsze uważałem, że „wspólna biblioteka” to dobry pomysł. Rozumiem przez to bibliotekę, która zawiera wspólną funkcjonalność, która jest często potrzebna przez kilka różnych aplikacji. Powoduje to mniejsze powielanie / redundancja kodu.

Niedawno przeczytałem artykuł (nie mogę teraz znaleźć), który stwierdził, że to rzeczywiście zły pomysł i posunąłem się do stwierdzenia, że ​​jest to „anty-wzór”

Chociaż są zalety tego podejścia. Kontrola wersji i zarządzanie zmianami oznacza test regresji dla pakietu aplikacji korzystających z tej biblioteki.

Trochę utknąłem w rutynie dla mojego nowego projektu (Golang). Deduplikacja kodu została wbita we mnie przez lata, ale czuję, że powinienem spróbować tym razem.

Pisząc to, zaczynam myśleć, że to „wspólne lib” jest wynikiem przeszukiwania architektury? Być może mój projekt wymaga więcej przemyślenia?

Chciałbym usłyszeć myśli.

Jim
źródło
2
Moje 2 centy ... Jeśli musisz wprowadzić zmiany we wspólnym interfejsie API, jeśli jeden z systemów tego potrzebuje, potrzebujesz tej zmiany, to ten interfejs API lub biblioteka wcale nie jest wspólną biblioteką
Raja Anbazhagan
1
Uważam, że istnieje fundamentalny kompromis między duplikacją kodu a sprzężeniem. Twoje pytanie jest tego najlepszym przykładem. Równowaga między nimi będzie prawdopodobnie zależeć od środowiska, w którym ostatecznie wykona się Twój kod.
Joel Cornett
Większość programistów C, których znam, ma kolekcję narzędzi, które nazywają „zestawem narzędzi”. Jednak zwykle nie jest gromadzone w jednej dołączalnej bibliotece. To bardziej „wybierz i wybierz”.
Mark Benningfield
2
Niech ktoś zadzwoni do Apache i naprawi to szaleństwo. Apache commons
Laiv

Odpowiedzi:

21

Biblioteki i ponowne użycie są absolutnie dobre. Mają jeden olbrzymi minus, to znaczy, że jeśli nie są starannie zarządzane, stają się odpowiednikiem szuflady w kuchni, która utrzymuje wszystkie szanse i cele, które nie idą gdzie indziej.

Zobaczyłem to w działaniu, kiedy stałem się odpowiedzialny za pierwsze porty kodu całej jednostki biznesowej (głównie dla mnie nowego) dla systemów 64-bitowych i dokonałem kompletnego przeglądu naszej kompilacji i pakowania, z których wiele zostało zrobione ręcznie, a czasem niezbyt dobrze. * Mieliśmy tendencję do gromadzenia tego, co wysłaliśmy ze stosu aplikacji, w którym klient powiedziałby: „Chciałbym systemu, który robi A, B, D i F, plus rzeczy M i N, którego jeszcze nie robisz i nieco inny klej integrujący je wszystkie. ” Łączyła je biblioteka szuflad śmieci, która przez kilka dziesięcioleci ** zgromadziła wszystkie rzeczy, które ludzie uważali za nadające się do ponownego wykorzystania. Krótko mówiąc, ułamek kodu w bibliotece nie był. Spędziliśmy wiele cennego czasu na budowaniu i utrzymywaniu tych zależności tylko po to, aby zainstalować wspólną bibliotekę, a nie dlatego, że naprawdę ich potrzebowaliśmy.

Morał polega na tym, że biblioteki należy traktować jak klasy i nie obciążać zbyt wieloma obowiązkami. Nie umieszczaj parsera JSON w tej samej bibliotece z funkcjami algebry liniowej, nawet jeśli każdy program, który piszesz, korzysta z obu.

Utrzymywanie ich w dyskrecji ma wiele zalet, z których największą jest to, że zmusza deweloperów i pakujących do przedstawienia szczegółowego rozliczenia tego, czego tak naprawdę potrzebują ich własne produkty, a nie tylko szuflady śmieci i dołączonego do niej bagażu. Podczas konfigurowania systemu za pomocą wbudowanych pakietów, drobnoziarniste zależności zapewniają, że instalowane są tylko niezbędne części. Nawet jeśli zaniedbujesz swoje repozytorium i nadal kompilujesz stare, chrupiące rzeczy, nic, co nie jest już w użyciu, wycieka do tego, co wysyłasz.

Istnieją oczywiście wyjątki, takie jak libcumieszczenie wielu funkcji w jednej bibliotece. Jest to jeden z przypadków, w których korzyści płynące z robienia tego w ten sposób można uzasadnić, zamiast ślepo śledzić fanatyka w korytarzu, który twierdzi, że jakakolwiek inna droga niż X jest zawsze złym postępowaniem.


* W trakcie tego procesu odkryłem plik binarny, który został przekazany i nie został ponownie skompilowany od zera przez sześć lat.

** W dziesięcioletnim kodzie nie ma nic złego. Mieliśmy szereg krytycznych algorytmów, które zostały tak dobrze udowodnione, że głupcami byłoby przepisywanie ich wyłącznie w interesie nowoczesności.

Blrfl
źródło
1
Moja ogólna zasada jest taka, że ​​jeśli zamierzasz nazwać swoją bibliotekę czymś w rodzaju „zwykłym”, masz kłopoty.
Karl Bielefeldt
9

Zawstydzająco wprowadziłem „wspólną” bibliotekę, nazwaną jako taką, w środowisku zespołowym kilka dekad temu. Nie bardzo wtedy rozumiałem dynamikę tego, co mogło się zdarzyć w luźno koordynowanym otoczeniu zespołu w ciągu zaledwie kilku miesięcy.

Kiedy go przedstawiłem, pomyślałem, że to jasno wyjaśniłem, a także udokumentowałem, że dla celów, które wszyscy zgodzilibyśmy się, że są przydatne na co dzień, że ma to być biblioteka minimalistyczna i że biblioteka nie powinna zależeć od niczego poza standardowa biblioteka, aby jak najłatwiej wdrożyć ją w nowych projektach. Myślałem wtedy, że było to nasze własne małe rozszerzenie standardowej biblioteki dla rzeczy, które w naszej konkretnej dziedzinie były przydatne na co dzień.

I zaczęło się dość dobrze. Zaczęliśmy od biblioteki matematycznej ( common/math*) procedur, z których wszyscy korzystaliśmy na co dzień, ponieważ pracowaliśmy nad grafiką komputerową, która często była ciężka dla algebry liniowej. A ponieważ często współpracowaliśmy z kodem C, uzgodniliśmy kilka przydatnych funkcji narzędziowych, takich jak find_indexktóre, w przeciwieństwie dostd::findw C ++ zwróciłby indeks do elementu znajdującego się w sekwencji zamiast iteratora, który naśladowałby działanie naszych funkcji C - tego rodzaju - trochę eklektyczny, ale minimalistyczny i wystarczająco szeroko stosowany, aby pozostać znajomym i praktycznym dla wszystkich , a natychmiastowa znajomość jest niezwykle ważnym kryterium, ponieważ widzę to w próbach stworzenia czegoś, co jest „powszechne” lub „standardowe”, ponieważ jeśli naprawdę jest „powszechne”, powinno mieć tę znajomą jakość dzięki swojej szerokiej adopcja i codzienne użytkowanie.

Ale z czasem intencje projektowe biblioteki wymknęły się z moich palców, gdy ludzie zaczęli dodawać rzeczy, których używali osobiście, o których po prostu myśleli, że mogą być przydatne dla kogoś innego, tylko po to, aby nikt inny ich nie używał. Później ktoś zaczął dodawać funkcje zależne od OpenGL do popularnych procedur związanych z GL. Następnie przyjęliśmy Qt i ludzie zaczęli dodawać kod zależny od Qt, więc już teraz wspólna biblioteka była zależna od dwóch bibliotek zewnętrznych. W pewnym momencie ktoś dodał wspólne procedury cieniowania, które były zależne od naszej biblioteki modułów cieniujących specyficznych dla aplikacji, i w tym momencie nie można było nawet wdrożyć go w nowym projekcie bez wprowadzenia Qt, OGL oraz naszej biblioteki modułów cieniujących i aplikacji nietrywialny skrypt kompilacji dla twojego projektu. Więc zamieniło się w ten eklektyczny, współzależny bałagan.

Ale odkryłem również, debatując, co powinno, a czego nie powinno wchodzić do tej biblioteki, że to, co jest uważane za „powszechne”, może łatwo przekształcić się w bardzo subiektywny pomysł, jeśli nie ustawisz bardzo twardej zasady, że to, co „wspólne”, jest co każdy uważa za przydatne na co dzień. Każde poluzowanie standardów i szybko degraduje się od rzeczy, które każdy uważa za przydatne na co dzień, do czegoś, co pojedynczy programista uważa za przydatne, które może być korzystne dla kogoś innego, i w tym momencie biblioteka bardzo szybko staje się eklektycznym bałaganem .

Co więcej, po osiągnięciu tego punktu niektórzy programiści mogą zacząć dodawać rzeczy z tego prostego powodu, że nie podoba im się język programowania. Mogą nie lubić składni pętli for lub wywołania funkcji, w którym to momencie biblioteka zaczyna się zapełniać rzeczami, które po prostu walczą z podstawową składnią języka, zastępując kilka linii prostego kodu, co tak naprawdę nie jest powielanie jakiejkolwiek logiki do pojedynczego zwięzłego wiersza egzotycznego kodu znanego tylko programistom, którzy wprowadzili takie skróty. Wtedy taki programista może zacząć dodawać więcej funkcji do wspólnej biblioteki zaimplementowanej przy użyciu takich skrótów, w tym momencie znaczące fragmenty wspólnej biblioteki przeplatają się z tymi egzotycznymi skrótami, które mogą wydawać się piękne i intuicyjne dla programisty, który je wprowadził, ale są brzydkie, obce i trudne do zrozumienia dla wszystkich innych. I w tym momencie myślę, że wiesz, że wszelka nadzieja na uczynienie czegoś prawdziwie „wspólnym” jest tracona, ponieważ „wspólne” i „nieznane” są biegunowo przeciwnymi ideami.

Są tam więc wszelkiego rodzaju puszki robaków, przynajmniej w luźno skoordynowanym środowisku zespołowym, z biblioteką o ambicjach tak szerokich i tak ogólnych, jak tylko „powszechnie używane rzeczy”. I choć podstawowym problemem mogła być przede wszystkim luźna koordynacja, przynajmniej wiele bibliotek miało służyć bardziej szczególnemu celowi, jak biblioteka przeznaczona do rutynowych zadań matematycznych i nic więcej, prawdopodobnie nie pogorszyłaby się tak znacząco pod względem projektuj czystość i zależności jako „wspólną” bibliotekę. Z perspektywy czasu myślę, że o wiele lepiej byłoby pomylić się po stronie bibliotek, które mają znacznie bardziej wyraźne intencje projektowe. Przez lata odkryłem również, że wąskie cele i wąskie zastosowania to radykalnie różne pomysły.

Poza tym jestem co najmniej trochę niepraktyczny i może trochę zbytnio dbam o estetykę, ale sposób, w jaki postrzegam mój pomysł na jakość biblioteki (a może nawet „piękno”) jest oceniany bardziej na podstawie jej najsłabszego ogniwa niż jego najsilniejszy, w podobny sposób, że jeśli podałeś mi najbardziej apetyczne jedzenie na świecie, ale na tym samym talerzu umieściłeś tam coś gnijącego, co naprawdę brzydko pachnie, zwykle chcę odrzucić cały talerz. A jeśli pod tym względem jesteś podobny do mnie i robisz coś, co zachęca do wszelkiego rodzaju dodatków jako coś, co nazywa się „wspólnym”, możesz spojrzeć na analogiczną płytkę z czymś gnijącym na boku. Podobnie myślę, że dobrze jest, jeśli biblioteka jest zorganizowana, nazwana i udokumentowana w taki sposób, że z czasem zapraszać coraz więcej dodatków. I może to nawet dotyczyć twoich osobistych tworów, ponieważ z pewnością stworzyłem jakieś zgniłe rzeczy tu i tam, i „skażają” o wiele mniej, jeśli nie zostaną dodane do największej płyty. Rozdzielanie rzeczy na małe, bardzo osobliwe biblioteki ma również tendencję do lepszego oddzielania kodu, choćby tylko z tego powodu, że łączenie wszystkiego staje się znacznie mniej wygodne.

Deduplikacja kodu została wbita we mnie przez lata, ale czuję, że powinienem spróbować tym razem.

To, co mogę zasugerować w twoim przypadku, to ułatwić sobie deduplikację kodu. Nie mówię, aby kopiować i wklejać duże fragmenty źle przetestowanego, podatnego na błędy kodu lub coś w tym rodzaju, ani nie powielać ogromnych ilości nietrywialnego kodu, który z dużym prawdopodobieństwem wymaga zmian w przyszłości.

Ale szczególnie, jeśli jesteś nastawiony na stworzenie „wspólnej” biblioteki, dla której zakładam, że twoim pragnieniem jest stworzenie czegoś szeroko stosowalnego, wysoce nadającego się do wielokrotnego użytku, a być może idealnie, co uznasz za równie użyteczne dzisiaj, jak za dziesięć lat. , to czasem może być potrzebne lub potrzebne powielanie, aby osiągnąć tę nieuchwytną jakość. Ponieważ duplikacja może faktycznie służyć jako mechanizm odsprzęgający. To tak, jakbyś chciał oddzielić odtwarzacz wideo od odtwarzacza MP3, musisz przynajmniej powielić niektóre rzeczy, takie jak baterie i dyski twarde. Nie mogą dzielić się tymi rzeczami lub są ze sobą nierozerwalnie połączone i nie można ich używać niezależnie od siebie, a w tym momencie ludzie mogą już nie być zainteresowani urządzeniem, jeśli wszystko, co chcą, to odtwarzać pliki MP3. Ale jakiś czas po rozdzieleniu tych dwóch urządzeń może się okazać, że odtwarzacz MP3 może korzystać z innej konstrukcji baterii lub mniejszego dysku twardego niż odtwarzacz wideo, w którym to momencie nie można już niczego kopiować; co początkowo zaczęło się od powielania, aby umożliwić temu współzależnemu urządzeniu podział na dwa oddzielne, niezależne urządzenia, może później okazać się, że przyniosą projekty i implementacje, które nie są już w ogóle zbędne.

Warto zastanowić się nad tym z perspektywy tej korzystającej z biblioteki. Czy naprawdę chcesz użyćbiblioteka, która minimalizuje duplikację kodu? Są szanse, że nie, bo taki, który będzie naturalnie zależał od innych bibliotek. Te inne biblioteki mogą zależeć od innych bibliotek, aby uniknąć powielania ich kodu, i tak dalej, dopóki nie będzie konieczne zaimportowanie / połączenie 50 różnych bibliotek, aby uzyskać tylko podstawowe funkcje, takie jak ładowanie i odtwarzanie pliku audio, i staje się to bardzo niewygodne . Tymczasem, jeśli taka biblioteka audio świadomie zdecyduje się powielić niektóre rzeczy tu i tam, aby osiągnąć swoją niezależność, staje się o wiele łatwiejsza w użyciu w nowych projektach, a są szanse, że nie będzie musiała być aktualizowana tak często, ponieważ wygrała ” Trzeba zmienić w wyniku zmiany jednej zależnej biblioteki zewnętrznej, która może próbować spełnić znacznie bardziej ogólny cel niż to, czego potrzebuje biblioteka audio.

Czasami więc warto celowo zdecydować się na powielenie trochę (świadomie, nigdy z lenistwa - właściwie z pracowitości) w celu oddzielenia biblioteki i uniezależnienia jej, ponieważ dzięki tej niezależności osiąga szerszy zakres praktycznego zastosowania i równomierna stabilność (koniec sprzężeń aferentnych). Jeśli chcesz zaprojektować biblioteki wielokrotnego użytku, które będą trwać od jednego projektu do następnego i na przestrzeni lat, to oprócz zawężenia jego zakresu do minimum, proponuję rozważenie powielenia tutaj. I oczywiście napisz testy jednostkowe i upewnij się, że są naprawdę dokładnie przetestowane i niezawodne w tym, co robi. Dotyczy to tylko bibliotek, które naprawdę chcesz poświęcić czas na uogólnienie do punktu, który wykracza daleko poza pojedynczy projekt.


źródło
3

Istnieją trzy różne kategorie funkcji, które można rozważyć umieszczenie w bibliotekach:

  1. Rzeczy warte ponownego użycia dla wszystkich.
  2. Rzeczy, które warto ponownie wykorzystać w swojej organizacji.
  3. Rzeczy nie warte ponownego użycia.

Kategoria pierwsza to coś, dla czego powinna istnieć standardowa biblioteka , ale z jakiegoś powodu nikt jej nie stworzył (a może ktoś? Czy dokładnie przeszukałeś?). W takim przypadku zastanów się nad stworzeniem biblioteki open source. Udostępnianie swojej pracy nie tylko pomaga innym, ale także pomaga, ponieważ będziesz otrzymywać raporty o błędach i łaty od innych użytkowników. Jeśli masz wątpliwości, czy ktoś wniósłby wkład w twoją bibliotekę, być może masz do czynienia z funkcjonalnością, która w rzeczywistości jest kategorią 2 lub 3.

Druga kategoria to rzeczy, których potrzebujesz w kółko, ale nikt inny na świecie tego nie potrzebuje. Na przykład implementacja niejasnego protokołu sieciowego do komunikacji z wewnętrznym systemem zaplecza. W takim przypadku sensowne może być umieszczenie tych rzeczy w bibliotece wewnętrznej, aby przyspieszyć rozwój nowych aplikacji. Tylko upewnij się, że nie wpływa to zbytnio na pełzanie funkcji i zaczyna zawierać rzeczy, które faktycznie pasują do kategorii 1 lub 3. Ponadto, porady Blrfl dotyczące modularyzacji są bardzo dobre: ​​Nie twórz jednej monolitycznej biblioteki Conor Corp. Utwórz wiele oddzielnych bibliotek dla oddzielnych funkcjonalności.

Kategoria trzecia to funkcjonalność, która jest albo tak trywialna w implementacji, że przeniesienie jej do biblioteki nie jest tego warte, albo nie masz pewności, czy kiedykolwiek będziesz jej potrzebować w dokładnie takiej formie w innej aplikacji. Ta funkcja powinna pozostać częścią aplikacji, dla której została opracowana. W razie wątpliwości prawdopodobnie należy do tej kategorii.

Philipp
źródło
1

Prawie wszystkie języki mają wspólną / standardową bibliotekę, więc jest to ogólnie uznawany za dobry pomysł. Dobrym pomysłem jest również używanie bibliotek z częściami trzecimi do różnych zadań zamiast ponownego wymyślania koła, chociaż koszt / korzyść i jakość biblioteki powinny być oczywiście oceniane w każdym przypadku.

Następnie istnieją biblioteki „wspólnych narzędzi” używane przez pojedynczego programistę lub instytucję w projektach niezwiązanych w inny sposób. Jest to rodzaj biblioteki, którą można uznać za anty-wzorzec. W przypadku, który widziałem, biblioteki te po prostu replikują funkcje ze standardowych bibliotek lub bardziej znanych bibliotek stron trzecich w niestandardowy i źle udokumentowany sposób.

JacquesB
źródło
these libraries just replicate functionality from standard librariesto nie jest całkiem zła rzecz, w javascript dodałeś biblioteki, które już implementują istniejące rzeczy, aby dodać obsługę starych silników js, masz także biblioteki wsparcia Androida dla starszych sdk itp.
svarog
@svarog: Czy myślisz o „polypełniaczach”, które emulują funkcjonalność w nowych standardach dla starszych silników, które nie obsługują go natywnie? Nie widzę powodu, aby pisać je sam, ponieważ istnieją dobrze znane biblioteki typu open source do tych celów.
JacquesB
0

Większość bibliotek współdzielonych przez zespoły powoduje więcej problemów niż rozwiązuje. "Droga do piekła jest wybrukowana dobrymi chęciami."

Wyjątek stanowią biblioteki spełniające większość poniższych kryteriów:

  • Mają bezpieczne finansowanie długoterminowej konserwacji
  • Posiadaj dedykowany zespół / społeczność wsparcia
  • Miej bugtrackera
  • Szeroki zakres testów
  • Mają jeden, dobrze zdefiniowany cel
  • Nie mają żadnych zależności (zarówno podczas kompilacji, jak i środowiska wykonawczego), innych niż biblioteki standardowe lub quasi-standardowe
  • Czyste rozróżnienie między publicznym interfejsem API a elementami wewnętrznymi
  • Mieć kanał komunikacji i proces dla wszystkich / wielu użytkowników, aby uzgodnić nowe funkcje i wydania

W typowych (nie-startupowych) firmach prawie żaden z powyższych warunków nie występuje dla bibliotek współdzielonych przez zespoły. To dlatego, że większość zespołów firmowych otrzymuje wynagrodzenie za dostarczanie produktów, a nie bibliotek. Niektóre firmy mają skuteczne strategie udostępniania, takie jak monorepo Google, ale wiąże się to z bardzo wysokimi inwestycjami w infrastrukturę do budowania i testowania.

tkruse
źródło