# Feedback von Beta-Testern Build für Build sammeln

> Ein Beta-Feedback-Formular mit einem persönlichen Link pro Tester und Build, zwei Bewertungsskalen und einer Wochenübersicht, die zeigt, ob jeder Build besser wurde.

Veröffentlicht am 2026-10-25 · https://ikiform.com/de/guides/feedback-form/beta-tester-feedback

Gib jedem Tester einen persönlichen Link mit einem undurchsichtigen Tester-Code und der Build-Nummer, damit das Formular weiß, wer und welcher Build, ohne zu fragen. Frag, wofür der Build genutzt wurde, zwei Bewertungen von 1 bis 5 für Stabilität und Nutzen und was die Person aufgehalten hat. Vergleiche dann Builds, nicht einzelne Antworten, um zu sehen, ob jedes Release die Zahlen bewegt hat.

## Was an Beta-Feedback anders ist

Öffentliches Feedback kommt von Fremden, einmal. Beta-Feedback kommt von derselben kleinen Gruppe, immer wieder, zu Software, die sich jede Woche ändert. Eine einzelne Antwort zählt weniger als der Trend: Fühlte sich Build 1.4.0 für dieselben Leute stabiler an als 1.3.0? Dafür braucht es zwei Dinge, die ein normales Feedback-Formular nicht hat: wer geantwortet hat und auf welchem Build.

Tester ihre E-Mail und Versionsnummer eintippen zu lassen, scheitert in der Praxis. Sie vergessen die Version, vertippen sich oder schreiben „neueste“. Also kommt beides in den Link, den du verschickst, und die Seite füllt es aus.

Diagramm: Die wöchentliche Beta-Feedback-Schleife, von einem neuen Build zum nächsten

1. Ein neuer Build geht an die Beta-Gruppe.
2. Ein Skript erstellt pro Tester einen Link mit Code und Build-Nummer, und du verschickst sie per E-Mail.
3. Der Tester öffnet den Link. Die Seite füllt die versteckten Felder für Tester und Build aus und zeigt, nach welchem Build sie fragt.
4. Ein Übersichtsskript liest jede Antwort über die API und bildet die Durchschnitte pro Build.
5. Die Blocker aus den niedrigsten Bewertungen werden zur Fix-Liste für den nächsten Build.

Vergleiche Spalten, nicht Zeilen. Die schlechte Woche eines Testers ist Rauschen; ein Rückgang in der ganzen Gruppe ist eine Regression.

## Die Felder

Formular: Beta feedback

- **Tester-Code** (`tester`, text, Pflichtfeld)
- **Build** (`build`, text, Pflichtfeld)
- **Wofür hast du diesen Build genutzt?** (`tried`, textarea, Pflichtfeld)
- **Wie stabil hat er sich angefühlt?** (`stability`, rating, Pflichtfeld)
- **Wie nützlich war er?** (`usefulness`, rating, Pflichtfeld)
- **Wenn du ihn nicht mehr nutzen könntest, wie würdest du dich fühlen?** (`would_miss`, radio, Pflichtfeld)
- **Hat dich etwas aufgehalten?** (`blockers`, textarea)

`tester` und `build` sind Hidden Inputs, die aus dem Link befüllt werden. Ihre Patterns lehnen alles ab, was nicht aus einer echten Einladung stammt.

| Feld | Warum |
| --- | --- |
| `tester` | Ein zufälliger Code aus 10 Zeichen. Damit verfolgst du eine Person über Builds hinweg, ohne ihre E-Mail in eine URL zu schreiben |
| `build` | Verknüpft jede Bewertung mit einem Release. Das Pattern akzeptiert nur Versionen wie `1.4.0` |
| `tried` | Was sie tatsächlich getan haben. Ohne das sind Bewertungen schwer zu deuten |
| `stability`, `usefulness` | Zwei `rating`-Felder, 1 bis 5. Getrennt, denn ein stabiler Build des Falschen ist immer noch das Falsche |
| `would_miss` | Wie enttäuscht sie ohne ihn wären. Ein wachsender Anteil von „Sehr“ ist das stärkste Zeichen, dass die Beta funktioniert |
| `blockers` | Optional, und hier entsteht die Fix-Liste |

## Einen Link pro Tester erstellen

Führe eine einfache Liste der Tester-E-Mails in `testers.txt`. Dieses Skript gibt jedem beim ersten Auftauchen einen festen Code, speichert die Zuordnung in `testers.json` und schreibt pro Tester einen Einladungslink für den Build, den du angibst. Beginne `testers.json` mit `{}`.

`links.mjs`:

```js
import { randomBytes } from "node:crypto";
import { readFileSync, writeFileSync } from "node:fs";

const BUILD = process.argv[2];
if (!/^\d+\.\d+\.\d+$/.test(BUILD ?? "")) throw new Error("Usage: node links.mjs 1.4.0");

const known = JSON.parse(readFileSync("testers.json", "utf8"));
const emails = readFileSync("testers.txt", "utf8").split(/\r?\n/).map((line) => line.trim().toLowerCase()).filter(Boolean);

for (const email of emails) {
  known[email] ??= randomBytes(5).toString("hex");
}
writeFileSync("testers.json", JSON.stringify(known, null, 2));

const rows = emails.map((email) => {
  const link = new URL("https://example.com/beta/feedback");
  link.searchParams.set("t", known[email]);
  link.searchParams.set("build", BUILD);
  return `${email},${link}`;
});
writeFileSync(`invites-${BUILD}.csv`, ["email,link", ...rows].join("\n"));
console.log(`${rows.length} links for build ${BUILD}`);
```

Verschick die CSV über die Serienbrief-Funktion deines E-Mail-Tools oder füge die Links bei einer kleinen Gruppe von Hand ein. Halte `testers.json` privat: Nur dort lässt sich ein Code einer Person zuordnen.

## Die Feedback-Seite

Setze das auf eine Seite unter `/beta/feedback` deiner Site. Sie liest Code und Build aus dem Link, zeigt das Formular ohne sie nicht an und sendet auf dem normalen Weg. Setze **Redirect after submit** auf eine Dankeseite. Das funktioniert auf jedem statischen Host oder Baukasten, der ein Skript erlaubt.

`beta/feedback.html`:

```html
<p id="beta-missing" hidden>This page needs the link from your invite email.</p>

<form id="beta" action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post" hidden>
  <h2>Feedback for build <span id="beta-build"></span></h2>
  <input type="hidden" name="tester" />
  <input type="hidden" name="build" />
  <label for="tried">What did you use this build for?</label>
  <textarea id="tried" name="tried" rows="4" required minlength="15"></textarea>
  <fieldset>
    <legend>How stable did it feel?</legend>
    <label><input type="radio" name="stability" value="1" required /> 1, it crashed a lot</label>
    <label><input type="radio" name="stability" value="2" /> 2</label>
    <label><input type="radio" name="stability" value="3" /> 3</label>
    <label><input type="radio" name="stability" value="4" /> 4</label>
    <label><input type="radio" name="stability" value="5" /> 5, no problems</label>
  </fieldset>
  <fieldset>
    <legend>How useful was it?</legend>
    <label><input type="radio" name="usefulness" value="1" required /> 1, not at all</label>
    <label><input type="radio" name="usefulness" value="2" /> 2</label>
    <label><input type="radio" name="usefulness" value="3" /> 3</label>
    <label><input type="radio" name="usefulness" value="4" /> 4</label>
    <label><input type="radio" name="usefulness" value="5" /> 5, I rely on it</label>
  </fieldset>
  <fieldset>
    <legend>If you could no longer use it, how would you feel?</legend>
    <label><input type="radio" name="would_miss" value="very" required /> Very disappointed</label>
    <label><input type="radio" name="would_miss" value="somewhat" /> Somewhat disappointed</label>
    <label><input type="radio" name="would_miss" value="not" /> Not disappointed</label>
  </fieldset>
  <label for="blockers">Anything that stopped you?</label>
  <textarea id="blockers" name="blockers" rows="3"></textarea>
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button>Send this week's feedback</button>
</form>

<script>
  const params = new URLSearchParams(location.search);
  const tester = params.get("t") ?? "";
  const build = params.get("build") ?? "";
  const form = document.getElementById("beta");
  if (/^[a-z0-9]{10}$/.test(tester) && /^\d+\.\d+\.\d+$/.test(build)) {
    form.elements.tester.value = tester;
    form.elements.build.value = build;
    document.getElementById("beta-build").textContent = build;
    form.hidden = false;
  } else {
    document.getElementById("beta-missing").hidden = false;
  }
</script>
```

> Wer den Link eines Testers hat, kann als dieser Tester antworten. Für eine Beta ist das vertretbar: Die Codes sind zufällig, die Gruppe ist klein, und eine merkwürdige Antwort fällt auf. Nutze den Code nicht, um Zugriff auf irgendetwas zu gewähren.

## Builds vergleichen

Das Dashboard zeigt einzelne Antworten. Um Builds zu vergleichen, lies sie alle mit dem SDK und gruppiere nach `build`. Führe das mit einem Secret Key in `IKIFORM_SECRET_KEY` aus:

`summary.mjs`:

```js
import { createClient } from "@ikiform/sdk";

const ikiform = createClient({ apiKey: process.env.IKIFORM_SECRET_KEY });
const builds = new Map();

for await (const submission of ikiform.submissions.iterate({ formId: "frm_xxxxxxxxxxxxxxxx", status: "received" })) {
  const { build, tester, stability, usefulness, would_miss } = submission.fields;
  const row = builds.get(build) ?? { testers: new Set(), stability: 0, usefulness: 0, very: 0, answers: 0 };
  row.testers.add(tester);
  row.stability += stability;
  row.usefulness += usefulness;
  row.very += would_miss === "very" ? 1 : 0;
  row.answers += 1;
  builds.set(build, row);
}

console.table(
  [...builds].map(([build, row]) => ({
    build,
    testers: row.testers.size,
    stability: (row.stability / row.answers).toFixed(1),
    usefulness: (row.usefulness / row.answers).toFixed(1),
    "very disappointed": `${Math.round((row.very / row.answers) * 100)}%`,
  })),
);
```

Bewertungen kommen als Zahlen an, also brauchen die Durchschnitte kein Parsing. Zähle neben jedem Durchschnitt die Tester: Ein Build mit drei Antworten lässt sich nicht fair mit einem mit dreißig vergleichen.

## Prüfen, ob es funktioniert

1. Führe `node links.mjs 1.4.0` aus und öffne einen der Links. Das Formular zeigt „Feedback for build 1.4.0“.
2. Öffne die Seite ohne Query-String. Nur der Hinweis auf die Einladung erscheint.
3. Sende eine Antwort, ändere dann `build` in den Entwicklertools des Browsers auf `latest` und sende erneut. Ikiform antwortet mit `422` und „Use a version like 1.4.0“.
4. Führe `summary.mjs` aus. Die Tabelle zeigt Build 1.4.0 mit einem Tester und deinen zwei Bewertungen.

## Stolperfallen

- Beta-Gruppen testen oft aus einem Büro, das sich eine IP teilt. Das Limit Standard zählt sie zusammen, also wähle für dieses Formular Relaxed, 20 pro Minute pro Besucher.
- Mobile Betas über TestFlight lassen Tester bereits Screenshot-Feedback an App Store Connect senden. Nutze das für Bugs auf Bildschirmebene und dieses Formular für die wöchentlichen Fragen.
- Stell jede Woche dieselben Fragen. Eine geänderte Formulierung macht den Vergleich zwischen Builds kaputt.
- Tester antworten im Laufe der Beta seltener. Halte das Formular bei ein paar Minuten und danke den Leuten namentlich in den Release Notes.
- Ikiform führt wiederholte Antworten nicht zusammen. Antwortet ein Tester für einen Build zweimal, behalte in deiner Übersicht die spätere.

## Fehlerbehebung

| Problem | Was zu tun ist |
| --- | --- |
| `422` mit „Open the link from your invite email“ | Der Wert von `tester` passt nicht zum Pattern, meist weil der Link in einem E-Mail-Client abgeschnitten wurde. Schick den Link erneut. |
| `429 rate_limited` | Ein Besucher hat mehr gesendet, als das Rate-Limit des Formulars erlaubt. Standard erlaubt 5 pro Minute und Besucher. Warte `Retry-After` ab oder wähle Relaxed oder Custom für Formulare, die von einer gemeinsamen IP aus senden. |
| `403 origin_not_allowed`, oder jeder Post landet im Spam | Die Domain der Seite steht nicht unter **Allowed domains** des Formulars. Füge sie hinzu, plus `localhost` für lokale Tests. Subdomains einer eingetragenen Domain sind erlaubt. |

## Fragen

### Woher weiß ich, welcher Beta-Tester Feedback gesendet hat?

Gib jedem Tester einen zufälligen Code und setze ihn in seinen persönlichen Link. Die Seite kopiert ihn in ein Hidden Field, und nur deine private Zuordnungsdatei verbindet den Code mit einer E-Mail.

### Wie verfolge ich Feedback pro Build?

Setze auch die Build-Nummer in den Link, validiere sie mit einem Pattern und gruppiere Antworten nach Build. submissions.iterate aus dem SDK liest jede Antwort für eine Übersicht.

### Welche Fragen sollte ein Beta-Feedback-Formular stellen?

Wofür der Build genutzt wurde, wie stabil und wie nützlich er sich auf einer Skala von 1 bis 5 angefühlt hat, wie enttäuscht die Person ohne ihn wäre und was sie aufgehalten hat. Stell jede Woche dieselben Fragen.

### Sollten Beta-Tester Bugs im selben Formular melden?

Führe für alles, was reproduziert werden muss, ein separates Bug-Report-Formular. Das wöchentliche Formular dreht sich darum, wie sich der Build insgesamt anfühlt; beides zu mischen macht beides schwerer lesbar.

## Weitere Anleitungen

- [Feedback-Formular für eine Website oder App](https://ikiform.com/de/guides/feedback-form.md): Setze ein Feedback-Formular auf jede Seite oder in deine App: eine Bewertung, ein Kommentar, die Herkunftsseite, Spamfilter und Alerts bei niedrigen Werten.
- [Formular für Bug-Reports](https://ikiform.com/de/guides/feedback-form/bug-report.md): Ein Bug-Report-Formular, das nach Schritten und Schweregrad fragt und URL, Browser, Viewport und letzte Fehler von der Seite selbst ergänzen lässt.
- [NPS-Umfrage](https://ikiform.com/de/guides/feedback-form/nps-survey.md): Führe eine NPS-Umfrage mit einem Wert von 0 bis 10 in der E-Mail durch, mit einer Folgefrage auf deiner eigenen Seite, die sich mit dem Wert ändert, und einem Skript, das den NPS berechnet.
- [Formular für Feature-Wünsche](https://ikiform.com/de/guides/feedback-form/feature-request.md): Ein Formular für Feature-Wünsche, das nach dem Problem statt nach der Lösung fragt, Wünsche nach Thema speichert, Personen statt Zeilen zählt und sie beim Release anschreibt.
- [Feedback-Widget für React](https://ikiform.com/de/guides/feedback-form/react-feedback-widget.md): Ein schwebender Feedback-Button für eine React-App, der ein kleines Panel öffnet, Ansicht, App-Version und Konto mit jeder Antwort sendet und nie einen Entwurf verliert.
- [Kündigungsumfrage](https://ikiform.com/de/guides/feedback-form/cancellation-survey.md): Frag im Kündigungsablauf deines SaaS, warum Kunden kündigen, halte Überspringen einen Klick entfernt, speichere den Grund in Stripe und Ikiform und verzögere die Kündigung nie.
