CV-as-Code: el currículum como artefacto generado (post vivo)
Este es un post vivo: lo escribo como plan antes de tocar código y lo voy completando a medida que el trabajo avanza. La idea es pensar en voz alta, que se vea el diseño, y decidir por dónde tirar antes de picar.
El problema
Actualizar un currículum es un suplicio recurrente: abres una plataforma online que no recuerdas cuál era, peleas con su editor, exportas un PDF, y seis meses después el PDF está desactualizado y desligado de cualquier fuente de verdad. El último que tengo es de octubre y se quedó congelado en un trabajo que dejé hace años: le falta todo lo reciente.
Mientras tanto, el portfolio (la web personal) sí
está al día: tiene la experiencia, los proyectos, los highlights. Pero
vive como atributos data'[…]'= incrustados
dentro de componentes web, y el PDF va por otro lado. Dos fuentes,
ninguna conectada.
El objetivo es reducir "actualizar el CV" a tres pasos:
- Editar un fichero de datos con la nueva chicha.
- Pulsar "Descargar CV (PDF)".
- PDF profesional, paginado, listo para enviar.
El insight: el PDF lo genera el cliente, no el servidor
La primera versión del plan proponía que el servidor lanzara un Chromium headless para imprimir el PDF. Mala idea: meter un navegador entero en un servidor pequeño de producción, para generar un documento que cambia una vez cada varios meses, es complejidad que no paga su precio.
La versión buena es mucho más simple: el navegador del visitante ya trae el mejor motor de impresión que existe. No hace falta generar nada en el servidor. Se sirve una página HTML diseñada para imprimirse en A4, y el botón "Descargar" no hace más que abrirla y disparar la impresión. El navegador produce el PDF.
Cero infraestructura nueva. Cero dependencias pesadas. El "botón mágico" es una página bien hecha y una línea de JavaScript.
La arquitectura
cv-data/*.json ──► /cv (HTML A4 print) ──► window.print() ──► PDF
(fuente única) (diseño imprimible) (motor del navegador)
Tres piezas:
1. Datos separados: cv-data/*.json
La fuente de verdad pasa a ser un puñado de ficheros de datos:
timeline.json— experiencia: empresas, puestos, fechas, proyectos, highlights, tecnologías, logros.contact.json— nombre, título, email, enlaces (LinkedIn, GitHub), ciudad. Sin dirección postal (eso fuera del repo público; el resto ya es público en la web).skills.json— competencias técnicas, idiomas, formación.
La buena noticia: la experiencia ya está escrita y actualizada dentro del portfolio. Es mover datos que ya existen a un sitio con nombre propio, no inventarlos. Lo único que falta rescatar del CV viejo es formación e idiomas.
2. La vista /cv: una página pensada para el papel
Aquí está el 80% del valor. Una ruta nueva que sirve una página distinta del portfolio:
- HTML puro, sin componentes web ni shadow DOM. Esto es importante: las reglas de impresión de CSS no atraviesan bien el shadow DOM, así que la página imprimible tiene que ser plana.
- A4 con márgenes de ~15 mm y reglas
@media printque eviten que una experiencia se parta a mitad entre dos páginas. - Estructura de dos columnas, inspirada en el CV clásico pero con
tipografía limpia (Inter / Source Sans en lugar de fuentes raras):
- Barra lateral oscura: foto, contacto, competencias, formación, idiomas.
- Columna principal: nombre, titular profesional, resumen y la línea de tiempo de experiencia (fecha a un lado, empresa y logros al otro).
- Tecnologías como etiquetas de texto, no logotipos gigantes.
Esta vista, por sí sola, ya sirve para imprimir un CV decente con el atajo de impresión del navegador, aunque el botón llegue después.
3. El botón "Descargar CV (PDF)"
En el portfolio, un botón que abre /cv
y llama a la impresión del navegador. Sin endpoint que genere PDFs, sin
caché, sin proceso aparte. La única pega menor: el navegador añade sus
propias cabeceras (fecha, URL) en el diálogo de impresión, pero se
quitan con un clic y además se controlan bastante desde CSS.
Decisiones tomadas
- Generación en el cliente (navegador), no en el servidor.
- Un solo idioma para empezar: español. La versión en inglés vendrá después (el diseño ya queda preparado para soportar el cambio).
- Una sola versión del CV (sin variante "corta" y "completa" todavía; se añade si hace falta, no antes).
- Foto: la del CV anterior, ya profesional. Se optimiza para web.
- Dirección postal fuera; el resto del contacto ya es público.
No-objetivos
- No rediseñar el portfolio (sigue como está; su puesta al día con la misma fuente de datos es un paso posterior, sin prisa).
- Nada de generadores de documentos pesados ni lenguajes de composición tipográfica. La fuente son datos estructurados y el destino es una página web; el navegador hace el resto.
Plan por fases
- Extraer los datos a
cv-data/*.json(sin romper el portfolio actual). - Construir la vista
/cvimprimible (el corazón). - Añadir el botón "Descargar CV" en el portfolio.
Por dónde tirar
Mi propuesta es empezar por la fase 2 reducida: montar /cv leyendo los datos ya existentes, ver cómo
queda impreso en A4 con la experiencia actual, y ajustar el diseño hasta
que se sostenga cuando la experiencia crezca. Con eso ya hay CV
imprimible. Las fases 1 y 3 (datos limpios y botón) caen solas
después.
Implementación (hecho)
Las tres fases están en producción. El CV imprimible vive en pascualmg.dev/cv y el portfolio tiene un botón "Descargar mi CV (PDF)" bajo los enlaces sociales.
- Datos:
cv-data/{timeline,contact,skills}.jsoncomo fuente única. La experiencia salió del propio portfolio (ya estaba al día); formación e idiomas, del CV anterior. Sin dirección postal en el repo. - Vista
/cv: página HTML plana servida por un controlador mínimo. Lee los datos porfetch, monta el DOM con JavaScript vainilla (sin framework) y trae un botón que llama awindow.print(). Dos columnas,@media printen A4, cortes de página protegidos para no partir una experiencia entre hojas. - Generación: el PDF lo produce el navegador al imprimir. El servidor no ejecuta ningún navegador headless ni genera ficheros: solo sirve HTML y JSON estáticos. Toda la complejidad que el primer plan ponía en el servidor desapareció.
Lo bonito del resultado: actualizar el currículum es ahora editar un fichero de datos y publicar. El documento deja de ser un artefacto muerto que se desincroniza; pasa a ser una vista de unos datos versionados.
Y un guiño meta: este post se publicó en el mismo blog que sirve el CV, a través de un endpoint que se volvió idempotente hace unos días. Publicar el plan y desplegar la solución fueron, literalmente, el mismo sistema.
Cómo se actualiza
La gracia del invento: actualizar el currículum no es "abrir un editor de CVs", es editar datos. Hay tres ficheros JSON:
timeline.json— la experiencia (lo que más se toca: añadir un trabajo es copiar un bloque y ponerlo arriba).contact.json— cabecera: nombre, título, resumen, contacto.skills.json— competencias, stack técnico, formación, idiomas.
El flujo es: editar el JSON, validarlo, publicar el cambio y
desplegar. La página /cv lee los datos en
cada carga, así que el CV refleja el cambio al instante, sin tocar una
línea de HTML ni de CSS. El PDF se genera imprimiendo desde el navegador
(márgenes "Ninguno", sin cabeceras).
El documento dejó de ser un artefacto que se pudre en una carpeta de Descargas: ahora es el reflejo, siempre al día, de unos datos versionados. Quien quiera el detalle exacto (formato de cada campo, dónde viven los ficheros, cómo desplegar) lo tiene en la documentación del proyecto.
Lo siguiente (que no es ahora)
Una vez que generas uno, la pregunta cae sola: ¿y un generador para muchos? La vista ya es genérica —lee un JSON y pinta—, así que el salto natural sería un endpoint que reciba el JSON completo de cualquiera (foto incluida por URL), lo guarde con su propio identificador y sirva un CV compartible por persona. Un "generador de currículums" en lugar de un currículum.
Pero eso será otro día, si llega. El objetivo de hoy era tener mi CV resuelto para siempre, y ese ya vale. La mejor manera de no caer en el sobre-diseño es justo esta: parar cuando el problema que tenías delante está resuelto, y dejar la idea grande apuntada para cuando de verdad la necesites.
Comentarios (0)
Sin comentarios todavia. Se el primero!
Deja un comentario