Eine zweisprachige Vereinsplattform, in der kein Fakt zweimal steht — und ein Test, der bei jedem Commit prüft, ob die Angaben noch gelten.
Eine Vereinswebsite ist selten technisch schwierig. Schwierig ist, dass sie drei Dinge gleichzeitig sein muss: ein Rechtsdokument, eine Förderakte und eine Vertriebsunterlage. Dieselbe Seite bedient vier Gruppen mit gegensätzlichen Erwartungen — wer Mitglied werden will, soll in fünf Sekunden verstehen, worum es geht; ein möglicher Sponsor sucht belastbare Angaben; eine Förderstelle sucht Statuten, Registernummer und Vorstand in höchstens zwei Klicks; und der ehrenamtliche Vorstand, der die Seite pflegt, braucht vor allem, dass nichts kaputtgehen kann. Der entscheidende Punkt: Sponsor und Förderstelle prüfen dieselben Angaben, und beide prüfen sie gegen andere Quellen. Daraus folgt die zentrale Entwurfsentscheidung — jeder Fakt darf nur an einer einzigen Stelle stehen.
Dieselbe Website muss vier Gruppen bedienen. Wer Mitglied werden will, soll in fünf Sekunden verstehen, worum es geht. Ein Unternehmen, das Sponsor werden könnte, sucht Seriosität und belastbare Angaben. Eine Förderstelle sucht Statuten, Vereinsregisternummer und Vorstand — in höchstens zwei Klicks. Und der ehrenamtliche Vorstand, der die Seite pflegt, braucht vor allem eines: dass nichts kaputtgehen kann.
Der interessante Teil: Sponsor und Förderstelle prüfen dieselben Angaben, und beide prüfen sie gegen andere Quellen. Daraus folgt die zentrale Entwurfsentscheidung des Projekts — jeder Fakt darf nur an einer einzigen Stelle stehen.
Die Anschrift des Vereins stand über Monate auf sechs Flächen: Fußzeile, Impressum, Kontaktseite, strukturierte Daten, E-Mail-Fußzeilen, Statuten-Zusammenfassung. Sie war formal korrekt aus dem Vereinsregister übernommen — und trotzdem ein Datenschutzproblem, weil es die Privatwohnung eines Vorstandsmitglieds war, weltweit indexiert und maschinenlesbar.
Der schnelle Weg wäre gewesen, sie an sechs Stellen zu ersetzen. Der handwerkliche Weg war, die Struktur so zu ändern, dass es sechs Stellen nicht mehr geben kann. Die Anschrift steht seither in genau einem Code-Literal und im CMS.
Die Datenmigration, die die alte Adresse aus der Produktionsdatenbank entfernt, hat bewusst keinen Rollback. Der Kommentar im Code sagt, warum: Einen Rechtstext zurückzurollen würde die Privatadresse wieder veröffentlichen.
Übliche Tests prüfen, ob Software funktioniert. Dieser prüft, ob sie die Wahrheit sagt.
Bei jedem Commit durchsucht er das gesamte Projekt — Code, Konfiguration, Dokumentation, öffentliche Dateien — nach Angaben, die nicht mehr gelten dürfen: zurückgezogene Kennzahlen, eine Privatanschrift, stillgelegte E-Mail-Domains, überholte Programmtermine. Zusätzlich prüft er gegen die laufende Datenbank, dass die beiden Postfächer des Vereins nicht zu einem zusammenfallen und dass jedes Formular auf das für es zuständige verweist.
Der Kommentar im Kopf der Datei benennt den Anlass ohne Beschönigung: Jeder dieser Fakten war irgendwann falsch veröffentlicht, und jeder wurde auf dieselbe Weise falsch — der Wert stand an mehr als einer Stelle, und nur eine wurde aktualisiert.
Bei einem Verein, dessen Website Förderanträge und Sponsorenprüfungen trägt, ist das die relevantere Frage.
Auf der Startseite standen drei Kennzahlen: Veranstaltungen, Teilnehmende, ehrenamtliche Stunden. Bei der Prüfung stellte sich heraus, dass für keine der drei eine Quelle existierte. Gleichzeitig lag die Seite Unternehmen vor, die gerade über eine Partnerschaft entschieden.
Wir haben nicht empfohlen, die Zahlen vorsichtiger zu formulieren. Wir haben empfohlen, sie zu entfernen.
Einen Tag später benannte der Vorstand die Quelle: eigene Vereinsaufzeichnungen, gültig zum Jahresende 2025. Die Zahlen sind seither wieder freigegeben und stehen wieder auf der Seite — aber der Stichtag ist jetzt Teil der Zahl, nicht eine Fußnote daneben. Die höhere, aktuellere Schätzung bleibt ungedruckt, bis das Veranstaltungsregister gefüllt und beschlossen ist.
Technisch ist der Stichtag keine Redaktionsdisziplin, sondern eine Bedingung: Fehlt er, liefert die Funktion, die den Block rendert, gar nichts zurück. Den Stichtag zu löschen ist damit der Weg, die Zahlen zurückzuziehen. Es gibt keine Feldkombination, die eine nackte Zahl veröffentlicht.
Das ist der Punkt. Eine Zahl zu löschen ist eine Geste. Eine Zahl zu löschen, eine Quelle zu verlangen, sie zu bekommen und die Zahl mit Stichtag zurückzustellen, ist ein Verfahren — und ein Verfahren kann ein Kunde übernehmen.
Der Test war dreimal grün, während die ausgelieferte Seite falsch war.
Beim ersten Mal durchsuchte er alle Dateien im Projekt. Eine Datenbankspalte ist keine Datei — die Privatanschrift stand zwei Tage länger auf der Live-Seite, als sie im Code existierte. Beim zweiten Mal las er zusätzlich die redaktionellen Grundtexte. Eine Projektseite ist kein Grundtext — dort stand ein Postfach, das es nie gegeben hat. Beim dritten Mal las er auch Projekte, Termine und Stimmen. Eine Liste hatte einen Eintrag zu wenig — sechs Kennzahlen ohne Quelle standen drei Monate auf genau der Seite, die eine Förderstelle öffnet.
Jedes Mal war die Prüfung genau dort gründlich, wo beim Schreiben jemand hingedacht hatte.
Die Konsequenz aus dem dritten Mal war nicht, die Liste zu ergänzen. Es gibt jetzt genau eine Stelle im System, an der eine Vereinszahl stehen kann, und sie kann ohne ihren Stichtag nichts anzeigen. Das zweite Feld wurde nicht abgesichert, sondern gelöscht. Und der Test prüft die Abwesenheit dieses Feldes strukturell statt textlich — ein Textmuster erwischt ein leeres Zahlenfeld erst, wenn schon jemand Zahlen hineingeschrieben hat.
Das ist die ehrliche Fassung: Der Anspruch stimmt nicht, weil jemand von Anfang an klug war. Er stimmt, weil dreimal derselbe Fehler passiert ist und beim dritten Mal die Struktur geändert wurde statt der Regel.
Ein Test erwischt, was schon einmal falsch war. Er erwischt nicht, was noch nie jemand hinterfragt hat.
Zwei Sätze standen monatelang auf der Seite, beide aus der ersten Befüllung der Datenbank, beide nie gegen eine Quelle geprüft. Der eine beschrieb eine Tätigkeit, die es so nicht gab. Der andere berührte eine rechtliche Frage, die der Verein gar nicht beantworten konnte. Niemand hatte sie je für prüfbedürftig gehalten — der eine klang selbstverständlich, der andere nach Formalie.
Aufgefallen sind sie, weil wir vor einer Textänderung gelesen haben, was dort eigentlich steht, und beim Vorstand nachgefragt haben. Beide sind seither weg.
Dafür gibt es kein Werkzeug. Es gibt nur die Gewohnheit, den Kunden nach der Quelle zu fragen — und die Bereitschaft, den Satz zu streichen, wenn keine kommt.
Eine Reihe von Angaben rund um das Programm ist im Vorstand noch nicht entschieden. Für keine davon gibt es ein CMS-Feld. Sie sind nicht „noch leer", sie sind nicht vorgesehen. Ergänzt wird, wenn entschieden ist — und dann zusammen mit dem Feld.
Das ist die unbequemere Variante. Ein leeres Feld ist bequem: Es sieht nach Vollständigkeit aus und wartet. Es lädt aber auch dazu ein, mit einem Platzhalter gefüllt zu werden, und ein Platzhalter auf einer Seite, die Förderstellen lesen, ist eine Angabe ohne Beschluss.
Genauso bei Partnern: Ein Partner erscheint erst öffentlich, wenn im CMS die schriftliche Freigabe vermerkt ist. Ein Verhandlungsstand kann technisch nicht versehentlich publik werden.
Die Workshop-Tage stehen auf der Seite als „Fr., 23. + Sa., 24. Oktober 2026". Nicht als „23.–24. Oktober". Ein Gedankenstrich liest sich als durchgehende Spanne und verspricht damit ein Wochenende, das es nicht gibt — die beiden Tage sind zwei getrennte Termine mit zwei Anmeldungen.
Solche Entscheidungen schreibt niemand in ein Briefing. Sie fallen auf, wenn dieselbe Person den Text liest und die Seite baut.
Ein zweites Beispiel derselben Art: Payload speichert Datumsangaben in UTC. Auf einem Server, der in UTC läuft, würde ein Wiener Beginn um 18:00 als „16:00" erscheinen — und zwar korrekt gespeichert und trotzdem falsch angezeigt. Die Anzeigezone ist deshalb fest auf Europa/Wien verdrahtet, Sommerzeit inklusive.
Eine Codebasis, ein Deployment, eine Datenbank. Payload läuft innerhalb der Next.js-Anwendung, nicht daneben. Für einen Verein, der keinen zweiten Dienstleister für ein Backend betreiben will, ist das der entscheidende Unterschied.
Termine, Bilder, Partner, Rechtstexte, Logo und Sponsoring-PDF sind CMS-Felder. Änderungen brauchen kein Deployment und keinen Anruf bei uns.
Jede Datenmigration prüft vorher, ob der gespeicherte Wert noch der bekannte alte ist. Hat der Vorstand im Verwaltungsbereich selbst eingegriffen, bleibt sein Wert unangetastet und es wird protokolliert. Beim Produktionsdeploy Ende Juli trat genau dieser Fall ein: Ein Schritt korrigierte, ein zweiter fand die bereits vom Vorstand vorgenommene Korrektur und ließ sie stehen.
Kein Baukasten, kein Framework-Zoo, keine proprietäre Plattform: gängige Technologien, ein reproduzierbares Deployment, ein versioniertes Betriebs-Runbook mit Fehlerkatalog, eine laufende Aufgabenliste — samt der Einträge, die wir dem Verein ausdrücklich abgeraten haben. Was ein Nachfolger wissen muss, steht geschrieben und nicht in unseren Köpfen.
Wesentliche Entscheidungen, ihre naheliegenden Alternativen und die Gründe dahinter.
Die Anschrift des Vereins stand über Monate auf sechs Flächen — Fußzeile, Impressum, Kontaktseite, strukturierte Daten, E-Mail-Fußzeilen, Statuten-Zusammenfassung. Formal korrekt aus dem Vereinsregister übernommen, und trotzdem ein Datenschutzproblem: Es war die Privatwohnung eines Vorstandsmitglieds, weltweit indexiert. Der schnelle Weg wäre gewesen, sie an sechs Stellen zu ersetzen. Der handwerkliche Weg war, die Struktur so zu ändern, dass es sechs Stellen nicht mehr gibt.
Umkehrbarkeit ist die Regel, und hier war sie falsch: Einen Rechtstext zurückzurollen würde die Privatadresse wieder veröffentlichen. Der Kommentar im Code sagt genau das — eine Ausnahme, die begründet dasteht, statt still zu passieren.
Übliche Tests prüfen, ob Software funktioniert. Bei einem Verein, dessen Website Förderanträge und Sponsorenprüfungen trägt, ist die relevantere Frage, ob sie die Wahrheit sagt. Jeder der geprüften Fakten war irgendwann falsch veröffentlicht — und jeder auf dieselbe Weise: Der Wert stand an mehr als einer Stelle, und nur eine wurde aktualisiert.
Ein leeres Feld ist bequem: Es sieht nach Vollständigkeit aus und wartet. Es lädt aber auch dazu ein, mit einem Platzhalter gefüllt zu werden — und ein Platzhalter auf einer Seite, die Förderstellen lesen, ist eine Angabe ohne Beschluss. Ergänzt wird, wenn entschieden ist, und dann zusammen mit dem Feld.
Eine Codebasis, eine Auslieferung, eine Datenbank. Für einen Verein, der keinen zweiten Dienstleister für ein Backend betreiben will, ist das der entscheidende Unterschied.
Der Verein hat sich dagegen entschieden. Nebeneffekt: Weil nichts getrackt wird, braucht die Seite keine Zustimmung abzufragen — ein Hinweis auf die technisch notwendigen Cookies reicht. Der Preis ist, dass es keine Reichweitendaten gibt.
Payload speichert Datumsangaben in UTC. Auf einem Server, der in UTC läuft, erschiene ein Wiener Beginn um 18:00 als „16:00" — korrekt gespeichert und trotzdem falsch angezeigt.
Ein Gedankenstrich liest sich als durchgehende Spanne und verspricht ein Wochenende, das es nicht gibt — es sind zwei getrennte Termine mit zwei Anmeldungen. Solche Entscheidungen schreibt niemand in ein Briefing; sie fallen auf, wenn dieselbe Person den Text liest und die Seite baut.
Live-Abruf gegen www.ikaze-africa.org am 02.08.2026, Repository-Werte zum selben Datum. Kein Lighthouse-Lauf, keine Antwortzeiten, kein Barrierefreiheits-Audit — was nicht gemessen wurde, steht hier nicht. Die Seite entwickelt sich weiter; die Zahlen sind ein Messpunkt mit Datum, kein Dauerzustand.