Diario de un bot que opera con dinero real — Entrada #2:
En la entrada anterior conté cómo un componente de mi sistema estuvo meses operando con un capital que nadie le asignó — un "par fantasma" que creía haber eliminado y seguía vivo. Toda la causa cabía en una línea de código. Una línea que probablemente tienes en tu proyecto ahora mismo:
Se ve inofensiva. Se ve hasta responsable — "si la variable no está, uso un valor por defecto y el programa no se rompe". Ese razonamiento es la trampa. Te cuento por qué.
Qué hace ese or
En cristiano: "usa el valor de la variable; si no hay valor, usa 133".
Suena bien. El problema es qué cuenta como "no hay valor". Si por accidente borras esa variable —un despliegue que pisa el archivo, un backup que restauras, un typo en el nombre— el programa no se detiene, no da error, no avisa. Simplemente empieza a usar 133 como si alguien lo hubiera decidido.
En mi caso, ese 133 era el capital con el que un bot operaba con dinero real. La variable que debía definirlo desapareció en un restore, y el default la reemplazó en silencio. El sistema no falló ruidosamente. Falló callado — que es peor, porque no deja rastro y lo descubres meses después.
La parte que engaña de verdad: el valor cero
Aquí está el detalle que casi me muerde. Supón que quieres desactivar ese componente poniendo su valor en cero. Lo natural: defines la variable como 0. Pues bien — dependiendo de dónde pongas el or, obtienes cosas opuestas:
En el segundo caso pusiste 0 y el programa te devolvió 133. ¿Por qué? Porque para el or, el número 0 cuenta como "vacío" (en jerga: es falsy). Entonces el or lo descarta y salta al default. Intentaste apagar algo poniéndolo en cero, y conseguiste justo lo contrario.
Lo peligroso: este bug solo aparece con el valor cero. Todos tus tests con valores normales pasan. El fallo espera callado hasta que alguien prueba el caso límite.
La lección de fondo, en una frase: or no distingue entre "no hay valor" y "el valor es cero". Para configuración, esas dos cosas son totalmente distintas — pero or las trata igual. Ahí vive el bug.
No es solo Python
Si usas otro lenguaje, el mismo patrón te espera con otra cara. En JavaScript, process.env.VAR || "133" tiene el problema idéntico con 0, "" y false. (Por eso JS moderno introdujo ??, el nullish coalescing, que solo cae al default cuando el valor es realmente nulo — creado justo para arreglar esto.) En Go se disfraza de if val == "" { val = "133" }, mezclando "no existe" con "cadena vacía".
El patrón es universal porque el impulso que lo crea es universal: quiero un valor sensato si la config falla. Buena intención. Mala ejecución.
Qué hacer en su lugar
La regla: si un valor es importante, su ausencia no debe ser silenciosa. Tres opciones, de más a menos estricta:
1. Si es obligatorio, que el programa grite. No inventes default; exige que exista.
Un programa que se niega a arrancar sin su config es mucho más seguro que uno que arranca con un valor fantasma.
2. Si quieres un default, usa el argumento correcto (no or*)*.
El segundo argumento de getenv solo actúa cuando la variable no existe — no cuando vale cero. Que es lo que de verdad querías.
3. Si el default es a propósito, hazlo visible.
Así, si un restore borra la variable, no tienes un bug silencioso: tienes una línea en el log que te dice exactamente qué pasó.
Esto no es solo trading
Mi caso fue un bot con capital real, pero el patrón vive en cualquier sistema donde un valor equivocado importa: un límite de acceso que cae a un default permisivo, una configuración de seguridad que desaparece y se reemplaza sin avisar, un umbral que silenciosamente vuelve a un valor de fábrica. En todos, un default silencioso convierte "se borró la config" en "el sistema hace algo que nadie pidió" — sin dejar rastro.
La lección para llevarte
Un default silencioso cambia un fallo que ves por uno que no ves.
El getenv("X") or default parece robustez, pero es fragilidad disfrazada: en vez de detenerse y avisarte cuando falta algo, sigue adelante con un valor que nadie decidió.
Un programa que se niega a arrancar te cuesta cinco minutos. Uno que arranca con el valor equivocado te cuesta encontrarlo cuando ya hizo daño. En mi caso, ese cambio de una línea fue la diferencia entre semanas de fuga silenciosa y un arranque limpio.
Los valores por defecto no son gratis. Son una decisión sobre qué pasa cuando el mundo no es como esperabas — y esa decisión merece más que un or al final de una línea.
En la próxima entrada cambio de escenario: Binance movió silenciosamente uno de sus endpoints de la API, y mi bot llevaba días colocando órdenes de protección que en realidad no existían. Cómo se detecta que una plataforma de la que dependes cambió las reglas sin avisar — y cómo migrar sin quedarte expuesto.
¿Cuál es el default silencioso más peligroso que has encontrado en código ajeno (o propio)? Los leo en los comentarios.




Top comments (0)