Coda
Un diario musical social: registrás lo que escuchás, reseñás álbumes y recibís recomendaciones que explican por qué.
- Rol
- Fullstack
- Año
- 2026
- Stack
Contexto y problema
Coda es un diario musical social. Registrás lo que escuchás, calificás y reseñás álbumes, armás listas rankeables y seguís la actividad de otras personas.
La idea central es que sea un diario, no un feed algorítmico: el centro es lo que vos escuchaste y opinaste. Las recomendaciones existen, pero siempre explican de dónde salen.
Arquitectura
Es un monolito modular: una sola API en NestJS organizada por módulos, con el trabajo pesado delegado a workers de BullMQ. Para un equipo chico, eso significa desplegar y depurar una sola cosa y avanzar rápido, sin pagar el costo operativo de microservicios.
El único candidato claro a separarse es un futuro servicio de recomendaciones en Python, y hoy está en el roadmap.
Tres decisiones técnicas
Un pipeline de catálogo que respeta a sus fuentes
- Decisión
- Spotify siembra el catálogo y MusicBrainz lo enriquece; un match se acepta solo con score
>= 80. Las importaciones son idempotentes y reanudables: ids de job determinísticos, checkpoint de paginación en Redis y upserts sobrespotifyIdúnico. - Por qué
- MusicBrainz permite 1 request por segundo, y el límite se aplica dos veces: un limiter de BullMQ para toda la flota (1 job cada 1100 ms) y un gate serializado en el cliente. Los fallos se reintentan hasta 5 veces con backoff exponencial.
- Tradeoff
- Importar es lento a propósito. A cambio, se respeta el límite de MusicBrainz y un proceso caído retoma donde quedó sin duplicar datos.
Búsqueda como proyección reconstruible
- Decisión
- Postgres es la fuente de verdad. Meilisearch es una proyección de lectura que se actualiza a través de la cola
search-sync, yreindex:searchla reconstruye desde cero. - Por qué
- La búsqueda tolerante a typos necesita un motor dedicado, pero ese motor no debería ser dueño de ningún dato. Si el índice se corrompe o cambia el esquema, se vuelve a generar.
- Tradeoff
- Consistencia eventual: entre una escritura y su sincronización, la búsqueda puede mostrar datos levemente desactualizados.
Recomendaciones v1: simples y explicables
- Decisión
- Una heurística precalculada en workers:
0.5 género + 0.35 artista + 0.15 popularidad (log). Un prefiltro SQL por los 5 géneros principales del usuario acota a 300 candidatos y se guardan los 50 mejores. Se regeneran con un debounce de 5 minutos y un refresh nocturno. - Por qué
- Cada recomendación guarda su razón (
topGenre,matchedArtist), así la interfaz puede mostrar “Because you like {género}” o “Because you follow this artist” en lugar de una caja negra. - Tradeoff
- Deliberadamente simple antes de cualquier modelo de ML. Captura menos matices, pero es barata, predecible y fácil de depurar.
Problemas difíciles
El techo de la búsqueda de Spotify
La búsqueda de Spotify corta la paginación en el offset 1000. Eso bloquea la meta de un catálogo de 100k álbumes si solo se siembra por búsqueda.
Un job que BullMQ ignoraba en silencio
Con ids determinísticos, un job que quedaba en estado completado hacía que BullMQ salteara el siguiente con el mismo id, sin error. Se resolvió con
removeOnComplete.Ids de job con dos puntos
BullMQ rechaza
:en los ids de job, así que los ids determinísticos tienen que armarse con otro separador.Specs que nunca corrían en CI
Un error de orden en el pipeline de CI hacía que los specs contra infraestructura real se saltearan siempre. Un CI en verde no prueba nada si los tests importantes no se ejecutan.
Cómo se testea
570+tests en la web
Todo se escribe con TDD estricto sobre Vitest: primero el test que falla, después el código.
En CI, los specs corren contra Postgres, Redis y Meilisearch reales. Así las colas, la búsqueda y las queries se prueban contra la misma infraestructura que usan, no contra mocks.
Hecho y roadmap
Hecho
- Importación de catálogo
- Búsqueda
- Registro de escuchas
- Reseñas
- Listas
- Feed social con paginación por cursor
- Notificaciones
- Recomendaciones v1
Roadmap
- Filtrado colaborativo
- Embeddings con pgvector
- Servicio de recomendaciones en Python
- App mobile
- Páginas públicas para SEO
Especificado, todavía no implementado.
¿Querés ver el resto?
El código de Coda es público. También podés volver a los demás proyectos.