Ronda técnica · identificar errores en un diseño
Alex dijo que la ronda de system design consiste en identificar errores. Eso no es diseñar de cero: es leer un sistema ajeno y ver lo que está mal. Es la prueba más preparable de todas. Doce casos, del más común al más sutil, con dos específicos de Node.
No empieces por los detalles. Recorre siempre estas seis capas, en orden, y di en voz alta cuál estás mirando:
| # | Capa | Pregunta que haces |
|---|---|---|
| 1 | Entrada | ¿Qué llega, validado, y qué pasa si llega dos veces? |
| 2 | Volumen | ¿Qué se rompe a 10×? ¿hay tope en algún sitio? |
| 3 | Estado | ¿Quién es la verdad? ¿qué pasa si dos escrituras coinciden? |
| 4 | Consultas | ¿cuántas por pantalla? ¿con qué índice? ¿cuánto devuelven? |
| 5 | Fallos | ¿reintentos? ¿timeouts? ¿qué se pierde y qué se duplica? |
| 6 | Operación | ¿cómo sabes que está roto antes que el usuario? |
for (const pedido of pedidos) {
const cliente = await db.cliente.findById(pedido.clienteId)
resultados.push({ ...pedido, cliente })
}
IN sobre los ids, o un JOIN. Se reduce a 2.app.post('/webhook/pago', async (req, res) => {
await cobrar(req.body.pedidoId)
res.send('ok')
})
event_id con restricción única y procesar una sola vez. Responder 2xx rápido y encolar.const fila = await db.stock.findOne({ id })
if (fila.cantidad >= n) {
fila.cantidad = fila.cantidad - n
await db.stock.save(fila)
}
UPDATE ... WHERE cantidad >= n y comprobar filas afectadas, o bloqueo de fila / transacción con nivel adecuado.Un servicio acepta eventos y los mete en una cola en memoria. Un worker los consume. En un pico, la cola crece a 2 millones de mensajes y el proceso muere.
Retry-After, descartar lo de menor valor, o escalar consumidores.async function llamar() {
for (let i = 0; i < 5; i++) {
try { return await fetch(url) } catch (e) { /* reintenta ya */ }
}
}
Retry-After. Cortacircuitos si falla seguido.Diez mil claves de caché se guardan con TTL de 24 horas al desplegar. Al día siguiente, a la misma hora, el sistema se cae.
SELECT * FROM eventos ORDER BY creado_en DESC LIMIT 50 OFFSET 5000;
(creado_en, id), que es estable y no recorre lo anterior.total = total + 0.1 + 0.2 // esperado 0.3
0.1 + 0.2 = 0.30000000000000004.Un dashboard pide "últimos 30 días" y ejecuta un GROUP BY sobre la tabla cruda, que tiene
40 millones de filas. Cada usuario que abre el panel lanza la consulta.
SELECT date(fecha) AS dia, count(*) FROM eventos GROUP BY 1;
date() trunca en la zona del servidor; cambia con el horario de verano y agrupa mal.app.get('/reporte', async (req, res) => {
const csv = fs.readFileSync('/datos/export.csv') // sincrónico
const hash = crypto.pbkdf2Sync(req.query.clave, 'sal', 600000, 64, 'sha512')
res.send(procesar(csv, hash))
})
readFileSync y pbkdf2Sync bloquean el hilo único. Mientras uno corre, el proceso no atiende ninguna otra petición: no es lento, es de una en una.fs.promises, crypto.pbkdf2) o worker_threads para CPU intensiva. Nunca *Sync en una ruta.El servicio corre en un contenedor. Al desplegar, el orquestador manda SIGTERM y el
proceso muere. Los usuarios ven errores 502 durante el despliegue y algunas peticiones se pierden
sin dejar rastro.
SIGTERM: marcar not ready, esperar un margen, drenar, cerrar recursos. server.close() y un timeout máximo.Si te dan un diseño y no ves el error de inmediato, estas cinco preguntas lo encuentran casi siempre. Hazlas en voz alta: el entrevistador está evaluando cómo piensas, no si adivinas.
1. ¿Qué pasa si esto llega dos veces?
2. ¿Qué pasa si esto crece diez veces?
3. ¿Dónde está el límite — el tope, el timeout, el tamaño máximo?
4. ¿Quién es la fuente de la verdad si dos escrituras coinciden?
5. ¿Cómo me entero de que esto está fallando antes que el usuario?
Y la frase que conviene decir al empezar: "Antes de opinar, voy a recorrer seis capas: entrada, volumen, estado, consultas, fallos y operación. Dime si prefieres que empiece por alguna." Eso te compra treinta segundos y ordena la conversación.