TechLead
Diseño de Sistemas
6 de abril de 202614 min de lectura

Cómo Diseñar Sistemas Que Escalen a Millones: Lecciones de Arquitecturas del Mundo Real

El diseño de sistemas teórico es fácil. Construir sistemas que realmente sobrevivan a millones de usuarios es difícil. Esta guía destila patrones de arquitectura del mundo real de empresas como Uber, Stripe y Discord en lecciones accionables para ingenieros de cualquier nivel.

Por TechLead
Diseño de Sistemas
Escalabilidad
Arquitectura
Sistemas Distribuidos
Base de Datos

Todo ingeniero estudia diseño de sistemas. Menos ingenieros han construido realmente sistemas que sirvan a millones de usuarios concurrentes bajo restricciones del mundo real — límites de presupuesto, alertas de guardia a las 3 AM, bugs de consistencia de datos que solo se manifiestan a escala y funcionalidades que marketing prometió para el próximo trimestre.

Este artículo no es otro tutorial de "cómo diseñar un acortador de URLs". Es una colección de lecciones ganadas con esfuerzo de arquitecturas de producción reales, organizadas en los patrones que más importan. Si estás preparándote para una entrevista de diseño de sistemas o construyendo tu próximo sistema de producción, estos son los principios que separan los proyectos de juguete de los sistemas que sobreviven al contacto con la realidad.

1. Sharding de Base de Datos: Lo Que Realmente Funciona

Cada artículo de escalamiento menciona el sharding. Pocos discuten la pesadilla operativa de mantenerlo. Esto es lo que nos enseñan los sistemas reales:

Sharding Basado en Hash

Discord hace sharding de su almacenamiento de mensajes por channel_id. Cada mensaje pertenece a un canal, y los canales se mapean determinísticamente a shards de base de datos. Esto funciona porque el patrón de acceso es casi siempre "obtener mensajes para este canal" — la clave de shard se alinea con el patrón de consulta.

-- Determinar shard desde channel_id
-- shard_number = channel_id % total_shards
-- Cada shard es una instancia separada de PostgreSQL

-- Shard 0: canales 0, 16, 32, ...
-- Shard 1: canales 1, 17, 33, ...
-- Shard N: canales N, N+16, N+32, ...

SELECT * FROM messages
WHERE channel_id = 98234
ORDER BY created_at DESC
LIMIT 50;

La Regla de Oro del Sharding

Tu clave de shard debe coincidir con tu patrón de acceso principal. Si haces sharding de usuarios por user_id pero tu consulta más común es "encontrar todos los usuarios en la organización X", cada consulta se convierte en un scatter-gather a través de todos los shards. Esto es peor que no hacer sharding en absoluto.

EmpresaClave de ShardPatrón de Acceso PrincipalPor Qué Funciona
Discordchannel_idMensajes por canalLecturas de un solo shard para el 99% de consultas
Stripemerchant_idTransacciones por comercianteTodos los datos del comerciante co-ubicados
Ubergeo_regionViajes/conductores por ciudadLa localidad geográfica coincide con el acceso real
Slackworkspace_idMensajes por workspaceLos datos del workspace son autocontenidos

Para un desglose completo de estrategias de sharding, consulta nuestro currículo de diseño de sistemas.

2. Jerarquías de Caché: El Patrón L1/L2/CDN

El caching no es "simplemente agregar Redis." Los sistemas de producción usan estrategias de caché por capas donde cada capa sirve un propósito diferente:

CapaTecnologíaLatenciaObjetivo de Hit RateQué Cachea
L1 (En Proceso)Mapa en memoria/LRU<1ms60-80%Config caliente, datos de sesión, feature flags
L2 (Distribuido)Redis / Memcached1-5ms85-95%Perfiles de usuario, resultados computados, respuestas de API
L3 (CDN Edge)CloudFront / Cloudflare5-50ms95-99%Assets estáticos, páginas renderizadas, respuestas de API
OrigenBase de datos10-100msN/AFuente de verdad

Estrategia de Invalidación de Caché

El problema más difícil del caching es la invalidación. Aquí hay tres patrones que funcionan a escala:

  • Expiración basada en TTL: Establece un tiempo de vida y acepta datos obsoletos dentro de esa ventana. Simple y efectivo para datos que no necesitan ser en tiempo real (catálogos de productos, perfiles de usuario).
  • Write-through: En cada escritura, actualiza el caché simultáneamente. Se usa cuando la consistencia lectura-después-de-escritura es crítica (carritos de compra, saldos de cuenta).
  • Invalidación dirigida por eventos: Publica un evento de invalidación de caché a una cola de mensajes cuando los datos cambian. Todos los servicios que cachean esos datos se suscriben y purgan sus copias locales. Esto es lo que Uber usa para los datos de ubicación de conductores.
// Ejemplo de caché por capas con write-through y TTL
class CacheHierarchy {
  private l1: Map<string, { data: unknown; expiry: number }> = new Map();
  private redis: RedisClient;

  async get(key: string): Promise<unknown> {
    // L1: Verificación en proceso
    const l1Entry = this.l1.get(key);
    if (l1Entry && l1Entry.expiry > Date.now()) {
      return l1Entry.data;
    }

    // L2: Verificación en Redis
    const l2Data = await this.redis.get(key);
    if (l2Data) {
      // Rellenar L1
      this.l1.set(key, { data: JSON.parse(l2Data), expiry: Date.now() + 30_000 });
      return JSON.parse(l2Data);
    }

    return null; // Cache miss — el llamador busca en la BD
  }

  async set(key: string, data: unknown, ttlMs: number): Promise<void> {
    // Write-through: actualizar ambas capas
    this.l1.set(key, { data, expiry: Date.now() + Math.min(ttlMs, 30_000) });
    await this.redis.set(key, JSON.stringify(data), "PX", ttlMs);
  }
}

3. Dirigido por Eventos vs. Dirigido por Solicitudes a Escala

A baja escala, la arquitectura dirigida por solicitudes (REST/gRPC síncrono) funciona bien. A alta escala, la arquitectura dirigida por eventos se vuelve esencial por tres razones:

  1. Desacoplamiento: Los servicios no necesitan conocerse entre sí. El servicio de pagos emite un evento "payment.completed"; el servicio de notificaciones, el servicio de analíticas y el servicio de inventario lo consumen de forma independiente.
  2. Resiliencia: Si el servicio de notificaciones se cae, los eventos se encolan en Kafka. Cuando se recupera, procesa el backlog. En un sistema síncrono, el servicio de pagos fallaría o necesitaría lógica de reintentos compleja.
  3. Throughput: Las particiones de Kafka permiten procesamiento paralelo. Stripe procesa millones de eventos de webhook por día usando streams de eventos particionados.
// Procesamiento de pedidos dirigido por eventos
// Productor: Servicio de Pedidos
await kafka.produce("order.events", {
  type: "order.placed",
  orderId: "ord_abc123",
  userId: "usr_xyz789",
  items: [{ sku: "WIDGET-01", quantity: 2, price: 29.99 }],
  timestamp: new Date().toISOString(),
});

// Consumidor: Servicio de Inventario (independiente)
kafka.consume("order.events", async (event) => {
  if (event.type === "order.placed") {
    await reserveInventory(event.items);
  }
});

// Consumidor: Servicio de Notificaciones (independiente)
kafka.consume("order.events", async (event) => {
  if (event.type === "order.placed") {
    await sendConfirmationEmail(event.userId, event.orderId);
  }
});

La decisión arquitectónica clave es qué operaciones deben ser síncronas y cuáles deben ser asíncronas. Una buena heurística: si el usuario está esperando el resultado, hazlo síncrono. Si el usuario no necesita confirmación inmediata, hazlo asíncrono.

4. Desarrollo Dirigido por Observabilidad

A escala, no puedes depurar leyendo logs. Necesitas observabilidad estructurada incorporada en la arquitectura desde el primer día. Los tres pilares son bien conocidos — métricas, logs, traces — pero los detalles de implementación importan:

El Stack de Observabilidad

  • Logging estructurado: Cada línea de log es JSON con un ID de correlación que rastrea una solicitud a través de los servicios. Nunca uses logs de texto no estructurado en producción.
  • Tracing distribuido: OpenTelemetry es ahora el estándar de la industria. Cada solicitud entrante crea un trace ID que se propaga a través de cada llamada de servicio, consulta de base de datos y búsqueda en caché.
  • SLOs sobre alertas: En lugar de alertar sobre métricas individuales (CPU > 80%), define Objetivos de Nivel de Servicio (ej., "99.9% de las solicitudes de checkout se completan en menos de 500ms") y alerta cuando el presupuesto de errores se está agotando demasiado rápido.
// Log estructurado con contexto de trace
logger.info({
  event: "order.processed",
  traceId: span.traceId,
  orderId: "ord_abc123",
  duration_ms: 142,
  cache_hit: true,
  shard: 7,
});

Para una guía completa de patrones de observabilidad, explora nuestra ruta de aprendizaje de arquitectura de software.

5. Arquitectura Consciente del Costo

En 2026, los costos de nube son una preocupación arquitectónica de primera clase. La era de "simplemente escala hacia arriba" ha terminado. FinOps (Operaciones Financieras) es ahora una disciplina que se sitúa junto a DevOps y SRE.

Patrones de Optimización de Costos

  • Dimensiona correctamente tu cómputo: El 40% de las instancias en la nube están sobredimensionadas. Usa auto-scaling con políticas agresivas de reducción. Las instancias spot/preemptibles pueden reducir los costos de cómputo en un 60-70% para cargas de trabajo tolerantes a fallos.
  • Almacenamiento por niveles: Mueve datos con más de 30 días a almacenamiento frío. Archiva datos con más de 90 días. Solo esto puede reducir los costos de almacenamiento en un 50%.
  • Caché antes que cómputo: Cada cache hit es una consulta a base de datos que no pagaste. A escala, una capa de caché bien ajustada puede reducir el tamaño (y costo) de tu nivel de base de datos en 5-10x.
  • Edge computing: Ejecuta cómputo en los edges del CDN para operaciones sensibles a la latencia. Esto reduce el tráfico al origen y mejora la experiencia del usuario simultáneamente.
OptimizaciónAhorro TípicoEsfuerzoRiesgo
Dimensionamiento correcto de instancias20-40%BajoBajo
Instancias spot para trabajos batch60-70%MedioMedio
Migración de almacenamiento por niveles40-60%MedioBajo
Optimización de capa de caché30-50% en costos de BDAltoMedio
Instancias reservadas (compromiso 1 año)30-40%BajoBajo (riesgo de compromiso)

6. Uniendo Todo

Aquí está el patrón de arquitectura que emerge de estas lecciones, aplicable ya sea que estés construyendo una plataforma fintech, una red social o un producto SaaS:

  1. Capa edge: CDN + funciones edge manejan assets estáticos y respuestas de API cacheables.
  2. API gateway: Rate limiting, autenticación, enrutamiento de solicitudes. Expone una superficie de API unificada.
  3. Capa de servicios: Servicios específicos del dominio se comunican vía gRPC (síncrono) y Kafka (asíncrono).
  4. Capa de datos: PostgreSQL con sharding para datos transaccionales, Redis para caché, almacenamiento de objetos para blobs.
  5. Capa de observabilidad: Traces de OpenTelemetry, logs estructurados, alertas basadas en SLOs a través de todas las capas.

Los sistemas que escalan no son los que tienen el código más ingenioso. Son los que están construidos sobre principios sólidos, rigurosamente observados y continuamente refinados. Comienza con los fundamentos en nuestros cursos de diseño de sistemas y arquitectura cloud, y construye desde ahí.