¿Hay algún estudio científicamente riguroso de los principios de estilo de codificación? [cerrado]

25

¿Es realmente bueno un principio de estilo de codificación, por ejemplo, el principio de salida única? ¿Siempre o solo a veces? ¿Cuánta diferencia realmente hace?

Cualesquiera que sean sus opiniones, estas son obviamente preguntas subjetivas. ¿O son?

¿Alguien ha intentado hacer un estudio objetivo, científicamente riguroso de los principios de estilo de codificación?

No puedo imaginar cómo alguien haría un estudio doble ciego de legibilidad, pero tal vez sea doblemente ignorante: use estudiantes que no conocen el principio que se estudia como sujetos y no programadores para administrar el estudio.

Steve314
fuente
55
Puede interesarle leer el código completo. No todo es mensurable, pero mucho sí, y encontrará una buena descripción general con datos o fuentes sin procesar en este libro.
deadalnix
También depende mucho del lenguaje, algunos principios se aplican a idiomas específicos y no a otros. Por ejemplo, el single-exit principlerealmente no se aplica a C ++ debido a RAII
Martin York
@Loki: tuve que pensar en eso y no estoy seguro de estar de acuerdo. Es cierto que RAII está diseñado en gran parte para hacer frente a las excepciones, que son puntos de salida alternativos, pero (al menos para algunas personas) cuentan como puntos de salida alternativos alternativos, sin contar realmente contra el principio de salida única en la forma en que break, gotoo returnhacer. La salida única IOW no es absoluta en C ++, pero de todos modos esa es mi opinión en C y en la mayoría de los otros lenguajes. Pero sigue siendo relevante en un sentido no estricto.
Steve314
1
@ Steve314, el artículo tiene una relevancia al menos distante: describe un diseño para una metodología de tal experimento, que es bastante importante debido a la obvia falta de evidencia experimental registrada adecuadamente en esta área.
SK-logic

Respuestas:

11

Me estoy haciendo eco del comentario de deadalnix: lea Code Complete 2 . El autor (Steve McConnell) analiza el estilo de codificación en profundidad y con frecuencia referencias de artículos y datos.

M. Dudley
fuente
Descripción general fundamental y bien presentada del desarrollo de software profesional, espero que algún día encuentre una similar para el aseguramiento de la calidad. Los capítulos sobre Programación Defensiva y Programación de Pseudocódigo fueron especialmente útiles para mí. El capítulo sobre prácticas de desarrollo colaborativo parece ser el más convincente de todo lo que he leído sobre estos asuntos hasta ahora.
mosquito
No he leído este libro, y tal vez debería, pero, en base a los comentarios en la respuesta de los mosquitos, ¿son estos documentos referenciados realmente rigurosos y objetivos desde el punto de vista científico? Si la respuesta es "tanto como puedan ser", ¿qué compromisos fueron necesarios? Como sugerí en la pregunta, ¿era necesario reemplazar el doble ciego con un estándar más débil?
Steve314
@ Steve314: ¡No sé, no he verificado las fuentes! Pero no siempre se necesita rigor científico para establecer las mejores prácticas. Una discusión de los pros y los contras a veces es suficiente.
M. Dudley
@emddudley: absolutamente cierto, pero no realmente de qué se trataba esta pregunta.
Steve314
@ Steve314: Code Complete sería un excelente punto de partida para usted, y estoy seguro de que algunas de sus referencias abordan el tema del análisis científico del estilo de codificación.
M. Dudley
12

Dudo mucho de la posibilidad de que un estudio sobre el tema arroje resultados objetivos y me mantendré escéptico hasta que me muestren algunas investigaciones convincentes.

Los programadores que han pasado años leyendo y escribiendo códigos que siguieron cierto estilo de codificación obviamente lo encontrarán más legible que algún estilo de codificación perfecto que verían por primera vez en sus vidas.

Es exactamente lo mismo con el diseño de escritura QWERTY más común: es fácil demostrar que es bastante subóptimo en términos de ergonomía (¿cree que todos los caracteres de la palabra TYPEWRITER se colocaron en la fila superior teniendo en cuenta nuestra conveniencia diaria?) .

Pero las alternativas mejoradas como Dvorak o Colemak nunca se han dado cuenta y es poco probable que lo hagan. Y, por lo tanto, las personas no son más productivas con ellos: hecho. Incluso si son superiores en algún sentido abstracto.

Además, sería difícil encontrar sujetos sin exposición previa a la programación (ya que esto contaminaría el resultado de nuestro estudio), PERO una aptitud para la programación, Y la voluntad de participar en un estudio durante un período lo suficientemente largo como para mostrar ambos cortos beneficios a tiempo y beneficios a largo plazo para que puedan sopesarse unos contra otros ... (No sé si son mutuamente excluyentes, pero los investigadores no podrían simplemente asumir que nunca lo son).

Konrad Morawski
fuente
1
Genial, nunca había oído hablar de Colemak antes
CaffGeek
1
@Chad aún menos conocido es Carpal X, con el que jugué durante un tiempo. Lo encontré mejor que Colemak (llegué a 90-100 palabras por minuto con carpalx). Incluso si no tiene la intención de cambiar a diseños exóticos, el sitio web de carpalx hace una lectura extremadamente interesante sobre la evaluación y optimización de diseños de teclado y el uso de algoritmos genéticos para esta categoría de problemas. Ver mkweb.bcgsc.ca/carpalx
Konrad Morawski el
1
A veces, los beneficios marginales de un enfoque alternativo serán lo suficientemente grandes como para justificar el costo de adoptarlo; de lo contrario, todos seguiríamos programando ensamblador y fortran. Esta respuesta realmente no responde a la pregunta original sobre si hay o no beneficios marginales. En el ejemplo de Dvorak, ciertamente hay y ha sido probado, pero no son beneficios lo suficientemente grandes como para justificar el aprendizaje de Dvorak.
Jeremy
@Jeremy "esta respuesta realmente no responde a la pregunta original sobre si hay o no beneficios marginales" - el OP no solicitó directamente los resultados de dichos estudios, preguntó si alguien ha intentado llevar a cabo tales estudios, lo cual es más abierto una pregunta. Respondí señalando un par de razones lógicas sobre por qué sería técnicamente difícil y por qué los resultados de dicho estudio probablemente estarían significativamente contaminados por el ruido estadístico. Entonces, si mi respuesta se consideró no útil por los motivos que usted ha dado, creo que fui rechazado injustamente.
Konrad Morawski
1
@Jeremy, la esencia de estos costos de adopción es que las personas se desempeñan mejor con una herramienta inferior siempre que hayan tenido más práctica con ella. Y esto es exactamente lo que aparecería en cualquier estudio que intente examinar qué tan bien sus sujetos manejan diferentes estilos de codificación. El ruido causado por su anterior familiaridad / falta de familiaridad con los estilos de codificación que haría que usaran disminuiría el impacto de cualquier cualidad innata de estos estilos. A menos que haya nivelado el patio de recreo con principiantes completos. Pero esto plantea una dificultad práctica, como señalé en el último párrafo de mi respuesta.
Konrad Morawski
4

¡La respuesta es un NO definitivo! ¿Son `break` y` continue` malas prácticas de programación? es un subconjunto de esta pregunta, así que voy a comenzar con una respuesta apenas modificada a eso ...

Puede [re] escribir programas sin declaraciones de interrupción (o retornos desde la mitad de los bucles, que hacen lo mismo). Pero al hacerlo, es posible que deba introducir variables adicionales y / o duplicación de código, lo que generalmente hace que el programa sea más difícil de entender. Pascal (el lenguaje de programación de fines de la década de 1960) fue muy malo, especialmente para los programadores principiantes por esa razón.

Hay un resultado informático llamado jerarquía de estructuras de control de Kosaraju, que se remonta a 1973 y que se menciona en la programación estructurada en papel de Knuth (más) famosa con declaraciones de 1974. Lo que S. Rao Kosaraju demostró en 1973 es que no es es posible reescribir todos los programas que tienen saltos de profundidad de niveles múltiples n en programas con profundidad de descanso menor que n sin introducir variables adicionales. Pero digamos que es solo un resultado puramente teórico. (¡¿Solo agregue algunas variables adicionales?! Seguramente puede hacer eso para sentirse en grupo con los usuarios de 3K + en stackexchange ...)

Lo que es mucho más importante desde una perspectiva de ingeniería de software es un artículo más reciente de 1995 de Eric S. Roberts titulado Salidas de bucle y programación estructurada: reapertura del debate (doi: 10.1145 / 199688.199815). Roberts resume varios estudios empíricos realizados por otros antes que él. Por ejemplo, cuando a un grupo de estudiantes del tipo CS101 se les pidió que escribieran código para una función que implementa una búsqueda secuencial en una matriz, el autor del estudio dijo lo siguiente sobre aquellos estudiantes que usaron un descanso / retorno para salir de la secuencial bucle de búsqueda justo cuando se encontró el elemento:

Todavía tengo que encontrar a una sola persona que haya intentado un programa usando [este estilo] que haya producido una solución incorrecta.

Roberts también dice que:

A los estudiantes que intentaron resolver el problema sin utilizar un retorno explícito del ciclo for les fue mucho peor: solo siete de los 42 estudiantes que intentaron esta estrategia lograron generar soluciones correctas. Esa cifra representa una tasa de éxito de menos del 20%.

Sí, es posible que tenga más experiencia que los estudiantes de CS101, pero sin usar la declaración de interrupción (o devolver / goto equivalente desde el medio de los bucles), eventualmente escribirá código que, aunque nominalmente está bien estructurado, es lo suficientemente complicado en términos de lógica adicional variables y duplicación de código que alguien, probablemente usted mismo, pondrá errores lógicos al intentar seguir alguna idea de estilo de codificación "correcta".

Y hay un problema más grande aquí además de las declaraciones de tipo return / break, por lo que esta pregunta es un poco más amplia que la de break. Los mecanismos de manejo de excepciones también están violando el paradigma del punto de salida única según algunos

Así que, básicamente, cualquiera que haya argumentado anteriormente que el principio de salida singe todavía es útil hoy en día también está argumentando en contra del paradigma de manejo de excepciones, a menos que se use de la manera extremadamente restrictiva descrita en ese último enlace; esas pautas básicamente limitan todas las excepciones de una función a throw (), es decir, no se permite la propagación de excepciones entre funciones. Disfrute de su nuevo Pascal con sintaxis similar a C ++.

Veo de dónde vino la noción de "solo un retorno".que la opinión predominante en este sitio es contraria a lo que publiqué aquí, así que entiendo completamente por qué ya me han votado en contra, a pesar de que soy la primera respuesta aquí para proporcionar algo que la pregunta me hizo: alguna información sobre las pruebas de usabilidad reales se centró en el problema de salida única. Supongo que no debería dejar que el conocimiento interfiera con las ideas preconcebidas, especialmente en un sitio de gamificación. Voy a seguir editando Wikipedia de ahora en adelante. Al menos, la información de buenas fuentes es apreciada y las afirmaciones vagas o incorrectas que pretenden estar respaldadas por fuentes eventualmente obtienen una prohibición. En este sitio, sucede todo lo contrario: las opiniones sin fundamento dominan los hechos. Espero que un mod elimine esta última parte, pero al menos ese tipo sabrá por qué me has perdido para siempre como colaborador aquí.

Efervescencia
fuente
No desestimé esto, pero en su "Pero al hacerlo puede que tenga que introducir variables adicionales y / o duplicación de código, lo que generalmente hace que el programa sea más difícil de entender". punto, esa es una afirmación subjetiva. Estoy de acuerdo en que agregar una duplicación de variable o código hace que sea difícil de entender, pero podría decirse que agregar un goto también hace que sea difícil de entender, además, posiblemente, el daño causado por la duplicación puede mitigarse factorizando el código duplicado en una función (aunque IMO se mueve la complejidad en el gráfico de llamadas no lo elimina automáticamente).
Steve314
Vi su punto sobre el artículo de 1995 solo después de ese último comentario, y decidí votar a favor: un punto interesante. Creo que su voto negativo puede ser más porque su publicación es larga y comienza con un punto subjetivo, por lo que probablemente el votante no leyó todo (al principio, al igual que yo). Básicamente, es una buena idea presentar su punto real temprano.
Steve314
De todos modos, creo que mucha gente piensa en las excepciones como un tipo de puntos de salida alternativos alternativos, porque están destinados a casos de error (más o menos) que realmente no cuentan. Sin embargo, entiendo que es un poco sensible a la cultura del idioma. En algunos idiomas, "excepción" es más que el nombre: un caso de éxito excepcional es válido (y IIRC Stroustrup dijo algo así sobre C ++, planteando un punto filosófico sobre si un error es un error si se maneja). Algunos incluso dicen que las excepciones son solo otro flujo de control para usar siempre que proporcione el flujo de control que necesita.
Steve314
1
@ Steve314 " más posiblemente el daño causado por la duplicación puede mitigarse factorizando el código duplicado en una función " Poner fuera de línea y fuera de la vista inmediata parte de una lógica de la función, una parte que no tiene sentido aislado. Haciendo aún más difícil entender la lógica de la función.
curioso
1
@curiousguy: sí, es cierto, y probablemente sea parte de la intención de mi punto de "trasladar la complejidad al gráfico de llamadas". Mi religión es que cada elección que haces es una compensación, así que ten en cuenta todas las opciones posibles y sus ventajas y desventajas, y conocer las mitigaciones comunes es importante, pero ten cuidado en caso de que la cura sea peor que la enfermedad. Excepto, por supuesto, que parte de la compensación es cuánto tiempo gastas (o malgastas) en preocuparte por las cosas.
Steve314
1

http://dl.acm.org/citation.cfm?id=1241526

http://www.springerlink.com/content/n82qpt83n8735l7t/

http://ieeexplore.ieee.org/xpl/freeabs_all.jsp?arnumber=661092

[Sus preguntas parecen ser respondidas por una sola palabra, "sí". Sin embargo, me han dicho que proporcionar respuestas cortas es "desdeñoso" de la pregunta. Si cree que he sido despectivo, marque la respuesta para que un moderador pueda eliminarla.]

S.Lott
fuente
1
@ luis.espinal: ¿Hacia qué final? ¿Qué información contendría el texto? La pregunta divaga un poco. ¿Qué parte de la pregunta debe abordarse con algún texto?
S.Lott
1
Como cuestión de estilo, y tal vez para proporcionar más información que los resúmenes de los enlaces pueden proporcionar (teniendo en cuenta que no sabemos si el OP es un miembro de pago de ACM / IEEE / Springer Verlag con acceso a los artículos completos y encontrar respuestas a sus preguntas). Por ejemplo, el resumen del artículo de ACM no menciona el estilo de codificación. A lo sumo, habla de corroborar el teorema del programa estructurado (que en sí mismo no habla del problema de retorno único o múltiple). Entonces podría haber explicado por qué ese enlace es relevante.
luis.espinal
1
El tercer artículo (afortunadamente tengo acceso a IEEE Xplore) no parece estar relacionado con lo que el OP está pidiendo hasta donde puedo decir. Es un artículo maravilloso, uno que estoy imprimiendo para una lectura más dedicada más adelante. Entonces, tal vez también podría haber explicado cómo este artículo ayuda al OP a responder su pregunta. En general, parece que simplemente lanzaste un montón de enlaces. No es una forma de ser despectivo (a menos que sea su intención), pero nuevamente, no veo cómo eso ayudó al OP. Y esta es la razón por la cual un póster debe agregar texto a lo largo de sus enlaces. Así que ahora sabes por qué lo dije;)
luis.espinal
1
de la boca del OP, Is a coding style principle - e.g. the single-exit principle - really a good thing?eso da contexto a la pregunta que está planteando, sobre los estilos de codificación. Además, el estilo de codificación no es lo mismo que la metodología de programación, en particular los métodos de diseño de alto nivel que son el foco del artículo de IEEE (claramente establecido por los autores). Es por eso que digo "no": los alcances son completamente diferentes.
luis.espinal
1
Sospecho de dónde viene el OP. Él está indicando claramente los estilos de codificación (no las metodologías), y en particular, los retornos individuales versus múltiples. He tenido que lidiar con eso un par de veces con un código bien escrito, inherentemente evidente, usando múltiples declaraciones de retorno que se reescriben en versiones más complicadas usando retornos únicos (en particular en grandes organizaciones grandes en cinta roja) * como por "el proceso". Y uno se pregunta (y desafía con evidencia) la validez, usabilidad y rentabilidad de tales mandatos arbitrarios. Las personas que fuerzan tales mandatos aún viven en los años 60: /
luis.espinal
1

Es un principio de estilo de codificación, por ejemplo, el principio de salida única

Las personas que aún no saben si se trata de una salida única o una salida múltiple todavía están atrapadas a fines de la década de 1960. En aquel entonces, tal discusión era importante ya que estábamos en la infancia del programador estructurado, y había un campo bastante numeroso que proclamaba que los hallazgos detrás del Teorema del Programa Estructurado de Bohm-Jacopini no eran universalmente aplicables a todas las construcciones de programación.

Es algo que debería haberse resuelto hace mucho tiempo. Bueno, se ha resuelto (casi 4 décadas para ser precisos, tanto en la Academia como en la industria), pero las personas (aquellas que están absolutamente a favor o en contra) no han estado prestando atención.

En cuanto al resto de mis respuestas, todo es relativo (¿qué no está en el software?):

  • realmente una buena cosa?

Sí. La mayoría de las veces para el caso general, con advertencias específicas para casos extremos y construcciones de programación específicas del lenguaje.

¿Siempre o solo a veces?

La mayor parte del tiempo

¿Cuánta diferencia realmente hace?

Depende

Código legible vs código ilegible. Mayor complejidad (que deberíamos saber ahora aumenta la probabilidad de introducir errores) frente a una complejidad más simple (y, por lo tanto, menor probabilidad de errores). Lenguajes cuyos compiladores no agregan un retorno implícito (por ejemplo, Pascal, Java o C #) y aquellos que predeterminado a int (C y C ++).

Al final, es una habilidad perfeccionada con hombre / horas detrás de un teclado. A veces, está bien tener múltiples declaraciones de retorno, como aquí (en algunos pseudocódigo de Pascal'esque):

function foo() : someType
  begin
  if( test1 == true )
  then
    return x;
  end
  doSomethignElseThatShouldnHappenIfTest1IsTrue();
  return somethingElse();
end;

La intención es clara, y el algoritmo es lo suficientemente pequeño y sencillo como para no garantizar la creación de una variable 'flag' que contenga el valor de retorno eventual utilizado en un único punto de retorno. El algoritmo podría estar en error, pero su estructura es tan simple que el esfuerzo para detectar un error es (muy probablemente) insignificante.

A veces no lo es (aquí usando un pseudocódigo tipo C):

switch(someVal)
{
case v1 : return x1;
case v2 : return x2:
case v3 : doSomething(); // fall-through
case v4: // fall-through
case v5: // fall-through
case v6: return someXthingie;
...
...
default:
   doSomething(); // no return statement yet
}

Aquí, el algoritmo no tiene una estructura simple, y la declaración de cambio (una de estilo C) permite pasos fallidos que pueden o no hacerse intencionalmente como parte del algoritmo.

Quizás el algoritmo sea correcto, pero esté mal escrito.

O tal vez, por fuerzas externas más allá de la capacidad del programador, esta es la representación real (y correcta) de un algoritmo legítimamente necesario.

Tal vez está mal.

Descubrir la verdad de todo esto requiere mucho más esfuerzo que en el ejemplo anterior. Y aquí hay algo en lo que creo firmemente (tenga en cuenta que no tengo estudios formales para respaldar esto):

Suponiendo un fragmento de código que se supone que es correcto:

  1. Las declaraciones de retorno múltiples aumentan la legibilidad y la simplicidad de dicho fragmento de código, si el fragmento representa un algoritmo simple con una estructura de flujo inherentemente simple. Por simple, no me refiero a pequeño, pero quiero decir inherentemente comprensible o evidencia de sí mismo , lo que no requiere un esfuerzo de lectura desproporcionado (ni inducir a las personas a vomitar, maldecir a la madre de alguien o tragarse una bala cuando tienen que leerlo). )

  2. Una sola declaración de retorno aumenta la legibilidad y la simplicidad de dicho fragmento de código si el valor de retorno se calcula a lo largo de la ejecución del algoritmo o si los pasos en el algoritmo responsable de calcularlo se pueden agrupar en una ubicación dentro de la estructura del algoritmo .

  3. Una sola declaración de retorno disminuye la legibilidad y la simplicidad de dicho fragmento de código si requiere asignaciones a una o más variables de indicador, y las ubicaciones de tales asignaciones no se ubican de manera uniforme en todo el algoritmo.

  4. Las declaraciones de retorno múltiples disminuyen la legibilidad y la simplicidad de dicho fragmento de código si las declaraciones de retorno no se distribuyen uniformemente a través del algoritmo y si demarcan bloques de código mutuamente excluyentes que no son uniformes en tamaño o estructura entre sí.

Esto está estrechamente relacionado con la complejidad de un fragmento de código en cuestión. Y esto a su vez está relacionado con las medidas de complejidad ciclomática y halstead. A partir de esto, se podría observar lo siguiente:

Cuanto mayor sea el tamaño de una subrutina o función, mayor y más compleja es su estructura de flujo de control interno, y mayor es la probabilidad de que tenga que preguntarse si usar declaraciones de retorno múltiples o únicas.

La conclusión de esto es: mantener sus funciones pequeñas haciendo una cosa y solo una cosa (y hacerlo bien). Si exhiben métricas ciclomáticas y de complejidad halstead nominalmente pequeñas, es probable que no solo sean correctas y que se implementen tareas que sean comprensibles, sino que sus estructuras internas también serán relativamente evidentes.

Entonces, y solo entonces puede hacerlo con bastante facilidad y sin perder mucho sueño, puede decidir si usar un solo retorno y múltiples retornos sin correr muchos riesgos de introducir errores con cualquiera de las dos opciones.

También se podría ver todo esto y sugerir que cuando las personas luchan con el tema de los retornos únicos o múltiples, es porque, ya sea por inexperiencia, estupidez o falta de ética laboral, no escriben código limpio y tienden a escribir funciones monstruosas sin tener en cuenta las medidas ciclomáticas y de halstead.

luis.espinal
fuente
1
El tipo de retorno de C ++ no está predeterminado en int: no hay un tipo de retorno predeterminado, por lo que debe especificarse en todos los casos.
Sjoerd
Desde antes escribí esta pregunta: programmers.stackexchange.com/questions/58237/… . Básicamente, abogo por el conocimiento del principio, pero no lo sigo estrictamente; si todos los puntos de salida son obvios, estoy feliz. Mi punto aquí: solo porque mencione un principio como ejemplo no significa que estoy abogando por ese principio, y ciertamente no en su forma estricta. Sin embargo, mi opinión subjetiva es solo eso: tal vez hay un argumento más fuerte para mi punto de vista, o tal vez hay un fuerte argumento de que estoy equivocado.
Steve314
¿De qué se trata "default to int"?
curioso
Quiero decir, y debería haberlo calificado, que la mayoría de los compiladores simplemente "empujarán" el valor de un registro de acumulador como valor de retorno si el código tiene una rama de ejecución sin un valor de retorno explícito. En efecto, eso significa devolver el resultado de la última operación aritmética (cualquier basura que pueda ser) en forma int. Y eso ciertamente sería basura (y ergo, comportamiento indefinido) independientemente de lo que la función pretendiera hacer en primer lugar. C y C ++ pueden advertirte, pero las compilaciones te permitirán compilar a menos que uses -Werror o algo similar.
luis.espinal