Por qué hacen falta los comentarios de “Why Not”
(buttondown.com)- Los comentarios pueden usar lenguaje humano, que es más expresivo que los identificadores, así que son adecuados para dejar constancia de opciones que no aparecen en el código y alternativas descartadas
- “comment the why, not the what” es un enfoque que busca meter la mayor cantidad posible de información en los identificadores, pero últimamente también hay una tendencia a trasladar incluso las razones a nombres largos de funciones o de pruebas
- En el build epub de
Logic for Programmersse usó una implementación lenta que reemplaza en secuencia 16 símbolos matemáticos en cada cadena, pero como por ahora solo hay 25 cadenas matemáticas, sigue siendo lo suficientemente rápida - Este tipo de comentario después indica dónde corregir cuando las cadenas matemáticas crezcan a cientos y el build se vuelva un cuello de botella, y deja claro que el código lento fue un trade-off consciente
- Los nombres de funciones o las pruebas se adhieren a lo que el código realmente hace, así que les cuesta autodocumentar información negativa sobre alternativas no elegidas y cosas que no hace
Información que un comentario transmite más fácilmente que el código
- El código es un lenguaje de máquina estructurado, y los comentarios se escriben en lenguaje humano más expresivo
- “comment the why, not the what” puede interpretarse como que hay que poner la mayor cantidad posible de información en los identificadores
- Los identificadores se parecen a una forma limitada de lenguaje humano dentro del código; no pueden contener todo el “what”, pero en muchos casos sí bastante
- Últimamente ha crecido la postura de que incluso el “why” puede ir no en comentarios sino en nombres largos de funciones o de casos de prueba
- Las bases de código autodocumentadas por lo general aumentan la documentación mediante más identificadores
- Como excepción, hay casos donde se dice que convertir comentarios en logging vuelve el código más autodocumentado
Qué abordan los comentarios de “Why Not”
- La información que cuesta expresar con código es la información negativa
- La información negativa llama la atención sobre lo que no existe en el sistema y por qué no se eligió cierta alternativa
- “why not” no busca explicar lo que el código hace, sino dejar registro de las opciones que no tomó y las razones
Caso de reemplazo de símbolos matemáticos en epub
- En el build epub de
Logic for Programmers, por razones técnicas, notaciones matemáticas como\\forallno se convertían a símbolos como∀ - Para resolverlo, se escribió un script que reemplaza directamente los tokens dentro de cadenas matemáticas por sus símbolos Unicode correspondientes
- La implementación más fácil consiste en llamar a
string = string.replace(old, new)para cada uno de los 16 símbolos matemáticos necesarios - Ese enfoque es ineficiente porque recorre cada cadena 16 veces, aunque la alternativa de hacer los 16 reemplazos en una sola pasada es más compleja
- La idea central del comentario que se dejó es la siguiente
- Recorre cada cadena 16 veces
- El libro actualmente solo tiene 25 cadenas matemáticas y la mayoría tiene menos de 5 caracteres
- Por eso sigue siendo suficientemente rápido
- Este comentario explica no solo “por qué usar código lento”, sino también “por qué no usar código rápido”
Una señal para quien lea después
- Aunque el código lento no cause un problema ahora mismo, puede volverse problemático más adelante
- Si en una futura versión de
Logic for Programmerslas cadenas matemáticas aumentan a cientos, esa etapa del build podría convertirse en un cuello de botella del build completo - Si se deja esta señal ahora, más adelante se puede ver de inmediato qué parte hay que corregir
- Aunque el código siga funcionando sin problemas, el comentario preserva el hecho de que el autor sabía del trade-off
- Cuando dentro de 2 años se vuelva a abrir
epub_math_fixer.py, hará menos falta investigar si ese código lento se debió a falta de experiencia, falta de tiempo o un error - Un comentario negativo deja constancia de que se conocía la implementación lenta, se evaluaron alternativas y se decidió no optimizar
Por qué es difícil reemplazarlo con nombres de funciones y pruebas
- Un nombre de función como
RunFewerTimesSlowerAndSimplerAlgorithmAfterConsideringTradeOffses demasiado largo y aun así no explica bien el trade-off real - Si después se optimiza el código, puede haber que cambiar ese nombre en varios lugares
- Un problema aún mayor es que ese tipo de nombre no dice qué hace realmente la función, así que termina debilitando la autodocumentación en vez de fortalecerla
- Los identificadores, como nombres de funciones y variables, solo pueden contener una cláusula de información
- Es difícil meter al mismo tiempo en un solo identificador “lo que hace la función” y “el trade-off que acepta la función”
Información negativa que también cuesta dejar en pruebas
- Se podría crear una prueba que haga grep de los bloques matemáticos del libro y falle si la cantidad supera 80
- Pero una prueba así no evalúa directamente a
EpubMathFixer - Dentro de la función no hay un punto donde esa prueba pueda engancharse
- La autodocumentación se adhiere al código escrito y explica lo que ese código hace
- Como la información negativa trata sobre lo que el código no hace, no encaja de raíz con la autodocumentación
Una pregunta más amplia
- Los comentarios de “why not” pueden verse como un caso de razonamiento contrafactual
- Queda abierta la pregunta de si los elementos abstractos de la comunicación humana en general pueden autodocumentarse
- Sigue en duda si información como metáforas, incertidumbre o afirmaciones éticas también puede autodocumentarse
1 comentarios
Comentarios de Hacker News
Me acordé de un chiste que creo haber visto en Twitter hace unos años: “los ingenieros junior escriben comentarios que explican qué hace el código, los ingenieros intermedios escriben comentarios que explican por qué lo hace así, y los ingenieros senior escriben comentarios que explican por qué el código no fue escrito de otra manera”
Por otro lado, si el significado quedaba claro solo con el nombre de la función y de las variables, también he escrito funciones bastante grandes sin comentarios
Por eso gana el lado con menos comentarios, pero cuando ves una base de código sin comentarios, solo revisándola directamente puedes distinguir si es una obra maestra pulida con elegancia o código inestable hecho por principiantes
Aunque parezca un parche total, a veces de verdad es lo mejor que se puede conseguir
Si creo que me va a servir cuando vuelva a ver el código dentro de un año, lo dejo todo en comentarios. Normalmente es el por qué y el por qué no, y cuando el código es complejo, también agrego brevemente el “qué” para ver mejor el flujo
Lo que no sirve son los comentarios obligatorios. Una API pública sí debe estar bien documentada, pero algunas organizaciones fuerzan comentarios incluso en funciones privadas, y termina siendo solo repetir con otras palabras un propósito demasiado obvio por el propio nombre de la función. Eso no solo es una pérdida de tiempo, también te vuelve insensible a los comentarios y te enseña a ignorarlos
Tampoco me gustan los comentarios de relleno que agregan las herramientas. Lo de poner
//foro//tryen cada loop es especialmente maloMejor eliminar los comentarios obligatorios y generados, y en temas oscuros cambiar los comentarios a un neón brillante para que resalten. Si un comentario está ahí, debería significar que es importante
Para mantener sistemas grandes y antiguos en un entorno de negocio con plazos ajustados, a veces hay que hacer cosas raras, y en esos casos hay que explicar por qué
Una razón importante para oponerse a los comentarios es que los comentarios también pasan a formar parte del código y necesitan mantenimiento. Pero la mayoría solo los lee cuando se atasca, y además los editores suelen difuminarlos en gris, haciéndolos psicológicamente invisibles. Por eso los comentarios envejecen muy fácil
Me pregunto si ese contexto para el “yo del futuro” también es útil en una base de código compartida que toca mucha gente, o si es un estilo que funciona mejor cuando solo unos pocos tocan el código
Así que le agregué “=> yes!” al comentario existente, y agradecí que mi yo del pasado hubiera documentado esa duda. En el trabajo, sobre todo al corregir bugs, suelo dejar una o dos líneas de comentario junto con el número del ticket encima de cambios que no son obvios
Mi formato favorito de comentario en código es esta plantilla: “DEAR MAINTAINER: este código está así por [razón]. Si intentas ‘arreglarlo’ y descubres que fue un error terrible, por favor incrementa el contador
total_hours_wasted_here = ncomo advertencia para la siguiente persona”Yo no fui el autor original, pero lo usé con agradecimiento una o dos veces, y me daba risa ver commits de una sola línea que solo aumentaban el contador
Un ingeniero más senior recibió la base de código, “arregló” todo para que fuera iterativo, e incluso quiso sermonearme por email sobre por qué la recursión era mala, pero su código en realidad no cumplía todos los requisitos y al final terminó reconstruyendo, en esencia, lo que yo había hecho con recursión
Después se disculpó por parte de lo que dijo, pero si hubiera habido un comentario así al principio, tal vez se habría evitado todo ese trabajo
Estoy de acuerdo en que el título es ambiguo, y por eso terminé leyendo el artículo. Personalmente prefiero usar pocos comentarios en general, pero los comentarios explicativos del texto claramente tienen valor. Es un buen recordatorio de que hay que explicar por qué se hizo así y por qué no de otra forma
Esto aplica especialmente a tu propio código si va a seguir manteniéndose dentro de 5, 10 o 15 años. Hace poco, revisando código nuevo de un colega, pensé “¿por qué hizo esto así?”, y 10 líneas más arriba estaba la razón por la que yo mismo había hecho exactamente lo mismo 8 años antes. Ese colega simplemente estaba siguiendo una regla clave del mantenimiento: hacer que se vea como el código existente
Es solo un caso especial de un principio más amplio: poner comentarios en las cosas sorprendentes al leer el código
Al escribir código, si en tu cabeza te preguntas constantemente “¿podré entender este código después?” y cada vez respondes por instinto “sí”, eso es arrogante y muchas veces está mal. Si la respuesta es “no estoy seguro”, la siguiente pregunta naturalmente es “¿por qué?”, y esa respuesta es justo lo que debería ir en el comentario
A veces la respuesta es “porque quien lo lea podría preguntarse por qué no se escribió de otra manera”, y ese es el caso especial del que trata este texto. Pero a veces es “porque no está claro cómo funciona o por qué es correcto”, y entonces hace falta otro tipo de comentario
Esto pasa sobre todo cuando se recortan cadenas o se recorre una estructura de datos poco común en un paso intermedio
Solo con los identificadores se puede avanzar bastante, pero no hasta el final. En lo personal, me gusta exigir comentarios de documentación como jsdoc/xmldoc para métodos públicos o variables, campos y parámetros
Ponerle un buen nombre a un método también es importante, pero escribir brevemente qué hace lo vuelve más claro y en especial hace visibles defectos evidentes. Muchas veces, en el momento de escribir la primera oración se te ocurre un nombre mejor, y si en la explicación empieza a aparecer “y”, eso es señal de que el método está haciendo demasiadas cosas y se puede dividir de forma más lógica
Es fácil pensar que una propiedad es tan obvia que no necesita documentación, pero algo como
/** The API key */ string ApiKey;deja demasiadas cosas fuera. No se sabe de dónde viene esa clave, si es solo para uso interno o si se intercambia con sistemas externos, si es obligatoria, si puede ser null o vacía, si tiene longitud máxima, qué pasa si el valor es inválido, o si hay más código o documentación para leerEs información que a quien la escribió originalmente le toma 1 o 2 minutos agregar, pero a alguien nuevo podría costarle horas averiguarla cuando le toque modificarla, usarla o entrar a corregir un bug años después
Si en una revisión de código puedo anticipar lo que dirá una persona revisora demasiado puntillosa, suelo poner comentarios del estilo “no hice X por Y”. La idea es reducir discusiones molestas de ida y vuelta
Hay otra variante de comentarios como “recorremos cada cadena 16 veces, pero hasta ahora el libro solo tiene 25 cadenas de fórmulas y la mayoría tiene menos de 5 caracteres, así que es suficientemente rápido”
Consiste en poner logs de depuración que se activen cuando la entrada crezca mucho más que las restricciones originales del diseño. Le dan casi el mismo mensaje a quien desarrolle en el futuro, pero permiten detectarlo antes y reducir todavía más el tiempo de diagnóstico y depuración
En un sistema ideal de logging y observabilidad, se mediría el tiempo de todos los componentes de la app y se dejaría información de depuración con frecuencia, pero quién usa realmente un sistema tan perfecto. Es más importante el esfuerzo de agregar logs concretos en partes que podrían volverse lentas después o que no hubo tiempo de optimizar
Viéndolo en retrospectiva, parece una idea obvia, pero hasta ahora lo que hacía era dejar comentarios en los lugares que había que volver a revisar después, esperando acordarme de esos comentarios cuando las cosas se pusieran mal
Sin importar lo que digan, uso muchos comentarios y docstrings por todo el código. Pero lo hago al revés: primero escribo de forma aproximada, como comentarios, la lista de etapas de la aplicación; luego, mientras desarrollo, divido las etapas grandes en etapas pequeñas, a veces borro los comentarios originales y a veces los dejo, y voy refinando los comentarios hasta que casi se convierten en el algoritmo terminado
Como normalmente programo de afuera hacia adentro, también escribo código mientras voy dividiendo los comentarios. A veces programo todo de una sola vez y después agrego comentarios hasta un punto que a la mayoría le daría flojera. Pongo explicaciones a todas las funciones y variables, e incluso a la función
deg_to_radle agrego"""Converts degrees to radians.""". El almacenamiento es baratoSé que a la mayoría no le gusta, pero no pasa nada. Si no les gusta verlo, pueden quitarlo con un script o eliminarlo en code review. Aun así, me resulta mucho más agradable leer mi código viejo que el código ajeno sin comentarios. En Python, el código simple y repetitivo como una Flask API suele ser autoexplicativo, pero justo ese tipo de boilerplate a veces cambia mucho y termina llevando comentarios importantes. En la industria, la parte algorítmica se reescribe completa con más frecuencia
Seguiré prefiriendo los comentarios y los docstrings
Me gusta hacer la conceptualización completa en mi cabeza, y al inicio siento que experimentar escribiendo varias opciones de diseño reales me vuelve más lento. Por eso, una vez que una decisión de diseño queda definida, hay que documentarla sí o sí. Los demás todavía no pueden entrar a mi cabeza
Espero que los detalles se vuelvan más claros para otros una vez que también terminen de conceptualizarlo. Aun así, si por alguna razón esa conceptualización no ocurre, puede hacerse más difícil de leer, así que además lo pulo para mantener al menos una legibilidad básica
Muchas veces actualizar comentarios termina siendo tanto trabajo como corregir el código. Por eso, en la práctica, los comentarios normalmente son mentiras esperando ocurrir. Al final, los comentarios y el código dejan de coincidir, y eso puede ser peor que no tener comentarios
Mejor pruebas automatizadas que muestren la intención. Las pruebas por lo general no pueden mentir, porque si no, no se habrían fusionado. Si hay un buen conjunto de pruebas que muestre cómo se pretendía usar el código, eso es descriptivo y además casi garantiza ser verdad, así que eso sí quiero verlo
Ayuda muchísimo cuando hay que volver a verlo 5 semanas después
Sigo la idea de que “los comentarios son una disculpa para mi yo del futuro”
Si el código es raro o lento, o si al explicárselo a alguien me sale decir “está medio improvisado”, normalmente dejo un comentario. En especial, si ya intenté cambiarlo antes, documento los casos en que no funcionó o lo que terminé corrigiendo
Si lo abordas con ese criterio, los comentarios innecesarios desaparecen de forma natural, y normalmente solo terminas documentando el por qué cuando de verdad hace falta. Si lo pruebas en tu propio codebase durante más o menos un mes, vas a entender la sensación