¿Cuál es el equilibrio correcto entre la consistencia del código y la mejora del código?


53

Recientemente tuve una discusión con un colega sobre el estilo de código. Estaba argumentando que su uso de las API y los patrones generales que está utilizando debería ser lo más similar posible con el código circundante, si no con la base de código en su conjunto, tal como lo haría con la apariencia del código (posicionamiento de llaves, mayúsculas, etc.) . Por ejemplo, si estuviera agregando un método a una clase DAO en C #, trataría de usar LINQ cuando sea apropiado para ayudar a que mi código sea limpio y fácil de mantener, incluso si ninguno de los otros métodos en esa clase lo estuviera usando. Sin embargo, mi colega argumentaría que no debería usarlo en esa instancia porque estaría en contra del estilo existente de esa clase y, por lo tanto, es más difícil de entender.

Al principio encontré su posición bastante extrema, pero después de pensarlo un momento, estoy empezando a ver su punto. Con el ejemplo hipotético de LINQ, ¿tal vez esta clase no lo contiene porque mis colegas no están familiarizados con LINQ? Si es así, ¿no sería más fácil mantener mi código para mis compañeros desarrolladores si no lo usara? Por otro lado, si realmente creo que usar una técnica de este tipo daría como resultado un código más limpio, ¿no debería usarlo incluso si difiere drásticamente del código circundante?

Creo que el quid de la discusión de mi colega es que si todos implementamos una funcionalidad similar en una base de código de diferentes maneras, y cada uno cree que nuestro camino es "mejor", entonces al final el código en su conjunto se vuelve más difícil comprender. Sin embargo, en este momento sigo pensando que si seguimos ciegamente el código existente demasiado, la calidad se pudrirá lentamente con el tiempo.

Entonces, ¿en qué medida los patrones son parte del estilo de código, y dónde debemos trazar la línea entre mantenerse consistente y hacer mejoras?


14
Entonces, ¿cómo mejoraría alguna vez la calidad del código, si todos se limitaran a la forma "mala"?
Izkata


Cuando escribes LINQ, ¿no te refieres a LINQ to SQL? Si es así, podría estar de acuerdo con tu amigo. Si estás hablando de LINQ, entonces no estoy de acuerdo con él. Todo el mundo tiene que estar familiarizado con LINQ en estos días. No es su elección. Se les paga por ser familiares.
Piotr Perak

@Peri Quise decir simplemente LINQ básico: supongo que mi ejemplo debería haber sido trabajar con IEnumerables en lugar de un DAO.
Robert Johnson

2
ICYMI - Dilbert comic sobre este mismo tema: dilbert.com/strips/2013-09-21
Jim Bethancourt

Respuestas:


53

Para dar una respuesta más general:

En un caso como este, tiene dos "mejores prácticas" de programación que son opuestas entre sí: la consistencia del código es importante, pero también lo es elegir el mejor método posible para cumplir su tarea. No hay una respuesta correcta a este dilema; depende de un par de factores:

  • ¿Qué tan beneficiosa es la forma "correcta"?

    • A veces, la mejor práctica nueva y mejorada aumentará drásticamente el rendimiento, eliminará errores, será mucho más fácil de programar, etc. En tal caso, me inclinaría mucho hacia el uso del nuevo método. Por otro lado , la "forma correcta" puede ser poco más que azúcar sintáctica, o un método idiomático acordado para hacer algo que en realidad no es superior. En ese caso, la consistencia del código es probablemente más importante.
  • ¿Qué gran problema crearía la inconsistencia?

    • ¿Qué tan interconectado está el nuevo código con el código heredado? ¿Su nuevo código es parte de una biblioteca? ¿Crea un objeto que se pasa a muchas partes del programa? En casos como estos, la consistencia es muy importante. El uso de una nueva API, o una nueva forma de hacer las cosas en general, puede crear resultados sutilmente diferentes que rompan los supuestos en otras partes de su programa. Por otro lado , si está escribiendo un código bastante aislado, es menos probable que la inconsistencia sea un problema.
    • ¿Qué tan grande y maduro es tu código base? ¿Cuántos desarrolladores necesitan entenderlo y trabajar en ello? Las normas consensuadas y consistentes son mucho más importantes para proyectos más grandes.
    • ¿Es necesario que el código se ejecute en entornos más antiguos que pueden no admitir las funciones más recientes?

Con base en el balance de estos problemas, debe tomar la decisión correcta sobre qué ruta tomar. Personalmente veo poco valor en la coherencia por el bien de la coherencia, y preferiría usar los mejores y más recientes métodos a menos que haya un costo significativo para hacerlo.

Por supuesto, hay una tercera opción: reescribir el código existente para que use los mejores métodos y sea ​​coherente. Hay momentos en que esto es necesario, pero tiene un alto costo.


77
Muy buena y equilibrada respuesta (+1). En mi experiencia, un equipo puede ser más productivo si todos están de acuerdo en un conjunto relativamente pequeño de expresiones idiomáticas bien entendidas que si cada miembro del equipo es libre de usar expresiones nuevas y diferentes que otros miembros del equipo podrían encontrar difíciles de entender. Un pequeño punto con respecto a la declaración "reescribir el código existente para que use las últimas prácticas y sea coherente": "último" no implica automáticamente "mejor". Uno siempre debe decidir de un caso a otro.
Giorgio

1
@ Jorge, gracias por los comentarios; Modifiqué el lenguaje del punto final.

66
Buena respuesta y buen comentario de Giorgio. Quizás valga la pena considerar que en este ejemplo, dependiendo del equipo / cultura, es posible tener una adopción coordinada del equipo en lugar de comenzar solo. Esto reduce algunos de los riesgos relacionados con el mantenimiento (ya que todos están a bordo). (es decir, poner más énfasis en las habilidades reales del equipo en este momento, en lugar del código que han escrito en el pasado.)
Matt

+1. De acuerdo, una respuesta completa y equilibrada: buena consideración de una variedad de escenarios. Nuevamente, buen comentario de Giorgio.
Robert Johnson

1
Con respecto a la adopción de nuevas expresiones idiomáticas, tecnologías: consideraría el comienzo de un nuevo proyecto como un buen punto para introducir cosas nuevas. Luego, durante toda la duración del proyecto, el estilo del código debe mantenerse lo más consistente posible. Recientemente tuve que desenterrar un código de 5 años y estaba muy contento de poder encontrar mi camino a través de ese código porque todo el equipo se adhirió a una arquitectura común y un conjunto de expresiones idiomáticas que habíamos acordado (¡no sin lucha! ) al comienzo del proyecto.
Giorgio

26

Mantenerse constante tiene poco valor en mi perspectiva; Hacer mejoras continuamente es una necesidad.

La posición de su colega realmente impide la innovación. El argumento de coherencia lo lleva a una situación en la que puede usar, por ejemplo, LINQ solo si migra todo el código para usar LINQ. Y bueno, no tenemos tiempo para esto, ¿verdad?

Prefiero tener una inconsistencia en la que todavía se está haciendo algún código foreachsobre ArrayLists y otras partes usan LINQ IEnumerable<T>, en lugar de apegarse a la forma más antigua de hacer las cosas hasta el final de los tiempos.

Es responsabilidad de sus colegas mantenerse relevantes y aprender nuevas formas de hacer las cosas.


3
"Es responsabilidad de sus colegas mantenerse relevantes y aprender nuevas formas de hacer las cosas": una posibilidad es utilizar nuevas expresiones idiomáticas y bibliotecas en un nuevo código (nuevos módulos) y mantener un estilo coherente en el código heredado.
Giorgio

8

La coherencia de la API es muy importante, tanto para las API públicas como para las internas.

La coherencia del formato del código es importante, y lo ideal es que se aplique mediante una herramienta de formato automático con las mismas reglas de formato para todos. Hace que vivir con una base de código compartida controlada por versiones sea más fácil.

Las convenciones de nomenclatura deben ser consistentes, también para cosas como variables locales, etc.

Las herramientas de análisis estático deben usarse para hacer cumplir ciertas otras convenciones y buenas prácticas (pero no sigas ciegamente los valores predeterminados de dicha herramienta, los valores predeterminados a veces pueden limitarse a locos), aunque si algo lo exige, no tengas miedo de desactivar algunos marque (generalmente con una directiva dentro de un comentario) temporalmente, si puede justificarlo.

Lo que sucede dentro de las funciones / métodos , aparte de lo que se menciona anteriormente, no necesita ser coherente de ninguna manera, siempre que sea un buen código por sí solo . Un código bueno, comprensible y bien comentado es muy importante, pero después de eso, si el compilador y las herramientas de análisis estático piensan que es un código consistente, es lo suficientemente consistente.


Acerca de las nuevas características del lenguaje como LINQ (bueno, eso no es exactamente nuevo), lo único que debe considerarse es, ¿se utilizará una versión lo suficientemente nueva del idioma / bibliotecas en todas partes donde se utilizará el código? En caso de duda, quédese con las características que se sabe que son compatibles. Por supuesto, esto no le impide impulsar una actualización de versión en todo el sistema, por lo que puede comenzar a usar las nuevas cosas buenas.

Y cada desarrollador que trabaje con código debe mantenerse actualizado, por lo que cualquier desarrollador de .NET debe conocer LINQ, y si no lo hacen, deben verse obligados a aprender, por su propio bien (nunca se sabe cuándo buscará un nuevo trabajo en este negocio, por una razón u otra).


6

La respuesta a esto es simple.

La consistencia es de suma importancia.

pero viene con una advertencia ...

Es probable que usted y su compañero de trabajo estén obsesionados con el tipo incorrecto de consistencia.

Las implementaciones son desechables. Se pueden revisar por completo con distintos grados de facilidad según la calidad y la amplitud del conjunto de pruebas. Preocuparse por cosas como "¿Debería ser esto una propiedad?", "¿No deberían los códigos delgados usar LINQ en lugar de una construcción de nivel inferior?" Es de dudoso valor. Es difícil vincular cualquier medida al valor de consistencia en el nivel de implementación. Una pregunta mucho mejor para hacer a este nivel es "¿Este código funciona como se anuncia?". La coherencia de la implementación de TL; DR es donde las "mentes pequeñas" obtienen su duende.

¿Por qué la consistencia no es tan importante aquí? Las implementaciones generalmente tienen una pequeña cantidad de contribuyentes. La mayoría de los métodos están escritos y nunca se vuelven a tocar. Del código restante, el número de métodos que tienen dos contribuyentes es casi seguro una mayoría. Este patrón continúa hasta el infinito . En este contexto, la consistencia no es tan importante. Si la vida útil del código es bastante pequeña ( unos pocos años ), las ganancias de la consistencia agresiva probablemente no sean un factor.

Esto no quiere decir que deba volverse loco en sus implementaciones. Más bien es decir que un diseño agradable, limpio y simple será de gran valor para su hipotético futuro mantenedor que la tonta consistencia de la placa de caldera método por método. Esto nos lleva al punto real ...

Las API no son desechables.

Este es todo el nivel de código de API, servicios web, SDK, etc. Estos deben, deben, DEBEN ser consistentes. Las ganancias de productividad de esta variedad de consistencia son enormes por varias razones:

Pruebas de integración:

Si mantiene la coherencia de su API, puede crear conjuntos de pruebas de integración. Esto permite a los desarrolladores intercambiar libremente los detalles de implementación y lograr la validación inmediata. ¿Quieres cambiar tu basura de trabajo a LINQ? ¿Se ejecutan las pruebas de integración? También proporciona validación cuando se prepara para ir a producción. Debido a que las computadoras son rápidas, una sola computadora portátil puede realizar el trabajo de miles de evaluadores que ejecutan tareas mundanas. Es equivalente a aumentar considerablemente el personal de su organización.

Productividad

Cuando las API son consistentes, puede adivinar cómo usar una API simplemente siguiendo lo que aprendió sobre el uso de otras partes de la API. Esto se debe a que la API ofrece una apariencia y sensación natural y consistente. Significa que su cliente pasa menos tiempo revisando la documentación. La incorporación es más fácil y más barata. Se hacen menos preguntas a las personas que desarrollaron la API. La consistencia hace que todos sean ganadores

¿Por qué es importante la coherencia en este escenario? Porque las API tienen exactamente el problema opuesto de las implementaciones. El número de personas que los usan es generalmente mucho mayor que el número de personas que contribuyen a sus implementaciones. Las pequeñas ganancias de un poco de consistencia se multiplican y los costos de mantener esa consistencia se amortizan.

Conclusión

La consistencia es cara. A primera vista, reduce la productividad. Restringe a los desarrolladores y les dificulta la vida. Establece limitaciones en las formas en que pueden resolver un problema, a veces obligándolos a resolverlo de una manera no óptima. Esto es a menudo por razones que no entienden, están mal concebidas o no están al tanto (contratos, políticas organizacionales o interorganizacionales más amplias).

Raymond Hettinger hizo algunos puntos excelentes en su charla de Pycon 2015 sobre el uso de la guía de estilo PEP8 para equipos de programadores de Python. Mostró que la obsesión por la coherencia estilística en un fragmento de código provocó que los revisores de código omitieran serias fallas lógicas y de diseño. Su hipótesis se puede resumir en que encontrar inconsistencias estilísticas es fácil; determinar la calidad real de un código es difícil

El punto aquí es ser crítico. Identifique dónde es importante la consistencia y protéjala agresivamente. Donde no es importante no pierdas tu tiempo. Si no puede proporcionar una forma objetiva de medir el valor de la consistencia (en los casos anteriores "personal efectivo", costo en función de la productividad) y no puede demostrar que los rendimientos son sustanciales, entonces probablemente esté haciendo un mal servicio a tu organización.


4

¿No está familiarizado con LINQ? Si es así, ¿no sería más fácil mantener mi código para mis compañeros desarrolladores si no lo usara?

El lenguaje C # todavía está evolucionando. Si las personas no aprendieran los cambios de C # 1, se estarían perdiendo:

  • Genéricos
  • Parciales
  • Métodos anónimos
  • Iteradores
  • Tipos anulables
  • Propiedades automáticas
  • Tipos anónimos
  • Métodos de extensión
  • Abadejo
  • Lambdas
  • Métodos asincrónicos

Esta es solo una pequeña selección de características comunes que se encuentran en el artículo de Wikipedia. Lo que quiero decir es que si los desarrolladores no aprenden, la base de código permanecerá en el vacío. Uno esperaría que sus desarrolladores mejoren continuamente y la base del código evolucione, respaldada por un conjunto de pruebas completo. ¿Recuerdas lo malo que fue con .NET 1 para implementar propiedades manualmente?

Linq hace la vida más fácil. Úselo donde pueda. Puede motivar a los miembros de su equipo.

Entonces, ¿en qué medida los patrones son parte del estilo de código, y dónde debemos trazar la línea entre mantenerse consistente y hacer mejoras?

Las mejoras son graduales. Para mí, no tiene sentido mantener un código de estilo antiguo que sea menos legible y potencialmente menos mantenible. Los miembros de su equipo al menos deberían poder resolver lo que está sucediendo, incluso si no pueden escribirlo.


2
Estoy totalmente de acuerdo con lo que está diciendo, pero mi pregunta es menos acerca de las nuevas características del lenguaje y la responsabilidad de los desarrolladores de aprenderlas, sino más bien la cuestión más general de resolver problemas similares de diferentes maneras dentro de un área relativamente pequeña de una base de código. Mencioné LINQ porque es un buen ejemplo del uso de una forma diferente de resolver un problema. Mi colega está feliz de usar nuevas funciones de lenguaje, pero solo si no van en contra del código en el que está trabajando.
Robert Johnson

@RobertJohnson Le diría a su colega que la capacidad de mantenimiento supera la coherencia, por lo que incluso si algo "va en contra del grano" del código existente, si es más fácil de mantener, debe hacerlo. Dudo que usar el DAO anterior sea más fácil de mantener que Linq.
Andy

3

Debe ser coherente, pero no necesariamente con el código anterior. Es decir, su equipo debe acordar la forma correcta de hacer algo, y usarlo de esa manera cada vez que se escriba un nuevo código o se realicen cambios sustanciales.

De esa manera, cosechará la mayoría de los beneficios de la coherencia (en particular, no importará quién escriba el código), pero aún puede mejorar si el equipo ve un beneficio claro al hacer las cosas de otra manera.

En un mundo ideal, reescribiríamos todo el código antiguo para adaptarlo a la nueva forma correcta. Si bien esto no es económicamente viable, debería tener sentido técnico (o de lo contrario, la nueva forma no es una mejor manera), y su plan a largo plazo debería ser actualizarlo. Si eso no vale la pena, no te molestes con la nueva forma. Dicho de otra manera, debe haber una clara ventaja en la nueva forma de considerar declararla correcta.


1

Creo que es importante seguir un estilo de código en un proyecto, por ejemplo. convenciones de nomenclatura, muescas, etc.

No estoy de acuerdo con que deba limitarse a usar nuevas construcciones de lenguaje o patrones de diseño simplemente porque sus colegas no los entienden o no se han utilizado en el proyecto antes.

Por supuesto, estas cosas deberían tener buenas razones para usarlas, por ejemplo. rendimiento o brevedad, si corresponde.


¿Para qué fue el menos?
ozz

0

Sería extremadamente cuidadoso siguiendo ciegamente los patrones de diseño actuales. Por supuesto, cuando no hay una desventaja apreciable, siempre se debe intentar seguir patrones similares en la base del código.

Sin embargo, es demasiado fácil entrar en circunstancias en las que la mayoría de los desarrolladores sienten que es una "mejor práctica" seguir los malos patrones existentes que conducen a situaciones en las que los buenos desarrolladores escriben código malo e imposible de mantener.

Por otro lado, la consistencia es importante. Cuando se trata de enormes bases de código, es extremadamente útil tratar de seguir patrones similares para que, aunque los desarrolladores no hayan leído cada centímetro de la base de código, sepan qué esperar.

Por lo tanto, creo que es mejor establecer y actualizar pautas sobre qué patrones deben seguir los desarrolladores si es posible. De esta manera, cualquier código nuevo es consistente con otro código nuevo.


-1

Tenemos reuniones técnicas regulares, bastante informales, que cualquiera de nosotros puede realizar. Si encontramos una nueva técnica, la presentamos en la reunión. En general, tiene una buena idea de lo bien que sus colegas aceptan y entienden la idea. Esto le ayuda a decidir si es conveniente incluirlo en su código, ayuda a otros a descifrarlo si lo hace y también establece la persona 'ir a' si otros necesitan ayuda con ese estilo. Si nadie reinventara la rueda, todavía estaríamos arrastrando cosas en los registros.


¿Para qué fue el voto negativo?
Ant

Yo no era el votante, pero esto realmente no parece dirigido a la pregunta. Es un proceso de equipo que podría aplicarse a cualquier cosa, en lugar de una discusión sobre problemas de estilo de codificación. Además, ¿no te refieres a "inventar la rueda" en lugar de "reinventar la rueda"?

Supongo que es semántica si un tronco es un tipo de rueda o no, pero gira, reduce la fricción, tiene una superficie que está en contacto sucesivo con el suelo y es redonda. Estoy seguro de que hay mejores ejemplos de lo que quiero decir: lo dejaré como un ejercicio para el lector.
Ant
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.