Colophon

¿Cómo está construido este sitio?

Este sitio es a la vez la demo y el case study: si vendo criterio técnico, lo justo es mostrar el mío. La idea rectora es architecture-first performance — rápido por la elección de stack, no por parches después. Acá está todo, sin discurso.

La arquitectura

Una lista de logos dice qué uso; un diagrama dice cómo pienso. Este es el container diagram real del sitio — el path del request, dónde está el caching, qué corre en el backend y qué se observa (uptime externo + RUM propio).

Arquitectura de joaquinmayer.com, como un mapa de metroDiagrama octilinear. El visitante llega por HTTPS al CDN edge de Netlify (línea de request). Desde el CDN una rama sube al sitio estático de Next.js 15 (Server Components prerenderizados) y otra baja a las Netlify Functions, que sirven /api/vitals, /perf y /status y almacenan el Real User Monitoring en Netlify Blobs (línea de datos). En paralelo, Upptime monitorea el sitio desde afuera cada 5 minutos (línea de observabilidad).
  • request
  • datos · RUM
  • observabilidad
Container diagram (C4) tratado como red de metro: octilinear y topológico, no a escala. El path crítico (visitante → edge → estático) no toca backend — por eso el LCP es ~500 ms. Solo el RUM de /perf y /status pasa por Functions + Blobs; Upptime observa desde afuera.

Decisiones & trade-offs

Cualquiera lista herramientas. Lo que demuestra criterio es saber qué se resignó al elegir cada una — y dónde vive en el código. Cada decisión, con su costo y su archivo:

ComponenteDecisión & códigoTrade-off
FrameworkDecisiónNext.js 15 (App Router) + React 19, en vez de un SSG puro como Astro.app/next.config.tsTrade-offAstro daría ~−90% de JS para un sitio read-only; elegí Next porque /status y /perf necesitan SSR dinámico real. El costo: ~102 KB de baseline de React.
EstilosDecisiónCSS propio a mano con design tokens en OKLCH. Sin Tailwind ni framework.app/globals.cssTrade-offCero KB de utilidades y control total del critical CSS; a cambio, más CSS que mantengo a mano en vez de delegar en un framework.
TipografíaDecisiónInter + JetBrains Mono self-hosted vía next/font, subset Latin, métricas de fallback calibradas.app/layout.tsxTrade-offCLS 0 garantizado y sin FOIT; el costo es la fuente en el critical path, mitigado con un preload quirúrgico solo de la del título.
ContenidoDecisiónMDX renderizado en el servidor (next-mdx-remote/rsc) para el blog.lib/blog.tsapp/blog/[slug]/page.tsxTrade-offContenido como código, versionable en git y citable por LLMs; a cambio, no hay CMS para que edite alguien no técnico.
ObservabilidadDecisiónRUM propio con web-vitals, uptime con Upptime y server spans con OpenTelemetry (instrumentation.ts). Cero GA/terceros.lib/vitals-store.tslib/uptime.tsinstrumentation.tsTrade-offDatos propios sin cookies, públicos en /status y /perf; los traces OTel exportan a un collector self-hosted si se configura. A cambio, lo opero yo: sin funnels ni dashboards listos.
TransicionesDecisiónView Transitions same-document nativas del navegador.app/layout.tsxTrade-offCross-fade entre páginas sin librería ni costo de LCP; a cambio, solo donde el navegador lo soporta — si no, degrada a navegación normal.
DeployDecisiónBuild servido desde el edge de Netlify, detrás del CDN.netlify.tomlTrade-offLatencia mínima y escala trivial para lo estático; a cambio, lo dinámico (/status, /perf) corre como Functions, no en el edge.
CalidadDecisiónLighthouse CI como performance budget que bloquea el merge..github/workflows/perf-budget.yml.lighthouserc.jsonTrade-offNinguna regresión de LCP/CLS/JS entra a prod; a cambio, unos minutos de CI por PR y algún rojo a calibrar.
Infra como códigoDecisiónLa config del site (build, env, dominio, DNS) declarada en Terraform — infra/ en el repo.infra/Trade-offCero clicks en la consola, reproducible y auditable, apply en CI; a cambio, hay que mantener el estado y adoptar el site con terraform import.

Versiones Next.js 15 · React 19 · TypeScript 5 (strict) · MDX + remark-gfm · web-vitals 4 · Netlify (plugin-nextjs 5, Blobs) · Upptime · Lighthouse CI · Terraform (provider netlify)

Deploy & CI/CD

El deploy es un flujo, no un evento manual. De push a producción, con un gate de calidad que bloquea el merge — cada etapa con su tiempo típico:

  1. instantepush / PRUn push o PR a GitHub dispara CI y un preview de Netlify, en paralelo.
  2. ~2 minlint + types + buildnext build valida TypeScript (strict) y compila. Falla acá = no avanza.
  3. ~3 minLighthouse CIgateCarga las páginas reales (smoke test) y mide. LCP < 1s, CLS < 0.05, JS budget. Rojo = no mergea.
  4. ~1–2 minNetlify buildBuild de deploy: genera llms.txt + build-info y compila el sitio.
  5. ~30 spreview deployURL aislada por cada PR para revisar el cambio en vivo antes de mergear.
  6. ~10 sprodMerge a main → swap atómico en el edge. Rollback = 1 click al deploy anterior.
  7. continuoobservabilityRUM (web-vitals) + Upptime registran qué pasó. Visible en /status y /perf.

Total: ~5–8 min de push a prod, con gate de calidad y preview por PR. El gate corre en GitHub Actions (.github/workflows/perf-budget.yml); el budget, en .lighthouserc.json. Tiempos aproximados.

El delta: deploy manual vs este pipeline

“Hago CI/CD” es table stakes. Lo que importa es el delta — qué cambia respecto de deployar a mano:

Deploy manualEste pipeline
Tiempo a producciónManual~15 min a manoEste pipeline~5–8 min, automatizado
RollbackManualre-subir el build viejoEste pipeline1 click al deploy anterior
PreviewManualse prueba en prodEste pipelineURL aislada por cada PR
Gate de calidadManualrevisión a ojoEste pipelineLighthouse bloquea el merge
Frecuencia seguraManual~1 deploy/semanaEste pipeline10+ deploys/día

Es el mismo patrón con el que un equipo pasa de 1 deploy/semana a 10/día — y más deploys = más experimentos = más aprendizaje de crecimiento. El pipeline rápido no es vanidad técnica: es la infra que deja testear hipótesis de growth sin fricción.

Principios

Architecture-first
El sitio es rápido porque eligió el stack correcto, no por optimización post-hoc. Lo difícil se vuelve fácil cuando la base es la adecuada.
JS mínimo
Cero librerías de animación pesadas, cero analytics de terceros. El JS por página es de 0.5–6 KB; el resto es el framework.
Accesibilidad AA
Contraste WCAG AA, focus visible, skip link, y respeto por prefers-reduced-motion en todas las animaciones y transiciones.
Privacy-first
Ninguna cookie, ningún script de terceros. La medición de performance es propia y anónima.
Medible
Nada se declara "rápido" sin un número detrás: budget en CI + Core Web Vitals reales de usuarios, públicos.

Verificalo

No hace falta creerme: el estado operativo en vivo está en /status, los Core Web Vitals reales en /perf, el desglose completo de cómo se logró está en el case study, y el código —incluida la infra del sitio declarada en Terraform (infra/)— vive en GitHub.

← Volver al inicio