Ir al contenido
wannacode

Comentarios que mienten y comentarios que ayudan

En resumen: Un buen comentario dice por qué el código es como es; un comentario que cuenta de nuevo el código se queda obsoleto y empieza a mentir, por eso se reemplaza por un buen nombre o una función.

Problema

El comentario en el que todos confían

La tienda da envío gratis a partir de cierto importe. La primavera pasada el umbral era de 50 €, y el comentario sobre delivery_fee dice justo eso. Luego marketing lo subió a 100 €. El número del código se cambió. El comentario no.

Este otoño un compañero escribe el banner del carrito. Lee el comentario, no el código, y pone 50:

# Envío gratis para pedidos desde 50 €.
def delivery_fee(total):
if total >= 100:
return 0
return 4.90
def banner(total):
if total >= 50:
return "¡El envío es gratis!"
return f"Faltan {50 - total:.2f} € para el envío gratis"
total = 60
print(f"Carrito: {total:.2f} €")
print(banner(total))
print(f"Envío: {delivery_fee(total):.2f} €")
Salida
Carrito: 60.00 €
¡El envío es gratis!
Envío: 4.90 €

Un cliente con un carrito de 60 € lee «¡El envío es gratis!» y luego paga 4,90 € de envío. Soporte mira delivery_fee y no encuentra ningún error: la función hace exactamente lo que dice su código. El error está en un texto que nadie ejecuta.

Míralo

El código siguió, el comentario se quedó

Esto es lo que pasó, paso a paso:

# Envío gratis para pedidos desde 50 €.
def delivery_fee(total):
if total >= 100:
return 0
return 4.90
def banner(total):
if total >= 50:
return "¡El envío es gratis!"
return f"Faltan {50 - total:.2f} € para el envío gratis"
total = 60
print(f"Carrito: {total:.2f} €")
print(banner(total))
print(f"Envío: {delivery_fee(total):.2f} €")
Paso a paso, en palabras
  1. La primera versión: el comentario (línea 1) y la comprobación (línea 3) dicen lo mismo: 50 €.
  2. El umbral pasa a 100 €. Alguien cambia el número de la línea 3 y no toca el comentario. El compilador revisa el código. Los comentarios no los revisa nadie.
  3. Un compañero escribe el banner. Lee el comentario, no el código, y en la línea 9 pone 50.
  4. Un carrito de 60 €. El banner pregunta su propio umbral: 60 ≥ 50, así que «¡El envío es gratis!».
  5. La tarifa pregunta el umbral real: 60 < 100, así que el envío cuesta 4,90 €.
  6. El cliente lee «gratis» y paga 4,90 €. La mentira está en la línea 1. El error que provocó está en la línea 9.

El compilador revisa cada línea de código. Las pruebas revisan lo que hace el código. Nadie revisa el comentario: es texto junto al código, y cada cambio en el código puede dejarlo atrás. Esto se llama un comentario obsoleto (stale comment). Es peor que no tener comentario, porque suena muy seguro de sí mismo.

La idea

Deja que lo diga el código

El umbral es una regla de la tienda, y la regla vive en dos lugares: en el código de la tarifa y en la cabeza de cada persona que leyó el comentario. Ponle un nombre, y las dos funciones preguntan por el nombre:

FREE_DELIVERY_FROM = 100
def delivery_fee(total):
if total >= FREE_DELIVERY_FROM:
return 0
return 4.90
def banner(total):
if total >= FREE_DELIVERY_FROM:
return "¡El envío es gratis!"
return f"Faltan {FREE_DELIVERY_FROM - total:.2f} € para el envío gratis"
total = 60
print(f"Carrito: {total:.2f} €")
print(banner(total))
print(f"Envío: {delivery_fee(total):.2f} €")
Salida
Carrito: 60.00 €
Faltan 40.00 € para el envío gratis
Envío: 4.90 €

El comentario ya no hace falta: el nombre dice lo mismo. La diferencia es que un nombre no puede quedarse atrás. Si el umbral pasa a 150 €, cambias una línea y banner recibe el nuevo umbral. Si alguien borra la constante, el programa deja de compilar (en Python se detiene en la primera llamada, con un error que dice que el nombre no existe).

Esta es la regla de la lección sobre nombres (Nombres en el código): antes de escribir un comentario, comprueba si un nombre puede decir lo mismo.

Pruébalo

Predice la salida

Un hotel vende noches en línea. ¿Qué imprime el programa?

# El precio con 20% de IVA.
def with_vat(net):
return net * 1.09
print(f"{with_vat(100):.2f} €")
Respuesta

El comentario dice 20%, y el código multiplica por 1.09. Las noches de hotel en Bulgaria llevan un IVA del 9%, y alguien cambió el número sin tocar el comentario. El programa escucha al código.

Míralo

Comentarios que repiten el código

Cada comentario aquí dice lo mismo que la línea de abajo:

# El precio del producto.
price = 25.00
# La cantidad.
quantity = 3
# Multiplicamos el precio por la cantidad.
total = price * quantity
# Imprimimos el total.
print(f"Total: {total:.2f} €")
Salida
Total: 75.00 €

Lo lees todo dos veces y no aprendes nada nuevo. Peor aún, aprendes a saltarte los comentarios, y te saltas también el que importa. Bórralos. El programa es el mismo, y la salida también.

Míralo

Un comentario en lugar de un nombre, un comentario en lugar de una función

Aquí los comentarios explican cosas que el código esconde:

n = 7 # número de pedidos del cliente
s = 420 # gasto del cliente en euros
# El cliente es VIP: al menos 5 pedidos o al menos 500 € gastados.
if n >= 5 or s >= 500:
print("Descuento VIP: 10%")
Salida
Descuento VIP: 10%

El comentario junto a n está ahí porque el nombre no dice nada. Dale el nombre del comentario: orders. El comentario sobre el if describe una regla. Una regla con nombre es una función:

def is_vip(orders, spent):
return orders >= 5 or spent >= 500
orders = 7
spent = 420
if is_vip(orders, spent):
print("Descuento VIP: 10%")
Salida
Descuento VIP: 10%

La salida es la misma, y no hay comentarios. La línea del if se lee como una frase. Si la regla pasa a ser «al menos 10 pedidos», cambias el cuerpo de is_vip y cada lugar que la llama recibe la nueva regla. Cómo extraer una función así está en la lección Funciones.

Míralo

Código comentado y un TODO sin responsable

def confirm(order_id):
send_email(order_id)
# send_sms(order_id)
# if sms_enabled:
# send_sms(order_id)
# TODO: arreglar
Salida
Correo del pedido #1063

¿Se envía un SMS? Nadie sabe si se desactivó a propósito, por un tiempo o por error, y nadie se atreve a borrarlo. Así vive el código comentado durante años. Bórralo: el código viejo está en el historial de git y vuelve con un solo comando.

Un TODO sin nombre y sin fecha es un deseo que nadie posee. Un buen TODO dice quién, qué ticket y cuándo:

def confirm(order_id):
send_email(order_id)
# TODO(maria, SHOP-412): añadir SMS cuando el nuevo proveedor pase las pruebas.
Salida
Correo del pedido #1063

Mejor aún, la tarea vive en el ticket, y en el código solo queda el enlace.

La idea

Qué dice un buen comentario

El código dice qué hace. Un buen comentario dice algo que el código no puede decir:

# El mensajero se queda con el 30% de la tarifa, redondeado hacia abajo al céntimo.
# Así lo dice el contrato con él (cláusula 4.2), por eso aquí no hay round().
def courier_share_cents(fee_cents):
return fee_cents * 30 // 100
fee = 495 # SHOP-388: el mensajero envía la tarifa en céntimos, no en euros.
print(f"Mensajero: {courier_share_cents(fee) / 100:.2f} €")
Salida
Mensajero: 1.48 €

Aquí hay tres cosas así:

  • por qué: redondear hacia abajo parece un error hasta que lees que el contrato lo dice. Sin el comentario, alguien lo «arreglará»;
  • dónde saber más: el ticket SHOP-388 cuenta la historia que no cabe en el código;
  • la unidad que el tipo no dice: céntimos, no euros. En la lección sobre code smells verás cómo esto pasa al propio tipo.

Una más: una advertencia como «Lento: cada llamada es una petición al banco, unos 300 ms. No la llames en un bucle.» Un comentario así le ahorra un día de búsqueda a la siguiente persona.

Un buen comentario también puede quedarse obsoleto. Pero rara vez lo hace, porque no repite el código: el contrato no cambia cada vez que cambias la función.

Más precisamente, para curiosos

git blame muestra cuándo se escribió cada línea. Si el código bajo un comentario es más nuevo que el propio comentario, ten cuidado: alguien tocó el código y quizá no llegó al comentario.

Míralo

Documentación para quien llama

Quien llama a delivery_fee desde otro módulo no quiere leer su cuerpo. Quiere saber qué pasar y qué recibe. Para esto cada lenguaje tiene un comentario de documentación, que el editor muestra cuando pasas el ratón sobre el nombre:

FREE_DELIVERY_FROM = 100
def delivery_fee(total: float) -> float:
"""La tarifa de envío de un pedido.
total es el total de los productos en euros, sin el envío. Devuelve 0
desde FREE_DELIVERY_FROM, si no la tarifa del mensajero.
"""
if total >= FREE_DELIVERY_FROM:
return 0
return 4.90
print(delivery_fee.__doc__.splitlines()[0])
print(f"Envío con 60 €: {delivery_fee(60):.2f} €")
print(f"Envío con 120 €: {delivery_fee(120):.2f} €")
Salida
La tarifa de envío de un pedido.
Envío con 60 €: 4.90 €
Envío con 120 €: 0.00 €
Esto es un docstring: una cadena que es la primera línea en el cuerpo de la función. No es un comentario sino parte de la función: el programa la lee como delivery_fee.__doc__, y help(delivery_fee) la muestra en la consola. total: float y -> float son anotaciones de tipo (type hints): Python no las comprueba al ejecutar, pero el editor y herramientas como mypy sí.

La documentación fija el contrato: qué significa el parámetro y en qué unidades, qué devuelve y cuándo hay un error. No dice cómo funciona por dentro. Los números del código no se repiten en ella: en lugar de «desde 100 €» nombra la constante, así que no puede quedarse atrás.

Más precisamente, para curiosos
Las anotaciones de tipo también son documentación, pero documentación que se puede comprobar: mypy falla si pasas "60" a delivery_fee. PEP 257 describe cómo escribir un docstring: la primera línea es una frase, luego una línea vacía y los detalles.

Completa la parte que falta:

FREE_DELIVERY_FROM = 100


def delivery_fee(total: ) -> float:
    """La tarifa de envío de un pedido.

    total es el total de los productos en euros, sin el envío. Devuelve 0
    desde FREE_DELIVERY_FROM, si no la tarifa del mensajero.
    """
    if total >= FREE_DELIVERY_FROM:
        return 0
    return 4.90

Pruébalo

Encuentra el error

La tienda envía un correo «¿Qué tal tu pedido?» un día después de la entrega. El último pedido del cliente es del 24/10, y el correo pregunta por el del 12/10:

orders = ["2026-10-19 #1057", "2026-10-24 #1063", "2026-10-12 #1041"]
print(reminder(orders))
Salida
¿Qué tal tu pedido 2026-10-12 #1041?

¿Qué línea toma el pedido equivocado?

Elige la línea con el error.

Ejercicio

Limpia los comentarios

Resuélvelo en tu propio editor.

Descuento para clientes fieles. La función tiene un comentario que miente, un comentario que repite el código, una línea comentada y un TODO sin responsable:

# Descuento para clientes fieles: 5%.
def loyalty_total(total, orders):
# si el cliente tiene más de 10 pedidos
if orders >= 20:
# total = total * 0.95
total = total * 0.90 # reducimos total
# TODO: redondeo
return total
print(f"Suma: {loyalty_total(200, 25):.2f} €")
print(f"Suma: {loyalty_total(200, 12):.2f} €")
Salida
Suma: 180.00 €
Suma: 200.00 €

Límpiala: quita el ruido y el código muerto, reemplaza los números por nombres, y o haz el TODO o dale un responsable y un ticket. La salida debe seguir igual.

Más tareas

  • Documenta el orden. Escribe un comentario de documentación para oldest_first de la tarea de los pedidos: qué recibe, qué devuelve y en qué orden.
  • El banner a 99,99 €. Ejecuta el banner de la tarjeta «Deja que lo diga el código» con un carrito de 99,99 € y de 100 €. Comprueba que las dos funciones coinciden justo en el umbral.
  • Tu proyecto. Abre tu último proyecto y busca tres comentarios. Para cada uno decide: ¿repite el código, dice por qué o miente?

Test

Ponte a prueba

1. El comentario sobre una función dice una cosa y el código de abajo hace otra. ¿En cuál confías?
2. ¿Qué tiene de malo «count += 1 # aumenta count en 1»?
3. La función tiene una línea comentada «# send_sms(order)». ¿Qué haces?
4. ¿Qué comentario vale la pena conservar?
5. ¿Qué TODO llegará a hacerse alguna vez?

Creado por Ivo Gergov como parte de la campaña de Evo Tech por una educación accesible en programación ·