Uma biblioteca comum é uma boa ideia?

16

Eu sempre pensei que uma "biblioteca comum" era uma boa idéia. Por isso, quero dizer uma biblioteca que contém a funcionalidade comum que geralmente é necessária para alguns aplicativos diferentes. Isso resulta em menos duplicação / redundância de código.

Recentemente, li um artigo (não é possível encontrar agora) que dizia que essa é realmente uma péssima idéia e chegou ao ponto de dizer que era um "antipadrão"

Embora haja vantagens nessa abordagem. O controle de versão e o gerenciamento de alterações significam teste de regressão para o conjunto de aplicativos que usam essa biblioteca.

Estou meio empolgado com o meu novo projeto (Golang). A desduplicação de código foi martelada em mim ao longo dos anos, mas acho que devo tentar desta vez.

Enquanto escrevia isso, estou começando a pensar que essa abordagem de "lib comum" é o resultado de uma análise da arquitetura? Talvez meu design precise de mais reflexão?

Interessado em ouvir pensamentos.

jim
fonte
2
Meus 2 centavos ... Se você tiver que fazer alterações na API comum, se um dos sistemas usar ela precisar dessa alteração, a API ou a biblioteca não será uma biblioteca comum
#
1
Acho que há uma troca fundamental entre duplicação de código e acoplamento. Sua pergunta é um excelente exemplo disso. A balança que você greve entre os dois, provavelmente, vai depender do ambiente de seu código acabará por executar no.
Joel Cornett
A maioria dos desenvolvedores de C que eu conheço tem uma coleção de utilitários que eles chamam de "caixa de ferramentas". Geralmente, ele não é coletado em uma única biblioteca que pode ser incluída. É mais "escolha e escolha".
precisa saber é o seguinte
2
Alguém liga para o Apache e conserta essa loucura. Apache commons
Laiv

Respostas:

21

Bibliotecas e reutilização são absolutamente boas. Eles têm uma desvantagem gigante: se não forem gerenciados com cuidado, eles se tornarão o equivalente à gaveta da sua cozinha que possui todas as probabilidades e fins que não vão a nenhum outro lugar.

Vi isso em ação quando me tornei responsável pelas primeiras portas do código de uma unidade de negócios inteira (principalmente novas para mim) para sistemas de 64 bits e fiz uma revisão completa de nossa construção e empacotamento, muito do que estava sendo feito manualmente e às vezes não muito bem. * Tendíamos a construir o que enviamos a partir de uma pilha de aplicativos, onde o cliente dizia: "Gostaria de um sistema que faça A, B, D e F, além das coisas M e N que você ainda não possui e cola levemente diferente que integra todos eles. " O que tudo isso tinha em comum era uma biblioteca de lixo eletrônico que, ao longo de duas décadas, ** acumulou todas as coisas que as pessoas pensavam que deveriam ser reutilizáveis. Para resumir uma longa história, uma fração do código da biblioteca não era '. Estávamos gastando muito tempo valioso construindo e mantendo essas dependências apenas para que a biblioteca comum fosse instalada, não porque realmente precisávamos delas.

A moral é que as bibliotecas precisam ser tratadas como classes e não sobrecarregadas com muitas responsabilidades. Não coloque seu analisador JSON na mesma biblioteca com suas funções de álgebra linear, mesmo que todos os programas que você está escrevendo usem os dois.

Mantê-los discretos tem muitos benefícios, o maior deles é que obriga seus desenvolvedores e empacotadores a apresentar uma contabilidade detalhada do que seus produtos realmente precisam, em vez de incluir apenas a gaveta de lixo eletrônico e a bagagem que vem com ele. Quando você configura um sistema usando os pacotes criados, as dependências refinadas garantem que apenas as peças necessárias sejam instaladas. Mesmo que você negligencie seu repositório e continue compilando coisas antigas e ruins, nada que não esteja mais em uso vazará para o que você envia.

Obviamente, há exceções como essa libcque agrupam muitas funções em uma biblioteca. Esse é um dos casos em que os benefícios de fazê-lo dessa maneira podem ser justificados, em vez de atender cegamente o fanático pelo corredor, que insiste que qualquer outro caminho além de X é sempre uma má prática.


* No processo, descobri um binário que havia sido repassado e não havia sido recompilado do zero em seis anos.

** Não há nada errado com o código de décadas. Tínhamos vários algoritmos críticos que haviam sido tão bem comprovados que teríamos sido tolos em reescrevê-los apenas no interesse da modernidade.

Blrfl
fonte
1
Minha regra geral é que, se você pretende nomear sua biblioteca como algo "comum", está em apuros.
Karl Bielefeldt
9

Embaraçosamente, apresentei uma biblioteca "comum", denominada como tal, em um ambiente de equipe algumas décadas atrás. Na verdade, eu não entendia a dinâmica do que poderia acontecer em uma equipe pouco coordenada em apenas alguns meses.

Quando o apresentei, pensei ter deixado claro e também documentado que é para coisas que todos concordamos que achamos úteis diariamente, que se destina a ser uma biblioteca minimalista e que a biblioteca não deve depender de mais nada além do biblioteca padrão para que seja o mais fácil de implantar possível em novos projetos. Na época, eu pensava que era a nossa pequena extensão da biblioteca padrão para coisas que, em nosso domínio específico, achamos úteis diariamente.

E começou bem o suficiente. Começamos com uma biblioteca de matemática ( common/math*) de rotinas que todos usamos diariamente, já que estávamos trabalhando em computação gráfica, muitas vezes pesada na álgebra linear. E, como frequentemente interoperávamos com o código C, concordamos com algumas funções úteis de utilidade find_index, como asstd::findem C ++, retornaria um índice a um elemento encontrado em uma sequência em vez de um iterador que imitava como nossas funções C funcionavam - coisas desse tipo - um pouco ecléticas, mas minimalistas e amplamente usadas o suficiente para permanecer familiar e prático para todos , e a familiaridade instantânea é um critério extremamente importante, na minha opinião, ao tentar criar algo que seja "comum" ou "padrão", pois, se realmente é "comum", ela deve ter essa qualidade familiar como resultado de sua ampla adoção e uso diário.

Mas, com o tempo, as intenções de design da biblioteca escaparam dos meus dedos quando as pessoas começaram a adicionar coisas que usavam pessoalmente, que apenas pensavam que poderiam ser úteis para outra pessoa, apenas para encontrar mais ninguém usando. E mais tarde alguém começou a adicionar funções que dependiam do OpenGL para rotinas comuns relacionadas à GL. Adotamos o Qt e as pessoas começaram a adicionar código que dependia do Qt; portanto, a biblioteca comum já dependia de duas bibliotecas externas. Em algum momento, alguém adicionou rotinas de sombreador comuns que dependiam de nossa biblioteca de sombreadores específicos de aplicativos e, naquele momento, você não podia implantá-lo em um novo projeto sem trazer Qt, OGL e nossa biblioteca de sombreadores e aplicativos específicos de aplicativos um script de construção não trivial para o seu projeto. Então se transformou em uma bagunça eclética e interdependente.

Mas também descobri debatendo o que deveria e o que não deveria entrar nesta biblioteca que o que é considerado "comum" pode facilmente se transformar em uma idéia subjetiva, se você não definir uma regra muito rígida de que o que é "comum" é o que todo mundo tende a achar útil diariamente. Qualquer afrouxamento dos padrões e rapidamente se degrada de coisas que todos acham úteis diariamente para algo que um único desenvolvedor considera útil que pode ter a possibilidade de ser benéfico para outra pessoa e, nesse ponto, a biblioteca se degrada em uma confusão eclética muito rápido .

Além disso, quando você chega nesse ponto, alguns desenvolvedores podem começar a adicionar coisas pelo simples motivo de que não gostam da linguagem de programação. Eles podem não gostar da sintaxe de um loop for ou de uma chamada de função; nesse momento, a biblioteca está começando a ficar cheia de coisas que estão apenas combatendo a sintaxe fundamental da linguagem, substituindo algumas linhas de código simples que não são realmente duplicar qualquer lógica em uma única linha concisa de código exótico, familiar ao desenvolvedor que introduziu essa abreviação. Em seguida, esse desenvolvedor pode começar a adicionar mais funcionalidades à biblioteca comum implementada usando essas abreviações, nesse ponto, seções significativas da biblioteca comum se entrelaçam com essas abreviações exóticas, que podem parecer bonitas e intuitivas para o desenvolvedor que a apresentou, mas são feias, estrangeiras e difíceis de entender para todos os outros. E, nesse ponto, acho que você sabe que qualquer esperança de tornar algo verdadeiramente "comum" se perde, uma vez que "comum" e "não familiar" são idéias polares opostas.

Portanto, existem todos os tipos de latas de vermes lá, pelo menos em um ambiente de equipe pouco coordenado, com uma biblioteca com ambições tão amplas e generalizadas quanto apenas "coisas de uso comum". E embora o problema subjacente possa ter sido a coordenação frouxa acima de tudo, pelo menos várias bibliotecas destinadas a servir a um propósito mais singular, como uma biblioteca destinada a fornecer rotinas matemáticas e nada mais, provavelmente não se degradariam significativamente em termos de projetar pureza e dependências como uma biblioteca "comum". Então, em retrospecto, acho que seria muito melhor errar do lado das bibliotecas que têm intenções de design muito mais claras. Ao longo dos anos, também descobri que objetivos estreitos e aplicabilidade estreitos são idéias radicalmente diferentes.

Também sou pelo menos um pouco impraticável e me preocupo um pouco demais com a estética, mas a maneira como percebo minha idéia da qualidade de uma biblioteca (e talvez até a "beleza") é julgada mais pelo seu elo mais fraco do que é mais forte, da mesma forma que, se você me apresentasse a comida mais apetitosa do mundo, mas, no mesmo prato, colocasse algo apodrecendo ali que cheira muito mal, tenho tendência a rejeitar o prato inteiro. E se você é como eu a esse respeito e faz algo que convida todos os tipos de acréscimos como algo chamado "comum", você pode estar olhando para essa placa analógica com algo apodrecendo ao lado. Da mesma forma, acho que é bom se uma biblioteca for organizada, nomeada e documentada de uma maneira que não t convide cada vez mais adições ao longo do tempo. E isso pode se aplicar às suas criações pessoais, já que eu certamente criei algumas coisas podres aqui e ali, e isso "prejudica" muito menos se não estiver sendo adicionado ao prato maior. Separar as coisas em bibliotecas pequenas e muito singulares também tem a tendência de desacoplar melhor o código, apenas pela simples virtude de que se torna muito menos conveniente começar a acoplar tudo.

A desduplicação de código foi martelada em mim ao longo dos anos, mas acho que devo tentar desta vez.

O que eu poderia sugerir no seu caso é começar a facilitar a desduplicação de código. Não estou dizendo para copiar e colar grandes trechos de código mal testado e propenso a erros, ou qualquer coisa desse tipo, ou duplicar grandes quantidades de código não trivial que tem uma probabilidade decente de exigir alterações no futuro.

Mas, especialmente, se você tem a mentalidade de criar uma biblioteca "comum", para a qual suponho que seu desejo é criar algo amplamente aplicável, altamente reutilizável e, talvez, idealmente, algo que você ache tão útil hoje quanto em uma década a partir de agora. , às vezes você pode precisar ou desejar duplicação para obter essa qualidade indescritível. Porque a duplicação pode realmente servir como um mecanismo de dissociação. É como se você deseja separar um reprodutor de vídeo de um MP3, pelo menos você precisa duplicar algumas coisas, como baterias e discos rígidos. Eles não podem compartilhar essas coisas ou então são indivisivelmente acoplados e não podem ser usados ​​independentemente um do outro, e nesse ponto as pessoas podem não estar mais interessadas no dispositivo se tudo o que querem é tocar MP3. Mas, algum tempo depois de dividir esses dois dispositivos, você pode descobrir que o MP3 player pode se beneficiar de um design de bateria diferente ou de um disco rígido menor que o do player de vídeo, quando não está mais duplicando nada; o que inicialmente começou como duplicação para permitir que esse dispositivo interdependente se dividisse em dois dispositivos independentes e separados pode resultar em projetos e implementações que não são mais redundantes.

Vale a pena considerar as coisas da perspectiva de quem usa uma biblioteca. Você realmente gostaria de usaruma biblioteca que minimiza a duplicação de código? As chances são de que você não vai, porque uma que depende naturalmente dependerá de outras bibliotecas. E essas outras bibliotecas podem depender de outras para evitar duplicar seu código, e assim por diante, até que você precise importar / vincular 50 bibliotecas diferentes para obter apenas algumas funcionalidades básicas, como carregar e reproduzir um arquivo de áudio, e isso se torna muito difícil de manejar. . Enquanto isso, se essa biblioteca de áudio deliberadamente optar por duplicar algumas coisas aqui e ali para alcançar sua independência, torna-se muito mais fácil usar em novos projetos, e as chances são de que não precisará ser atualizada com tanta frequência, pois ganhou ' não é necessário mudar como resultado de uma mudança nas suas bibliotecas externas dependentes, o que pode estar tentando cumprir um propósito muito mais generalizado do que o que a biblioteca de áudio precisa.

Portanto, às vezes vale a pena escolher deliberadamente duplicar um pouco (conscientemente, nunca por preguiça - na verdade por diligência), a fim de desacoplar uma biblioteca e torná-la independente porque, por meio dessa independência, ela alcança uma ampla gama de aplicabilidade prática e estabilidade uniforme (sem acoplamentos aferentes). Se você deseja projetar as bibliotecas mais reutilizáveis ​​possíveis, que vão durar de um projeto para o próximo e ao longo dos anos, além de reduzir seu escopo ao mínimo, eu sugeriria considerar a possibilidade de duplicar um pouco aqui. E, naturalmente, escreva testes de unidade e verifique se ele é realmente testado e confiável no que está fazendo. Isso é apenas para as bibliotecas que você realmente deseja dedicar um tempo para generalizar a um ponto que vai muito além de um único projeto.


fonte
3

Existem três categorias diferentes de funções que você pode considerar colocar nas bibliotecas:

  1. Coisas que valem a pena reutilizar para todos.
  2. Só vale a pena reutilizar coisas para sua organização.
  3. Material que não vale a pena reutilizar.

A categoria um é algo para o qual uma biblioteca padrão deveria existir, mas, por algum motivo, ninguém conseguiu criar uma (ou alguém? Você pesquisou minuciosamente?). Nesse caso, considere tornar sua biblioteca de código aberto. Compartilhar seu trabalho não apenas ajuda os outros, mas também ajuda, porque você receberá relatórios de bugs e correções de outros usuários. Quando você duvida que alguém contribuiria para sua biblioteca, você pode estar lidando com funcionalidades que são realmente as categorias 2 ou 3.

A segunda categoria são as coisas que você precisa repetidas vezes, mas ninguém mais precisa no mundo. Por exemplo, a implementação do protocolo de rede obscuro para se comunicar com seu sistema de back-end desenvolvido internamente. Nesse caso, pode fazer sentido colocar essas coisas em uma biblioteca interna para melhorar a velocidade de desenvolvimento de novos aplicativos. Apenas certifique-se de que não seja muito afetado pela fluência dos recursos e comece a conter itens que realmente se encaixam nas categorias 1 ou 3. Além disso, o conselho da Blrfl sobre a modularização é muito bom: não crie uma biblioteca monolítica da Conor Corp. Crie várias bibliotecas separadas para funcionalidades separadas.

A categoria três é uma funcionalidade que é tão trivial de implementar que movê-la para uma biblioteca não vale a pena ou onde você não tem certeza de que precisará dela novamente exatamente dessa forma em outro aplicativo. Essa funcionalidade deve permanecer parte do aplicativo para o qual foi desenvolvido. Em caso de dúvida, provavelmente pertence a esta categoria.

Philipp
fonte
1

Quase todos os idiomas têm uma biblioteca comum / padrão, então isso é amplamente reconhecido como uma boa ideia. Usar bibliotecas de terceiros para várias tarefas em vez de reinventar a roda também é geralmente considerado uma boa idéia, embora o custo / benefício e a qualidade da biblioteca devam obviamente ser avaliados em cada caso.

Depois, há as bibliotecas de "utilitários comuns" usadas por um desenvolvedor ou instituição individual em projetos não relacionados. Esse é o tipo de biblioteca que pode ser considerado um antipadrão. No caso que eu vi, essas bibliotecas apenas replicam a funcionalidade de bibliotecas padrão ou de bibliotecas de terceiros mais conhecidas de maneira não-padrão e mal documentada.

JacquesB
fonte
these libraries just replicate functionality from standard librariesisso não é inteiramente uma coisa ruim, em javascript que você adicionou bibliotecas que já implementam as coisas existente para adicionar suporte on antigos motores js, você também tem libs apoio android para o SDK mais velhos etc ..
svarog
@svarog: Você está pensando em "polyfills", que emula a funcionalidade em novos padrões para mecanismos mais antigos que não o suportam nativamente? Não vejo motivo para escrevê-los, já que existem bibliotecas de código aberto conhecidas para esses fins.
JacquesB
0

A maioria das bibliotecas compartilhadas entre equipes causa mais problemas do que resolve. "O caminho para o inferno é pavimentado com boas intenções."

Exceções são bibliotecas que satisfazem a maioria dos itens abaixo:

  • Ter financiamento seguro de manutenção a longo prazo
  • Tenha uma equipe / comunidade de suporte dedicada
  • Tenha um bugtracker
  • Tenha ampla cobertura de teste
  • Tenha um objetivo único e bem definido
  • Não possuem dependências próprias (tanto na compilação quanto no tempo de execução), além das bibliotecas padrão ou quase-padrão
  • Tenha uma distinção clara entre API pública e internas
  • Ter um canal de comunicação e processo para todos / muitos usuários concordarem com novos recursos e lançamentos

Nas empresas típicas (não iniciantes), quase nenhuma das condições acima está presente para bibliotecas compartilhadas entre equipes. Isso ocorre porque a maioria das equipes da empresa é paga para entregar produtos, não bibliotecas. Algumas empresas têm estratégias de compartilhamento bem-sucedidas, como o monorepo do Google, mas isso vem com um investimento muito alto na infraestrutura de construção e teste.

tkruse
fonte