Formulário de feedback para um site ou app

Formulário de feedback para um site ou app7 min de leitura

Colete pedidos de funcionalidades que você consegue contar, priorizar e responder

Um formulário de pedido de funcionalidade que pergunta pelo problema em vez da solução, armazena pedidos por tema, conta pessoas, não linhas, e avisa quando sair.

Preet SutharFundador, Ikiform

Pergunte o que a pessoa está tentando fazer e como faz isso hoje, não qual funcionalidade ela quer, e exija um e-mail para poder avisá-la quando sair. Armazene cada pedido numa tabela via webhook, agrupe os pedidos em temas e priorize os temas por quantas pessoas diferentes estão bloqueadas. Quando um tema sair, mande e-mail para todos que pediram.

O que muda nos pedidos de funcionalidades

Um relato de bug está resolvido quando é corrigido. Um pedido de funcionalidade é um dado: uma de muitas pessoas descrevendo a mesma lacuna com palavras diferentes. Daí vêm três coisas:

  • As pessoas descrevem soluções. "Adicionem um botão de CSV" pode querer dizer "meu contador precisa das faturas do mês passado". Pergunte pela tarefa, e você pode escolher uma solução melhor do que a pedida.
  • A mesma necessidade chega muitas vezes. O Ikiform armazena todo envio e não junta repetições, e duas pessoas raramente usam as mesmas palavras de qualquer forma. Você mesmo agrupa os pedidos, por tema, e conta pessoas distintas.
  • O valor está no retorno. Dizer a alguém "você pediu, nós fizemos" é o melhor motivo para perguntar. Isso exige um e-mail em todo pedido, então este formulário o torna obrigatório.
Como um pedido de funcionalidade vira um tema contado e um e-mail de lançamentopedidoenviadoproblema,área,impacto,e-maillinhana suatabelachave: iddo envioencaixanumtema?vocêmarcatodasemanatema +1pessoasdistintas,bloqueiosprimeirotemasaie-mail aquempediutemanovocomeçacom umapessoawebhooksimbuildnãoComo um pedido de funcionalidade vira um tema contado e um e-mail de lançamentopedido enviadoproblema, área,impacto, e-maillinha na suatabelachave: id do envioencaixa numtema?você marca todasemanatema + 1pessoas distintas,bloqueios primeirotema saie-mail a quem pediutema novocomeça comuma pessoawebhooksimbuildnão
  1. Alguém descreve o que está tentando fazer, escolhe uma área e um impacto e deixa um e-mail.
  2. Um relay de webhook insere o pedido no seu banco de dados, uma vez por id de envio.
  3. Toda semana você marca os pedidos novos com um tema, à mão ou com um padrão.
  4. Se ele se encaixa num tema existente, esse tema agora tem mais uma pessoa por trás.
  5. Se não, ele começa um tema novo.
  6. Quando um tema sai, você seleciona todos os e-mails dele e envia uma única mensagem.
A prioridade é pessoas por tema, não pedidos brutos. Um cliente pedindo cinco vezes continua sendo um cliente.

Pergunte pelo problema

Prévia

Roda o validador do Ikiform no seu navegador. Nada é enviado.

O quanto isso importa?

Vamos escrever quando sair, ou se tivermos alguma dúvida.

problem tem um mínimo de 30 caracteres, o que transforma "modo escuro pfv" numa frase com um motivo.
CampoPor que está aí
problemA tarefa a ser feita, nas palavras da pessoa. De longe o campo mais útil
workaroundMostra o quanto dói. "Copio à mão para uma planilha" é um sinal forte
areaUma primeira triagem grosseira, para você marcar dentro de cobrança, não no meio de tudo
impactSepara clientes bloqueados de curiosos, formulado como resultados que as pessoas sabem avaliar
emailObrigatório. Sem ele você não consegue fechar o ciclo nem fazer uma pergunta

Deixe de fora um campo de prioridade ou de voto. Todo mundo marca o próprio pedido como alta prioridade, e a contagem de pessoas por tema é um voto melhor que qualquer botão.

O formulário

O formulário não precisa de script: ele envia e redireciona. Defina Redirect after submit para uma página que diga o que acontece em seguida. Para uma versão que fica na página no seu framework, pegue o código do formulário do guia principal e troque pelos campos abaixo.

feature-request.html
<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>

Armazene os pedidos via webhook

O painel e a exportação CSV funcionam para as primeiras dezenas de pedidos. Quando você estiver marcando temas e contando pessoas toda semana, uma tabela é mais fácil. Crie uma no Postgres:

schema.sql
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
);

Depois adicione um endpoint de webhook no Ikiform apontando para esta rota. Ela roda em qualquer host Node com o pacote pg, e seu próprio servidor cobre o mesmo padrão com mais detalhes:

app/api/feature-requests/route.ts
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 rejeita tudo o que não foi assinado com o secret whsec_ do seu endpoint.
  • on conflict (submission_id) do nothing torna as novas tentativas seguras. O Ikiform reenvia uma entrega que falhou com o mesmo envio, e o segundo insert não faz nada.
  • Um erro no banco de dados lança uma exceção, a rota responde 500 e o Ikiform tenta de novo depois. Você não perde pedidos durante um restart do banco.
  • O e-mail é convertido para minúsculas na entrada, então a contagem de pessoas distintas funciona.
O Ikiform não junta nem rejeita envios repetidos por e-mail. Uma repetição idêntica em até 24 horas só soma à pontuação de spam. Remova os duplicados onde a lista fica, ou ao exportar.

Marque e priorize

Marque os pedidos novos

Uma vez por semana, leia as linhas sem tema e dê um tema a cada uma. Padrões resolvem os óbvios; leia o resto você mesmo, porque os pedidos interessantes não batem com um padrão.

tag.sql
update feature_requests
set theme = 'invoice-export'
where theme is null
  and area = 'billing'
  and problem ilike any (array['%export%', '%accountant%', '%download%']);

Priorize os temas

Isto ordena os temas por quantos pedidos dizem estar bloqueados e depois por quantas pessoas diferentes pediram:

ranking.sql
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;
  1. Leia por inteiro os textos de problem dos três primeiros temas antes de desenhar qualquer coisa.
  2. Olhe as alternativas que as pessoas usam. Muitas vezes uma pequena mudança remove a pior parte.
  3. Pese a prioridade contra o seu plano; ela mostra demanda, não o que é certo construir.

Feche o ciclo

Quando um tema sair, puxe todos que pediram por ele:

notify.sql
select distinct email
from feature_requests
where theme = 'invoice-export';

Envie um e-mail curto pela sua ferramenta de e-mail ou da sua própria caixa de entrada, dizendo o que saiu e com um link. O Ikiform não envia e-mail para quem preenche o formulário. Os e-mails de notificação vão só para você e sua equipe, então não há resposta automática, e-mail de confirmação nem double opt-in. Envie isso pela sua ferramenta de e-mail, disparada por um webhook. O mesmo vale aqui: o e-mail de lançamento vem de você, não do Ikiform.

Confira se funciona

  1. Envie um pedido com um problema de 10 caracteres. O Ikiform responde 422 apontando problem.
  2. Envie um pedido real. Ele aparece em Submissions e como uma linha em feature_requests.
  3. Em Webhooks, reenvie a entrega. A tabela continua com uma única linha para esse envio.
  4. Envie duas vezes com o mesmo e-mail em maiúsculas e minúsculas diferentes. A consulta de prioridade conta uma pessoa.

Pegadinhas

  • Um quadro público de feedback, como o Canny, adiciona votação e uma página pública de status. Se você quer que os clientes vejam os pedidos uns dos outros, essa é a ferramenta melhor; esta configuração mantém os pedidos privados.
  • Pedidos com vários links podem ser pontuados como spam. Confira o filtro Spam toda semana enquanto o volume for baixo.
  • Se você adicionar uma opção de area depois, adicione-a primeiro na definição. Um valor que não está na lista é recusado.
  • Armazene o que você precisa e nada mais. Uma tabela de pedidos com e-mails é dado pessoal; diga isso no seu aviso de privacidade e apague as linhas de que não precisa mais.

Solução de problemas

ProblemaO que fazer
Linhas faltando na tabelaAbra o endpoint em Webhooks. Um 400 significa que o secret está errado; um 500 com erro de banco de dados significa que o insert falhou e o Ikiform está tentando de novo.
422 validation_failedO envio não corresponde à definição de campos do formulário. error.fields aponta cada campo e o motivo. Faça o name do input bater com o id do campo.
O envio está no painel, mas nenhum e-mail ou webhook chegouEle foi armazenado como spam. Spam não envia nada. Abra-o no filtro Spam para ver os motivos e marque-o como não spam para enviar o e-mail e os webhooks dele.

Perguntas

Travou em algum passo ou quer um guia para o seu caso? Envie um e-mail para hello@ikiform.com.