mi portafolio son 42 repositorios y no dice nada
dev · Oct 4, 2026 · 8 min read
el perfil de github detrás de este sitio tiene 42 repositorios públicos. es lo más completo que he hecho nunca y también es lo menos útil.
toda la autodescripción de esa cuenta son seis caracteres: 20x dev.
esta es la entrada que pondría primero, porque es la razón por la que existe todo lo demás.
qué son en realidad 42 repositorios
medí el perfil con la api de github en lugar de suponer, y aquí está todo:
| qué | cuántos |
|---|---|
| repositorios públicos | 42 |
| de esos, forks | 15 |
| sin descripción alguna | 12 |
| estrellas totales, los 42 juntos | 14 |
| forks totales | 1 |
| primer commit | 2023-01-29 |
| push más reciente | 2026-09-22 |
y los lenguajes, que es donde el perfil deja de hablar de mí:
| lenguaje | repos |
|---|---|
| JavaScript | 11 |
| Java | 9 |
| Python | 6 |
| ninguno (sin git linguist) | 5 |
| CSS, Vue | 2 cada uno |
| Rust, OCaml, PowerShell, Brainfuck, TypeScript, Svelte, HTML | 1 cada uno |
nueve repositorios en java. casi todos de minecraft: un cliente, un plugin, un complemento de recompensas, un aldeano en un balde, una versión pública replicada. esa es una parte real y honesta de mi historia — era un crío al que le gustaba construir cosas para un juego — pero no es un portafolio de diseño, y no es lo que hago ahora.
así que un visitante llega a este perfil y concluye una de dos cosas: o esta persona dejó de ser interesante alrededor de 2023, o esta persona nunca ha tenido una opinión sobre nada. las dos lecturas son falsas, y la página no les da forma de averiguarlo. eso no es un problema de formato. es un argumento que nunca se hizo.
por qué una lista no es un portafolio
una lista responde «qué existe». un portafolio tiene que responder «por qué».
42 filas de nombre · lenguaje · estrellas · descripción son una respuesta completa a la primera pregunta y nula a la segunda. la completitud no sustituye al argumento — si acaso lo esconde, porque 42 filas parecen evidencia y ninguna es un motivo.
el fallo concreto aquí es que una fila no puede contener una decisión. todas las filas tienen la misma forma, así que todas dicen lo mismo, y lo único que las distingue es un número que mide la atención de otra gente. ordena la página por estrellas y tienes cuatro proyectos y mucho espacio vacío. ordénala por pushed_at y tienes un registro de cambios.
lo que muestro de verdad: 9 de 42
este sitio muestra nueve. es el 21% del perfil, y la selección son nueve líneas de un script de compilación, no una consulta:
const FEATURED: { name: string; tags?: string[] }[] = [
{ name: "portfolio-finder" },
{ name: "dektop-cleaner" },
{ name: "terraria_mods_extractor" },
{ name: "4webvideo" },
{ name: "sorting-visualizer", tags: ["JavaScript", "React", "Visualization"] },
{ name: "wplace-theme-changer" },
{ name: "darkwplace-extension" },
{ name: "task-manager" },
{ name: "Frenxys" },
];
ese array es el portafolio. no la respuesta de la api, no la base de datos — nueve nombres en el orden en que quiero que se vean, con todo lo demás descargado y deliberadamente no mostrado.
el orden es la primera decisión de diseño y es con la que más luché. la api me da el perfil ordenado por updated, que es la respuesta correcta a «qué tocaste por última vez» y una respuesta inútil a «qué debería mirar». la descarga conserva mi orden y descarta el orden por defecto:
/** conserva el orden curado y avisa en voz alta si desaparece un repo destacado. */
function selectFeatured(repos: GhRepo[]) {
for (const entry of FEATURED) {
const repo = byName.get(entry.name.toLowerCase());
if (!repo) { missing.push(entry.name); continue; }
picked.push(repo);
}
un proyecto de 2025 está junto a uno de 2026 porque ese es el argumento, no por una marca de tiempo. la actualidad no es un argumento.
la segunda decisión es más silenciosa: los nueve destacados suman 8 estrellas entre ellos, y cuatro tienen cero. si curara por estrellas la página estaría vacía. las estrellas son una propiedad de la atención ajena, no del trabajo.
cada proyecto, y la decisión que representa
esta es la parte que una lista no puede hacer. cada tarjeta de la cuadrícula de proyectos está aquí porque representa algo que decidí sobre cómo se comporta este sitio:
- portfolio-finder (JavaScript, 3 estrellas) — una herramienta que encuentra los portafolios de otras personas. está aquí porque este sitio hace lo mismo en tiempo de compilación: descargar una vez, generar un archivo, y que nadie consulte github en ejecución. el respaldo es una lista codificada en
constants.ts, y existe por la misma razón que existe aquella herramienta. - dektop-cleaner (JavaScript/electron, 2 estrellas) — está aquí para dejar claro lo de
pushedAt: el campo se lleva al modelo de datos y nunca se usa para ordenar. una herramienta de 2025 se gana la misma tarjeta que una de 2026. - terraria_mods_extractor (PowerShell, 0 estrellas) — un único script de shell, y se lleva la misma tarjeta que una app en react.
buildTagssiempre pone el lenguaje primero, así que la insignia dicePOWERSHELLen la misma posición con el mismo peso. la cuadrícula no ordena por prestigio, porque una cuadrícula que ordena por prestigio es una tabla de posiciones. - 4webvideo (Python, 0 estrellas) — está aquí por una decisión que no tomé: no hay filtro de idioma ni búsqueda sobre nueve elementos. ambos costarían más en código y en ruido visual de lo que devolverían.
- sorting-visualizer (JavaScript/React) — el único proyecto con etiquetas escritas a mano, porque las entradas de
FEATUREDaceptan untagspropio. los metadatos automáticos son un valor por defecto, no una jaula: cuando la máquina no puede describir el trabajo, lo escribe la persona. - wplace-theme-changer y darkwplace-extension (JavaScript, 1 estrella cada uno) — ambos son el mismo problema que luego resolví para mí: repintar en tiempo de ejecución algo que no te pertenece, sin recargar. el tema de la extensión del navegador, el tema de la app. están junto al sistema de temas de este sitio por una razón, y esa razón es la única por la que están en la página.
- task-manager (JavaScript) — su descripción en github es literalmente solo una url:
https://taskmanager-enea.web.app/. por eso el tipoProjecttiene un campohomepageen absoluto. la descripción debería apuntar a la cosa que funciona, y cuando no lo hace, el modelo tiene otro sitio donde ponerlo. - Frenxys (Brainfuck, 1 estrella) — un repositorio de broma: un readme, y el mismo readme escrito en brainfuck. está destacado porque la curación es editorial y un portafolio no tiene que ser uniformemente serio. si quitara todo lo no serio, también quitaría todo lo que hacía que la cuenta mereciera la pena mirar.
la parte que borré, que es lo más honesto de aquí
la lista curada tenía antes entradas escritas a mano para cuatro proyectos antiguos. las quité, y el comentario en constants.ts dice por qué:
las entradas escritas a mano anteriores (demarkify, torr, beetrap, sniffcli y compañía) se eliminaron: esos repositorios solo existen bajo la cuenta del propietario antiguo y dan 404 bajo Frenxys, así que mantenerlos significaba enviar enlaces muertos.
lo comprobé, y es cierto. las cuatro devuelven 404 bajo Frenxys y 200 bajo la cuenta antigua. así que mi portafolio curado estaba enviando enlaces a la nada.
eso es peor que no tener portafolio, porque parece curado. un enlace muerto en una lista elegida a mano es una mentira con un diseño alrededor: dice «elegí esto» apuntando a nada. prefiero nueve tarjetas vivas que quince muertas, y borré cuatro para que el número sea cierto.
lo que construir este sitio costó de verdad
porque una entrada así puede convertirse en autobombo, la factura. este sitio es una app react renderizada en cliente con:
- 189 urls en el sitemap — 35 rutas, 7 de ellas navegación y 28 entradas, en los 7 y los 3 idiomas donde ese contenido existe de verdad — y un generador que falla la compilación si una entrada del código no tiene url en el sitemap, que es la única forma que encontré de parar las páginas huérfanas
- una descarga de github en tiempo de compilación con respaldo, así que ningún visitante consulta github nunca
- un service worker, recursos con hash, repintado del tema en ejecución, una versión terminal
q/ctrl+c servida acurl - 28 entradas, y durante meses solo 8 estaban realmente publicadas — un helper
with_placeholder_copyreescribía las otras 20 con lorem ipsum en ejecución, así que borradores terminados que llevaban medio año en el código se publicaban como texto de relleno - una puerta
check:distque nunca había fallado:ok()imprimíaFAILy luego se olvidaba de contarlo, así que 1.710 aserciones no surtían efecto
me gasté todo eso en el marco. nueve proyectos son visibles. esa proporción es el punto entero, y es el argumento a favor del marco: la lista nunca fue el problema, así que el esfuerzo puesto en la lista no compra nada.
por qué existe este sitio
un perfil de github es una afirmación sin argumento. este sitio es el argumento.
existe para que cada afirmación pueda ir seguida del código que la hace y, donde me equivoqué, de la entrada donde lo digo. ocho de estas entradas son reales y todas terminan con algo que hice mal — un sniffer que no detecta lo que su readme promete, un honeypot cuyo fallo de enlace es silencioso, un sitemap que dejó huérfanas 37 páginas, un tema publicado con sombras codificadas, un bug que arreglé con tres ediciones y nunca diagnostiqué, un respaldo que hubo que reescribir por un bot, y una auditoría de privacidad que encontró tres orígenes de terceros que no sabía que estaban ahí.
ese es el portafolio. no los 42 repositorios — la capacidad de mostrar el trabajo y luego negarse a tapar las costuras. una lista de enlaces no puede hacer eso. solo puede apuntar a un repositorio y esperar que no lo leas.
qué le diría a mi yo del pasado
- la completitud no es un argumento. 42 no es mejor que 9. si no puedes decir por qué algo está en la lista, no has curado, has reflejado — y los 15 forks son la prueba de que sé lo que es reflejar.
- borra antes de publicar enlaces muertos. una lista curada con una entrada rota es peor que ninguna lista curada, porque la curación es la afirmación y el enlace es la prueba.
- la lista nunca fue el problema. seguía pensando que la solución era escribir mejores descripciones. la solución era tener algo que decir.