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::find
em 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.