¿Debería mi código ser SECO o legible si no puede ser ambos?

14

Estoy escribiendo código Ruby para un simple ejercicio de encriptación y con frecuencia me he encontrado con este dilema (el ejercicio es un cifrado solitario si debe saberlo). Se trata de si debo rellenar mi lógica con variables descriptivas y declaraciones de un solo paso que hacen que la función sea legible en lugar de declaraciones concisas, incluso densas, que eliminen la repetición y / o minimicen las oportunidades de errores.

Mi ejemplo más reciente: mi programa recibe información y, debido a las pautas de formato rígido, puede determinar fácilmente si la información debe cifrarse o descifrarse. Para simplificar, una vez que la clave de cifrado y el mensaje se convierten / generan para ser compatibles, se trata de restar la clave del mensaje cifrado o agregar la clave a un mensaje no cifrado, para obtener la salida deseada (piense en la clave como cifrado, mensaje + cifrado = código; código - cifrado = mensaje). La posición DRY me dice que debo convertir mi mensaje cifrado de manera diferente a mi mensaje no cifrado para que la función que toma la clave de cifrado y la aplica al mensaje nunca necesite distinguir. Descubrí que esto significa que necesito algunas instrucciones anidadas if en la función, pero la lógica parece ser sólida. Sin embargo, este código no es fácil de leer.

Por otro lado, podría escribir dos funciones diferentes que se invocan en función de un indicador que se establece cuando la aplicación determina el cifrado o descifrado. Esto sería más fácil de leer, pero duplicaría la función de alto nivel de aplicar la clave de cifrado a un mensaje (lo que hace que se cifre o descifre).

¿Debo inclinarme hacia un código legible o un código conciso? ¿O he perdido otra forma de obtener esta funcionalidad y satisfacer ambos principios? ¿Es una posición a lo largo de una escala en la que uno debe considerar el propósito del proyecto y tomar las mejores decisiones para cumplir ese propósito?

Hasta ahora, tiendo a enfatizar el código conciso y SECO sobre el código legible.

Lutze
fuente
2
Primero utilizable, segundo SECO, tercero legible. El código altamente utilizable da como resultado un código de consumo altamente legible que cumple más fácilmente DRY y legible. Es por eso que desea tomar complejidades desagradables y encajonarlas en algún lugar con una buena API, si no pueden mejorarse; al menos el código que interactúa con ellos se salvará de ser malo también con este enfoque.
Jimmy Hoffa
44
algunos ejemplos de código reales pueden ayudar, sospecho que falta alguna tercera vía, como las funciones de orden superior para tener un código seco y legible
jk.
2
No es un duplicado Conciso v. Legible y DRY v. Legible son dos comparaciones muy diferentes. No estar SECO es mucho más peligroso que no ser conciso.
djechlin
Simplemente no caigas en la trampa de que asumir un código muy duplicado es más legible porque cae en un patrón predecible. Sospecho que puede ser fácil pensar de esa manera, hasta que haya mantenido un sistema que se duplique tanto que encuentre errores sutiles en una rama que no estén en la otra, ramas casi idénticas.
KChaloux
No entiendo lo que quiere decir con "función de alto nivel de aplicar la clave de cifrado al mensaje". Si quiere decir que hay una superposición entre cifrar y descifrar, considere usar la refactorización del método de extracción para factorizar las partes comunes.
Aaron Kurtzhals

Respuestas:

20

DRY es una guía, no una religión. Aquellos que lo llevan al punto de SECO por encima de todo lo demás, lo han llevado demasiado lejos.

En primer lugar, el código utilizable es primordial. Si el código no es útil y utilizable, no lo es ... y no tiene sentido escribirlo en primer lugar.

En segundo lugar, algún día, alguien tendrá que mantener su código. Si su código no se puede mantener, romperán su diseño "hermoso" denso, conciso y SECO mientras maldicen su nombre. No hagas esto. He sido esa persona y cada vez que veo un cierto nombre en la anotación del código, me estremezco.

Hacer código denso que carece de "variables descriptivas" y pegar todo en expresiones ternarias anidadas con lambdas sin ninguna documentación es inteligente. Es bueno saber que puedes hacerlo, pero no lo hagas. El código inteligente es muy difícil de depurar. Evite escribir código inteligente .

La mayor parte del tiempo dedicado al software se dedica al mantenimiento del software, no a escribirlo la primera vez. Escriba el código para que usted (u otra persona) pueda corregir rápida y fácilmente los errores y agregar funciones según sea necesario con tan pocos cambios de diseño ideales.

Comunidad
fuente
Intuitivamente o no, has encontrado un problema que estaba pasando por alto. He estado escribiendo un código 'inteligente' tanto como pude para este ejercicio, solo para saber que podía hacerlo, lo cual acepto es bueno saber, pero estoy de acuerdo contigo aquí en que es una mala práctica. Ahora, tengo mucha refactorización que hacer.
lutze
1
@lutze Soy un codificador perl y, a veces, rubí, y ciertamente puedo entender la tentación de escribir código inteligente allí. Una versión previa de esta respuesta incluía una cita de Larry Wall sobre la arrogancia : creo que esta es una virtud muy importante cuando se trabaja con código que algún día se mantendrá. Debido a la posible brevedad de los idiomas, a veces es difícil obtener la ventaja de escribir más código en lugar de intentar un código inteligente y denso ... hasta que tenga que mantenerlo.
17

No estoy seguro según la pregunta que entiendes DRY. El código DRY no es lo mismo que conciso. Muy a menudo es lo contrario.

Aquí, no estoy seguro de cuál es el gran problema para este ejemplo. Haga una función para cifrar, una función para descifrar, ayudantes para la funcionalidad común (bytes de mezcla) y un front-end simple para tomar entradas y determinar cifrar / descifrar ... No se repita.

Sin embargo, en general, tanto DRY como legibilidad existen para ayudar a mantener y extender el código. No todos los escenarios son iguales, un gran golpe de legibilidad para eliminar una pequeña repetición no es bueno, y tampoco lo es un montón de duplicación para agregar un poco de legibilidad.

Si se presiona, preferiría la legibilidad. El código duplicado todavía se puede probar: el código ilegible hace que usted haga (y pruebe) lo incorrecto.

Telastyn
fuente
Grandes puntos, de hecho, había combinado un poco el principio DRY con el objetivo del código conciso.
lutze
12

No estoy familiarizado con su caso específico, pero puedo dar algunas pautas generales.

El propósito de la legibilidad y DRY es la mantenibilidad .

La capacidad de mantenimiento es importante en la mayoría de las situaciones. Pasará más tiempo manteniendo el código que escribiendo. Esto es especialmente cierto si considera que escribir código es un tipo especial de mantenimiento.

Desafortunadamente, DRY a menudo es mal entendido. Esto se debe en parte a que parece muy simple, pero como muchas cosas simples puede ser, bueno ... complicado.

La intención de DRY es que cada unidad de funcionalidad exista en un solo lugar. Si se sigue el principio, el responsable del código que tiene la tarea de cambiar o verificar esa funcionalidad puede manejar el código de una vez. Si no se sigue SECO, existe un peligro muy real de que alguna copia de la funcionalidad no se mantenga correctamente.

La violación más obvia de DRY es la codificación de copiar y pegar, donde bloques enteros de código se repiten textualmente en la base del código. Esto es sorprendentemente común en mi experiencia, y de ninguna manera se agrega a la legibilidad. Por lo tanto, refactorizar el código para introducir un método común aumenta invariablemente la conformidad y legibilidad de DRY.

La segunda violación más evidente es "copiar y pegar y cambiar un poco". Nuevamente, refactorizar para introducir un método común con parámetros, o dividir una función en pasos y abstraer los elementos comunes casi siempre aumenta la legibilidad.

Luego están las violaciones más sutiles, donde la funcionalidad se duplica pero el código es diferente. Esto no siempre es fácil de detectar, pero cuando lo ve y refactoriza para extraer el código común en un solo método / clase / función, entonces el código es usualmente más legible de lo que era antes.

Finalmente, están los casos de repetición de diseño. Por ejemplo, podría haber utilizado el patrón de estado varias veces en su base de código, y está considerando refactorizar para eliminar esta repetición. En este caso, proceda con precaución. Es posible que esté reduciendo la legibilidad al introducir más niveles de abstracción. Al mismo tiempo, en realidad no se trata de duplicar la funcionalidad sino de duplicar la abstracción. A veces vale la pena ... pero a menudo no lo es. Su principio rector será preguntar, "cuál de estos es más fácil de mantener".

Cada vez que hago estos juicios, trato de considerar la cantidad de tiempo que las personas pasarán manteniendo el código. Si el código es la funcionalidad principal del negocio, entonces probablemente necesitará más mantenimiento. En esos casos, mi objetivo es hacer que el código se pueda mantener para las personas que están familiarizadas con la base del código y las abstracciones involucradas. Sería más feliz presentar un poco más de abstracción para reducir la repetición. Por el contrario, un guión que rara vez se usa y se mantiene con poca frecuencia podría ser menos fácil de entender para los encargados del mantenimiento si implica demasiada abstracción. En ese caso, me equivocaría del lado de la repetición.

También considero el nivel de experiencia de otros miembros de mi equipo. Evito abstracciones "elegantes" con desarrolladores sin experiencia, pero aprovecho los patrones de diseño reconocidos por la industria con grupos más maduros.

En concusión, la respuesta a su pregunta es hacer lo que haga que su código sea más fácil de mantener. Lo que eso significa en su escenario depende de usted decidir.

Kramii
fuente
Creo que has solucionado uno de mis problemas exactamente. El ejemplo que di de la funcionalidad que estaba tratando de no duplicar es más probable la duplicación de la abstracción.
lutze
+1. Actualmente estoy lidiando con un sistema que abusó de copiar y pegar en un grado ridículo. Factorizando un solo método del código copiado / pegado eliminó más de 400 líneas de una de mis clases hoy. Desafortunadamente, lo encontré en un método de 900 líneas ...
KChaloux
1
Si dos construcciones de código actualmente hacen lo mismo, pero un cambio en uno generalmente no implica un cambio en el otro, copiar / pegar puede ser mejor que la funcionalidad común factorizada. Si el código usa dos métodos idénticos de carácter por carácter, haga lo mismo para diferentes propósitos, y basa consistentemente la elección del método en el propósito requerido, entonces si las cosas deben hacerse ligeramente para uno de esos propósitos, cambiando solo el apropiado El método afectará solo aquellos comportamientos que deberían verse afectados. El uso de un único método común podría haber hecho el cambio mucho más difícil.
supercat
7

Si sus funciones se vuelven más complicadas y más largas (por lo tanto, menos legibles) cuando intenta hacerlas SECAS, entonces lo está haciendo mal. Está tratando de poner demasiada funcionalidad en una función porque cree que esta es la única forma de evitar repetir los mismos códigos en una segunda función.

Hacer que el código DRY casi siempre signifique refactorizar la funcionalidad común a funciones más pequeñas. Cada una de esas funciones debería ser más simple (y, por lo tanto, más legible) que la original. Para mantener todo en la función original en el mismo nivel de abstracción, esto también puede significar refactorizar partes adicionales que no se usan en otros lugares. Su función original se convierte en una "función de alto nivel", llamando a las más pequeñas, y también se hace más pequeña y simple.

Hacer esto, en consecuencia, conduce principalmente a diferentes niveles de abstracciones en su código, y algunas personas creen que este tipo de código es menos legible. Según mi experiencia, esto es una falacia. El punto clave aquí es dar buenos nombres a las funciones más pequeñas, introducir tipos de datos bien nombrados y abstracciones que puedan ser fácilmente captados por una segunda persona. De esa manera, "SECO" y "legibilidad" solo deberían entrar en conflicto muy, muy raramente.

Doc Brown
fuente
2

Si bien no estoy seguro de qué es exactamente lo que está causando el problema aquí, mi experiencia es que cuando te encuentras con DRY y la legibilidad en conflicto, eso dice que es hora de refactorizar algo.

Loren Pechtel
fuente
0

Elegiría legible (= mantenible) en lugar de SECO cualquier día de la semana. En realidad, ambos a menudo se alinean, alguien que obtiene el concepto de DRY generalmente produciría (en su mayoría) código legible.

Zdravko Danev
fuente
2
sin una explicación, esta respuesta puede volverse inútil en caso de que alguien más publique una opinión opuesta. Por ejemplo, si alguien publica un reclamo como "Yo elegiría DRY (= mantenible) en lugar de legible cualquier día de la semana", ¿cómo ayudaría esta respuesta al lector a elegir dos opiniones opuestas? Considere editarlo en una mejor forma
mosquito
0

Muy, muy, muy, muy pocas veces en mi carrera he encontrado un caso legítimo que enfrenta la legibilidad contra DRY. Hazlo legible primero. Nombra bien las cosas. Copie y pegue si es necesario.

Ahora retroceda y mire la totalidad que ha creado, como un artista que retrocede para mirar su pintura. Ahora puedes identificar adecuadamente la repetición.

El código repetido debe ser refactorizado en su propio método, o extraído en su propia clase.

La repetición de código debería ser el último recurso. Primero legible Y SECO, si no es legible si limita el alcance del código repetido.

Greg Burghardt
fuente