¿Es buena idea una biblioteca común?

16

Siempre he pensado que una "biblioteca común" era una buena idea. Con eso quiero decir una biblioteca que contiene la funcionalidad común que a menudo necesitan algunas aplicaciones diferentes. Resulta en menos duplicación de código / redundancia.

Hace poco leí un artículo (no puedo encontrarlo ahora) que decía que en realidad es una mala idea y llegó a decir que era un "antipatrón"

Si bien hay ventajas en este enfoque. El control de versiones y la gestión del cambio significa una prueba de regresión para el conjunto de aplicaciones que usan esta biblioteca.

Estoy atascado en una rutina para mi nuevo proyecto (Golang). La desduplicación del código se ha introducido en mí a lo largo de los años, pero siento que debería intentarlo esta vez.

Mientras escribo esto, ¿estoy empezando a pensar que este enfoque de "lib común" es el resultado del descremado en la arquitectura? ¿Quizás mi diseño necesita más reflexión?

Interesado en escuchar pensamientos.

jim
fuente
2
Mis 2 centavos ... Si tiene que hacer cambios a la API común si uno de los sistemas lo necesita, ese API o biblioteca no es una biblioteca común
Raja Anbazhagan
1
Me parece que hay una compensación fundamental entre la duplicación de código y el acoplamiento. Su pregunta es un excelente ejemplo de eso. El equilibrio que establezca entre los dos probablemente dependerá del entorno en el que finalmente se ejecutará su código.
Joel Cornett
La mayoría de los desarrolladores de C que conozco tienen una colección de utilidades a las que se refieren como su "caja de herramientas". Sin embargo, por lo general no se recopila en una sola biblioteca que se puede incluir. Es más "escoger y elegir".
Mark Benningfield
2
Alguien llame a Apache y arregle esta locura. Apache commons
Laiv

Respuestas:

21

Las bibliotecas y la reutilización son absolutamente una buena cosa. Tienen una desventaja gigante, que es que si no se maneja con cuidado, se convierten en el equivalente del cajón de su cocina que tiene todas las probabilidades y los extremos que no van a ningún otro lado.

Vi esto en acción cuando me hice responsable de los primeros puertos del código de una unidad de negocio completa (en su mayoría nuevos para mí) a los sistemas de 64 bits y haciendo una revisión completa de nuestra compilación y empaque, muchos de los cuales se estaban haciendo. a mano y, a veces, no muy bien. * Tendemos a construir lo que enviamos de una pila de aplicaciones, donde el cliente decía: "Me gustaría un sistema que haga A, B, D y F, además de cosas M y N que aún no hagas y pegamento ligeramente diferente que los integra a todos ". Todo lo que tenía en común era una biblioteca de cajones de basura que, durante un par de décadas, ** había acumulado todas las cosas que la gente pensaba que deberían ser reutilizables. Para resumir, una fracción del código en la biblioteca no era. Estábamos gastando mucho tiempo valioso construyendo y manteniendo esas dependencias solo para que la biblioteca común se instalara, no porque realmente las necesitáramos.

La moraleja es que las bibliotecas deben tratarse como clases y no sobrecargarse con demasiadas responsabilidades. No coloque su analizador JSON en la misma biblioteca con sus funciones de álgebra lineal, incluso si cada programa que está escribiendo usa ambos.

Mantenerlos discretos tiene muchos beneficios, el mayor de los cuales es que obliga a sus desarrolladores y empacadores a elaborar una contabilidad detallada de lo que realmente necesitan sus propios productos en lugar de simplemente incluir el cajón de basura y el equipaje que viene con él. Cuando configura un sistema utilizando los paquetes integrados, las dependencias específicas aseguran que solo se instalen las partes necesarias. Incluso si descuida su repositorio y continúa compilando las cosas viejas y crudas, nada de lo que ya no está en uso se filtra en lo que envía.

Hay, por supuesto, excepciones como libcque agrupan muchas funciones en una biblioteca. Ese es uno de los casos en los que los beneficios de hacerlo de esa manera se pueden razonar en lugar de prestar atención ciegamente al fanático del pasillo que insiste en que cualquier otra forma que X siempre es una mala práctica.


* En el proceso, descubrí un binario que se había pasado y que no se había compilado desde cero en seis años.

** No hay nada malo con el código de décadas. Teníamos una serie de algoritmos críticos que habían sido tan bien probados que habría sido una tontería reescribirlos únicamente en interés de la modernidad.

Blrfl
fuente
1
Mi regla general es que si tiene la intención de nombrar a su biblioteca como "común", se enfrentará a problemas.
Karl Bielefeldt
9

Vergonzosamente, introduje una biblioteca "común", nombrada como tal, en un entorno de equipo hace un par de décadas. Realmente no entendía la dinámica de lo que podría suceder en un entorno de equipo poco coordinado en cuestión de meses.

Cuando lo presenté, pensé que lo había dejado claro y también documenté que es para cosas que todos estaríamos de acuerdo en que encontramos útiles a diario, que pretende ser una biblioteca minimalista y que la biblioteca no debe depender de nada más que biblioteca estándar para que sea tan fácil de implementar como sea posible en nuevos proyectos. En ese momento, pensaba que era nuestra pequeña extensión de la biblioteca estándar para cosas que, en nuestro dominio particular, encontramos útiles a diario.

Y comenzó lo suficientemente bien. Comenzamos con una biblioteca matemática (common/math* ) de rutinas que todos usábamos a diario, ya que estábamos trabajando en gráficos de computadora que a menudo eran pesados ​​en el álgebra lineal. Y como a menudo interaccionábamos con el código C, acordamos algunas funciones útiles de utilidad como las find_indexcuales, a diferencia destd::finden C ++, devolvería un índice a un elemento encontrado en una secuencia en lugar de un iterador que imitaba cómo funcionaban nuestras funciones en C, cosas de este tipo, un poco eclécticas pero minimalistas y lo suficientemente utilizadas como para ser familiares y prácticas para todos , y la familiaridad instantánea es un criterio extremadamente importante, ya que lo veo al tratar de hacer cualquier cosa que sea "común" o "estándar", ya que si realmente es "común", debería tener esa calidad familiar como resultado de su amplia adopción y uso diario.

Pero con el tiempo, las intenciones de diseño de la biblioteca se escaparon de mis dedos cuando la gente comenzó a agregar cosas que usaban personalmente que simplemente pensaron que podrían ser útiles para otra persona, solo para encontrar a nadie más usándola. Y más tarde, alguien comenzó a agregar funciones que dependían de OpenGL para rutinas comunes relacionadas con GL. Más adelante, adoptamos Qt y la gente comenzó a agregar código que dependía de Qt, por lo que la biblioteca común ya dependía de dos bibliotecas externas. En algún momento, alguien agregó rutinas de sombreadores comunes que dependían de nuestra biblioteca de sombreadores específicos de la aplicación, y en ese punto ni siquiera podía implementarlo en un nuevo proyecto sin incorporar Qt, OGL y nuestra biblioteca de sombreadores específicos de la aplicación y escribir un script de compilación no trivial para su proyecto. Entonces se convirtió en este lío ecléctico e interdependiente.

Pero también descubrí al debatir qué debería o no entrar en esta biblioteca que lo que se considera "común" puede convertirse fácilmente en una idea muy subjetiva si no establece una regla de línea muy estricta de que lo que es "común" es lo que todos tienden a encontrar útil a diario. Cualquier aflojamiento de los estándares y se degrada rápidamente de cosas que todos encuentran útiles diariamente a algo que un desarrollador único encuentra útil que podría tener la posibilidad de ser beneficioso para otra persona, y en ese momento la biblioteca se degrada en un lío ecléctico muy rápido .

Pero, además, cuando llegue a ese punto, algunos desarrolladores pueden comenzar a agregar cosas por la simple razón de que no les gusta el lenguaje de programación. Es posible que no les guste la sintaxis de un bucle for o una llamada de función, momento en el que la biblioteca comienza a llenarse de cosas que solo combaten la sintaxis fundamental del lenguaje, reemplazando un par de líneas de código directo que no es realmente duplicar cualquier lógica en una sola línea concisa de código exótico que solo es familiar para el desarrollador que introdujo tal taquigrafía. Entonces, tal desarrollador podría comenzar a agregar más funcionalidades a la biblioteca común implementada usando tales shorthands, en ese punto, secciones importantes de la biblioteca común se entrelazan con estas exóticas shorthands que pueden parecer hermosas e intuitivas para el desarrollador que la introdujo, pero feas, extrañas y difíciles de entender para todos los demás. Y en ese punto creo que sabes que cualquier esperanza de hacer algo verdaderamente "común" se pierde, ya que "común" y "desconocido" son ideas polares opuestas.

Así que hay todo tipo de latas de gusanos allí, al menos en un entorno de equipo poco coordinado, con una biblioteca con ambiciones tan amplias y tan generalizadas como simplemente "cosas de uso común". Y aunque el problema subyacente podría haber sido la falta de coordinación por encima de todo lo demás, al menos varias bibliotecas destinadas a servir a un propósito más singular, como una biblioteca destinada a proporcionar rutinas matemáticas y nada más, probablemente no se degradarían tan significativamente en términos de su Diseñe la pureza y las dependencias como una biblioteca "común". Entonces, en retrospectiva, creo que sería mucho mejor errar del lado de las bibliotecas que tienen intenciones de diseño mucho más claras. También he descubierto a lo largo de los años que el propósito limitado y la aplicabilidad limitada son ideas radicalmente diferentes.

Además, al menos soy un poco poco práctico y me importa demasiado la estética, pero la forma en que tiendo a percibir mi idea de la calidad de una biblioteca (y tal vez incluso la "belleza") se juzga más por su eslabón más débil que es más fuerte, de manera similar que si me presentas la comida más atractiva del mundo pero, en el mismo plato, pones algo podrido allí que huele realmente mal, tiendo a querer rechazar todo el plato. Y si eres como yo en ese sentido y haces algo que invita a todo tipo de adiciones como algo llamado "común", es posible que te encuentres mirando esa placa analógica con algo podrido a un lado. Del mismo modo, creo que es bueno si una biblioteca está organizada, nombrada y documentada de tal manera que no Invite a más y más y más adiciones con el tiempo. Y eso incluso puede aplicarse a sus creaciones personales, ya que ciertamente he creado algunas cosas podridas aquí y allá, y "contamina" mucho menos si no se agrega al plato más grande. Separar las cosas en bibliotecas pequeñas y muy singulares también tiende a desacoplar mejor el código, aunque solo sea por la simple virtud de que resulta mucho menos conveniente comenzar a acoplar todo.

La desduplicación del código se ha introducido en mí a lo largo de los años, pero siento que debería intentarlo esta vez.

Lo que podría sugerir en su caso es comenzar a tomarlo con calma en la deduplicación de código. No estoy diciendo que copie y pegue fragmentos grandes de códigos propensos a errores mal probados ni nada por el estilo, ni duplicar grandes cantidades de código no trivial que tiene una probabilidad decente de requerir cambios en el futuro.

Pero especialmente si tiene la mentalidad de crear una biblioteca "común", para lo cual asumo que su deseo es crear algo ampliamente aplicable, altamente reutilizable y quizás idealmente algo que encuentre tan útil hoy como lo hace dentro de una década. , a veces incluso puede que necesite o desee duplicación para lograr esta escurridiza calidad. Porque la duplicación en realidad podría servir como un mecanismo de desacoplamiento. Es como si quisieras separar un reproductor de video de un reproductor de MP3, entonces al menos tienes que duplicar algunas cosas como baterías y discos duros. No pueden compartir estas cosas o de lo contrario están acoplados de manera indivisible y no se pueden usar independientemente el uno del otro, y en ese momento las personas podrían no estar interesadas en el dispositivo si todo lo que quieren hacer es reproducir MP3. Pero algún tiempo después de separar estos dos dispositivos, es posible que el reproductor de MP3 pueda beneficiarse de un diseño de batería diferente o un disco duro más pequeño que el reproductor de video, en ese momento ya no está duplicando nada; lo que inicialmente comenzó como una duplicación para permitir que este dispositivo interdependiente se dividiera en dos dispositivos separados e independientes podría resultar en diseños e implementaciones que ya no son redundantes.

Vale la pena considerar las cosas desde la perspectiva del que usa una biblioteca. ¿De verdad quieres usar¿Una biblioteca que minimiza la duplicación de código? Lo más probable es que no lo haga porque uno que sí dependerá naturalmente de otras bibliotecas. Y esas otras bibliotecas pueden depender de otras bibliotecas para evitar duplicar su código, y así sucesivamente, hasta que necesite importar / vincular 50 bibliotecas diferentes para obtener algunas funciones básicas como cargar y reproducir un archivo de audio, y eso se vuelve muy difícil de manejar . Mientras tanto, si una biblioteca de audio de este tipo eligió deliberadamente duplicar algunas cosas aquí y allá para lograr su independencia, se volverá mucho más fácil de usar en nuevos proyectos, y es probable que no necesite actualizarse tan a menudo desde que ganó ' No es necesario cambiar como resultado de una modificación de sus bibliotecas externas dependientes que podrían estar tratando de cumplir un propósito mucho más generalizado que el que necesita la biblioteca de audio.

Entonces, a veces vale la pena elegir deliberadamente duplicar un poco (conscientemente, nunca por pereza, en realidad por diligencia) para desacoplar una biblioteca y hacerla independiente porque, a través de esa independencia, logra una gama más amplia de aplicabilidad práctica y incluso estabilidad (no más acoplamientos aferentes). Si desea diseñar las bibliotecas más reutilizables posibles que le duren de un proyecto al siguiente y a lo largo de los años, además de reducir su alcance al mínimo, sugeriría considerar duplicar un poco aquí. Y, naturalmente, escriba pruebas unitarias y asegúrese de que esté realmente probado y confiable en lo que está haciendo. Esto es solo para las bibliotecas que realmente desea tomarse el tiempo para generalizar a un punto que va mucho más allá de un solo proyecto.


fuente
3

Hay tres categorías diferentes de funciones que puede considerar poner en bibliotecas:

  1. Cosas que vale la pena reutilizar para todos.
  2. Cosas que solo vale la pena reutilizar para su organización.
  3. Cosas que no vale la pena reutilizar.

La categoría uno es algo para lo que debería existir una biblioteca estándar , pero por alguna razón nadie se las arregló para crear una (¿o alguien hizo una búsqueda exhaustiva? En ese caso, considere hacer que su biblioteca sea de código abierto. Compartir su trabajo no solo ayuda a otros, también lo ayuda a usted, porque recibirá informes de errores y parches de otros usuarios. Cuando dude de que alguien contribuya a su biblioteca, entonces podría estar lidiando con una funcionalidad que en realidad es de categoría 2 o 3.

La segunda categoría son cosas que necesita una y otra vez, pero nadie más en el mundo lo necesita. Por ejemplo, la implementación del oscuro protocolo de red para comunicarse con su sistema interno desarrollado internamente. En ese caso, podría tener sentido colocar esas cosas en una biblioteca interna para mejorar la velocidad de desarrollo de nuevas aplicaciones. Solo asegúrese de que no se vea demasiado afectado por el arrastre de características y comience a contener cosas que realmente se ajusten a las categorías 1 o 3. Además, el consejo de Blrfl con respecto a la modularización es muy bueno: no cree una biblioteca monolítica de Conor Corp. Crear múltiples bibliotecas separadas para funcionalidades separadas.

La categoría tres es una funcionalidad que es tan trivial de implementar que no vale la pena moverla a una biblioteca o donde no está seguro de que alguna vez la volverá a necesitar exactamente en esa forma en otra aplicación. Esta funcionalidad debería seguir siendo parte de la aplicación para la que se desarrolló. En caso de duda, probablemente pertenece a esta categoría.

Philipp
fuente
1

Casi todos los idiomas tienen una biblioteca común / estándar, por lo que se reconoce ampliamente como una buena idea. El uso de bibliotecas de terceros para diversas tareas en lugar de reinventar la rueda también se considera generalmente una buena idea, aunque el costo / beneficio y la calidad de la biblioteca obviamente deben evaluarse en cada caso.

Luego están las bibliotecas de "utilidades comunes" utilizadas por un desarrollador o institución individual en proyectos que de otro modo no estarían relacionados. Este es el tipo de biblioteca que podría considerarse un antipatrón. En el caso que he visto, estas bibliotecas simplemente replican la funcionalidad de bibliotecas estándar o bibliotecas de terceros más conocidas de una manera no estándar y mal documentada.

JacquesB
fuente
these libraries just replicate functionality from standard librariesesto no es del todo malo, en javascript ha agregado bibliotecas que ya implementan cosas existentes para agregar soporte en motores js antiguos, también tiene libs de soporte de Android para sdk's antiguos, etc.
svarog
@svarog: ¿Está pensando en "polyfills" que emulan la funcionalidad en los nuevos estándares para motores más antiguos que no lo admiten de forma nativa? No veo ninguna razón para escribir esto usted mismo, ya que hay bibliotecas de código abierto conocidas disponibles para estos fines.
JacquesB
0

La mayoría de las bibliotecas compartidas entre los equipos causan más problemas de los que resuelven. "El camino al infierno está pavimentado con buenas intenciones."

Las excepciones son bibliotecas que satisfacen la mayoría de los siguientes:

  • Tener fondos de mantenimiento seguros a largo plazo
  • Tener un equipo de apoyo dedicado / comunidad
  • Tener un rastreador de errores
  • Tener una amplia cobertura de prueba
  • Tener un único propósito bien definido
  • No tienen dependencias (tanto en tiempo de compilación como en tiempo de ejecución), que no sean bibliotecas estándar o cuasi-estándar
  • Tener una distinción clara entre API pública e internos
  • Tenga un canal de comunicación y un proceso para que todos / muchos usuarios acuerden nuevas funciones y lanzamientos

Dentro de las empresas típicas (que no son de inicio), casi ninguna de las condiciones anteriores está presente para las bibliotecas compartidas entre los equipos. Esto se debe a que a la mayoría de los equipos de la compañía se les paga para entregar productos, no bibliotecas. Algunas compañías tienen estrategias de intercambio exitosas, como el monorepo de Google, pero esto conlleva una inversión muy alta en infraestructura de construcción y prueba.

tkruse
fuente