0Pricing
Coding Interview Prep · Lección

Marco de la entrevista de diseño de sistemas

Recorra el marco RADIO de cinco pasos (Requirements, API, Data, Infrastructure, Optimise) y practique su aplicación a un acortador de URL.

Marco de la entrevista de diseño de sistemas es una lección gratuita de Coding Interview Prep en CoddyKit. Esta es la lección 1 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Coding Interview Prep, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Coding Interview Prep incluye 4 lecciones en total.

Por qué el diseño de sistemas es importante en las entrevistas

Las entrevistas de diseño de sistemas evalúan su capacidad para pensar a escala: ¿cómo diseñaría Twitter, YouTube o un acortador de URL para miles de millones de usuarios? A diferencia de los problemas de programación, que suelen tener una única respuesta correcta, el diseño de sistemas es abierto: debe tomar decisiones de compromiso y justificar sus ventajas y desventajas. Los puestos sénior y de nivel staff dedican exclusivamente entre 30 y 45 minutos a esta fase.

Los entrevistadores evalúan si puede aclarar los requisitos, estimar la carga, proponer una arquitectura de alto nivel, profundizar en los componentes clave y analizar las ventajas y desventajas, todo ello comunicándose con claridad. Un marco estructurado evita que se disperse y garantiza que cubra todas las dimensiones críticas.

# System design is not about a single correct answer.
# Interviewers look for:
eval_criteria = [
    'Ability to clarify requirements before designing',
    'Back-of-envelope capacity estimation',
    'High-level architecture with clear components',
    'Data modelling and storage choice',
    'Handling scalability (10x, 100x load)',
    'Trade-off discussion (consistency vs availability, etc.)',
    'Communication: talking through decisions as you make them',
]
for c in eval_criteria:
    print('-', c)

Descripción general del marco RADIO

El marco RADIO proporciona una estructura repetible de 5 pasos para cualquier entrevista de diseño de sistemas:

  • R — Requisitos: funcionales y no funcionales
  • A — Diseño de la API: ¿qué operaciones expone el sistema?
  • D — Modelo de datos: ¿qué datos se almacenan y cómo?
  • I — Infraestructura: componentes de alto nivel (servidores, colas, cachés)
  • O — Optimizar: cuellos de botella, caché, sharding y replicación

Avance siempre por estos pasos en orden, pero vuelva a los pasos anteriores y perfílelos cuando surjan nuevas conclusiones. Dedique aproximadamente el mismo tiempo a cada fase. No empiece nunca a dibujar cajas sin haber aclarado primero los requisitos.

RADIO = {
    'R': 'Requirements — What must the system do? What scale?',
    'A': 'API         — Define endpoints/operations the system exposes',
    'D': 'Data Model  — Entities, schemas, storage types',
    'I': 'Infra       — High-level architecture: servers, queues, caches, CDN',
    'O': 'Optimise    — Identify and address bottlenecks, trade-offs',
}
for step, desc in RADIO.items():
    print(f'[{step}] {desc}')

print('\nTiming guide for a 45-min interview:')
print('  R: 5 min | A: 5 min | D: 10 min | I: 15 min | O: 10 min')

Paso R: Aclarar los requisitos

No empiece a diseñar sin aclarar los requisitos. Pregunte por los requisitos funcionales (qué hace el sistema) y los requisitos no funcionales (escala, latencia y disponibilidad). Para un acortador de URL:

  • Funcionales: acortar una URL, redirigir a la URL original y, opcionalmente, admitir alias personalizados y fechas de caducidad
  • No funcionales: ¿cuántas URL se crean al día? ¿Predominan las lecturas o las escrituras? ¿Qué disponibilidad se exige (99.9% frente a 99.99%)? ¿Qué latencia es aceptable?

Exponer las suposiciones de forma explícita demuestra madurez. A menudo los entrevistadores proporcionan especificaciones deliberadamente vagas para comprobar si formula las preguntas adecuadas. Dos minutos de aclaraciones evitan que diseñe el sistema equivocado.

# Requirements questions for URL Shortener:
functional = [
    'Shorten a given URL to a 7-character alias',
    'Redirect short URL to original URL',
    'Allow custom aliases (optional)',
    'URL expiry (optional)',
    'Analytics: click count per URL (optional)',
]
non_functional = [
    '100 million new URLs per day (write: ~1160/sec)',
    '10:1 read:write ratio => 11,600 redirects/sec',
    'Redirects must be < 100ms p99 latency',
    '99.99% availability (< 1 hr downtime/year)',
    'URLs must be globally accessible',
]
print('Functional:')
for f in functional: print(' -', f)
print('\nNon-functional:')
for nf in non_functional: print(' -', nf)

Paso R: Estimación aproximada

Después de definir los requisitos, estime la capacidad. Esto demuestra que puede razonar sobre la escala antes de proponer soluciones. Las cifras clave que debe obtener son: peticiones por segundo (RPS), almacenamiento necesario por día y por año, ancho de banda y memoria para la caché.

Utilice cifras redondas y haga aproximaciones sin reparos. A los entrevistadores les interesa el orden de magnitud, no las cifras exactas. Ejemplo para un acortador de URL: 100M escrituras/día ÷ 86400 ≈ 1160 escrituras/s. 10B lecturas/día ÷ 86400 ≈ 115K lecturas/s. Cada registro de URL ≈ 500 bytes: 100M × 500B = 50 GB/día, 18 TB/año.

# Back-of-envelope for URL Shortener
writes_per_day = 100_000_000        # 100 million URLs/day
read_write_ratio = 100              # 100:1 read/write
reads_per_day = writes_per_day * read_write_ratio
bytes_per_url = 500                 # url string + metadata
years_to_store = 5

print('=== Capacity Estimation ===')
print(f'Writes/sec:  {writes_per_day / 86400:.0f}')
print(f'Reads/sec:   {reads_per_day / 86400:,.0f}')
print(f'Storage/day: {writes_per_day * bytes_per_url / 1e9:.1f} GB')
print(f'Storage total ({years_to_store}y): {writes_per_day * bytes_per_url * 365 * years_to_store / 1e12:.1f} TB')

cache_hit_rate = 0.80
hot_urls = reads_per_day * (1 - cache_hit_rate)
print(f'\n80% cache hit rate: {cache_hit_rate*100}% of reads from cache')
print(f'DB reads/sec: {hot_urls / 86400:,.0f}')

Paso A: Diseño de la API

Defina la superficie de la API: las operaciones que el sistema expone a los clientes y a los servicios internos. Especifique claramente el método HTTP, la ruta del endpoint, los parámetros de la solicitud y el formato de la respuesta. Esto sirve de base para el resto del diseño: todo lo demás existe para implementar estas API.

Para un acortador de URL, las dos API principales son: (1) POST /shorten para crear una URL corta y (2) GET /{alias} para redirigir. Opcionalmente: DELETE /{alias} para eliminar y GET /{alias}/stats para obtener analíticas. Especifique los códigos de respuesta (201 Created, 301 Redirect, 404 Not Found).

# API Design for URL Shortener
apis = [
    {
        'method': 'POST',
        'path': '/api/v1/shorten',
        'request': '{"long_url": "https://...", "alias": "optional", "expires_at": "optional"}',
        'response': '201 Created: {"short_url": "https://short.ly/abc1234", "alias": "abc1234"}',
    },
    {
        'method': 'GET',
        'path': '/{alias}',
        'request': 'No body',
        'response': '301 Redirect to long_url (or 404 Not Found)',
    },
    {
        'method': 'GET',
        'path': '/api/v1/{alias}/stats',
        'request': 'Optional: date range query params',
        'response': '200 OK: {"clicks": 42000, "unique_visitors": 15000}',
    },
]
for api in apis:
    print(f'{api["method"]} {api["path"]}')
    print(f'  Request:  {api["request"]}')
    print(f'  Response: {api["response"]}')
    print()

Paso D: Modelo de datos

El modelo de datos define qué se almacena y cómo. Identifique las entidades principales y sus atributos. Para un acortador de URL: una tabla urls con alias (clave primaria), long_url, created_at, expires_at y user_id. También puede incluirse una tabla clicks para las analíticas.

Elegir el tipo de almacenamiento adecuado es fundamental: una base de datos relacional para datos estructurados con consultas complejas; un almacén de clave-valor (Redis, DynamoDB) para búsquedas de alias O(1) a escala; un almacén de objetos (S3) para blobs grandes. Para un acortador de URL, un almacén de clave-valor indexado por alias es ideal para las lecturas, junto con una base de datos relacional para las escrituras y la administración.

# Data model for URL Shortener

# Core table (PostgreSQL)
urls_schema = '''
CREATE TABLE urls (
    alias       VARCHAR(16) PRIMARY KEY,   -- e.g., 'abc1234'
    long_url    TEXT NOT NULL,
    user_id     UUID REFERENCES users(id),
    created_at  TIMESTAMP DEFAULT NOW(),
    expires_at  TIMESTAMP,
    click_count BIGINT DEFAULT 0
);
CREATE INDEX ON urls(user_id);
'''

# Cache layer (Redis) for hot reads
redis_schema = '''
alias  =>  long_url       # O(1) GET on cache hit
TTL = 24 hours (or until expiry)
Cache eviction: LRU
'''

print('PostgreSQL schema:')
print(urls_schema)
print('Redis cache:')
print(redis_schema)
print('Storage split: Redis for hot reads (~80%), PostgreSQL for writes and cold reads')

Paso I: Infraestructura de alto nivel

Dibuje la infraestructura de alto nivel: qué servidores gestionan cada responsabilidad, cómo fluyen los datos entre los componentes y qué servicios externos se utilizan. Para un acortador de URL a escala:

  • Balanceador de carga: distribuye el tráfico entre las réplicas de los servicios de escritura y lectura
  • Servicio de escritura: genera el alias, valida su unicidad, escribe en la base de datos e invalida la caché
  • Servicio de lectura/redirección: comprueba primero la caché de Redis y, si no encuentra el dato, recurre a la base de datos
  • Base de datos relacional: fuente de verdad (con réplicas de lectura)
  • Clúster de Redis: almacena en caché las asignaciones de URL más consultadas para lecturas en menos de un milisegundo
# ASCII architecture sketch
architecture = '''
         Clients
            |
       [Load Balancer]
       /             \\
  [Write API]    [Read/Redirect API]
      |                  |  |
  [Alias Gen]       [Redis Cache]
      |                  |
  [PostgreSQL]---->[Read Replicas]

Alias Generation:
- Base62 encoding of auto-incrementing ID
- 62^7 = 3.5 trillion unique URLs (enough for 5 years at 100M/day)
- OR random 7-char Base62 with collision check
'''
print(architecture)
print('Key design decisions:')
print('  - Read/Write split: separate services for scalability')
print('  - Cache-aside pattern: read from Redis, fallback to DB')
print('  - 301 vs 302 redirect: 301 cached by browser (less load), 302 always hits server (analytics)')

Paso O: Optimizar y gestionar los cuellos de botella

La fase de optimización aborda los cuellos de botella y permite escalar el sistema. En un acortador de URL, las cuestiones principales son: la latencia de redirección (coloque Redis cerca de los usuarios mediante una CDN o cachés perimetrales), la unicidad de los alias a escala (utilice un servidor central de asignación o una función hash con detección de colisiones) y el cuello de botella de escritura de la base de datos (realice escrituras por lotes o escrituras asíncronas mediante una cola).

Analice explícitamente las ventajas y desventajas: las redirecciones 301 reducen la carga del servidor, pero disminuyen la precisión de las analíticas; las redirecciones 302 se pueden rastrear, pero añaden latencia. Una caché con una caducidad larga reduce la carga de la base de datos, pero puede provocar que se utilicen URL obsoletas. Demostrar que comprende estas tensiones es señal de un razonamiento propio de un nivel sénior.

# Key optimisation discussion points
optimisations = {
    'Read latency': [
        'Redis cluster in multiple regions (CDN-edge caching)',
        '301 redirect for non-tracked URLs (client caches)',
        '302 redirect when click analytics are needed',
    ],
    'Write throughput': [
        'Async writes: accept request, enqueue to Kafka, batch-commit to DB',
        'Ticket server (centralised ID generator) to avoid UUID collision',
        'Or: hash(user_id + timestamp + random) with retry on collision',
    ],
    'Availability': [
        'Multi-AZ PostgreSQL with automatic failover',
        'Redis Sentinel or Redis Cluster for HA cache',
        'Health checks + circuit breaker on each service',
    ],
    'Storage': [
        'Partition urls table by hash(alias) for horizontal scaling',
        'Archive expired URLs to cold storage (S3)',
    ],
}
for area, points in optimisations.items():
    print(f'{area}:')
    for p in points: print(f'  - {p}')
    print()

Generación de alias: codificación Base62

Un detalle técnico fundamental de los acortadores de URL es cómo generar alias cortos y únicos. El enfoque estándar consiste en utilizar un identificador entero autoincremental de la base de datos y codificarlo en Base62 (dígitos del 0 al 9 y letras de la a a la z y de la A a la Z). Una cadena Base62 de 7 caracteres puede representar 62^7 ≈ 3,5 billones de URL únicas, suficiente para varias décadas a razón de 100M al día.

Este método evita las colisiones (cada identificador es único) y produce cadenas cortas y seguras para URL. La correspondencia entre el ID y el alias es determinista y reversible. La desventaja es que los identificadores secuenciales generan alias predecibles (un problema de seguridad). Desordene el alfabeto Base62 o utilice un desplazamiento del contador para reducir la predictibilidad.

import string

BASE62_CHARS = string.digits + string.ascii_lowercase + string.ascii_uppercase
BASE = 62

def encode_base62(num):
    if num == 0:
        return BASE62_CHARS[0]
    result = ''
    while num:
        result = BASE62_CHARS[num % BASE] + result
        num //= BASE
    return result

def decode_base62(s):
    result = 0
    for c in s:
        result = result * BASE + BASE62_CHARS.index(c)
    return result

# Generate aliases for IDs 1, 100, 1000, 10^9
for id_ in [1, 100, 1000, 1_000_000, 10**9, 3_521_614_606207]:
    alias = encode_base62(id_)
    decoded = decode_base62(alias)
    print(f'ID {id_:20,} => alias "{alias}" (len={len(alias)}) => decoded={decoded}')

Aplicación de RADIO al diseño del feed de Twitter

Apliquemos brevemente RADIO al diseño de un feed de noticias similar a Twitter para mostrar que el marco se puede generalizar:

  • R: Los usuarios publican tweets, siguen a otras personas y ven un feed de tweets de las cuentas que siguen. Escala: 300M de usuarios, 500M de tweets al día; el feed debe cargarse en <2s.
  • A: POST /tweets, GET /feed, GET /timeline/{user_id}
  • D: tabla tweets (id, user_id, content, created_at); tabla follows (follower_id, followee_id); caché del feed por usuario
  • I: el servicio de fan-out escribe los tweets nuevos en los feeds de los seguidores (precalculados); Cassandra para las tablas de follows y tweets con muchas escrituras; Redis para las cachés de los feeds
# Fan-out on Write vs Fan-out on Read trade-off
fan_out_strategies = {
    'Fan-out on Write (Push)': {
        'How': 'When user A posts, immediately write to all followers feeds',
        'Pro': 'O(1) feed read — feed is precomputed',
        'Con': 'Celebrities with 10M followers => 10M writes per post; very slow write',
        'Best for': 'Users with few followers (regular users)',
    },
    'Fan-out on Read (Pull)': {
        'How': 'When user reads feed, query followees tweets and merge',
        'Pro': 'Writes are fast (one DB write per tweet)',
        'Con': 'Feed read is slow: must query all N followees',
        'Best for': 'Celebrity accounts (few reads per post)',
    },
    'Hybrid': {
        'How': 'Push for regular users, pull for celebrities',
        'Pro': 'Balances read and write cost',
        'Con': 'More complex implementation',
        'Best for': 'Production systems like Twitter',
    },
}
for strategy, details in fan_out_strategies.items():
    print(f'{strategy}:')
    for k, v in details.items(): print(f'  {k}: {v}')
    print()

Errores comunes en las entrevistas de diseño de sistemas

Evite estos errores frecuentes que pueden hacer descarrilar las entrevistas de diseño de sistemas:

  • Saltar directamente a las soluciones: dibujar cajas antes de aclarar los requisitos indica malos hábitos de ingeniería
  • No hacer estimaciones: diseñar sin conocer la escala es pura especulación
  • Sobreingeniería: diseñar para 1.000 millones de usuarios cuando el enunciado habla de 10.000 desperdicia el tiempo de la entrevista
  • No mencionar los compromisos: cada decisión tiene ventajas y desventajas; no mencionarlas sugiere una comprensión superficial
  • Guardar silencio: los entrevistadores necesitan escuchar su proceso de razonamiento; explique sus decisiones a medida que las toma
# Checklist: before you stop talking, verify you covered:
checklist = [
    '[ ] Asked clarifying questions about scale and constraints',
    '[ ] Made capacity estimates (RPS, storage, bandwidth)',
    '[ ] Defined the API surface clearly',
    '[ ] Described the data model and storage choices',
    '[ ] Drew a high-level architecture with named components',
    '[ ] Identified the main bottleneck and proposed a solution',
    '[ ] Discussed at least one trade-off explicitly',
    '[ ] Verified the design meets the stated requirements',
]
for item in checklist:
    print(item)

Comprobación rápida

Compruebe sus conocimientos sobre los conceptos de Data Structures & Algorithms — Coding Interview Prep de esta lección.

Resumen de la lección

En esta lección ha aprendido: el marco RADIO estructura las entrevistas de diseño de sistemas en Requisitos, API, Modelo de datos, Infraestructura y Optimización, debe aclarar siempre los requisitos funcionales y no funcionales y hacer estimaciones de capacidad antes de proponer un diseño, y debe analizar explícitamente los compromisos: cada decisión de diseño tiene ventajas y desventajas que los entrevistadores esperan que explique. A continuación, exploraremos cómo elegir entre motores de almacenamiento SQL y NoSQL según los patrones de acceso, la consistencia y la escala.

Preguntas frecuentes

¿La lección «Marco de la entrevista de diseño de sistemas» es gratis?

Sí — el texto completo de «Marco de la entrevista de diseño de sistemas» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Coding Interview Prep, actualiza a CoddyKit PRO. El curso de Coding Interview Prep incluye 4 lecciones en total.

¿Qué aprenderé en «Marco de la entrevista de diseño de sistemas»?

Recorra el marco RADIO de cinco pasos (Requirements, API, Data, Infrastructure, Optimise) y practique su aplicación a un acortador de URL. Practicas Coding Interview Prep con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Coding Interview Prep?

No se requiere experiencia previa. Coding Interview Prep en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 1 de 4.

¿Cuánto tiempo toma la lección «Marco de la entrevista de diseño de sistemas»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Coding Interview Prep?

Sí. Cada lección de Coding Interview Prep incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Marco de la entrevista de diseño de sistemas
  2. Almacenamiento de datos escalable: SQL frente a NoSQL
  3. Caché, CDN y balanceo de carga
  4. Diseñar un limitador de solicitudes y un feed de Twitter
← Volver a Coding Interview Prep