mein portfolio sind 42 repositories und es sagt nichts aus
dev · Oct 4, 2026 · 8 min read
das github-profil hinter dieser seite hat 42 öffentliche repositories. es ist das vollständigste ding, das ich je gemacht habe, und zugleich das nutzloseste.
die gesamte selbstbeschreibung auf diesem account sind sechs zeichen: 20x dev.
das ist der beitrag, den ich an erster Stelle setzen würde, denn er ist der grund, warum es den rest davon überhaupt gibt.
wie 42 repositories in echt aussehen
ich habe das profil mit der github-api gemessen, statt zu raten, und das ist das ganze ergebnis:
| was | wie viele |
|---|---|
| öffentliche repositories | 42 |
| davon forks | 15 |
| ganz ohne beschreibung | 12 |
| sterne insgesamt, alle 42 zusammen | 14 |
| forks insgesamt | 1 |
| erster commit | 2023-01-29 |
| letzter push | 2026-09-22 |
und die sprachen, wo das profil aufhört, über mich zu handeln:
| sprache | repos |
|---|---|
| JavaScript | 11 |
| Java | 9 |
| Python | 6 |
| nichts (kein git linguist) | 5 |
| CSS, Vue | je 2 |
| Rust, OCaml, PowerShell, Brainfuck, TypeScript, Svelte, HTML | je 1 |
neun Java-repositories. fast alle davon minecraft: ein client, ein plugin, ein beute-addon, ein dorfbewohner in einem eimer, eine gespiegelte öffentliche release. das ist ein echter und ehrlicher teil meiner geschichte — ich war ein kind, das gerne sachen für ein spiel gebaut hat — aber es ist kein design-portfolio, und es ist nicht, was ich heute mache.
also landet ein besucher auf diesem profil und kommt zu einem von zwei schlüssen: entweder ist diese person um 2023 herum langweilig geworden, oder diese person hatte nie eine meinung zu irgendetwas. beide lesarten sind falsch, und die seite gibt ihnen keine möglichkeit, es herauszufinden. das ist kein formatierungsproblem. es ist ein argument, das nie vorgebracht wurde.
warum eine liste kein portfolio ist
eine liste beantwortet „was existiert“. ein portfolio muss „warum“ beantworten.
42 zeilen name · sprache · sterne · beschreibung sind eine vollständige antwort auf die erste frage und eine null-antwort auf die zweite. vollständigkeit ist kein ersatz für ein argument — im gegenteil, sie verdeckt es, denn 42 zeilen wirken wie beweise, und keine davon ist ein grund.
der konkrete fehler hier ist, dass eine zeile keine entscheidung enthalten kann. jede zeile hat dieselbe form, also sagt jede zeile dasselbe, und das einzige, was sie unterscheidet, ist eine zahl, die die aufmerksamkeit anderer misst. sortierst du die seite nach sternen, bekommst du vier projekte und viel leerraum. sortierst du nach pushed_at, bekommst du ein changelog.
was ich tatsächlich zeige: 9 von 42
diese seite zeigt neun. das sind 21% des profils, und die auswahl sind neun zeilen eines build-skripts, keine query:
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" },
];
dieses array ist das portfolio. nicht die api-antwort, nicht die datenbank — neun namen in der reihenfolge, in der ich sie gesehen haben will, mit allem anderen geholt und dann absichtlich nicht gezeigt.
die reihenfolge ist die erste designentscheidung, und es ist die, gegen die ich am härtesten gekämpft habe. die api gibt mir das profil sortiert nach updated, was die richtige antwort auf „was hast du zuletzt angefasst" ist und eine nutzlose auf „was sollte ich mir anschauen". der fetch behält meine reihenfolge und verwirft die sortierung:
/** behält die kuratierte reihenfolge und warnt laut, wenn ein featured-repo verschwindet. */
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);
}
ein projekt von 2025 steht neben einem von 2026, weil das das argument ist, nicht wegen eines zeitstempels. aktualität ist kein argument.
die zweite entscheidung ist leiser: die neun vorgestellten haben zusammen 8 sterne, und vier davon haben null. wenn ich nach sternen kuratiert hätte, wäre die seite leer. sterne sind eine eigenschaft der aufmerksamkeit anderer, nicht der arbeit.
jedes projekt, und die entscheidung, für die es steht
das ist der teil, den eine liste nicht kann. jede karte im projektraster steht hier, weil sie für etwas steht, das ich über das verhalten dieser seite entschieden habe:
- portfolio-finder (JavaScript, 3 sterne) — ein werkzeug, das die portfolios anderer findet. es ist hier, weil diese seite zur build-zeit dasselbe tut: einmal holen, eine datei erzeugen, und niemanden zur laufzeit github abfragen lassen. der fallback ist eine fest verdrahtete liste in
constants.ts, und sie existiert aus demselben grund wie jenes werkzeug. - dektop-cleaner (JavaScript/electron, 2 sterne) — es ist hier, um den punkt über
pushedAtzu machen: das feld wird ins datenmodell getragen und nie zum sortieren benutzt. ein werkzeug von 2025 verdient dieselbe karte wie eines von 2026. - terraria_mods_extractor (PowerShell, 0 sterne) — ein einzelnes shell-skript, und es bekommt dieselbe karte wie eine react-app.
buildTagsstellt die sprache immer nach vorn, also liest sich das badgePOWERSHELLan derselben stelle mit demselben gewicht. das raster rankt nicht nach prestige, denn ein raster, das nach prestige rankt, ist eine bestenliste. - 4webvideo (Python, 0 sterne) — es ist hier wegen einer entscheidung, die ich nicht getroffen habe: es gibt keinen sprachfilter und keine suche über neun elemente. beides würde mehr code und mehr visuelles rauschen kosten, als es zurückbringt.
- sorting-visualizer (JavaScript/React) — das einzige projekt mit handgeschriebenen tags, weil
FEATURED-einträge einentags-override akzeptieren. die automatisierten metadaten sind ein standard, kein käfig: wenn die maschine die arbeit nicht beschreiben kann, schreibt der mensch die zeile. - wplace-theme-changer und darkwplace-extension (JavaScript, je 1 stern) — beide sind dasselbe problem, das ich mir später selbst gelöst habe: ein ding neu bemalen, das dir nicht gehört, zur laufzeit, ohne neuladen. das browser-extension-theme, das app-theme. sie stehen aus einem grund neben dem themensystem dieser seite, und dieser grund ist der einzige grund, warum sie auf der seite stehen.
- task-manager (JavaScript) — seine github-beschreibung ist wörtlich nur eine url:
https://taskmanager-enea.web.app/. deshalb hat derProject-typ überhaupt einhomepage-feld. die beschreibung sollte auf das laufende ding zeigen, und wenn sie es nicht tut, hat das modell eine andere stelle dafür. - Frenxys (Brainfuck, 1 stern) — ein joke-repo: eine readme, und dieselbe readme in brainfuck geschrieben. es ist vorgestellt, weil kuratierung redaktionell ist und ein portfolio nicht durchgängig ernst sein muss. wenn ich alles unseriöse entfernen würde, würde ich auch alles entfernen, was das account sehenswert macht.
der teil, den ich gelöscht habe, und das ist das ehrlichste hier
die kuratierte liste hatte früher handgeschriebene einträge für vier ältere projekte. ich habe sie entfernt, und der kommentar in constants.ts sagt warum:
die vorherigen handgeschriebenen einträge (demarkify, torr, beetrap, sniffcli und freunde) wurden entfernt: diese repositories existieren nur unter dem alten owner-account und geben unter Frenxys einen 404, sie zu behalten hätte tote links ausgeliefert.
ich habe nachgesehen, und es stimmt. alle vier geben 404 unter Frenxys und 200 unter dem alten account. mein kuratiertes portfolio lieferte also links ins nichts.
das ist schlimmer als gar kein portfolio, weil es kuratiert aussieht. ein toter link in einer handausgewählten liste ist eine lüge mit einem design drumherum: er sagt „ich habe das gewählt" und zeigt auf nichts. ich hätte lieber neun lebende karten als fünfzehn tote, und ich habe vier gelöscht, damit die zahl wahr ist.
was diese seite wirklich gekostet hat
weil so ein beitrag in selbstgratulation kippen kann, die rechnung. diese seite ist eine clientseitig gerenderte react-app mit:
- 189 urls in der sitemap — 35 routen, davon 7 chrome und 28 beiträge, wobei jede url nur in den locales existiert, die den inhalt wirklich haben — und einem generator, der den build fehlschlagen lässt, wenn ein beitrag im code keine url in der sitemap hat, was der einzige weg war, den ich gefunden habe, um verwaiste seiten zu stoppen
- einen github-fetch zur build-zeit mit fallback, damit nie ein besucher github abfragt
- einen service worker, gehashte assets, themen-umfärbung zur laufzeit, eine
q/ctrl+c-terminalversion, die ancurlausgeliefert wird - 28 beiträge, von denen monatelang nur 8 tatsächlich veröffentlicht waren — ein
with_placeholder_copy-helper schrieb die anderen 20 zur laufzeit mit lorem ipsum um, also wurden fertige entwürfe, die ein halbes jahr im code lagen, als platzhaltertext ausgeliefert - ein
check:dist-gate, das noch nie fehlgeschlagen war:ok()druckteFAILund vergaß dann, es zu zählen, also liefen 1.710 assertions ohne wirkung
ich habe das alles auf den Rahmen verwendet. neun projekte sind sichtbar. dieses verhältnis ist der ganze punkt, und es ist das argument für den rahmen: die liste war nie das problem, also bringt aufwand für die liste nichts.
warum es diese seite gibt
ein github-profil ist eine behauptung ohne argument. diese seite ist das argument.
sie existiert, damit auf jede behauptung der code folgen kann, der sie wahr macht, und — wo ich es falsch hatte — der beitrag, in dem ich es sage. acht dieser beiträge sind echt, und jeder einzelne endet mit etwas, das ich falsch gemacht habe — ein sniffer, der nicht erkennt, was seine readme behauptet, ein honeypot, dessen bind-fehler stumm ist, eine sitemap, die 37 seiten verwaiste, ein theme, das mit fest verdrahteten schatten ausgeliefert wurde, ein bug, den ich mit drei edits behoben und nie diagnostiziert habe, ein fallback, der für einen bot eine neuschreibung brauchte, und ein privacy-audit, der drei drittanbieter-originse fand, von denen ich nichts wusste.
das ist das portfolio. nicht die 42 repositories — die fähigkeit, die arbeit zu zeigen und sich dann zu weigern, die nähte zu verdecken. eine linkliste kann das nicht. sie kann nur auf ein repository zeigen und hoffen, dass du es nicht liest.
was ich meinem vergangenen ich sagen würde
- vollständigkeit ist kein argument. 42 ist nicht besser als 9. wenn du nicht sagen kannst, warum etwas in der liste ist, hast du nicht kuratiert, du hast gespiegelt — und die 15 forks sind der beweis, dass ich weiß, wie spiegeln aussieht.
- lösche, bevor du tote links auslieferst. eine kuratierte liste mit einem kaputten eintrag ist schlimmer als keine kuratierte liste, weil die kuratierung die behauptung ist und der link der beweis.
- die liste war nie das problem. ich dachte die ganze zeit, die lösung sei, bessere beschreibungen zu schreiben. die lösung war, etwas zu sagen zu haben.