Leonard Surac

Zur Startseite

Live · seit 2026

surac.dev

Ein Lebenslauf zeigt nicht, wie ich arbeite. Also habe ich diese Website als Arbeitsprobe gebaut: offen im Code, mit Bewerbungs-Links und einem Lebenslauf-PDF aus denselben Daten.

ASP.NET Core 10 · EF Core · Angular 22 SSR · PostgreSQL · QuestPDF · Playwright · Docker

Problem

Bei Bewerbungen schicke ich ein PDF, und das sagt nichts darüber, wie ich Software baue. Ich wollte eine Seite, die jemand aus dem Recruiting in einer halben Minute erfasst und die einer Entwicklerin im Code zeigt, wie sie entstanden ist.

Meine Rolle

Ich habe alles selbst gemacht: Konzept, testgetriebene Entwicklung mit KI-Agenten unter schriftlichen Regeln, Betrieb mit Staging und Produktion.

Fallstudie

Meine Lösung

Eine Website für zwei Leser: oben das, was Recruiting sucht (Rolle, Stack, Lebenslauf, Kontakt), darunter die Tiefe für Fachleute. Den Lebenslauf erzeuge ich aus denselben Daten als PDF, damit nie zwei Stände auseinanderlaufen.

  • Bewerbungs-Links mit drei Zielgruppen-Profilen, die die Reihenfolge ändern, nie den Inhalt
  • Zeugnisse und Anschreiben gebe ich pro Link frei, das vollständige PDF mit Telefon und Anschrift nur dort
  • Statistik zu Aufrufen und Antwortquote, ohne Daten über die Besucher zu speichern
  • Deutsch und Englisch vollständig, im Admin nebeneinander gepflegt

Umsetzung

Gebaut ist alles mit ASP.NET Core, EF Core, PostgreSQL und Angular mit Server-Side-Rendering, den Lebenslauf rendert QuestPDF. Ein Bewerbungs-Link ist eine Capability-URL: 128 zufällige Bits, gespeichert wird nur der Hash.

SuracDev.Api/Domains/Applications/Domain/ApplicationLink.cs
public static (string Token, string Hash) Generate()
{
  var token = Base64Url(RandomNumberGenerator.GetBytes(TokenBytes));
  return (token, Hash(token));
}

public static string Hash(string token)
  => Convert.ToHexStringLower(SHA256.HashData(Encoding.UTF8.GetBytes(token)));

Die Regeln, nach denen ich und die Agenten arbeiten, stehen in AGENTS.md; jede Entscheidung mit ihrer Abwägung in docs/decisions.md.

Entscheidungen

DDD-light statt Zeremonie
Ich schneide nach Fachdomänen, Entities schützen ihre Regeln selbst, Value Objects gibt es nur, wo sie Fehler verhindern. Repositories, MediatR und CQRS habe ich bewusst weggelassen: Für eine Handvoll Entities wären sie kein Gewinn.
Ein Profil ändert die Reihenfolge, nie den Inhalt
Bewerbungs-Links ordnen die Abschnitte für Recruiting oder Tech Lead um, blenden aber nichts ein oder aus. Ich verzichte auf Zuschnitt, damit nichts manipuliert wirkt, wenn zwei Leser vergleichen.
Ich prüfe das PDF als Text
Statt eines Headless-Browsers nutze ich QuestPDF, und meine Tests vergleichen den Text, den ein Bewerbermanagementsystem aus dem PDF liest. Das ist auf jedem Rechner stabil, prüft aber nicht das Layout.

Ergebnis

Das Repository ist öffentlich und hat über 90 % Testabdeckung in der API. Für jede Bewerbung erzeuge ich einen eigenen Link mit passender Reihenfolge, sehe, wann er geöffnet wird, und werde montags an offene Bewerbungen erinnert. Lighthouse: Barrierefreiheit und SEO 100.