Feedback-Formular für eine Website oder App

Feedback-Formular für eine Website oder App6 Min. Lesezeit

Bug-Report-Formular hinzufügen, das Seite, Browser und Schritte erfasst

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.

Preet SutharGründer, Ikiform

Frag die meldende Person nur nach dem, was sie weiß: was schiefging, was sie davor getan hat und wie sehr es sie blockiert. Lass die Seite den Rest beim Absenden ergänzen: die Seiten-URL, den User-Agent des Browsers, den Viewport und die letzten JavaScript-Fehler. Sende alles an ein Formular-Backend und leite jeden Bericht per Webhook an deinen Issue-Tracker weiter.

Was an einem Bug-Report anders ist

Ein Bug-Report ist nur nützlich, wenn du den Fehler reproduzieren kannst, und Meldende kennen die Details, die du brauchst, selten. Fragst du jemanden nach dem Browser, bekommst du „Chrome“ oder „der normale“. Fragst du nach der URL, bekommst du den Namen der Seite. Teile das Formular also in zwei Teile: Fragen, die nur die meldende Person beantworten kann, und Kontext, den die Seite schon kennt.

Welche Teile eines Bug-Reports die Person eintippt und welche die Seite ergänztdie Person tipptdie Seite ergänzt beim AbsendenZusammenfassungeine Zeile, 8bis 140ZeichenSchrittewas direktdavor geschahSchweregradblocker, majoroder minorLink zumScreenshotoptional, eineURL, keineDateipage_urlOrigin undPfad, ohneQueryuser_agentBrowser und OSViewportGröße undPixel Ratiorecent_errorsletzte fünf,ohne HostsWelche Teile eines Bug-Reports die Person eintippt und welche die Seite ergänztdie Person tipptdie Seite ergänzt beim AbsendenZusammenfassungeine Zeile, 8 bis 140 ZeichenSchrittewas direkt davor geschahSchweregradblocker, major oder minorLink zum Screenshotoptional, eine URL, keine Dateipage_urlOrigin und Pfad, ohne Queryuser_agentBrowser und OSViewportGröße und Pixel Ratiorecent_errorsletzte fünf, ohne Hosts
  1. Die meldende Person schreibt eine einzeilige Zusammenfassung.
  2. Sie beschreibt, was sie direkt vor dem Fehler getan hat.
  3. Sie wählt, wie sehr es sie blockiert.
  4. Sie kann einen Link zu einem Screenshot oder einer Aufnahme einfügen.
  5. Beim Absenden ergänzt die Seite ihre eigene Adresse ohne Query-String.
  6. Sie ergänzt den User-Agent des Browsers.
  7. Sie ergänzt Fenstergröße und Pixel Ratio.
  8. Sie ergänzt die letzten JavaScript-Fehler, die sie gesehen hat, ohne Hostnamen.
Die obere Reihe ist alles, was die meldende Person sieht. Die untere Reihe sind Hidden Inputs, die im Moment des Klicks auf Senden befüllt werden.

Drei Einschränkungen prägen den Code:

  • Der Referrer ist nicht die Seite. Browser senden bei einem Cross-Site-Post standardmäßig nur deinen Origin, also verrät dir meta.referrer den Pfad nicht. Sende page_url selbst.
  • Ein Server-Post verbirgt den Browser. Ikiform speichert den User-Agent dessen, der den Request stellt. Bei einer Server Action ist das dein Server. Leite den user-agent-Header des Besuchers als Feld weiter.
  • Stacktraces sind voller Links. Jeder Frame hat eine Skript-URL, und drei oder mehr Links erhöhen den Spam-Score um 2. Entferne die Hosts, bevor du Fehler sendest, sonst kann ein Bericht mit einem eingefügten Screenshot-Link in den Spam kippen.

Die Felder

Vorschau

Führt den Validator von Ikiform in deinem Browser aus. Es wird nichts gesendet.

Wie sehr blockiert es dich?
Die letzten fünf Felder sind im echten Formular Hidden Inputs. Sind sie definiert, prüft Ikiform ihre Länge und ihren Typ wie bei jedem anderen Feld.

Der Schweregrad ist als Auswirkung auf die meldende Person formuliert, nicht als Prioritätslabel. Leute wählen „Ich komme nicht weiter“ treffend; „Kritisch“ wählen sie für alles. Die Schritte haben ein Minimum von 20 Zeichen, was aus „geht nicht“ einen Satz macht, mit dem du arbeiten kannst.

Den Kontext erfassen

Der Browser füllt die Hidden Fields im submit-Handler, nachdem die Person fertig getippt hat, sodass die Fehlerliste alles enthält, was schiefging, während sie den Bericht schrieb.

Das Formular sendet auf dem normalen Weg; das Skript füllt nur während submit die Hidden Inputs, bevor der Browser den Request baut. Setze data-app-version beim Build auf das <html>-Element. Der Browser sendet direkt, also stimmt Ikiforms eigenes meta.user_agent mit dem Feld user_agent überein.

report-bug.html
<form id="bug" action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
  <label for="summary">What went wrong?</label>
  <input id="summary" name="summary" required minlength="8" maxlength="140" />
  <label for="steps">What did you do right before?</label>
  <textarea id="steps" name="steps" rows="5" required minlength="20"></textarea>
  <label for="expected">What did you expect to happen?</label>
  <textarea id="expected" name="expected" rows="3"></textarea>
  <fieldset>
    <legend>How much does it block you?</legend>
    <label><input type="radio" name="severity" value="blocker" required /> I can't continue</label>
    <label><input type="radio" name="severity" value="major" /> I found a workaround</label>
    <label><input type="radio" name="severity" value="minor" /> It's cosmetic</label>
  </fieldset>
  <label for="screenshot_url">Link to a screenshot or recording</label>
  <input id="screenshot_url" name="screenshot_url" type="url" />
  <label for="email">Email for follow-up questions</label>
  <input id="email" name="email" type="email" />
  <input type="hidden" name="page_url" />
  <input type="hidden" name="user_agent" />
  <input type="hidden" name="viewport" />
  <input type="hidden" name="app_version" />
  <input type="hidden" name="recent_errors" />
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button>Send report</button>
</form>

<script>
  const recent = [];
  function remember(text) {
    recent.push(String(text).replace(/https?:\/\/[^/\s]+/g, "").slice(0, 400));
    if (recent.length > 5) recent.shift();
  }
  window.addEventListener("error", (event) => remember(`${event.message} at ${event.filename}:${event.lineno}`));
  window.addEventListener("unhandledrejection", (event) => remember(`Unhandled: ${event.reason?.stack ?? event.reason}`));

  document.getElementById("bug").addEventListener("submit", (event) => {
    const fields = event.currentTarget.elements;
    fields.page_url.value = location.origin + location.pathname;
    fields.user_agent.value = navigator.userAgent;
    fields.viewport.value = `${innerWidth}x${innerHeight} @${devicePixelRatio}x`;
    fields.app_version.value = document.documentElement.dataset.appVersion ?? "";
    fields.recent_errors.value = recent.join("\n---\n");
  });
</script>
Die serverseitigen Versionen senden mit einem Secret Key, daher überspringt Ikiform Allowed domains und seine Menschlichkeitsprüfung. Jeder Post kommt dann von der IP deines Servers, und das Standard-Limit von 5 pro Minute und Besucher zählt alle deine Besucher zusammen. Wähle für ein Formular, das du über einen Server sendest, das Rate-Limit Relaxed oder Custom, und halte IKIFORM_SECRET_KEY in reinen Server-Umgebungsvariablen.

Die Erfolgsmeldung zeigt die ID der Einsendung. Schreibt die meldende Person später an den Support, findet diese ID den Bericht mit einer Suche. Die Bug-Report-Vorlage hat eine kürzere Version ohne den erfassten Kontext.

In deinen Issue-Tracker

Berichte gehören dorthin, wo die Behebung verfolgt wird. Füge einen Webhook hinzu, der auf ein Relay zeigt, das ein Issue anlegt: summary als Titel, Schritte und Kontext als Body, severity als Label. Die Anleitung zu Linear und GitHub Issues enthält dieses Relay, mit Signaturprüfung und Wiederholungen. Die Webhook-Doku listet jeden meta-Wert auf, den der Payload enthält. Setze die erfassten Felder in einen eingeklappten Block am Ende des Issue-Bodys, damit die Schritte lesbar bleiben.

FeldIm Issue
summaryTitel
steps, expectedBody, zuerst
severityEin Label, damit sich Blocker filtern lassen
page_url, user_agent, viewport, app_versionEin details-Block unter den Schritten
recent_errorsEin Codeblock, zuletzt

Prüfen, ob es funktioniert

  1. Öffne die Seite in einem Desktop-Browser, führe undefinedFunction() in der Konsole aus und sende dann einen Bericht.
  2. Öffne die Einsendung im Dashboard. recent_errors enthält den ReferenceError ohne https://-Host.
  3. user_agent zeigt auch in den Server-Versionen deinen Browser, nicht node.
  4. page_url enthält den Pfad, aber nicht den Query-String.
  5. Sende einen Bericht mit 10 Zeichen in steps. Ikiform antwortet mit 422 und nennt steps, und das Formular zeigt die Meldung.

Stolperfallen

  • Datei-Uploads werden noch nicht unterstützt. Ein Post mit einer Datei bekommt 422 files_not_supported. Bitte um einen Link zu einem Screenshot oder lade das Bild zuerst in deinen eigenen Speicher hoch und sende seine URL.
  • Jeder Wert kann bis zu 16 KB groß sein. Fünf auf 400 Zeichen gekürzte Fehler bleiben weit darunter; ein kompletter Konsolen-Dump nicht.
  • Chromium-Browser frieren Teile des User-Agent-Strings ein, etwa die macOS-Version, die immer 10_15_7 lautet. Nutze bei der Triage Browsername und Hauptversion, nicht die OS-Version.
  • Fehler aus Skripten von einem anderen Origin zeigen nur Script error., außer das Skript wird mit CORS-Headern ausgeliefert und mit crossorigin geladen.
  • Erfasse keine Formularinhalte, keinen Local Storage und keine Cookies im Bericht. Sie können Passwörter und Tokens enthalten, und du würdest sie im Klartext speichern.

Fehlerbehebung

ProblemWas zu tun ist
422 validation_failedDer Post passt nicht zur Felddefinition des Formulars. error.fields nennt jedes Feld und den Grund. Gleiche den name des Eingabefelds an die Feld-ID an.
422 files_not_supportedDas Formular hat ein <input type="file"> mit ausgewählter Datei. Datei-Uploads werden noch nicht unterstützt; entferne das Eingabefeld oder bitte stattdessen um einen Link.
Berichte mit Screenshot-Links landen im SpamZähle die Links über alle Felder. Drei oder mehr erhöhen den Score; mit einer Wiederholung desselben Berichts erreicht er den Schwellenwert. Entferne Hosts aus den Fehlern und markiere den Bericht als kein Spam.
403 origin_not_allowed, oder jeder Post landet im SpamDie 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

Hängst du bei einem Schritt fest oder willst eine Anleitung für deinen Fall? Schreib an hello@ikiform.com.