Mucha gente afirma que "los comentarios deberían explicar 'por qué', pero no 'cómo'". Otros dicen que "el código debe autodocumentarse" y los comentarios deben ser escasos. Robert C. Martin afirma que (reformulado a mis propias palabras) a menudo "los comentarios son disculpas por código mal escrito".
Mi pregunta es la siguiente:
¿Qué tiene de malo explicar un algoritmo complejo o un fragmento de código largo y complicado con un comentario descriptivo?
De esta manera, en lugar de que otros desarrolladores (incluido usted mismo) tengan que leer el algoritmo completo línea por línea para descubrir lo que hace, simplemente pueden leer el comentario descriptivo amigable que escribió en inglés simple.
El inglés está "diseñado" para que los humanos lo entiendan fácilmente. Java, Ruby o Perl, sin embargo, han sido diseñados para equilibrar la legibilidad humana y la legibilidad de la computadora, comprometiendo así la legibilidad humana del texto. Un humano puede entender una parte del inglés mucho más rápido que él / ella puede entender una parte del código con el mismo significado (siempre y cuando la operación no sea trivial).
Entonces, después de escribir un código complejo escrito en un lenguaje de programación parcialmente legible para humanos, ¿por qué no agregar un comentario descriptivo y conciso que explique el funcionamiento del código en un inglés amigable y comprensible?
Algunos dirán que "el código no debería ser difícil de entender", "haga que las funciones sean pequeñas", "use nombres descriptivos", "no escriba código de espagueti".
Pero todos sabemos que eso no es suficiente. Estas son meras pautas, importantes y útiles, pero no cambian el hecho de que algunos algoritmos son complejos. Y, por lo tanto, son difíciles de entender al leerlos línea por línea.
¿Es realmente tan malo explicar un algoritmo complejo con unas pocas líneas de comentarios sobre su funcionamiento general? ¿Qué tiene de malo explicar el código complicado con un comentario?
fuente
Respuestas:
En términos simples:
Línea de fondo:
Explicarte es bueno, no tener que hacerlo es mejor.
fuente
Hay muchas razones diferentes para que el código sea complicado o confuso. Las razones más comunes se abordan mejor refactorizando el código para que sea menos confuso, no agregando comentarios de ningún tipo.
Sin embargo, hay casos en los que un comentario bien elegido es la mejor opción.
Si es el algoritmo en sí mismo lo que es complicado y confuso, no solo su implementación, del tipo que se escribe en revistas de matemáticas y siempre se conoce como Algoritmo de Mbogo, entonces pones un comentario al comienzo de la implementación, leyendo algo así como "Este es el algoritmo de Mbogo para refrobnicar widgets, descrito originalmente aquí: [URL del documento]. Esta implementación contiene refinamientos de Alice y Carol [URL de otro documento]". No trates de entrar en más detalles que eso; Si alguien necesita más detalles, probablemente necesite leer el documento completo.
Si ha tomado algo que se puede escribir como una o dos líneas en alguna notación especializada y lo ha expandido en una gran cantidad de código imperativo, colocar esas una o dos líneas de notación especializada en un comentario sobre la función es una buena manera de dile al lector lo que se supone que debe hacer. Esta es una excepción al argumento "pero qué pasa si el comentario no se sincroniza con el código", porque la notación especializada es probablemente mucho más fácil de encontrar errores que el código. (Es al revés si escribiste una especificación en inglés). Un buen ejemplo está aquí: https://dxr.mozilla.org/mozilla-central/source/layout/style/nsCSSScanner.cpp#1057 ...
Si el código es sencillo en general, pero contiene una o dos cosas que parecen excesivamente enrevesadas, innecesarias o simplemente incorrectas, pero tienen que ser así por razones, entonces coloca un comentario inmediatamente por encima del aspecto sospechoso, en el que Usted declara las razones . Aquí hay un ejemplo simple, donde lo único que necesita explicación es por qué una constante tiene un cierto valor.
fuente
4
debería serCHAR_BIT / 2
;-)CHAR_BITS
fue 16 y sizeof (size_t) fue 2, pero el valor máximo de size_t fue, por ejemplo, 2 ^ 20 [size_t que contiene 12 bits de relleno]?reallocarray
, y OpenBSD generalmente no cree en atender las posibilidades que no ocurren en su ABI.int
. Tal como está, dadouint32_t x,y,z;
, el significado de(x-y) > z
depende del tamaño deint
. Además, un lenguaje diseñado para escribir código robusto debería permitir a los programadores distinguir entre un tipo en el que se espera que los cálculos superen el rango del tipo y deberían ajustarse silenciosamente, frente a uno donde los cálculos que exceden el rango del tipo deberían atrapar, frente a uno donde los cálculos no se espera que excedan el rango del tipo, pero ...No se trata de lo correcto o incorrecto, sino de la 'mejor práctica', como se define en el artículo de Wikipedia :
Por lo tanto, la mejor práctica es intentar mejorar el código primero y usar el inglés si eso no es posible.
No es una ley, pero es mucho más común encontrar código comentado que requiere refactorización que código refactorizado que requiere comentarios, la mejor práctica lo refleja.
fuente
//This code seriously needs a refactor
Llegará un día en que su código hermoso, perfectamente diseñado, bien estructurado y legible no funcionará. O no funcionará lo suficientemente bien. O surgirá un caso especial donde no funciona y necesita un ajuste.
En ese punto, deberá hacer algo que cambie las cosas para que funcione correctamente. Particularmente en el caso de que haya problemas de rendimiento, pero también a menudo en escenarios donde una de las bibliotecas, API, servicios web, gemas o sistemas operativos con los que está trabajando no se comporta como se esperaba, puede terminar haciendo sugerencias que no son necesariamente poco elegantes, pero son contra intuitivos o no obvios.
Si no tiene algunos comentarios para explicar por qué ha elegido ese enfoque, hay una muy buena posibilidad de que alguien en el futuro (y que alguien pueda ser usted) vea el código, vea cómo se puede "arreglar" algo más legible y elegante e inadvertidamente deshace su arreglo, porque no parece un arreglo.
Si todos escribieran siempre el código perfecto, entonces sería obvio que el código que parece imperfecto está funcionando alrededor de alguna intervención complicada del mundo real, pero no es así como funcionan las cosas. La mayoría de los programadores a menudo escriben código confuso o algo enredado, por lo que, cuando nos encontramos con esto, es una inclinación natural ordenarlo. Juro que mi yo pasado es un verdadero idiota cada vez que leo el código antiguo que he escrito.
Así que no pienso en los comentarios como una disculpa por un código incorrecto, sino tal vez como una explicación de por qué no hiciste lo obvio. Tener
// The standard approach doesn't work against the 64 bit version of the Frobosticate Library
permitirá a los futuros desarrolladores, incluido tu futuro yo, prestar atención a esa parte del código y probar esa biblioteca. Claro, también puede poner los comentarios en sus confirmaciones de control de fuente, pero la gente solo los verá después de que algo haya salido mal. Leerán los comentarios del código a medida que cambian el código.Las personas que nos dicen que siempre debemos escribir código teóricamente perfecto no siempre son personas con mucha experiencia en programación en entornos del mundo real. A veces necesita escribir código que funcione a cierto nivel, a veces necesita interoperar con sistemas imperfectos. Eso no significa que no pueda hacerlo de manera elegante y bien escrita, pero las soluciones no obvias necesitan explicación.
Cuando escribo código para proyectos de pasatiempos que sé que nadie más leerá, sigo comentando partes que me parecen confusas, por ejemplo, cualquier geometría 3D implica matemáticas con las que no estoy completamente en casa, porque sé cuándo vuelvo en seis meses habré olvidado por completo cómo hacer esto. Eso no es una disculpa por un mal código, es un reconocimiento de una limitación personal. Todo lo que haría al dejarlo sin comentar es crear más trabajo para mí en el futuro. No quiero que mi yo futuro tenga que volver a aprender algo innecesariamente si puedo evitarlo ahora. ¿Qué valor posible tendría eso?
fuente
La necesidad de comentarios es inversamente proporcional al nivel de abstracción del código.
Por ejemplo, el lenguaje ensamblador es, para la mayoría de los propósitos prácticos, ininteligible sin comentarios. Aquí hay un extracto de un pequeño programa que calcula e imprime los términos de la serie Fibonacci :
Incluso con comentarios, puede ser bastante complicado asimilarlo.
Ejemplo moderno: las expresiones regulares a menudo son construcciones de abstracción muy bajas (letras minúsculas, número 0, 1, 2, nuevas líneas, etc.). Probablemente necesiten comentarios en forma de muestras (Bob Martin, IIRC, lo reconoce). Aquí hay una expresión regular que (creo) debería coincidir con las URL HTTP (S) y FTP:
A medida que los lenguajes progresan en la jerarquía de abstracción, el programador puede usar abstracciones evocativas (nombre de variable, nombres de función, nombres de clase, nombres de módulos, interfaces, devoluciones de llamada, etc.) para proporcionar documentación incorporada. Negarse a aprovechar esto, y usar comentarios para ocultarlo es vago, un mal servicio y una falta de respeto por parte del mantenedor.
Estoy pensando en Numerical Recipes en C traducido textualmente en su mayoría a Numerical Recipes en C ++ , lo que deduzco que comenzó como Numerical Recipes (en FORTAN), con todas las variables
a
,aa
,b
,c
,cc
, etc mantenido a través de cada versión. Los algoritmos pueden haber sido correctos, pero no aprovecharon las abstracciones que proporcionaban los idiomas. Y me joden. Muestra de un artículo del Dr. Dobbs - Transformada rápida de Fourier :Como un caso especial sobre la abstracción, cada idioma tiene modismos / fragmentos de código canónico para ciertas tareas comunes (eliminar una lista dinámica vinculada en C), e independientemente de cómo se vean, no deben documentarse. Los programadores deben aprender estos modismos, ya que no son parte oficial del lenguaje.
Así que la conclusión: el código no idiomático construido a partir de bloques de construcción de bajo nivel que no se puede evitar necesita comentarios. Y esto es necesario WAAAAY menos de lo que sucede.
fuente
dec dword [term] ; decrement loop counter
. Por otro lado, lo que falta en su ejemplo de lenguaje ensamblador es un comentario antes de cada "párrafo de código" que explica lo que hace el siguiente bloque de código. En ese caso, el comentario generalmente sería equivalente a una sola línea en pseudocódigo, como;clear the screen
, seguido de las 7 líneas que realmente toma para borrar la pantalla.^(((ht|f)tps?)\:\/\/)?(www\.)*[a-zA-Z0-9\-\.]+\.(com|edu|gov|mil|net|org|biz|info|name|museum|us|ca|uk)(\:[0-9]+)*(\/($|[a-zA-Z0-9\.\,\;\?\'\\\+&%\$#\=~_\-]+))*$
? Tenga en cuenta las direcciones numéricas."The need for comments is inversely proportional to the abstraction level of the code."
Resume casi todo allí mismo.No creo que haya nada malo con los comentarios en el código. La idea de que los comentarios son de alguna manera malos en mi opinión se debe a que algunos programadores llevan las cosas demasiado lejos. Hay mucho movimiento de banda en esta industria, particularmente hacia vistas extremas. En algún momento, el código comentado se convirtió en equivalente al código incorrecto y no estoy seguro de por qué.
Los comentarios tienen problemas: debe mantenerlos actualizados a medida que actualiza el código al que hacen referencia, lo que ocurre con muy poca frecuencia. Un wiki o algo es un recurso más apropiado para una documentación exhaustiva sobre su código. Su código debe ser legible sin requerir comentarios. El control de versiones o las notas de revisión deben estar donde describe los cambios de código que realizó.
Sin embargo, ninguno de los anteriores invalida el uso de comentarios. No vivimos en un mundo ideal, así que cuando cualquiera de los anteriores falla por alguna razón, prefiero tener algunos comentarios para retroceder.
fuente
Creo que estás leyendo demasiado en lo que dice. Hay dos partes distintas en su queja:
(1) es inevitable. No creo que Martin esté en desacuerdo contigo. Si está escribiendo algo así como la raíz cuadrada inversa rápida , necesitará algunos comentarios, incluso si es simplemente "piratería de nivel de bits de punto flotante malvado". Salvo algo simple como un DFS o una búsqueda binaria, es poco probable que la persona que lee su código tenga experiencia con ese algoritmo, por lo que creo que debería haber al menos una mención en los comentarios sobre lo que es.
Sin embargo, la mayoría del código no es (1). Rara vez escribirá una pieza de software que no sea más que implementaciones de mutex enrolladas a mano, oscuras operaciones de álgebra lineal con poco soporte de biblioteca y algoritmos novedosos conocidos solo por el grupo de investigación de su empresa. La mayoría del código consiste en llamadas de biblioteca / marco / API, IO, repetitivo y pruebas unitarias.
Este es el tipo de código del que habla Martin. Y aborda su pregunta con la cita de Kernighan y Plaugher en la parte superior del capítulo:
Si tiene secciones largas y complicadas en su código, no ha podido mantener su código limpio . La mejor solución a este problema no es escribir un comentario de un párrafo en la parte superior del archivo para ayudar a los futuros desarrolladores a resolverlo; La mejor solución es reescribirlo.
Y esto es exactamente lo que dice Martin:
Este es tu (2). Martin está de acuerdo en que el código largo y complicado necesita comentarios, pero atribuye la culpa de ese código a los hombros del programador que lo escribió, no una idea nebulosa de que "todos sabemos que eso no es suficiente". Argumenta que:
fuente
Nada como tal. Documentar su trabajo es una buena práctica.
Dicho esto, aquí tienes una falsa dicotomía: escribir código limpio versus escribir código documentado: los dos no están en oposición.
En lo que debe enfocarse es en simplificar y abstraer el código complejo en un código más simple, en lugar de pensar "el código complejo está bien siempre que se comente".
Idealmente, su código debe ser simple y documentado.
Cierto. Esta es la razón por la cual todos sus algoritmos públicos de API deben explicarse en la documentación.
Idealmente, después de escribir un código complejo, debería (no una lista exhaustiva):
Ninguno de estos pasos es trivial (es decir, cada uno puede tomar algunas horas) y las recompensas por hacerlo no son inmediatos. Como tal, estos pasos están (casi) siempre comprometidos (por los desarrolladores que cortan las esquinas, los gerentes que cortan las esquinas, los plazos, las restricciones del mercado / otras condiciones del mundo real, la falta de experiencia, etc.).
Nunca debería tener que confiar en leer la implementación para descubrir qué hace una API. Cuando haces eso, estás implementando código de cliente basado en la implementación (en lugar de la interfaz) y eso significa que el acoplamiento de tu módulo ya está hecho un desastre, potencialmente estás introduciendo dependencias indocumentadas con cada nueva línea de código que escribes, y Ya agregando deuda técnica.
No, eso es bueno. Sin embargo, agregar algunas líneas de comentarios no es suficiente.
El hecho de que no debería tener un código complicado, si eso se puede evitar.
Para evitar códigos complicados, formalice sus interfaces, gaste ~ 8 veces más en diseño de API de lo que gasta en la implementación (Stepanov sugirió gastar al menos 10 veces en la interfaz, en comparación con la implementación), y vaya a desarrollar un proyecto con el conocimiento de que estás creando un proyecto, no solo escribiendo algún algoritmo.
Un proyecto incluye documentación API, documentación funcional, medidas de código / calidad, gestión de proyectos, etc. Ninguno de estos procesos son pasos rápidos únicos (todos toman tiempo, requieren previsión y planificación, y todos requieren que vuelva a ellos periódicamente y los revise / complete con detalles).
fuente
Consideraría esto un ligero abuso de los "comentarios". Si el programador quiere leer algo en lugar del algoritmo completo, entonces para eso está la documentación de la función. OK, entonces la documentación de la función podría aparecer en los comentarios en la fuente (tal vez para la extracción por parte de las herramientas de documentación), pero aunque sintácticamente es un comentario en lo que respecta a su compilador, debe considerarlos como elementos separados con propósitos separados. ¡No creo que "los comentarios deberían ser escasos" necesariamente significa "la documentación debería ser escasa" o incluso "los avisos de copyright deberían ser escasos"!
Los comentarios en la función son para que alguien los lea , así como el código. Entonces, si tiene algunas líneas en su código que son difíciles de entender y no puede hacer que sean fáciles de entender, entonces un comentario es útil para que el lector lo use como marcador de posición para esas líneas. Esto podría ser muy útil mientras el lector solo intenta obtener una idea general, pero hay un par de problemas:
Hay excepciones, pero la mayoría de los lectores deberán comprender el código en sí. Los comentarios deben escribirse para ayudar a eso, no para reemplazarlo, por lo que generalmente se recomienda que los comentarios digan "por qué lo estás haciendo". Un lector que conoce la motivación para las próximas líneas de código tiene una mejor oportunidad de ver lo que hace y cómo.
fuente
A menudo tenemos que hacer cosas complicadas. Ciertamente es correcto documentarlos para una comprensión futura. A veces, el lugar correcto para esta documentación es el código, donde la documentación puede mantenerse actualizada con el código. Pero definitivamente vale la pena considerar la documentación por separado. Esto también puede ser más fácil de presentar a otras personas, incluidos diagramas, imágenes en color, etc. Entonces el comentario es solo:
o incluso solo
Ciertamente, las personas están contentas con las funciones nombradas
MatchStringKnuthMorrisPratt
oencryptAES
opartitionBSP
. Vale la pena explicar nombres más oscuros en un comentario. También puede agregar datos bibliográficos y un enlace a un documento desde el que ha implementado un algoritmo.Si un algoritmo es complejo y novedoso y no es obvio, definitivamente vale la pena un documento, aunque solo sea para la circulación interna de la empresa. Verifique el documento en el control de origen si le preocupa que se pierda.
Hay otra categoría de código que no es tanto algorítmica como burocrática. Debe configurar parámetros para otro sistema o interactuar con los errores de otra persona:
fuente
doPRD239Algorithm
si me dijera nada sobre la función sin tener que buscar el algoritmo, la razónMatchStringKnuthMorrisPratt
y elencryptAES
trabajo es que comienzan con una descripción de lo que hacen, luego siguen con una descripción de la metodología.Me olvido de donde lo leí, pero no es una línea nítida y clara entre lo que debe aparecer en el código y lo que debería aparecer como un comentario.
Creo que deberías comentar tu intención, no tu algoritmo . Es decir, comentar lo que querías hacer, no lo que haces .
Por ejemplo:
Aquí no hay ningún intento de indicar qué realiza cada paso, todo lo que dice es lo que se supone que debe hacer.
PD: Encontré la fuente a la que me refería : Horror de codificación: el código te dice cómo, los comentarios te dicen por qué
fuente
Future
e indica que unget()
seguido de un cheque contranull
detecta si elFuture
ya se ha ejecutado - documentar correctamente la intención en lugar de la proceso .De Verdad? ¿Desde cuando?
Un código bien diseñado con buenos nombres es más que suficiente en la gran mayoría de los casos. Los argumentos en contra del uso de comentarios son bien conocidos y documentados (como se refiere).
Pero estas son pautas (como cualquier otra cosa). En el raro caso (en mi experiencia, aproximadamente una vez cada 2 años) donde las cosas empeorarían cuando se refactorizara en funciones legibles más pequeñas (debido a las necesidades de rendimiento o cohesión), luego continúe: escriba un comentario largo que explique qué es realmente la cosa haciendo (y por qué estás violando las mejores prácticas).
fuente
El propósito principal del código es ordenarle a una computadora que haga algo, por lo que un buen comentario nunca sustituye a un buen código porque los comentarios no se pueden ejecutar.
Dicho esto, los comentarios en la fuente son una forma de documentación para otros programadores (incluido usted). Si los comentarios son sobre temas más abstractos que lo que está haciendo el código en cada paso, lo está haciendo mejor que el promedio. Ese nivel de abstracción varía con la herramienta que está utilizando. Los comentarios que acompañan a las rutinas de lenguaje ensamblador generalmente tienen un nivel más bajo de "abstracción" que, por ejemplo, este APL
A←0⋄A⊣{2⊤⍵:1+3×⍵⋄⍵÷2}⍣{⍺=A+←1}⎕
. Creo que probablemente merecería un comentario sobre el problema que está destinado a resolver, ¿mmm?fuente
Si el código es trivial, no necesita un comentario explicativo. Si el código no es trivial, lo más probable es que el comentario explicativo tampoco lo sea.
Ahora, el problema con el lenguaje natural no trivial es que muchos de nosotros no somos muy buenos para leerlo o escribirlo. Estoy seguro de que sus habilidades de comunicación escrita son excelentes, pero, sin embargo, alguien con un menor conocimiento del lenguaje escrito podría entender mal sus palabras.
Si te esfuerzas mucho en escribir un lenguaje natural que no pueda malinterpretarse, terminas con algo así como un documento legal (y como todos sabemos, estos son más detallados y difíciles de entender que el código).
El código debería ser la descripción más concisa de su lógica, y no debería haber mucho debate sobre el significado de su código porque su compilador y plataforma tienen la última palabra.
Personalmente no diría que nunca debes escribir un comentario. Solo que debe considerar por qué su código necesita un comentario y cómo puede solucionarlo. Este parece ser un tema común en las respuestas aquí.
fuente
Un punto aún no mencionado es que a veces comentar con precisión lo que hace un fragmento de código puede ser útil en los casos en que un lenguaje utiliza una sintaxis particular para múltiples propósitos. Por ejemplo, suponiendo que todas las variables sean de tipo
float
, considere:El efecto de emitir explícitamente un
float
tofloat
es forzar que el resultado se redondee a precisión simple; Por lo tanto, el comentario podría verse como simplemente diciendo lo que hace el código. Por otro lado, compare ese código con:Aquí, el propósito del lanzamiento es evitar que el compilador grazne en la forma más eficiente de calcular con precisión (f2 / 10) [es más preciso que multiplicar por 0.1f, y en la mayoría de las máquinas es más rápido que dividir por 10.0f].
Sin el comentario, alguien que estaba revisando el código anterior podría pensar que el elenco se agregó con la creencia errónea de que sería necesario para evitar que el compilador graznara y que no era necesario. De hecho, el modelo sirve para hacer exactamente lo que dice la especificación del lenguaje: forzar que el resultado del cálculo se redondee a precisión simple incluso en máquinas donde el redondeo sería más costoso que mantener el resultado en una precisión más alta. Dado que un elenco
float
puede tener varios significados y propósitos diferentes, tener un comentario que especifique qué significado se pretende en un escenario particular puede ayudar a aclarar que el significado real se alinea con la intención.fuente
someFloatProperty
la representación más precisa de lof2/10
que puede; El objetivo principal del segundo lanzamiento es, por lo tanto, simplemente hacer que el código se compile . Sin embargo, en el primer ejemplo, el reparto claramente no es necesario para su propósito normal (cambiar un tipo de tiempo de compilación a otro) ya que los operandos ya lo sonfloat
. El comentario sirve para aclarar que el reparto es necesario para un propósito secundario (redondeo).(float)
elenco en el segundo ejemplo. La pregunta es sobre la constante literal0.1
. Usted explicó (en el siguiente párrafo del texto) por qué escribiríamos0.1
: "es más preciso que multiplicar por 0.1f". Estoy sugiriendo que esas son las palabras que deberían estar en el comentario.double
para constantes o cálculos intermedios cuyo valor puede no ser representable comofloat
[aunque en idiomas que requieren molestos moldes explícitos de doble flotación, pereza puede presionar para usar el uso defloat
constantes no para velocidad, sino para minimizar molestias].Los comentarios que explican lo que hace el código son una forma de duplicación. Si cambia el código y luego olvida actualizar los comentarios, esto puede causar confusión. No estoy diciendo que no los uses, solo úsalos juiciosamente. Me suscribo al tío Bob maxim: "Solo comente lo que el código no puede decir".
fuente