¿Qué hay de malo con los comentarios que explican el código complejo?

236

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?

Aviv Cohn
fuente
14
Si es tan complicado, intente refactorizarlo en pedazos más pequeños.
Vaughan Hilts
151
En teoría, no hay diferencia entre teoría y práctica. En la práctica, hay.
Scott Leadley
55
@mattnz: más directamente, en el momento en que escribe el comentario está inmerso en el problema que resuelve este código. La próxima vez que visite, tendrá menos capacidad con este problema .
Steve Jessop
26
"Qué" hace la función o método debe ser obvio por su nombre. Cómo lo hace es obvio por su código. Por qué se hace de esta manera, qué supuestos implícitos se usaron, qué documentos hay que leer para comprender el algoritmo, etc., deben aparecer en los comentarios.
SK-logic
11
Siento que muchas de las respuestas a continuación están malinterpretando deliberadamente su pregunta. No hay nada de malo en comentar tu código. Si siente que necesita escribir un comentario explicativo, entonces necesita hacerlo.
Tony Ennis

Respuestas:

408

En términos simples:

  • No hay nada malo con los comentarios per se. Lo que está mal es escribir código que necesita ese tipo de comentarios, o asumir que está bien escribir código complicado siempre y cuando lo expliques en un lenguaje sencillo.
  • Los comentarios no se actualizan automáticamente cuando cambia el código. Es por eso que a menudo los comentarios no están sincronizados con el código.
  • Los comentarios no hacen que el código sea más fácil de probar.
  • Disculparse no es malo. Lo que hiciste requiere disculpas (escribir código que no es fácil de entender) es malo.
  • Un programador que es capaz de escribir código simple para resolver un problema complejo es mejor que uno que escribe código complejo y luego escribe un comentario largo explicando qué hace su código.

Línea de fondo:

Explicarte es bueno, no tener que hacerlo es mejor.

Tulains Córdova
fuente
9191
Con frecuencia es imposible justificar el gasto del código de reescritura de dinero del empleador para ser más claro, cuando un buen comentario puede hacer el trabajo en mucho menos tiempo. Un programador obediente debe usar su juicio cada vez.
aecolley
34
@aecolley Escribir código autoexplicativo para empezar es mejor aún.
Tulains Córdova
127
A veces, el código autoexplicativo no es lo suficientemente eficiente como para resolver un problema con el HW&SW de hoy. Y la lógica de negocios es notoriamente ... retorcida. El subconjunto de problemas que tienen soluciones de software elegantes es significativamente más pequeño que el conjunto de problemas que son económicamente útiles para resolver.
Scott Leadley
62
@rwong: por el contrario, a menudo me encuentro escribiendo más comentarios en lógica de negocios, porque es importante mostrar exactamente cómo se alinea el código con los requisitos establecidos: "esta es la línea que nos impide ir a la cárcel por fraude electrónico en la Sección Cualquiera de el código penal ". Si es solo un algoritmo, bueno, un programador puede resolver el propósito desde cero si es absolutamente necesario. Para la lógica de negocios, necesita un abogado y el cliente en la misma habitación al mismo tiempo. Posiblemente mi "sentido común" esté en un dominio diferente del de los programadores de aplicaciones promedio ;-)
Steve Jessop
29
@ user61852 Excepto que lo que se explica por sí mismo para usted que acaba de escribir ese código y pasó el último período de $ inmerso en él podría no explicarse por sí mismo para usted que debe mantenerlo o editarlo dentro de cinco años, y mucho menos posibles personas que no son usted que pueden tener que mirarlo. "Autoexplicativo" es un nebuloso santo grial de definiciones.
Shadur
110

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 ...

    /**
     * Scan a unicode-range token.  These match the regular expression
     *
     *     u\+[0-9a-f?]{1,6}(-[0-9a-f]{1,6})?
     *
     * However, some such tokens are "invalid".  There are three valid forms:
     *
     *     u+[0-9a-f]{x}              1 <= x <= 6
     *     u+[0-9a-f]{x}\?{y}         1 <= x+y <= 6
     *     u+[0-9a-f]{x}-[0-9a-f]{y}  1 <= x <= 6, 1 <= y <= 6
    
  • 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.

    /* s1*s2 <= SIZE_MAX if s1 < K and s2 < K, where K = sqrt(SIZE_MAX+1) */
    const size_t MUL_NO_OVERFLOW = ((size_t)1) << (sizeof(size_t) * 4);
    if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
        nmemb > 0 && SIZE_MAX / nmemb < size)
      abort();
    
zwol
fuente
25
Eso es un ultraje, 4debería ser CHAR_BIT / 2;-)
Steve Jessop
@SteveJessop: ¿Algo impediría una implementación donde CHAR_BITSfue 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]?
supercat
2
@supercat No veo nada que obviamente excluya eso en C99, lo que significa que ese ejemplo es técnicamente incorrecto. Sucede que está tomado de (una versión ligeramente modificada de) OpenBSD reallocarray, y OpenBSD generalmente no cree en atender las posibilidades que no ocurren en su ABI.
zwol
3
@Zack: si el código está diseñado en torno a los supuestos POSIX, el uso de CHAR_BITS podría dar la impresión de que el código podría funcionar con valores distintos de 8.
supercat
2
@Zack: para que los tipos sin signo de ancho exacto sean útiles, su semántica debería definirse independientemente del tamaño de int. Tal como está, dado uint32_t x,y,z;, el significado de (x-y) > zdepende del tamaño de int. 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 ...
supercat
61

Entonces, ¿qué hay de malo en explicar el código complicado con un comentario?

No se trata de lo correcto o incorrecto, sino de la 'mejor práctica', como se define en el artículo de Wikipedia :

Una mejor práctica es un método o técnica que ha mostrado resultados consistentemente superiores a los logrados con otros medios, y que se utiliza como punto de referencia.

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.

FMJaguar
fuente
42
+1 para "es mucho más común encontrar código comentado que requiere refactorización que código refactorizado que requiere comentarios"
Brandon
77
Bien, pero con qué frecuencia es ese comentario: //This code seriously needs a refactor
Erik Reppen
2
Por supuesto, cualquier supuesta mejor práctica no respaldada por un estudio científico riguroso es simplemente una opinión.
Blrfl
54

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 Librarypermitirá 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?

glenatron
fuente
55
@Christian es? La primera línea hace referencia a esa afirmación, sin duda, pero más allá de eso es un poco más amplia, según tengo entendido.
glenatron
99
"Juro que mi yo pasado es un verdadero idiota cada vez que leo el viejo código que he escrito". Cuatro años en mi carrera de desarrollo y encuentro que esto es algo que ocurre cada vez que miro algo mayor de 6 meses.
Ken
66
En muchos casos, la información histórica más informativa y útil se relaciona con cosas que se consideran pero se deciden en contra. Hay muchos casos en los que alguien elige el enfoque X para algo y algún otro enfoque Y parecería mejor; en algunos de esos casos, Y "casi" funcionará mejor que X, pero tendrá algunos problemas insuperables. Si se evitó Y debido a esos problemas, dicho conocimiento puede ayudar a evitar que otros pierdan su tiempo en intentos fallidos de implementar el enfoque Y.
supercat
44
En el día a día también uso mucho los comentarios de trabajo en progreso: no están allí a largo plazo, pero dejar una nota TODO o una sección corta para recordarme lo que iba a hacer a continuación puede ser útil recordatorio en la mañana.
glenatron
1
@Lilienthal, no creo que el último párrafo esté restringido a proyectos personales, dijo "... todavía comento partes que encuentro confusas".
Comodín el
29

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 :

main:   
; initializes the two numbers and the counter.  Note that this assumes
; that the counter and num1 and num2 areas are contiguous!
;
    mov ax,'00'                     ; initialize to all ASCII zeroes
    mov di,counter                  ; including the counter
    mov cx,digits+cntDigits/2       ; two bytes at a time
    cld                             ; initialize from low to high memory
    rep stosw                       ; write the data
    inc ax                          ; make sure ASCII zero is in al
    mov [num1 + digits - 1],al      ; last digit is one
    mov [num2 + digits - 1],al      ; 
    mov [counter + cntDigits - 1],al

    jmp .bottom         ; done with initialization, so begin

.top
    ; add num1 to num2
    mov di,num1+digits-1
    mov si,num2+digits-1
    mov cx,digits       ; 
    call    AddNumbers  ; num2 += num1
    mov bp,num2         ;
    call    PrintLine   ;
    dec dword [term]    ; decrement loop counter
    jz  .done           ;

    ; add num2 to num1
    mov di,num2+digits-1
    mov si,num1+digits-1
    mov cx,digits       ;
    call    AddNumbers  ; num1 += num2
.bottom
    mov bp,num1         ;
    call    PrintLine   ;
    dec dword [term]    ; decrement loop counter
    jnz .top            ;
.done
    call    CRLF        ; finish off with CRLF
    mov ax,4c00h        ; terminate
    int 21h             ;

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:

^(((ht|f)tp(s?))\://)?(www.|[a-zA-Z].)[a-zA-Z0-9\-\.]+\.(com|edu|gov|m
+il|net|org|biz|info|name|museum|us|ca|uk)(\:[0-9]+)*(/($|[a-zA-Z0-9\.
+\,\;\?\'\\\+&amp;%\$#\=~_\-]+))*$

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 :

void four1(double* data, unsigned long nn)
{
    unsigned long n, mmax, m, j, istep, i;
    double wtemp, wr, wpr, wpi, wi, theta;
    double tempr, tempi;

    // reverse-binary reindexing
    n = nn<<1;
    j=1;
    for (i=1; i<n; i+=2) {
        if (j>i) {
            swap(data[j-1], data[i-1]);
            swap(data[j], data[i]);
        }
        m = nn;
        while (m>=2 && j>m) {
            j -= m;
            m >>= 1;
        }
        j += m;
    };

    // here begins the Danielson-Lanczos section
    mmax=2;
    while (n>mmax) {
        istep = mmax<<1;
        theta = -(2*M_PI/mmax);
        wtemp = sin(0.5*theta);
        wpr = -2.0*wtemp*wtemp;
        wpi = sin(theta);
        wr = 1.0;
        wi = 0.0;
        for (m=1; m < mmax; m += 2) {
            for (i=m; i <= n; i += istep) {
                j=i+mmax;
                tempr = wr*data[j-1] - wi*data[j];
                tempi = wr * data[j] + wi*data[j-1];

                data[j-1] = data[i-1] - tempr;
                data[j] = data[i] - tempi;
                data[i-1] += tempr;
                data[i] += tempi;
            }
            wtemp=wr;
            wr += wr*wpr - wi*wpi;
            wi += wi*wpr + wtemp*wpi;
        }
        mmax=istep;
    }
}

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.

Kristian H
fuente
1
En realidad, nadie debería estar escribiendo una línea como esta en lenguaje ensamblador: 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.
Scott Whitlock
1
Sí, hay algunos comentarios innecesarios en la muestra de ensamblaje, pero para ser justos, es bastante representativo del estilo de ensamblaje 'bueno'. Incluso con un prólogo de párrafo de una o dos líneas, el código sería realmente difícil de seguir. Entendí la muestra ASM mejor que el ejemplo FFT. Programé un FFT en C ++ en la escuela de posgrado, y no se parecía en nada a esto, pero luego estábamos usando STL, iteradores, functors y bastantes llamadas a métodos. No es tan rápido como la función monolítica, pero es mucho más fácil de leer. Intentaré agregarlo para contrastar con la muestra NRinC ++.
Kristian H
Quiso decir ^(((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.
izabera
Más o menos mi punto: algunas cosas construidas a partir de abstracciones de muy bajo nivel no son fáciles de leer o verificar. Los comentarios (y, para no desviarse demasiado, TESTS) pueden ser útiles y no perjudiciales. Al mismo tiempo, no usar abstracciones de nivel superior que estén disponibles (: alfa:: num: donde estén disponibles) hace que sea más difícil de entender, incluso con buenos comentarios, que usar abstracciones de nivel superior.
Kristian H
3
+1: "The need for comments is inversely proportional to the abstraction level of the code." Resume casi todo allí mismo.
Gerrat
21

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.

Roy
fuente
18

Creo que estás leyendo demasiado en lo que dice. Hay dos partes distintas en su queja:

¿Qué tiene de malo explicar (1) un algoritmo complejo o (2) un fragmento de código largo y complicado con un comentario descriptivo?

(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:

No comente el código incorrecto, vuelva a escribirlo.

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:

El uso apropiado de los comentarios es para compensar nuestra falla en expresarnos en código ... Los comentarios son siempre fallas. Debemos tenerlos porque no siempre podemos descubrir cómo expresarnos sin ellos, pero su uso no es motivo de celebración.

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:

El código claro y expresivo con pocos comentarios es muy superior al código desordenado y complejo con muchos comentarios. En lugar de dedicar tu tiempo a escribir los comentarios que explican el desorden que has hecho, pasa el tiempo limpiando ese desorden.

Patrick Collins
fuente
3
Si un desarrollador con el que estuviera trabajando simplemente escribiera "piratería de nivel de bits de punto flotante malvado" para explicar el algoritmo rápido de raíz cuadrada, ellos hablarían conmigo. Sin embargo, siempre que incluyeran una referencia a algún lugar más útil, sería feliz.
Michael Anderson
8
No estoy de acuerdo de alguna manera: un comentario que explica cómo funciona algo malo es mucho más rápido. Dado que es probable que no se vuelva a tocar algún código (supongo que la mayoría del código), entonces un comentario es una mejor solución de negocios que una gran refactorización, que a menudo introduce errores (ya que una solución que mata el error confiable sigue siendo un error). Un mundo perfecto de código perfectamente comprensible no está disponible para nosotros.
gbjbaanb
2
@trysis jaja, sí, pero en un mundo donde los programadores son responsables y no empresarios, nunca enviarán, ya que siempre están chapando en oro una base de código constantemente refactorizada en una vana búsqueda de la perfección.
gbjbaanb
44
@PatrickCollins casi todo lo que leo en la web se trata de hacerlo bien la primera vez. ¡Casi nadie quiere escribir artículos sobre arreglar desorden! Los físicos dicen "dado una esfera perfecta ..." Comp. Los científicos dicen "dado un desarrollo greenfield ..."
gbjbaanb
2
La mejor solución es reescribirlo en un tiempo infinito; pero dado el código base de otra persona, los plazos corporativos típicos y la realidad; a veces lo mejor es comentar, agregar TODO: Refactor y obtener ese refactor en la próxima versión; y ese arreglo que tenía que hacerse ayer, hecho ahora. Lo importante de toda esta charla idealista sobre la refactorización es que no explica cómo funcionan realmente las cosas en el lugar de trabajo; a veces hay prioridades más altas y plazos lo suficientemente pronto que evitarán la fijación de código heredado de mala calidad. Así es como es.
hsanders
8

¿Qué tiene de malo explicar un algoritmo complejo o un fragmento de código largo y complicado con un comentario descriptivo?

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.

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.

Cierto. Esta es la razón por la cual todos sus algoritmos públicos de API deben explicarse en la documentación.

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?

Idealmente, después de escribir un código complejo, debería (no una lista exhaustiva):

  • considérelo un borrador (es decir, planee reescribirlo)
  • formalice los puntos de entrada / interfaces / roles / etc. del algoritmo (analice y optimice la interfaz, formalice abstracciones, precondiciones de documentos, condiciones posteriores y efectos secundarios y casos de errores de documentos).
  • escribir pruebas
  • limpieza y refactorización

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.).

[...] algunos algoritmos son complejos. Y, por lo tanto, son difíciles de entender al leerlos línea por línea.

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.

¿Es realmente tan malo explicar un algoritmo complejo con unas pocas líneas de comentarios sobre su funcionamiento general?

No, eso es bueno. Sin embargo, agregar algunas líneas de comentarios no es suficiente.

¿Qué tiene de malo explicar el código complicado con un comentario?

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).

utnapistim
fuente
3
"Nunca debería tener que confiar en leer la implementación para descubrir qué hace una API". A veces, esto se le inflige por un flujo ascendente que está comprometido a usar. Tuve un proyecto particularmente insatisfactorio lleno de comentarios de la forma "el siguiente código feo de Heath Robinson existe porque simpleAPI () no funciona correctamente en este hardware a pesar de lo que el vendedor afirma".
pjc50
6

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.

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:

  • Los comentarios no son necesariamente ciertos, mientras que el código hace lo que hace. Entonces el lector está tomando su palabra, y esto no es lo ideal.
  • El lector aún no comprende el código en sí mismo, por lo que hasta que vuelvan a leerlo más tarde todavía no están calificados para modificarlo o reutilizarlo. En cuyo caso, ¿qué están haciendo al leerlo?

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.

Steve Jessop
fuente
55
Un lugar útil para comentarios: en el código científico, a menudo puede tener cálculos que son bastante complejos, que involucran muchas variables. Para la cordura del programador, tiene sentido mantener nombres de variables realmente cortos, para que pueda ver las matemáticas, en lugar de los nombres. Pero eso hace que sea realmente difícil de entender para el lector. Por lo tanto, una breve descripción de lo que está sucediendo (o mejor, una referencia a la ecuación en un artículo de revista o similar) puede ser realmente útil.
naught101
1
@ naught101: sí, especialmente porque el documento al que te refieres también probablemente usaba nombres de variables de una letra. Por lo general, es más fácil ver que el código sigue el documento si usa los mismos nombres, pero eso está en conflicto con el objetivo de que el código se explique por sí mismo (en su lugar, se explica en el documento ). En este caso, un comentario donde se define cada nombre, que dice lo que realmente significa, sustituye a los nombres significativos.
Steve Jessop
1
Cuando estoy buscando algo específico en el código (¿dónde se maneja este caso específico?), No quiero leer y comprender párrafos de código solo para descubrir que, después de todo, no es el lugar. Necesito comentarios que resuman en una sola línea lo que está haciendo el siguiente párrafo. De esta manera, localizaré rápidamente las partes del código relacionadas con mi problema y omitiré detalles poco interesantes.
Florian F
1
@FlorianF: la respuesta tradicional es que los nombres de variables y funciones deben indicar aproximadamente de qué se trata el código y, por lo tanto, le permiten echar un vistazo a cosas que ciertamente no se refieren a lo que está buscando. Estoy de acuerdo con usted en que esto no siempre tiene éxito, pero no estoy tan de acuerdo que creo que todo el código debe ser comentado para facilitar la búsqueda o la lectura rápida. Pero tiene razón, ese es un caso en el que alguien está leyendo su código (más o menos) y legítimamente no necesita entenderlo.
Steve Jessop
2
@Snowman La gente podría hacer eso con nombres de variables. He visto código donde la variable listOfApples contenía una lista de Bananas. Alguien copió el código procesando la lista de manzanas y lo adaptó para plátanos sin molestarse en cambiar los nombres de las variables.
Florian F
5

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:

// This code implements the algorithm described in requirements document 239.

o incluso solo

void doPRD239Algorithm() { ...

Ciertamente, las personas están contentas con las funciones nombradas MatchStringKnuthMorrisPratto encryptAESo partitionBSP. 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:

/* Configure the beam controller and turn on the laser.
The sequence is timing-critical and this code must run with interrupts disabled.
Note that the constant 0xef45ab87 differs from the vendor documentation; the vendor
is wrong in this case.
Some of these operations write the same value multiple times. Do not attempt
to optimise this code by removing seemingly redundant operations.
*/
pjc50
fuente
2
Yo argumentaría en contra de nombrar funciones / métodos después de su algoritmo interno, la mayoría de las veces el método utilizado debe ser una preocupación interna, documentar la parte superior de su función con el método utilizado, pero no lo llame como doPRD239Algorithmsi me dijera nada sobre la función sin tener que buscar el algoritmo, la razón MatchStringKnuthMorrisPratty el encryptAEStrabajo es que comienzan con una descripción de lo que hacen, luego siguen con una descripción de la metodología.
scragar
5

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:

// The getter.
public <V> V get(final K key, Class<V> type) {
  // Has it run yet?
  Future<Object> f = multitons.get(key);
  if (f == null) {
    // No! Make the task that runs it.
    FutureTask<Object> ft = new FutureTask<Object>(
            new Callable() {

              public Object call() throws Exception {
                // Only do the create when called to do so.
                return key.create();
              }

            });
    // Only put if not there.
    f = multitons.putIfAbsent(key, ft);
    if (f == null) {
      // We replaced null so we successfully put. We were first!
      f = ft;
      // Initiate the task.
      ft.run();
    }
  }
  try {
    /**
     * If code gets here and hangs due to f.status = 0 (FutureTask.NEW)
     * then you are trying to get from your Multiton in your creator.
     *
     * Cannot check for that without unnecessarily complex code.
     *
     * Perhaps could use get with timeout.
     */
    // Cast here to force the right type.
    return (V) f.get();
  } catch (Exception ex) {
    // Hide exceptions without discarding them.
    throw Throwables.asRuntimeException(ex);
  }
}

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é

OldCurmudgeon
fuente
8
El primer comentario: ¿Ya se ha ejecutado? ¿Qué ha corrido todavía? Lo mismo para los otros comentarios. Para alguien que no sabe lo que hace el código, esto es inútil.
gnasher729
1
@ gnasher729: tomado fuera de contexto, casi cualquier comentario será inútil: este código es una demostración de agregar comentarios que indican la intención en lugar de intentar describir . Lamento que no haga nada por ti.
OldCurmudgeon
2
Un mantenedor de ese código no tendrá un contexto. No es particularmente difícil averiguar qué hace el código, pero los comentarios no ayudan. Si escribe comentarios, tómese su tiempo y concéntrese cuando los escriba.
gnasher729
BTW - El tiene todavía ejecutar comentario se refiere a la Futuree indica que un get()seguido de un cheque contra nulldetecta si el Futureya se ha ejecutado - documentar correctamente la intención en lugar de la proceso .
OldCurmudgeon
1
@OldCurmudgeon: Su respuesta está lo suficientemente cerca de lo que estaba pensando, que simplemente agregaré este comentario como un ejemplo de su punto. Si bien no es necesario un comentario para explicar el código limpio, un comentario ES bueno para explicar por qué la codificación se realizó DE UNA MANERA SOBRE OTRA. En mi experiencia limitada, los comentarios a menudo son útiles para explicar las idiosincrasias del conjunto de datos sobre el que está trabajando el código, o las reglas de negocio que el código debe aplicar. El código de comentarios que se agrega para corregir un error es un buen ejemplo, si ese error ocurrió porque una suposición acerca de los datos era incorrecta.
Randall Stewart
4

Pero todos sabemos que eso no es suficiente.

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).

Telastyn
fuente
77
Sé que no es suficiente.
Florian F
2
¿Desde cuando? Aparentemente, ya sabes la respuesta a eso. "Un código bien diseñado con buenos nombres es más que suficiente en la gran mayoría de los casos". Por lo tanto, probablemente no sea suficiente en una minoría de casos, que es exactamente lo que pregunta el autor de la pregunta.
Ellesedil
3
Siempre trato de descifrar el código de otras personas a quienes desearía haber agregado algunos comentarios más de una vez cada dos años.
Ogre Psalm33
@ OgrePsalm33 - ¿Tienen métodos pequeños y usan buenos nombres? El código incorrecto es incorrecto, independientemente de los comentarios.
Telastyn
2
@Telastyn Desafortunadamente, cuando se trabaja en una base de código grande, los métodos "pequeños" y los nombres "buenos" son subjetivos para cada desarrollador (por lo tanto, es un buen comentario). Un desarrollador que escriba el código del algoritmo de procesamiento gráfico de Flarbigan durante 7 años, puede escribir algo perfectamente claro para él y desarrolladores similares, pero sería críptico con el nuevo tipo que pasó los últimos 4 años desarrollando el código de infraestructura de red de Perbian. Luego, 2 semanas después, el experto de Flarbigan renuncia.
Ogre Psalm33
2

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?

Scott Leadley
fuente
2

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í.

Martín
fuente
Exactamente lo que estaba pensando cuando no estaba de acuerdo con la afirmación "Un humano puede entender un pedazo de inglés mucho más rápido que él / ella puede entender un código con el mismo significado (siempre y cuando la operación no sea trivial)" El código es siempre menos ambiguo y más conciso.
stephenbayer
0

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:

f1 = (float)(f2+f3); // Force result to be rounded to single precision
f4 = f1-f2;

El efecto de emitir explícitamente un floatto floates 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:

thing.someFloatProperty = (float)(f2*0.1); // Divide by ten

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 floatpuede 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.

Super gato
fuente
No estoy seguro de que J. Random Programmer, al ver el segundo ejemplo, se dé cuenta de que la constante se escribe 0.1 por una buena razón, en lugar de porque el programador original olvidó escribir una 'f'.
David K
Especialmente durante la depuración, nunca asumes que se ha hecho algo por una buena razón.
gnasher729
@DavidK: El propósito de mi segundo código de ejemplo era contrastarlo con el primer fragmento de código. En la segunda parte del código, la intención del programador es probablemente tener someFloatPropertyla representación más precisa de lo f2/10que 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 son float. El comentario sirve para aclarar que el reparto es necesario para un propósito secundario (redondeo).
supercat
Estoy de acuerdo con la idea de que no necesitas hacer ningún comentario sobre el (float)elenco en el segundo ejemplo. La pregunta es sobre la constante literal 0.1. Usted explicó (en el siguiente párrafo del texto) por qué escribiríamos 0.1: "es más preciso que multiplicar por 0.1f". Estoy sugiriendo que esas son las palabras que deberían estar en el comentario.
David K
@DavidK: Ciertamente incluiría el comentario si supiera que 0.1f sería inaceptablemente impreciso, y usaría 0.1f si supiera que la pérdida de precisión sería aceptable y que 0.1f sería de hecho materialmente más rápido que 0.1 . Si no sé que ninguna de esas cosas es cierta, preferiría que mi hábito de codificación fuera usar doublepara constantes o cálculos intermedios cuyo valor puede no ser representable como float[aunque en idiomas que requieren molestos moldes explícitos de doble flotación, pereza puede presionar para usar el uso de floatconstantes no para velocidad, sino para minimizar molestias].
supercat
-1

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".

Murungu
fuente