---
name: bug-hunt
description: Búsqueda dirigida de bugs de correctitud en código C#/.NET y TypeScript - concurrencia, nulos, fechas, transacciones a medias y errores tragados. Úsala cuando pidan encontrar bugs, revisar un módulo sospechoso o entender por qué algo falla de forma intermitente.
version: 1.0.0
license: CC-BY-4.0
updated: 2026-08-16
---

# Cazar bugs, no oler mal el código

Esto no es una revisión de estilo. El único objetivo es encontrar código que
**produce un resultado incorrecto** o que se rompe en producción. Un nombre
feo no es un bug.

## Método

1. **Empieza por el síntoma si lo hay.** Un stack trace, un log o un dato
   incorrecto acotan la búsqueda mil veces mejor que leer el módulo entero.
2. **Sin síntoma, empieza por los bordes**: entrada del usuario, llamadas a
   sistemas externos, escritura en base de datos, código concurrente y
   cualquier cosa con fechas. Ahí vive casi todo.
3. **Sigue el dato, no el fichero.** Toma un valor de entrada y recórrelo hasta
   donde se persiste o se muestra. Los bugs aparecen en las conversiones y en
   los límites entre capas.
4. **Cada hallazgo necesita un escenario concreto**: entrada + estado → salida
   incorrecta. Si no sabes escribir el escenario, no has encontrado un bug: has
   encontrado una sospecha, y así hay que presentarla.

## Patrones que producen bugs de verdad

**Nulos y colecciones vacías**
`First()`, `Single()`, `[0]`, `.Value` sobre un `Nullable`, desestructuración
de una respuesta que puede venir vacía. El caso "no hay resultados" casi nunca
se prueba.

**Async mal hecho (C#)**
- `async void` fuera de un manejador de eventos: la excepción no se puede
  capturar y tumba el proceso.
- `.Result` o `.Wait()` sobre una tarea → deadlock en cuanto haya contexto de
  sincronización.
- Tareas lanzadas sin `await` (fire and forget): si el proceso termina antes,
  el trabajo se pierde en silencio.
- `CancellationToken` que se recibe y no se pasa hacia abajo.

**Concurrencia**
Estado estático mutable, singletons con campos de instancia, `DbContext`
compartido entre hilos (no es thread-safe), lectura-modificación-escritura sin
concurrencia optimista: dos peticiones simultáneas y la última pisa a la
primera sin que nadie se entere.

**Fechas y zonas horarias**
`DateTime.Now` en lugar de `UtcNow`, guardar sin `Kind`, comparar un instante
UTC con uno local, aritmética de días sobre cambios de hora, y el clásico
"funciona salvo el último día del mes".

**Números**
`double` para dinero (usa `decimal`), división entera donde se esperaba
decimal, redondeo aplicado dos veces, desbordamiento en un `int` que acumula.

**Transacciones a medias**
Varias escrituras sin transacción, o una llamada a un sistema externo dentro de
la transacción: si el commit falla después de cobrar, el cliente pagó y el
pedido no existe. Pregunta siempre: **¿qué queda si esto falla en el paso 3?**

**Errores tragados**
`catch { }`, `catch (Exception) { return null; }`, `catch` que loguea y sigue
como si nada. Convierten un fallo ruidoso en datos corruptos silenciosos, que
es infinitamente peor.

**Cadenas y cultura**
`ToLower()` sin cultura invariante, comparaciones dependientes de la cultura en
lógica de negocio, `Parse` de decimales con coma o punto según el servidor.

**TypeScript**
`==` con `null`/`undefined` mezclado con `0` y `''`, `as` que miente sobre el
tipo real de una respuesta HTTP, `Promise` sin `await` dentro de un `forEach`,
mutación de un objeto que también está en el estado de la interfaz.

## Cómo se reporta

Uno por hallazgo, ordenados por impacto:

```
ALTO · src/Application/Orders/OrderService.cs:88
Dos confirmaciones simultáneas del mismo pedido lo cobran dos veces.
Se lee Status, se comprueba y se escribe sin control de concurrencia; entre
la lectura y el guardado cabe otra petición.
Escenario: dos clics en "Pagar" con 200 ms de diferencia.
Arreglo: token de concurrencia (rowversion) en Order, o filtro en el UPDATE
por el estado esperado y comprobar filas afectadas.
```

- **Impacto primero**: alto (dato incorrecto o pérdida de dinero), medio (fallo
  recuperable), bajo (caso raro).
- **Un arreglo propuesto** por hallazgo, no tres alternativas.
- **Separa lo confirmado de lo sospechado.** Mezclar diez sospechas con dos
  bugs reales hace que no se arregle ninguno.
- Si el bug es reproducible, el arreglo empieza por **un test que falle** antes
  de tocar nada.
