Saltar al contenido
← Todos los ensayos

De un generador de PDFs a un portal en 5 idiomas: seis meses vibecodeando pkmntools

Hoy pkmntools.com es un portal de Pokémon TCG en cinco idiomas, con un blog editorial, precios de cartas en vivo, un rastreador de colecciones con base de datos propia y despliegue continuo a mi propia VPS. Hace seis meses era un generador de PDFs hecho en un fin de semana.

pkmntools.com hoy, en desktop y móvil

Antes de contar cómo pasó, una aclaración que me importa: no soy desarrollador profesional. Soy arquitecto de soluciones. Mi trabajo es diseñar sistemas, no escribir código ocho horas al día. Todo lo que sigue lo construí vibecodeando: definiendo qué quería, por qué, y con qué restricciones, y dejando que agentes de IA escribieran la mayor parte del código bajo mi dirección. De los 535 commits del proyecto, 370 tienen co-autoría de Claude. No lo escondo; es el punto de este artículo.

La versión corta, por si decides quedarte: de 10 impresiones en Google a más de 55.000 en seis meses, usuarios multiplicados por 15, un bloqueo de API que tumbó medio sitio, cero pesos ganados y una factura de hosting que terminó en cero. Esta es la historia completa, con los errores incluidos.

Enero: una subasta de 16 millones y unos placeholders de 3 euros

En enero de este año estaba un poco desubicado. Venían cambios grandes en mi vida, y en las noches en que no podía dormir temprano le daba vueltas a dos búsquedas paralelas: un proyecto personal que me ayudara a aprender, practicar y explorar áreas que potenciaran mi carrera, y un hobbie que me permitiera compartir más tiempo con mi hija, Fernanda: algo entretenido, que nos tomara tiempo y que dejara recuerdos duraderos de nuestra relación.

Lo primero no fue fácil. Empezaba proyectos sin dirección ni inspiración real, y sin una utilidad concreta era difícil encontrarles un norte. Lo segundo me encontró a mí: un día, scrolleando Instagram, me topé con el hype de la subasta del Pikachu Illustrator de Logan Paul, que cerró en 16,5 millones de dólares, el récord histórico para una carta coleccionable. (Meses después, esa misma carta terminaría encabezando el artículo más leído de pkmntools; la vida da vueltas curiosas.) Eso me devolvió a Pokémon: el anime que veía de niño y adolescente, y las cartas que llegué a ver pero que nunca me llamaron la atención.

Después de semanas de investigación y de mucho conversar del tema con Gemini y Claude, me decidí: compré mi primera Ultra Premium Collection y un Battle Academy para entrar al mundo Pokémon con Fernanda. Un par de meses después, entre clasificar cartas, buscar en internet y decidir por dónde llevar la colección, me encontré con la necesidad de unos placeholders para el binder… y pagué 3 euros por ellos. Me quedó la frustración de haber pagado por algo tan “tonto” y sencillo que pude haber generado yo mismo. De ahí nació la idea de tener mi propio Placeholder Dex.

Abril: el generador de PDFs (o el mito del “si lo construyes, vendrán”)

Con la idea clara (un generador de PDFs de cartas placeholder para binders de colección), lo levanté con create-next-app, Tailwind y un deploy a Vercel: el camino de menor resistencia para alguien que quiere resultado y no fricción. En cuatro días salieron 38 commits: paleta de colores TCG, analytics con GTM y hasta un botón de donaciones de Ko-fi, porque en mi cabeza el sitio iba a despegar solo.

Abril cerró con 120 usuarios y 10 impresiones en Google. Diez.

Mayo: el mes en que casi muere

En mayo no hay un solo commit. Las métricas lo dicen todo: 22 usuarios, 8 impresiones, cero clics orgánicos.

La verdad es que no fue solo falta de tiempo, aunque tiempo tampoco sobraba: tengo mucho trabajo, una familia que mantener y con la que quiero estar, y justo me tocó un traslado de ciudad por trabajo. Pero el motivo de fondo era otro: no sabía cómo hacer crecer el sitio de forma orgánica. Creí que con ponerlo en línea bastaba, y no fue así. Nadie llega a un sitio que nadie enlaza y que Google no tiene razones para mostrar.

De esa frustración nació la idea que cambió todo: si el problema era que no había razones para llegar al sitio, había que crearlas. Contenido. Un blog.

Junio: el giro editorial (y las tecnologías del porqué)

El 3 de junio el proyecto revivió, y las decisiones de ese mes definieron todo lo que vino después. Las cuento con su porqué, porque ahí está lo que de verdad aprendí:

Blog en MDX, sin CMS. Los artículos viven como archivos .mdx en el repositorio, con componentes React embebidos (galerías de cartas, tablas de precios, líneas de tiempo). ¿Por qué no un CMS? Porque el editor soy yo con un agente de IA en sesiones editoriales: investigamos un tema, redactamos, y el artículo entra por un commit como cualquier otro código. Un CMS habría agregado infraestructura para un flujo que no existe.

i18n de servidor, no de cliente. En abril había hecho un i18n ingenuo con localStorage: el usuario cambiaba el idioma y el texto cambiaba en el navegador. Para Google eso es invisible: existe una sola página. En junio lo rehice como se debe: rutas por idioma (/en/, /pt/, /fr/, /de/), renderizado en servidor y hreflang recíproco entre traducciones. Cinco idiomas, cinco sitios indexables.

Precios en vivo con ISR. Los artículos muestran precios actuales de las cartas vía la API pública de TCGdex, regenerados con Incremental Static Regeneration de Next.js. La regla editorial más importante del proyecto: el precio con el que se publicó un artículo nunca se edita, porque es evidencia histórica. Los precios vivos van aparte, marcados como tales.

MCPs propios para los agentes. Aquí es donde mi rol de arquitecto pesó más que cualquier línea de código. Si los agentes de IA iban a ser mi equipo de desarrollo y mi equipo editorial, necesitaban acceso estructurado a los datos del dominio. Construí servidores MCP (Model Context Protocol) que exponen identidad de cartas, sets y precios como herramientas que cualquier agente puede invocar. Escribir un artículo sobre un set pasó de “copiar y pegar datos” a “el agente consulta las cartas y sus precios él mismo”.

El blog de pkmntools hoy: editorial, bilingüe y con precios en vivo

Ese mismo mes el proyecto se desbordó de su repositorio: nació una tienda de cartas construida sobre MedusaJS con su propio storefront, un catalogador de cartas físicas por visión de IA, y el stack de infraestructura en una VPS. La tienda fue, ante todo, un experimento para probar la plataforma (quería saber de primera mano qué se siente montar comercio sobre Medusa), pero eventualmente puede convertirse en una nueva vía de crecimiento del proyecto. Esa historia merece su propio artículo; volvamos al portal.

Julio: la curva en J

El 26 de julio activé las traducciones piloto a portugués y alemán con el hreflang recíproco completo. En cuatro días, las impresiones diarias pasaron de 162 a 424. Alemania se convirtió en mi mercado #1: la home alemana llegó a ser la página de entrada más visitada del sitio, con búsquedas del tipo “platzhalter pdf” que no tenían competencia.

Julio cerró con 274 usuarios y 4.607 impresiones. El canal orgánico, que en abril era el 5% del tráfico, iba camino de ser el 72%. La lección me la repito desde entonces: el contenido multilingüe bien estructurado fue la palanca de crecimiento más rentable de todo el proyecto, y costó una fracción de lo que costó cualquier feature.

Agosto: 248 commits y el mes que me enseñó humildad

Agosto fue el mes más intenso: 248 commits. Salió el Collection Tracker: rastrea tu colección, con login por Google o por código OTP al correo (Auth.js + Resend), listas públicas compartibles y resumen mensual por email. Debajo, una base de datos Postgres con Drizzle como ORM y los primeros cron jobs.

El Collection Tracker: marca tus cartas y mira el valor de tu colección en vivo

También fue el mes de los errores que más me enseñaron:

El bug que los tests no veían. La API de precios usa la clave reverse-holofoil, con guion. Mi código la leía en camelCase. Resultado: los precios de reverse holo se descartaban en silencio en todo el sitio. Los tests pasaban porque los mocks usaban la misma clave equivocada. Moraleja: un test que mockea tu suposición solo verifica que sigues suponiendo lo mismo.

La IA dejó su firma en producción. Un subagente que tradujo un artículo al francés dejó al final del archivo las etiquetas de su formato interno de llamadas a herramientas. El MDX no compiló y el artículo dio error 500 en producción. Vibecodear no te exime de revisar lo que se publica; te obliga a revisarlo mejor.

Respetar las APIs de terceros. Un error mío multiplicó las llamadas a una API externa hasta que me bloquearon temporalmente, y partes del sitio dependientes de esos datos cayeron dos días. La solución fue de arquitectura, no de disculpas: un freno global de peticiones y un circuit breaker. La tasa de error pasó de 20,3% a 0,6% y el p95 de esas páginas bajó de 10 segundos a 89 milisegundos.

Septiembre: la migración a mi VPS

Con el tráfico creciendo, la cuenta de Vercel también. Un solo descuido lo ilustró: una llamada a headers() en el layout raíz volvía dinámico el renderizado de todo el sitio: cada visita a una página pesada se servía desde el origen, y la factura lo reflejó.

Yo ya tenía una VPS (Hostinger, 2 vCPU / 8 GB) corriendo otros servicios con Docker y Traefik como proxy TLS. La decisión fue de arquitecto: ¿por qué pagar hosting serverless premium por un sitio mayormente estático, teniendo capacidad ociosa ya pagada?

La migración fue en dos pasos, cada uno reversible:

  1. Primero los crons, de Vercel a GitHub Actions. Eso solo ya permitía bajar de plan.
  2. Después el sitio completo: Dockerfile multi-stage con salida standalone de Next.js, build en GitHub Actions, imagen a GitHub Container Registry, deploy a la VPS con health-gate (si /api/health no responde, no hay cutover), y Cloudflare como CDN al frente. El plan de rollback era cambiar un registro DNS: menos de 5 minutos.

Una página de set servida desde la VPS: HeartGold & SoulSilver

Salió limpia. Sin caída, sin daño al rastreo de Google, y con mejor latencia que antes (p95 ~65 ms). El costo de hosting pasó de unos USD $22/mes a prácticamente $0 marginal, porque la VPS ya existía. Desde el 20 de septiembre, cada push a main construye y despliega solo.

Los números honestos

Prometí transparencia, así que van los dos lados.

Lo que funcionó:

Lo que no funcionó (todavía):

Lo que me llevo

Seis meses después, esto es lo que le diría al yo de abril:

  1. Publicar no es crecer. Sin una razón para que te encuentren (contenido, en mi caso), el mejor producto es invisible. El blog no fue un extra: fue el negocio.
  2. La IA multiplica al que dirige, no reemplaza al que piensa. Mis mejores aportes al proyecto no fueron código: fueron specs claras, restricciones bien puestas (“el precio publicado es sagrado”), y darles a los agentes las herramientas (MCPs) para trabajar con datos reales.
  3. Los errores caros son de arquitectura, y los baratos también. Un headers() mal puesto, una clave con guion, un circuit breaker ausente. Nada de eso lo arregla “más IA”; lo arregla entender el sistema.
  4. Sé dueño de tu infraestructura cuando los números lo pidan. Serverless fue perfecto para empezar y caro para crecer. La VPS con Docker, Traefik y un pipeline de CI/CD me costó un fin de semana y me devuelve $22 todos los meses.

El proyecto sigue vivo, con más preguntas abiertas que cerradas: la retención, la monetización, una app de escaneo en el horizonte. Si algo de esto te sirvió, o si eres otro arquitecto vibecodeando a escondidas, escríbeme. La siguiente historia probablemente sea la de la tienda.