El patrón más peligroso que veo en código de producción no es un bug: es un catch vacío. Un error silenciado no desaparece, solo reaparece más tarde y más lejos del sitio donde ocurrió.
Falla rápido y ruidoso
Si algo no debería pasar, no lo escondas. Un error visible en desarrollo es un bug que arreglas hoy; un error tragado es un ticket de soporte la semana que viene.
// ❌ el error se evapora
try {
await saveUser(user);
} catch {}
// ✅ al menos sabes qué pasó
try {
await saveUser(user);
} catch (err) {
logger.error('saveUser failed', { userId: user.id, err });
throw err;
}
Modela el error, no lo improvises
En el frontend, lanzar strings o any te deja sin información. Prefiero tipos de error explícitos que digan qué falló y cómo recuperarse:
type Result<T> =
| { ok: true; value: T }
| { ok: false; error: 'network' | 'unauthorized' | 'not_found' };
Quien consume la función está obligado por el tipo a manejar cada caso. Esta idea me llevó a publicar http-sentinel, justo para estructurar errores HTTP en el cliente.
La regla
Un error o se maneja (con una acción concreta) o se propaga (hacia arriba con contexto). Lo que nunca se hace es ignorarlo. El silencio en el manejo de errores siempre se paga con intereses.