Estoy teniendo algunas conversaciones con mis nuevos colegas sobre los comentarios. A ambos nos gusta Clean Code , y estoy perfectamente de acuerdo con el hecho de que los comentarios de código en línea deben evitarse y que los nombres de clase y métodos deben usarse para expresar lo que hacen.
Sin embargo, soy un gran admirador de agregar pequeños resúmenes de clase que intentan explicar el propósito de la clase y lo que realmente representa, principalmente para que sea fácil mantener el patrón del principio de responsabilidad única . También estoy acostumbrado a agregar resúmenes de una línea a los métodos que explican qué debe hacer el método. Un ejemplo típico es el método simple.
public Product GetById(int productId) {...}
Estoy agregando el siguiente resumen del método
/// <summary>
/// Retrieves a product by its id, returns null if no product was found.
/// </summary
Creo que el hecho de que el método devuelve nulo debe documentarse. Un desarrollador que quiera llamar a un método no debería tener que abrir mi código para ver si el método devuelve nulo o produce una excepción. A veces es parte de una interfaz, por lo que el desarrollador ni siquiera sabe qué código subyacente se está ejecutando.
Sin embargo, mis colegas piensan que este tipo de comentarios son " olor a código " y que "los comentarios son siempre fracasos" ( Robert C. Martin ).
¿Hay alguna manera de expresar y comunicar este tipo de conocimiento sin agregar comentarios? Como soy un gran admirador de Robert C. Martin, me estoy confundiendo un poco. ¿Los resúmenes son lo mismo que los comentarios y, por lo tanto, siempre fallan?
No se trata de comentarios en línea.
fuente
Respuestas:
Como han dicho otros, hay una diferencia entre los comentarios de documentación de API y los comentarios en línea. Desde mi punto de vista, la principal diferencia es que se lee un comentario en línea junto con el código , mientras que un comentario de documentación se lee junto con la firma de lo que sea que esté comentando.
Dado esto, podemos aplicar el mismo principio DRY . ¿El comentario dice lo mismo que la firma? Veamos tu ejemplo:
Esta parte solo repite lo que ya vemos del nombre
GetById
más el tipo de retornoProduct
. También plantea la pregunta de cuál es la diferencia entre "obtener" y "recuperar", y cuál es el código de soporte versus el comentario sobre esa distinción. Entonces es innecesario y un poco confuso. En todo caso, se interpone en el camino de la segunda parte del comentario realmente útil:Ah! Eso es algo que definitivamente no podemos saber con certeza solo por la firma, y proporciona información útil.
Ahora lleva esto un paso más allá. Cuando la gente habla de comentarios como olores de código, la pregunta no es si el código tal como es necesita un comentario, sino si el comentario indica que el código podría escribirse mejor, para expresar la información en el comentario. Eso es lo que significa "olor a código": no significa "¡no hagas esto!", Significa "si estás haciendo esto, podría ser una señal de que hay un problema".
Entonces, si sus colegas le dicen que este comentario sobre nulo es un olor a código, simplemente debe preguntarles: "Bien, ¿cómo debo expresar esto entonces?" Si tienen una respuesta factible, has aprendido algo. Si no es así, probablemente matará sus quejas muertas.
Con respecto a este caso específico, generalmente se sabe que el problema nulo es difícil. Hay una razón por la cual las bases de códigos están llenas de cláusulas de protección, por qué los cheques nulos son una precondición popular para los contratos de códigos, por qué la existencia de nulos se ha llamado un "error de mil millones de dólares". No hay tantas opciones viables. Sin embargo, uno popular que se encuentra en C # es la
Try...
convención:En otros idiomas, puede ser idiomático usar un tipo (a menudo llamado algo así como
Optional
oMaybe
) para indicar un resultado que puede o no estar allí:Entonces, en cierto modo, esta postura anti-comentario nos ha llevado a alguna parte: al menos hemos pensado si este comentario representa un olor y qué alternativas podrían existir para nosotros.
Si realmente deberíamos preferirlos a la firma original es otro debate, pero al menos tenemos opciones para expresar a través del código en lugar de comentar lo que sucede cuando no se encuentra ningún producto. Debes discutir con tus colegas cuál de estas opciones piensan que es mejor y por qué, y con suerte ayudar a avanzar más allá de las declaraciones dogmáticas generales sobre los comentarios.
fuente
Linq
equivalente deTry...
,...OrDefault
que se devuelvedefault(T)
si la cláusula llevaría a un resultado vacío.TryGetValue
patrón es una forma razonable de hacer esto en C #, pero la mayoría de los lenguajes funcionales tienen una mejor manera de representar un valor perdido. Lea más aquíT TryGetValue(params, ref bool success)
para cualquier tipo T oT TryGetValue(params)
, con nulo que indica falla, para el tipo T con restricción de clase, pero elTryGetXX
patrón que devuelvebool
es incompatible con la covarianza.Optional<Product>
para indicar que puede que no haya un Producto devuelto por el método.La cita de Robert C. Martin está fuera de contexto. Aquí está la cita con un poco más de contexto:
(Copiado de aquí , pero la cita original es de Clean Code: A Handbook of Agile Software Craftsmanship )
La forma en que esta cita se reduce a "Los comentarios son siempre fracasos" es un buen ejemplo de cómo algunas personas tomarán una cita sensata fuera de contexto y la convertirán en un estúpido dogma.
Documentación de la API (como javadoc) se supone que documentar la API para que el usuario pueda utilizarlo sin tener que leer el código fuente . Entonces, en este caso, la documentación debe explicar lo que hace el método. Ahora puede argumentar que "Recupera un producto por su id" es redundante porque ya está indicado por el nombre del método, pero la información que
null
puede devolverse es definitivamente importante para documentar, ya que esto no es obvio de ninguna manera.Si desea evitar la necesidad del comentario, debe eliminar el problema subyacente (que es el uso
null
como un valor de retorno válido), haciendo que la API sea más explícita. Por ejemplo, podría devolver algún tipo deOption<Product>
tipo, por lo que la firma del tipo en sí comunica claramente lo que se devolverá en caso de que no se encuentre el producto.Pero, en cualquier caso, no es realista documentar una API completamente solo a través de nombres de métodos y firmas de tipo. Use doc-comments para cualquier información adicional no obvia que el usuario debe saber. Tomemos como ejemplo la documentación de la API de
DateTime.AddMonths()
BCL:¡No hay forma de que puedas expresar esto usando solo el nombre y la firma del método! Por supuesto, la documentación de su clase puede no requerir este nivel de detalle, es solo un ejemplo.
Los comentarios en línea tampoco son malos.
Los malos comentarios son malos. Por ejemplo, comentarios que solo explican lo que se puede ver trivialmente del código, el ejemplo clásico es:
Los comentarios que explican algo que podría aclararse cambiando el nombre de una variable o método o reestructurando el código, es un olor a código:
Este es el tipo de comentarios que Martin critica. El comentario es un síntoma de una falla al escribir código claro, en este caso para usar nombres que se explican por sí mismos para variables y métodos. Por supuesto, el comentario en sí no es el problema, el problema es que necesitamos el comentario para comprender el código.
Pero los comentarios deben usarse para explicar todo lo que no es obvio en el código, por ejemplo, por qué el código está escrito de cierta manera no obvia:
Los comentarios que explican lo que hace una pieza de código demasiado complicada también es un olor, pero la solución no es prohibir los comentarios, ¡la solución es arreglar el código! En la palabra real, el código complicado ocurre (con suerte solo temporalmente hasta un refactorizador) pero ningún desarrollador ordinario escribe código limpio perfecto la primera vez. Cuando ocurre un código complicado, es mucho mejor escribir un comentario explicando lo que hace que no escribir un comentario. Este comentario también facilitará la refactorización posterior.
A veces el código es inevitablemente complejo. Puede ser un algoritmo complicado, o puede ser un código que sacrifica la claridad por razones de rendimiento. De nuevo los comentarios son necesarios.
fuente
x++
puede ser bueno si es algo así como "incrementar x en uno, envolviendo si es UINT32_MAX"; cualquiera que conozca la especificación del lenguaje sabría que incrementar unuint32_t
ajuste de voluntad, pero sin el comentario, uno podría no saber si dicho ajuste era una parte esperada del algoritmo que se está implementando.Hay una diferencia entre comentar su código y documentar su código .
Se necesitan comentarios para mantener el código más adelante, es decir, cambiar el código en sí.
Los comentarios pueden ser percibidos como problemáticos. El punto extremo sería decir que siempre indican un problema, ya sea dentro de su código (código demasiado difícil de entender) o dentro del idioma (idioma que no puede ser lo suficientemente expresivo; por ejemplo, el hecho de que el método nunca regrese
null
podría expresarse a través de contratos de código en C #, pero no hay forma de expresarlo a través de código en, digamos, PHPSe necesita documentación para poder utilizar los objetos (clases, interfaces) que desarrolló. El público objetivo es diferente: no se trata de las personas que mantendrán su código y lo cambiarán de lo que estamos hablando aquí, sino de las personas que apenas necesitan usarlo .
Eliminar la documentación porque el código es lo suficientemente claro es una locura, ya que la documentación está aquí específicamente para permitir el uso de clases e interfaces sin tener que leer miles de líneas de código .
fuente
Bueno, parece que su colega lee libros, capta lo que dicen y aplica lo que aprendió sin pensar y sin tener en cuenta el contexto.
Su comentario sobre lo que hace la función debe ser tal que pueda tirar el código de implementación, leo el comentario de la función y puedo escribir el reemplazo para la implementación, sin que nada salga mal.
Si el comentario no me dice si se produce una excepción o si se devuelve nil, no puedo hacer eso. Por otra parte, si el comentario no le dice que si se produce una excepción o si se devuelve nil, entonces siempre se llama a la función, debe asegurarse de que su código funciona correctamente si se produce una excepción o se devuelve nulo.
Entonces tu colega está totalmente equivocado. Y sigue leyendo todos los libros, pero piensa por ti mismo.
PD. Vi su línea "a veces es parte de una interfaz, por lo que ni siquiera sabe qué código se está ejecutando". Incluso con funciones virtuales, no sabe qué código se está ejecutando. Peor aún, si escribiste una clase abstracta, ¡ni siquiera hay ningún código! Entonces, si tiene una clase abstracta con una función abstracta, los comentarios que agrega a la función abstracta son lo único que un implementador de una clase concreta tiene que guiarlos. Esos comentarios también pueden ser lo único que puede guiar a un usuario de la clase, por ejemplo, si todo lo que tiene es una clase abstracta y una fábrica que devuelve una implementación concreta, pero nunca ve ningún código fuente de la implementación. (Y, por supuesto, no debería mirar el código fuente de una implementación).
fuente
Hay dos tipos de comentarios a tener en cuenta: aquellos visibles para las personas con el código y aquellos utilizados para generar documentación.
El tipo de comentario al que se refiere el tío Bob es el tipo que solo es visible para las personas con el código. Lo que él defiende es una forma de SECO . Para una persona que está mirando el código fuente, el código fuente debe ser la documentación que necesita. Incluso en el caso de que las personas tengan acceso al código fuente, los comentarios no siempre son malos. A veces, los algoritmos son complicados o necesita capturar por qué está tomando un enfoque no obvio para que otros no terminen rompiendo su código si intentan corregir un error o agregar una nueva característica.
Los comentarios que está describiendo son documentación de la API. Estas son cosas que son visibles para las personas que usan su implementación, pero que pueden no tener acceso a su código fuente. Incluso si tienen acceso a su código fuente, pueden estar trabajando en otros módulos y no estar viendo su código fuente. Estas personas encontrarían útil tener esta documentación disponible en su IDE mientras escriben su código.
fuente
El valor de un comentario se mide en el valor de la información que proporciona menos el esfuerzo necesario para leerlo y / o ignorarlo. Entonces si analizamos el comentario
Por valor y costo, vemos tres cosas:
Retrieves a product by its id
repite lo que dice el nombre de la función, por lo que es un costo sin valor. Debería ser eliminado.returns null if no product was found
Es una información muy valiosa. Es probable que reduzca los tiempos que otros codificadores tendrán que analizar la implementación de la función. Estoy bastante seguro de que ahorra más lectura que el costo de lectura que presenta. Debería quedarse.Las líneas
No llevar información alguna. Son puro costo para el lector del comentario. Pueden estar justificados si su generador de documentación los necesita, pero en ese caso probablemente debería pensar en un generador de documentación diferente.
Esta es la razón por la cual el uso de generadores de documentación es una idea discutible: generalmente requieren muchos comentarios adicionales que no contienen información o repiten cosas obvias, solo por el bien de un documento pulido.
Una observación que no he encontrado en ninguna de las otras respuestas:
Incluso los comentarios que no son necesarios para comprender / usar el código pueden ser muy valiosos. Aquí hay uno de esos ejemplos:
Probablemente una gran cantidad de texto, completamente innecesario para entender / usar el código. Sin embargo, explica una razón por la cual el código se ve como se ve. Se detendrá a las personas que miran el código, se preguntan por qué no se hace de la manera obvia y comenzarán a refactorizar el código hasta que se den cuenta de por qué el código se escribió así en primer lugar. E incluso si el lector es lo suficientemente inteligente como para no saltar directamente a la refactorización, todavía necesitan descubrir por qué el código se ve como se ve antes de darse cuenta de que debería mantenerse como está. Este comentario literalmente podría ahorrarle horas de trabajo. Por lo tanto, el valor es más alto que el costo.
Del mismo modo, los comentarios pueden comunicar las intenciones de un código, en lugar de cómo funciona. Y pueden pintar el panorama general que generalmente se pierde en los detalles minuciosos del código en sí. Como tal, tienes razón al estar a favor de los comentarios de la clase. Valoro más los comentarios de clase si explican la intención de la clase, cómo interactúa con otras clases, cómo se debe usar, etc. Por desgracia, no soy un gran autor de tales comentarios ...
fuente
GetById
plantea la pregunta, qué es id, y también, qué obtener de dónde. El comentario de la documentación debe permitir que el entorno de desarrollo muestre la respuesta a estas preguntas. A menos que se explique en otra parte en los comentarios del documento del módulo, también sería un lugar para decir por qué uno obtendría la identificación de todos modos.El código no comentado es un código incorrecto. Es un mito generalizado (si no universal) que el código puede leerse de la misma manera que, por ejemplo, el inglés. Debe ser interpretado, y para cualquier código que no sea el más trivial que requiere tiempo y esfuerzo. Además, todos tienen un nivel diferente de capacidad tanto para leer como para escribir cualquier idioma. Las diferencias entre el autor y los estilos y capacidades de codificación del lector son fuertes barreras para una interpretación precisa. También es un mito que puede derivar la intención del autor de la implementación del código. En mi experiencia, rara vez es incorrecto agregar comentarios adicionales.
Robert Martin y col. considérelo un "mal olor" porque puede ser que el código haya cambiado y los comentarios no. Digo que es algo bueno (exactamente de la misma manera que se agrega un "mal olor" al gas doméstico para alertar al usuario de fugas). La lectura de comentarios le brinda un punto de partida útil para interpretar el código real. Si coinciden, habrá aumentado la confianza en el código. Si difieren, entonces ha detectado un olor de advertencia y necesita investigar más a fondo. La cura para un "mal olor" no es eliminar el olor sino sellar la fuga.
fuente
i++; // Adjust for leap years.
En algunos idiomas (F #, por ejemplo), todo este comentario / documentación puede expresarse en la firma del método. Esto se debe a que en F #, nulo generalmente no es un valor permitido a menos que esté específicamente permitido.
Lo que es común en F # (y también en varios otros lenguajes funcionales) es que, en lugar de nulo, utiliza un tipo de opción
Option<T>
que podría serNone
oSome(T)
. El lenguaje entonces entendería esto y lo obligaría (o le advertirá si no lo hace) coincidir en ambos casos cuando intente usarlo.Entonces, por ejemplo, en F #, podría tener una firma que se vea así
Y entonces esta sería una función que toma un parámetro (un int) y luego devuelve un producto o el valor Ninguno.
Y luego podrías usarlo así
Y obtendría advertencias del compilador si no coincide con los dos casos posibles. Por lo tanto, no hay forma posible de obtener excepciones de referencia nula allí (a menos que ignore las advertencias del compilador, por supuesto), y sepa todo lo que necesita simplemente mirando la firma de la función.
Por supuesto, esto requiere que su lenguaje esté diseñado de esta manera, que muchos lenguajes comunes como C #, Java, C ++, etc., no tienen. Por lo tanto, esto puede no serle útil en su situación actual. Pero es (con suerte) bueno saber que existen idiomas que le permiten expresar este tipo de información de forma estática sin recurrir a comentarios, etc. :)
fuente
Aquí hay algunas respuestas excelentes y no quiero repetir lo que dicen. Pero déjame agregar algunos comentarios. (Sin juego de palabras).
Hay muchas afirmaciones que hacen las personas inteligentes, sobre el desarrollo de software y muchos otros temas, supongo, que son muy buenos consejos cuando se entienden en contexto, pero qué personas tontas que toman fuera de contexto o aplican en situaciones inapropiadas o toman extremos ridículos
La idea de que el código debe autodocumentarse es una excelente idea. Pero en la vida real, hay limitaciones en la practicidad de esta idea.
Un inconveniente es que el lenguaje puede no proporcionar características para documentar lo que debe documentarse de manera clara y concisa. A medida que mejoran los lenguajes informáticos, esto se convierte en un problema cada vez menor. Pero no creo que haya desaparecido por completo. En los días en que escribí en ensamblador, tenía mucho sentido incluir un comentario como "precio total = precio de artículos no gravables más precio de artículos gravables más precio de artículos gravables * tasa impositiva". Exactamente lo que estaba en un registro en un momento dado no era necesariamente obvio. Tomó muchos pasos para hacer una operación simple. Etc. Pero si está escribiendo en un lenguaje moderno, tal comentario sería una reexpresión de una línea de código.
Siempre me molesto cuando veo comentarios como "x = x + 7; // agregue 7 a x". Como, wow, gracias, si hubiera olvidado lo que significa un signo más, eso podría haber sido muy útil. En lo que realmente podría estar confundido es en saber qué es "x" o por qué fue necesario agregarle 7 en este momento en particular. Este código puede hacerse autodocumentado dando un nombre más significativo a "x" y usando una constante simbólica en lugar de 7. Al igual que si hubiera escrito "total_price = total_price + MEMBERSHIP_FEE;", entonces probablemente no sea necesario un comentario .
Suena genial decir que el nombre de una función debería decirle exactamente lo que hace una función para que cualquier comentario adicional sea redundante. Tengo buenos recuerdos de la época en que escribí una función que verificaba si un número de elemento estaba en nuestra tabla de elementos de la base de datos, devolviendo verdadero o falso, y al que llamé, "ValidateItemNumber". Parecía un nombre decente. Luego apareció otra persona y modificó esa función para crear también un pedido para el artículo y actualizar la base de datos, pero nunca cambió el nombre. Ahora el nombre era muy engañoso. Parecía que hizo una pequeña cosa, cuando realmente hizo mucho más. Más tarde, alguien incluso sacó la parte sobre la validación del número de artículo y lo hizo en otro lugar, pero aún así no cambió el nombre. Entonces, aunque la función ahora no tiene nada que ver con la validación de números de artículo,
Pero en la práctica, a menudo es imposible que el nombre de una función describa completamente lo que hace la función sin que el nombre de la función sea tan largo como el código que constituye esa función. ¿El nombre nos dirá exactamente qué validaciones se realizan en todos los parámetros? ¿Qué sucede en condiciones de excepción? ¿Deletrear cada posible ambigüedad? En algún momento, el nombre se alargaría tanto que se volvería confuso. Acepto String BuildFullName (String firstname, String lastname) como una firma de función decente. Aunque eso no explica si el nombre se expresa "primero último" o "último, primero" o alguna otra variación, qué hace si una o ambas partes del nombre están en blanco, si impone límites en la longitud combinada y qué hace si se excede,
fuente