← Blog

Unsere Website und unsere App waren zwei Produkte. Jetzt sind sie eins.

Man konnte an unserem Produkt am unteren Seitenrand ablesen, wo man sich gerade befand. Die Marketing-Site hatte einen fünfspaltigen Footer; das Dashboard eine einzelne Linkzeile. Verschiedene Navigationsleisten, verschiedene Abstände, stellenweise verschiedene Schriften. Divergenz wie von zwei Teams, produziert von einer Person — weil es zwei Codebasen waren.

Sie sind jetzt eine einzige Aurelia-Anwendung. Startseite, Katalog, Blog, diese Wissensdatenbank und das Dashboard sind Routen derselben App und teilen sich Navigationsleiste, Footer, Design-Tokens und Komponenten.

Der Teil, der wirklich schwer ist

Eine Single-Page-App ist für alles unsichtbar, was kein JavaScript ausführt. Eine Marketing-Site in eine solche zu verschieben ist ein guter Weg, jedes Suchmaschinen-Ranking zu verlieren, das man hat.

Also prerendert der Build. Er startet die echte Anwendung in einem Headless-DOM, navigiert zu jeder öffentlichen Route, wartet, bis sie zur Ruhe kommt, und schreibt das entstandene Dokument. Ein Crawler bekommt vollständiges HTML — Titel, Beschreibung, Canonical, strukturierte Daten — erzeugt von demselben Code, den der Browser ausführt. Es gibt keine zweite Implementierung der Seiten, die auseinanderdriften könnte.

Der Prerenderer weigert sich außerdem, schlechte Seiten auszuliefern. Ein fehlender Titel, eine Beschreibung unter vierzig Zeichen oder etwas anderes als genau ein <h1> lässt den Build scheitern. Diese Prüfung fing vier Rechtsseiten mit je zwei Überschriften — binnen einer Minute, nachdem sie geschrieben war.

Zwei Bugs, die wir durch Nachdenken nie gefunden hätten

Die kanonische URL leitete um. Seiten als models/index.html zu schreiben brachte nginx dazu, /models per 301 auf /models/ umzuleiten — jede URL in unserer Sitemap prallte also erst einmal ab, bevor sie antwortete. Seiten werden jetzt als models.html geschrieben und über try_files $uri $uri.html ausgeliefert, unter exakt der Adresse, die wir bewerben.

Der Fallback lieferte die falsche Seite. Rein clientseitige Routen fielen auf index.html zurück — was nach dem Prerendern die Startseite ist. /login zeigte also den Marketing-Hero, bis der Router übernahm, und ein Crawler auf einer privaten URL hätte darunter die Startseite indexiert. Der Fallback ist jetzt eine neutrale Hülle.

Beide Bugs wurden gefunden, indem wir die Seiten anforderten und lasen, was zurückkam. Keiner von beiden wäre in einem grünen Build aufgetaucht.

Was Sie davon haben

Ein Design, ehrlich gemeint. Der Theme-Umschalter zog in den Footer, wo eine Präferenz hingehört; der Sprachumschalter nahm seinen Platz in der Leiste ein. Jede Datentabelle klappt auf dem Telefon zu lesbaren Karten zusammen, statt seitwärts zu scrollen. Und jede Seite — Marketing wie Produkt — erbt eine Korrektur in dem Moment, in dem wir sie machen, weil es nur noch eine Komponente zu korrigieren gibt.

Wie das ganze System funktioniert · Wissensdatenbank · Unseren Code lesen

Read next

a GPU died mid-sentence. the sentence finished.

Consumer hardware disappears without notice — someone sits down at their PC, a breaker trips, Wi-Fi drops. Our streams now survive that: another machine picks the answer up mid-word, and the dead segment is billed to nobody.

one model, every card: how a 27b runs on 8 gb

Qwen3.8 27B ships in five builds, from Q5_K_M on a 24 GB card down to IQ2_XXS on 8 GB. Why we split models from builds, what quantization actually costs, and how a quantized KV cache doubled our context overnight.

Model catalogue · Knowledge base · Run a GPU