¿Hay casos excepcionales en los que podemos aceptar códigos duplicados?

57

Estoy trabajando en un proyecto de software donde tenemos que construir tres API. Uno para el canal de banca doméstica , uno para el canal de la agencia y un tercero para el canal móvil .

La API de la agencia es la más completa ya que tiene todas las funcionalidades ... luego una API de inicio un poco más pequeña y luego una API móvil.

Los arquitectos aquí crearon una capa común (servicios EJB de canal cruzado compartidos por todas las API). Pero entonces las API son diferentes.

No hay una gran diferencia por ahora entre las API. El gran equipo comenzó con el canal de la agencia, y ahora lo estamos adaptando para el canal local. Solo estamos enriqueciendo objetos específicamente para nuestra aplicación de inicio. De lo contrario, el código es 95% similar entre las API. La API está construida sobre Spring MVC , y tiene (controladores, modelos y algunas utilidades).

Básicamente, los controladores están haciendo el mapeo de BO a ChannelObject (me parece que no es el lugar correcto para hacerlo), y algunas utilidades y serializadores adicionales. Todo está duplicado por ahora. Dicen que la razón de la duplicación es que quieren que las API sean independientes. "¡Si mañana queremos un comportamiento diferente para el hogar que la agencia o el móvil, no tendremos problemas!"

¿Hay algún caso en el que debamos aceptar código duplicado?

Mohamed Amine Mrad
fuente
22
Y si tres mañana en el futuro deciden que quieren acceso y representación de datos consistentes entre todas las API ... bueno ... "¡Lucha!"
Becuzz
26
El código duplicado no es necesariamente algo malo. El dicho "DRY es el enemigo del desacoplamiento" no se puede enfatizar lo suficiente. Habiendo dicho eso, diseñar para el futuro, en lugar de por ahora, realmente es algo muy malo. Ese futuro casi nunca se cumple. En cambio, diseñe una solución altamente desacoplada, cubierta por buenas pruebas automatizadas para lo que se necesita ahora. Luego, en el futuro, si se necesita algo diferente, será más fácil cambiarlo.
David Arno
2
Creo que los dos casos en los que nunca me he arrepentido de duplicar código son (a) donde el código duplicado es una parte muy pequeña y no muy importante del total, y (b) donde estoy copiando código de un sistema que ya está muriendo en un nuevo sistema diseñado para la longevidad. Hay muchos otros casos en los que lloré lágrimas amargas después de bifurcar el código.
Michael Kay
14
Donde he trabajado, a menudo se ha notado que uno debería (a menudo) duplicar una vez y abstraer lo común la tercera vez. Esto elimina el espíritu de época para la abstracción temprana, posiblemente inapropiada, que aumenta el acoplamiento en lugar de la cohesión. Cuando los requisitos futuros se entienden bien, por supuesto, se pueden hacer excepciones.
Pieter Geerkens
3
Un caso trivial en el que el código duplicado puede ser aceptable es si se genera automáticamente
samgak

Respuestas:

70

La duplicación puede ser lo correcto, pero no por este motivo.

Decir "podríamos querer que estos tres lugares en la base del código se comporten de manera diferente aunque en este momento sean idénticos" no es una buena razón para la duplicación a gran escala. Esta noción podría aplicarse a todos los sistemas y podría usarse para justificar cualquier duplicación, lo que obviamente no es razonable.

La duplicación debe tolerarse solo cuando eliminarla sería, en general, más costoso ahora por alguna otra razón (no puedo pensar en una buena en este momento, pero tenga la seguridad de que puede haber una: prácticamente todo en programación es una compensación en lugar de un ley).

Para lo que está haciendo, la solución correcta podría ser, por ejemplo, extraer el comportamiento que está duplicado en este momento en una Estrategia o algún otro patrón que modele el comportamiento como clases y luego use tres instancias de la misma clase. De esa manera, cuando no quiere cambiar el comportamiento de uno de los tres lugares, es suficiente con crear una nueva estrategia y cree una instancia que en un solo lugar. De esa manera, solo tiene que agregar algunas clases y dejar el resto de la base de código casi intacta.

Kilian Foth
fuente
1
Si dos cosas se comportan igual, eso es duplicación. Aunque a menos que se comporten de la misma manera por una razón, no deberían fusionarse. ¿Se puede extraer alguna parte común de las implementaciones? A veces hay bloques de construcción aún no abstraídos, quién sabe.
Deduplicador
32
Como ejemplo, teníamos un fragmento de código utilizado por tres partes de nuestra aplicación, y estaba muy terriblemente escrito con muchas pequeñas ramas condicionales en todas partes para cubrir excepciones para cada una de esas tres. Pero , y esto era lo importante, se había usado mucho durante dos años sin ningún informe de error significativo de ningún tipo. Entonces, cuando una cuarta parte de la aplicación necesitaba hacer algo con ella, pero de nuevo ligeramente diferente, se tomó la decisión de no tocar el código defectuoso que se ejecutó sin problemas, y simplemente copiarlo y crear una base flexible mejor escrita para el futuro.
KRyan
2
La duplicación es lo correcto cuando los costos de legibilidad aumentan demasiado en comparación con los costos de mantenimiento que terminan vinculados. No creo que esto se aplique en absoluto en este escenario, y a menudo se ve a menor escala en un proyecto, no algo así. Incluso entonces, es muy raro que se encuentre en una situación en la que este sea el caso, sucederá si tiene alguna duplicación anidada de nicho que lo obligaría a usar el patrón de Método de plantilla o similar, pero en lugares pequeños. Esto generalmente terminará ofuscando el código.
opa
Un ejemplo en el que la duplicación es útil (y "eliminarla en general ahora será más costoso ") es la valuación de entrada en las aplicaciones web: empleamos una primera validación (posiblemente simplificada) en el cliente para que el usuario reciba comentarios inmediatos sobre los problemas; y luego hacemos la misma validación (o más exhaustiva) en el servidor porque no se puede confiar en los clientes.
Hagen von Eitzen
3
@HagenvonEitzen Pero eso no necesita ser duplicación, dependiendo de la pila de tecnología. Por ejemplo, es posible que tenga la entrada de verificación de JavaScript en el navegador y luego la verificación de Java en el servidor, por lo que tiene una funcionalidad duplicada. Pero si tuviera que ejecutar Node.js en el servidor, podría usar la misma validación de JavaScript en el navegador y en el servidor, eliminando la duplicación. Todavía desea que el código se ejecute en varios lugares (un entorno confiable y no confiable), pero el código no necesariamente tiene que duplicarse.
Joshua Taylor
87

Sandi Metz, una reconocida ingeniera de software y autora en el ecosistema de Ruby, tiene una excelente publicación de blog y una charla en la que también habla sobre la relación entre la duplicación y la abstracción. Ella llega a la siguiente conclusión

la duplicación es mucho más barata que la abstracción incorrecta

Y estoy completamente de acuerdo con ella. Déjame darte más contexto a esta cita. A veces, encontrar la abstracción correcta es muy difícil. En tales casos, es tentador ir a cualquier abstracción para reducir la duplicación. Pero más tarde puede descubrir que su abstracción no es válida para todos los casos. Sin embargo, es costoso cambiar todo nuevamente y tomar una ruta diferente (para una explicación mucho mejor, ¡mira cómo habla!).

Entonces, sí, para mí, hay casos excepcionales, donde aceptar la duplicación es la decisión correcta, especialmente cuando no está seguro de lo que vendrá y es probable que los requisitos cambien. De su publicación, supongo que ahora hay mucha duplicación, pero sus colegas sugieren que esto podría cambiar y no acoplar ambas aplicaciones entre sí. En mi opinión, este es un argumento válido y no se puede ignorar en general.

larsbe
fuente
23
Ah sí, si no puede deducir las reglas generalizadas, tratar de abstraerlas solo puede fallar. Y si la correspondencia es casual, puede ser de corta duración.
Deduplicador
55
Puede ser costoso cambiar una abstracción una vez que está en su lugar, pero deshacerse de la duplicación una vez que está en su lugar puede ser aún más problemático. Por supuesto, esto depende mucho del idioma, etc., los sistemas modernos de tipo fuerte-estático pueden hacer un buen trabajo al ayudarlo a obtener incluso cambios a gran escala para una correcta abstracción. Sin embargo, mantener las características duplicadas sincronizadas no es algo con lo que un sistema de tipos pueda ayudarlo mucho (porque los duplicados son, para el sistema de tipos , simplemente diferentes). Pero supongo que en lenguajes dinámicos, tipo pato, es más bien al revés; entonces podría tener sentido para Ruby.
Leftaroundabout
34

Si la gente comienza a razonar sobre el diseño con las palabras "si mañana" , esto es a menudo una gran señal de advertencia para mí, especialmente cuando el argumento se usa para justificar una decisión que incluye trabajo y esfuerzo adicionales, por lo que nadie sabe realmente si esto alguna vez pagar, y que es más difícil de cambiar o revertir que la decisión opuesta.

La duplicación de código reduce el esfuerzo solo a corto plazo, pero aumentará los esfuerzos de mantenimiento casi de inmediato, proporcionalmente a la cantidad de líneas de código duplicadas. Tenga en cuenta también que una vez que se duplica el código, será difícil eliminar la duplicación cuando resulte que esta fue una decisión incorrecta, mientras que si uno no duplica el código ahora, aún es fácil introducir la duplicación más tarde si se queda pegado a DRY Fue la decisión equivocada.

Dijo que, en organizaciones más grandes, a veces es beneficioso favorecer la independencia de los diferentes equipos sobre el principio DRY. Si eliminando la duplicación extrayendo el 95% de las partes comunes de las API dos, un nuevo componente conduce a un acoplamiento de dos equipos independientes, esta podría no ser la mejor decisión. Por otro lado, si tiene recursos limitados y solo habrá un equipo que mantenga ambas API, estoy seguro de que será de su interés no crear ningún esfuerzo doble y evitar cualquier duplicación innecesaria de código.

Tenga en cuenta además que hace una diferencia si las API "Home" y "Agency" son utilizadas exclusivamente por aplicaciones completamente diferentes, o si uno puede intentar escribir una compilación de componentes sobre esas API que también se pueden usar en un contexto "Home" como en un contexto de "Agencia". Para esta situación, tener las partes comunes de las API exactamente idénticas (que solo puede garantizar si las partes comunes no están duplicadas), hará que el desarrollo de dicho componente sea mucho más fácil.

Entonces, si resulta que habrá sub equipos realmente diferentes, cada uno responsable de cada API, cada uno con un horario y recursos diferentes, entonces es hora de duplicar el código, pero no "por si acaso".

Doc Brown
fuente
3
Una señal de advertencia aún mayor que "si mañana" para mí es "Eso nunca cambiaría".
abuzittin gillifirca
@abuzittingillifirca: ¿y qué tiene esto que ver con la pregunta o mi respuesta?
Doc Brown
1
Estoy desafiando tu primer párrafo.
abuzittin gillifirca
2
@abuzittingillifirca: bueno, lea la pregunta nuevamente: se trata de razonar con un argumento de "si mañana" para justificar una decisión de duplicación que es difícil de revertir , por lo que el software es realmente más difícil de cambiar en ciertos casos. Puede ser un poco contradictorio, pero la mejor manera de mantener el software modificable para el futuro no es hacer suposiciones (probablemente incorrectas) sobre el futuro, sino mantener el software lo más pequeño y SÓLIDO posible.
Doc Brown
13

Duplicación para evitar el acoplamiento . Digamos que tiene dos sistemas grandes y los obliga a usar la misma biblioteca. Puede estar acoplando el ciclo de lanzamiento de ambos sistemas. Puede que esto no sea tan malo, pero digamos que un sistema necesita introducir un cambio. El otro necesita analizar el cambio y puede verse afectado. A veces puede romper cosas. Incluso si ambas partes pueden coordinar los cambios, podría haber muchas reuniones, pasando por gerentes, pruebas, dependencias y el final del pequeño equipo autónomo.

Entonces está pagando el precio del código duplicado para ganar autonomía e independencia.

Borjab
fuente
Entonces esta es una duplicación entre componentes para evitar el acoplamiento. Pero igual querrás evitar la duplicación intracomponente, ¿verdad?
TemplateRex
Bueno, sí, desea minimizar el código duplicado, ya que hará que su código sea más fácil de entender y modificar. Pero recuerde que hay buenas respuestas con excepciones viables. La abstracción correcta para evitar la duplicación puede ser difícil de encontrar.
Borjab