Ejercicio entrevista: acortador de URLs para campañas de marketing
Ejercicio práctico de diseño de sistemas nivel básico — un acortador de URLs con métricas para medir campañas de marketing.
Un ejercicio típico de diseño de sistemas para un perfil junior, con respuestas modelo. Ideal para practicar porque es simple de plantear pero da mucho para conversar.
Planteo del problema
El equipo de marketing lanza campañas en varios canales (email, Instagram, YouTube…) y quiere
compartir enlaces cortos y con marca, por ejemplo codea.se/verano26. Cuando alguien entra al
enlace corto, lo redirigimos a la URL original (la landing de la campaña).
Pero además hay un requisito clave del negocio: marketing necesita saber cuántos clics recibió cada enlace para medir qué campaña funcionó mejor.
¿Cómo diseñarías este servicio?
Qué preguntar primero (requisitos)
Antes de tirar código, conviene aclarar el alcance:
- ¿Cuántos enlaces por día? ¿Se leen (clics) mucho más de lo que se crean? → Sí: muchísima lectura, poca escritura.
- ¿El código corto lo genera el sistema o marketing quiere personalizarlo (
/verano26)? - ¿Necesitamos solo el total de clics o también desglose por canal/fecha?
Solución propuesta
El diseño
Cuando alguien entra al enlace corto pasan tres cosas: (1) el servicio busca la URL —primero en la caché, y solo si no está va a la base de datos—, (2) redirige al usuario, y (3) registra el clic de forma asíncrona (por una cola) para no demorar el redirect.
flowchart LR
subgraph campana["Campaña de marketing"]
direction LR
email["Email"]
ig["Instagram"]
yt["YouTube"]
end
email --> corta["codea.se/verano26"]
ig --> corta
yt --> corta
corta --> api["Servicio acortador"]
%% 1. Lectura: buscar la URL, con caché delante de la base de datos
api -->|"1. busca el codigo"| cache[("Caché")]
cache -.->|"si no está (miss)"| db[("Base de datos")]
db -.->|"guarda en caché"| cache
%% 2. Redirect al usuario
api -->|"2. 301 redirect"| destino["Landing de la campaña"]
%% 3. Escritura de métricas, asíncrona
api -.->|"3. evento de clic"| cola["Cola de eventos"]
cola --> worker["Worker de métricas"]
worker --> metricas[("Métricas por campaña")]
dash["Panel de marketing"] --> metricas
style cache fill:#9dcc4d,stroke:#4d6c20,color:#0f172a
style metricas fill:#9dcc4d,stroke:#4d6c20,color:#0f172a
Las dos operaciones (la API)
POST /acortar→ recibe la URL larga y, opcionalmente, un código personalizado y el nombre de la campaña. Devuelve el enlace corto.GET /{codigo}→ busca el código, registra el clic y hace un301/302redirect a la URL original.
Un ejemplo concreto de cada una:
POST /acortar
{
"url": "https://codeaseguro.dev/landing/verano",
"codigo": "verano26", // opcional
"campania": "Verano 2026"
}
→ 201 Created
{ "corta": "https://codea.se/verano26" }
GET /verano26
→ 302 Found
Location: https://codeaseguro.dev/landing/verano
Cómo generar el código corto
- Automático: un contador autoincremental en la base de datos codificado en Base62
(
a-z A-Z 0-9) → códigos cortos y únicos (125 → "cb"). - Personalizado: marketing elige el código (
verano26); solo hay que validar que no exista.
Convertir el id numérico a Base62 es sencillo:
const ALFABETO = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
function aBase62(id: number): string {
let codigo = '';
do {
codigo = ALFABETO[id % 62] + codigo;
id = Math.floor(id / 62);
} while (id > 0);
return codigo;
}
// aBase62(125) === "cb"
Con solo 6 caracteres en Base62 hay 62⁶ ≈ 56.800 millones de combinaciones: más que suficiente para cualquier volumen de campañas.
Guardar los datos
Dos cosas separadas: los enlaces y los clics.
- Tabla
enlaces:codigo (PK) | url_original | campania | fecha_creacion. - Los clics se cuentan aparte: un contador por enlace (o un registro por clic con fecha/canal si marketing quiere desglose).
CREATE TABLE enlaces (
codigo VARCHAR(12) PRIMARY KEY, -- "verano26"
url_original TEXT NOT NULL,
campania VARCHAR(120),
fecha_creacion TIMESTAMP DEFAULT now()
);
-- Un registro por clic → permite desglosar por fecha y canal.
CREATE TABLE clics (
id BIGSERIAL PRIMARY KEY,
codigo VARCHAR(12) REFERENCES enlaces(codigo),
canal VARCHAR(60), -- de ?utm_source=instagram
fecha TIMESTAMP DEFAULT now()
);
La búsqueda de un redirect es un SELECT url_original FROM enlaces WHERE codigo = $1 por clave
primaria: rapidísima.
El punto clave: no frenar el redirect
El usuario tiene que ser redirigido rápido. Registrar la métrica no debería hacerlo esperar.
TIP
Registrá el clic de forma asíncrona: respondé el redirect enseguida y mandá el evento de “clic” a una cola para procesarlo después. Así las métricas nunca frenan al usuario.
Y como los redirects son la operación más frecuente, se agrega una caché (codigo → url)
delante de la base de datos, para que la mayoría de los clics ni la toquen.
CAUTION
Si contás los clics escribiendo en la base de datos en cada redirect, en un pico de campaña esas escrituras pueden saturarla. Por eso conviene la cola + caché en lugar de escribir directo.
Qué evaluamos en el candidato
- Que pregunte los requisitos antes de diseñar.
- Que separe escritura (crear enlace) de lectura (redirigir), y note que es read-heavy.
- Que entienda que registrar métricas no debe bloquear el redirect (async / cola).
- Bonus: que proponga desglosar clics por canal usando parámetros UTM (
?utm_source=instagram).
- #arquitectura
- #diseno-de-sistemas
- #entrevista
- #ejercicio-practico