ウェブサイトやアプリのフィードバックフォーム1 分で読めます
数えて、順位をつけて、返答できる機能要望を集める
解決策ではなく課題を尋ね、要望をテーマ別に保存し、件数ではなく人数で数え、リリース時にメールで知らせる機能要望フォームです。
欲しい機能ではなく、何をしようとしていて今はどうやっているかを尋ね、リリース時に知らせられるようメールアドレスを必須にします。要望は Webhook でテーブルに保存し、テーマごとにタグを付け、困っている人の人数でテーマを順位付けします。テーマをリリースしたら、要望した全員にメールを送ります。
機能要望ならではの違い
バグ報告は直せば終わりです。機能要望はデータの 1 点で、同じ不足を多くの人が違う言葉で語っているうちの 1 件です。そこから 3 つのことが言えます。
- 人は解決策を語ります。「CSV ボタンを追加して」は「経理担当が先月の請求書を必要としている」という意味かもしれません。やりたいことを尋ねれば、要望された方法より良い解決策を選べます。
- 同じニーズが何度も届きます。 Ikiform はすべての送信を保存し、重複をまとめません。そもそも 2 人が同じ言葉を使うことはまれです。要望はテーマごとに自分でまとめ、異なる人の数を数えます。
- 価値はフォローアップにあります。「ご要望いただいた機能を作りました」と伝えられることこそ、要望を募る一番の理由です。そのためにはすべての要望にメールアドレスが必要なので、このフォームでは必須にしています。
- 利用者がやりたいことを書き、領域と影響度を選び、メールアドレスを残します。
- Webhook の中継が、送信 ID ごとに 1 回だけ要望をデータベースに挿入します。
- 毎週、新しい要望に手作業かパターンでテーマのタグを付けます。
- 既存のテーマに当てはまれば、そのテーマを求める人が 1 人増えます。
- 当てはまらなければ、新しいテーマになります。
- テーマをリリースしたら、そのテーマのメールアドレスをすべて選び、メッセージを 1 通送ります。
課題を尋ねる
プレビュー
Ikiform のバリデーターをブラウザーで実行します。何も送信されません。
problem は 30 文字以上が必要なので、「ダークモードほしい」が理由の入った文になります。| フィールド | 尋ねる理由 |
|---|---|
problem | 本人の言葉で書かれた、やりたいこと。群を抜いて役立つフィールド |
workaround | どれだけ困っているかがわかります。「手作業でスプレッドシートにコピーしている」は強いシグナル |
area | 大まかな一次分類。全体ではなく請求の中でタグ付けできます |
impact | 困っている顧客と興味本位の人を分けます。本人が判断できる結果の形で表現します |
email | 必須。これがないとフォローアップも質問もできません |
優先度や投票のフィールドは入れないでください。誰もが自分の要望を高優先度にしますし、テーマごとの人数のほうがどんなボタンよりも良い投票になります。
フォーム
このフォームにスクリプトは不要です。送信してリダイレクトするだけです。Redirect after submit には、この後どうなるかを書いたページを設定してください。フレームワークでページ内に留まる版が欲しければ、ピラーのフォームのコードを使い、フィールドをこれに差し替えてください。
<form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
<label for="problem">What are you trying to do?</label>
<textarea id="problem" name="problem" rows="5" required minlength="30"
placeholder="Send last month's invoices to our accountant in one go"></textarea>
<label for="workaround">How do you do it today?</label>
<textarea id="workaround" name="workaround" rows="3"></textarea>
<label for="area">Which part of the product?</label>
<select id="area" name="area" required>
<option value="billing">Billing and invoices</option>
<option value="reports">Reports</option>
<option value="team">Team and permissions</option>
<option value="integrations">Integrations and API</option>
<option value="other">Something else</option>
</select>
<fieldset>
<legend>How much does it matter?</legend>
<label><input type="radio" name="impact" value="blocking" required /> It stops us using the product for something</label>
<label><input type="radio" name="impact" value="slow" /> It costs us time every week</label>
<label><input type="radio" name="impact" value="nice" /> It would be nice</label>
</fieldset>
<label for="email">Email</label>
<input id="email" name="email" type="email" required />
<input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
<button>Send request</button>
</form>要望を Webhook で保存する
最初の数十件ならダッシュボードと CSV エクスポートで十分です。毎週テーマにタグを付けて人数を数えるようになったら、テーブルのほうが楽です。Postgres に作成します:
create table feature_requests (
submission_id text primary key,
area text not null,
impact text not null,
problem text not null,
workaround text,
email text not null,
theme text,
created_at timestamptz not null
);次に、Ikiform でこのルートを指す Webhook のエンドポイントを追加します。pg パッケージがあればどの Node ホストでも動きます。同じパターンは自分のサーバーでさらに詳しく解説しています:
import { verifyWebhook, WebhookVerificationError } from "@ikiform/sdk/webhooks";
import { Pool } from "pg";
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const INSERT = `insert into feature_requests (submission_id, area, impact, problem, workaround, email, created_at)
values ($1, $2, $3, $4, $5, lower($6), $7)
on conflict (submission_id) do nothing`;
export async function POST(request: Request) {
const payload = await request.text();
let event;
try {
event = await verifyWebhook({
payload,
headers: request.headers,
secret: process.env.IKIFORM_WEBHOOK_SECRET!,
});
} catch (error) {
if (error instanceof WebhookVerificationError) return new Response(error.reason, { status: 400 });
throw error;
}
if (event.type !== "submission.created") return new Response(null, { status: 204 });
const { problem, workaround, area, impact, email } = event.data.fields;
await pool.query(INSERT, [
event.data.submission_id,
area,
impact,
problem,
workaround ?? null,
email,
event.data.created_at,
]);
return new Response(null, { status: 204 });
}verifyWebhookは、エンドポイントのwhsec_シークレットで署名されていないものをすべて拒否します。on conflict (submission_id) do nothingでリトライが安全になります。Ikiform は失敗した配信を同じ送信データで再送するので、2 回目の挿入は何もしません。- データベースのエラーは例外になり、ルートは
500を返し、Ikiform があとでリトライします。データベースの再起動中でも要望は失われません。 - メールアドレスは受け取る時点で小文字にするので、異なる人数を正しく数えられます。
タグ付けと順位付け
新しい要望にタグを付ける
週に 1 回、タグのない行を読んで、それぞれにテーマを付けます。わかりやすいものはパターンで処理できますが、残りは自分で読んでください。興味深い要望ほどパターンには当てはまりません。
update feature_requests
set theme = 'invoice-export'
where theme is null
and area = 'billing'
and problem ilike any (array['%export%', '%accountant%', '%download%']);テーマを順位付けする
このクエリは、困っていると答えた要望の数でテーマを順位付けし、次に要望した異なる人の数で並べます:
select coalesce(theme, 'untagged') as theme,
count(*) as requests,
count(distinct email) as people,
count(*) filter (where impact = 'blocking') as blocking
from feature_requests
group by 1
order by blocking desc, people desc;- 何かを設計する前に、上位 3 テーマの
problemの本文をすべて読んでください。 - 回避策に注目してください。小さな変更 1 つで最悪の部分が解消することもよくあります。
- 順位は自分の計画と照らし合わせて判断してください。示すのは需要であって、作るべきものではありません。
フォローアップする
テーマをリリースしたら、それを要望した全員を取り出します:
select distinct email
from feature_requests
where theme = 'invoice-export';メールツールか自分の受信箱から、何をリリースしたかとそのリンクを書いた短いメールを 1 通送ります。Ikiform はフォームを入力した本人にはメールを送りません。通知メールが届くのはあなたとチームだけなので、自動返信、確認メール、ダブルオプトインはありません。それらは Webhook をきっかけに、自分のメールツールから送ってください。ここでも同じで、リリースのお知らせメールは Ikiform ではなくあなたから送ります。
動作を確認する
- 10 文字の課題で要望を送信します。Ikiform は
problemを指摘して422を返します。 - 本物の要望を送信します。Submissions に表示され、
feature_requestsに行が追加されます。 - Webhooks で配信を再送します。その送信データの行はテーブルに 1 行のままです。
- 大文字と小文字を変えた同じメールアドレスで 2 回送信します。順位付けのクエリでは 1 人と数えられます。
ハマりどころ
- Canny のような公開フィードバックボードなら、投票と公開ステータスページが加わります。顧客同士で要望を見せたいならそちらが適したツールです。この構成では要望を非公開に保ちます。
- リンクを複数含む要望はスパム判定されることがあります。件数が少ないうちは毎週 Spam フィルターを確認してください。
- あとで
areaの選択肢を追加するなら、先に定義に追加してください。一覧にない値は拒否されます。 - 必要なものだけを保存してください。メールアドレス入りの要望テーブルは個人データです。プライバシーポリシーにそう記載し、不要になった行は削除してください。
トラブルシューティング
| 問題 | 対処法 |
|---|---|
| テーブルに行がない | Webhooks でエンドポイントを開きます。400 ならシークレットが間違っています。データベースのエラー付きの 500 なら挿入に失敗しており、Ikiform がリトライ中です。 |
422 validation_failed | 送信がフォームのフィールド定義に合っていません。error.fields に各フィールドと理由が示されます。入力欄の name をフィールド ID に合わせてください。 |
| 送信データはダッシュボードにあるのに、メールも Webhook も届かない | スパムとして保存されています。スパムでは何も送られません。Spam フィルターで開いて理由を確認し、スパムではないとマークすると、メールと Webhook が送られます。 |
よくある質問
その人が何をしようとしているか、今はどうやっているか、製品のどの部分に関わるか、どのくらい重要か、そしてメールアドレスです。機能ではなく課題を尋ねることで、より良い解決策を探す余地が生まれます。
重複は起きるものと考え、行ではなく人を数えてください。各要望にテーマのタグを付け、異なるメールアドレスの数でテーマを順位付けします。Ikiform はすべての送信データを保存し、重複をまとめません。
フォームだけではできません。フォームは要望を非公開に保ちます。Canny のような公開ボードなら投票が加わります。テーマごとに異なる人数を数えれば、非公開のまま同等のことができます。
各要望にメールアドレスを保存しておき、リリースしたテーマのメールアドレスをすべて選んで、メールツールからメッセージを 1 通送ります。Ikiform 自体は送信者にメールを送りません。
手順で詰まったときや、あなたの状況に合うガイドがほしいときはメールでどうぞ: hello@ikiform.com