# Kumpulkan feedback beta tester per build

> Formulir feedback beta dengan tautan pribadi per tester dan build, dua skala rating, dan ringkasan mingguan yang menunjukkan apakah setiap build makin baik.

Terbit 2026-10-25 · https://ikiform.com/id/guides/feedback-form/beta-tester-feedback

Beri setiap tester tautan pribadi yang membawa kode tester yang tidak bisa ditebak dan nomor build, sehingga formulir tahu siapa dan build mana tanpa bertanya. Tanyakan untuk apa mereka memakai build itu, dua rating 1 sampai 5 untuk stabilitas dan kegunaan, serta apa yang menghambat mereka. Lalu bandingkan antar-build, bukan jawaban individual, untuk melihat apakah setiap rilis menggeser angkanya.

## Apa yang berbeda dari feedback beta

Feedback publik datang dari orang asing, sekali saja. Feedback beta datang dari kelompok kecil orang yang sama, berulang kali, tentang software yang berubah setiap minggu. Satu jawaban kurang penting dibanding trennya: apakah build 1.4.0 terasa lebih stabil daripada 1.3.0 bagi orang yang sama? Itu butuh dua hal yang tidak dimiliki formulir feedback biasa: siapa yang menjawab, dan build mana yang mereka pakai.

Meminta tester mengetik email dan nomor versinya gagal dalam praktik. Mereka lupa versinya, salah ketik, atau menulis "terbaru". Jadi keduanya dimasukkan ke tautan yang Anda kirim, dan halaman yang mengisinya.

Diagram: Siklus feedback beta mingguan, dari satu build baru ke build berikutnya

1. Build baru dikirim ke grup beta.
2. Script membuat satu tautan per tester dengan kode mereka dan nomor build, lalu Anda mengirimnya lewat email.
3. Tester membuka tautan. Halaman mengisi field tersembunyi tester dan build, serta menampilkan build mana yang ditanyakan.
4. Script ringkasan membaca setiap jawaban lewat API dan menghitung rata-rata skor per build.
5. Hambatan dari skor terendah menjadi daftar perbaikan untuk build berikutnya.

Bandingkan kolom, bukan baris. Minggu buruk satu tester hanyalah noise; penurunan di seluruh grup adalah regresi.

## Field-nya

Formulir: Beta feedback

- **Kode tester** (`tester`, text, wajib)
- **Build** (`build`, text, wajib)
- **Untuk apa Anda memakai build ini?** (`tried`, textarea, wajib)
- **Seberapa stabil rasanya?** (`stability`, rating, wajib)
- **Seberapa berguna build ini?** (`usefulness`, rating, wajib)
- **Jika Anda tidak bisa memakainya lagi, bagaimana perasaan Anda?** (`would_miss`, radio, wajib)
- **Ada yang menghambat Anda?** (`blockers`, textarea)

`tester` dan `build` adalah input tersembunyi yang diisi dari tautan. Pattern-nya menolak apa pun yang bukan dari undangan asli.

| Field | Alasan |
| --- | --- |
| `tester` | Kode acak 10 karakter. Memungkinkan Anda mengikuti satu orang di berbagai build tanpa menaruh email mereka di URL |
| `build` | Mengikat setiap skor ke sebuah rilis. Pattern-nya hanya menerima versi seperti `1.4.0` |
| `tried` | Apa yang sebenarnya mereka lakukan. Skor tanpa ini sulit ditafsirkan |
| `stability`, `usefulness` | Dua field `rating`, 1 sampai 5. Dipisah, karena build yang stabil untuk hal yang salah tetaplah hal yang salah |
| `would_miss` | Seberapa kecewa mereka tanpanya. Porsi "Sangat" yang naik adalah tanda terkuat bahwa beta-nya berhasil |
| `blockers` | Opsional, dan dari sinilah daftar perbaikan berasal |

## Buat satu tautan per tester

Simpan daftar email tester biasa di `testers.txt`. Script ini memberi masing-masing kode tetap saat pertama kali melihatnya, menyimpan pemetaannya di `testers.json`, dan menulis satu tautan undangan per tester untuk build yang Anda sebutkan. Mulai `testers.json` dengan `{}`.

`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}`);
```

Kirim CSV lewat fitur mail merge alat email Anda, atau tempel tautannya secara manual untuk grup kecil. Jaga `testers.json` tetap privat: hanya di sanalah kode dipetakan kembali ke seseorang.

## Halaman feedback

Taruh ini di halaman `/beta/feedback` di situs Anda. Halaman membaca kode dan build dari tautan, menolak menampilkan formulir tanpa keduanya, dan mengirim dengan cara biasa. Atur **Redirect after submit** ke halaman terima kasih. Ini bekerja di host statis atau pembuat situs mana pun yang mengizinkan script.

`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>
```

> Siapa pun yang memegang tautan seorang tester bisa menjawab sebagai tester itu. Itu bisa diterima untuk beta: kodenya acak, grupnya kecil, dan jawaban yang janggal akan menonjol. Jangan pakai kode itu untuk memberi akses ke apa pun.

## Bandingkan build

Dashboard menampilkan jawaban satu per satu. Untuk membandingkan build, baca semuanya dengan SDK dan kelompokkan berdasarkan `build`. Jalankan ini dengan secret key di `IKIFORM_SECRET_KEY`:

`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)}%`,
  })),
);
```

Rating datang sebagai angka, jadi rata-ratanya tidak perlu parsing. Hitung jumlah tester di samping setiap rata-rata: build dengan tiga jawaban tidak bisa dibandingkan secara adil dengan build yang punya tiga puluh.

## Pastikan berfungsi

1. Jalankan `node links.mjs 1.4.0` dan buka salah satu tautannya. Formulir menampilkan "Feedback for build 1.4.0".
2. Buka halaman tanpa query string. Hanya pesan undangan yang tampil.
3. Kirim jawaban, lalu ubah `build` menjadi `latest` di dev tools browser dan kirim lagi. Ikiform menjawab `422` dengan "Use a version like 1.4.0".
4. Jalankan `summary.mjs`. Tabel menampilkan build 1.4.0 dengan satu tester dan dua skor Anda.

## Hal yang sering menjebak

- Grup beta sering menguji dari satu kantor, yang berbagi satu IP. Batas Standard menghitung mereka bersama-sama, jadi pilih Relaxed, 20 per menit per pengunjung untuk formulir ini.
- Beta mobile di TestFlight sudah memungkinkan tester mengirim feedback screenshot ke App Store Connect. Gunakan itu untuk bug di level layar dan formulir ini untuk pertanyaan mingguan.
- Ajukan pertanyaan yang sama setiap minggu. Mengubah kata-katanya merusak perbandingan antar-build.
- Tester makin jarang menjawab seiring berjalannya beta. Jaga formulir agar selesai dalam beberapa menit, dan ucapkan terima kasih dengan menyebut nama di catatan rilis.
- Ikiform tidak menggabungkan jawaban berulang. Jika seorang tester menjawab dua kali untuk satu build, simpan yang terakhir di ringkasan Anda.

## Pemecahan masalah

| Masalah | Yang harus dilakukan |
| --- | --- |
| `422` dengan "Open the link from your invite email" | Nilai `tester` tidak cocok dengan pattern, biasanya karena tautannya terpotong di klien email. Kirim ulang tautannya. |
| `429 rate_limited` | Satu pengunjung mengirim melebihi rate limit formulir. Standard mengizinkan 5 per menit per pengunjung. Tunggu sesuai `Retry-After`, atau pilih Relaxed atau Custom untuk formulir yang mengirim dari satu IP bersama. |
| `403 origin_not_allowed`, atau setiap kiriman masuk ke spam | Domain halaman tidak ada di **Allowed domains** formulir. Tambahkan, ditambah `localhost` untuk uji lokal. Subdomain dari domain yang terdaftar diizinkan. |

## Pertanyaan

### Bagaimana saya tahu beta tester mana yang mengirim feedback?

Beri setiap tester kode acak dan taruh di tautan pribadi mereka. Halaman menyalinnya ke field tersembunyi, dan hanya file pemetaan privat Anda yang menghubungkan kode itu ke email.

### Bagaimana cara melacak feedback per build?

Taruh juga nomor build di tautan, validasi dengan pattern, dan kelompokkan jawaban berdasarkan build. submissions.iterate dari SDK membaca setiap jawaban untuk ringkasan.

### Pertanyaan apa yang sebaiknya diajukan formulir feedback beta?

Untuk apa mereka memakai build itu, seberapa stabil dan berguna rasanya pada skala 1 sampai 5, seberapa kecewa mereka tanpanya, dan apa yang menghambat mereka. Pertahankan pertanyaan yang sama setiap minggu.

### Haruskah beta tester melaporkan bug di formulir yang sama?

Sediakan formulir laporan bug terpisah untuk apa pun yang perlu direproduksi. Formulir mingguan adalah untuk kesan keseluruhan terhadap build; mencampur keduanya membuat keduanya lebih sulit dibaca.

## Panduan lainnya

- [Formulir feedback untuk website atau aplikasi](https://ikiform.com/id/guides/feedback-form.md): Pasang formulir feedback di halaman mana pun atau di dalam aplikasi Anda: rating, komentar, halaman asalnya, filter spam, dan peringatan saat skornya rendah.
- [Formulir laporan bug](https://ikiform.com/id/guides/feedback-form/bug-report.md): Formulir laporan bug yang menanyakan langkah dan tingkat keparahan, dan membiarkan halaman menambahkan URL, browser, viewport, dan error terbaru dengan sendirinya.
- [Survei NPS](https://ikiform.com/id/guides/feedback-form/nps-survey.md): Jalankan survei NPS dengan skor 0 sampai 10 di email, pertanyaan lanjutan di halaman Anda sendiri yang berubah sesuai skor, dan script yang menghitung NPS.
- [Formulir permintaan fitur](https://ikiform.com/id/guides/feedback-form/feature-request.md): Formulir permintaan fitur yang menanyakan masalahnya alih-alih solusinya, menyimpan permintaan per tema, menghitung orang bukan baris, dan mengirim email saat fiturnya rilis.
- [Widget feedback React](https://ikiform.com/id/guides/feedback-form/react-feedback-widget.md): Tombol feedback mengambang untuk aplikasi React yang membuka panel kecil, mengirim layar, versi aplikasi, dan akun bersama setiap jawaban, dan tidak pernah kehilangan draf.
- [Survei pembatalan](https://ikiform.com/id/guides/feedback-form/cancellation-survey.md): Tanyakan mengapa pelanggan membatalkan di dalam alur pembatalan SaaS Anda, jaga Skip tetap satu klik, simpan alasannya ke Stripe dan Ikiform, dan jangan pernah menunda pembatalan.
