Formulario de feedback para un sitio web o una app

Formulario de feedback para un sitio web o una app7 min de lectura

Recoge peticiones de funcionalidades que puedas contar, priorizar y responder

Un formulario de peticiones de funcionalidades que pregunta por el problema en lugar de la solución, guarda las peticiones por tema, cuenta personas y no filas, y les avisa por email cuando sale.

Preet SutharFundador, Ikiform

Pregunta qué intenta hacer la persona y cómo lo hace hoy, no qué funcionalidad quiere, y exige un email para poder avisarle cuando salga. Guarda cada petición en una tabla mediante un webhook, agrupa las peticiones en temas y ordena los temas por cuántas personas distintas están bloqueadas. Cuando sale un tema, escribe a todos los que lo pidieron.

Qué tienen de distinto las peticiones de funcionalidades

Un reporte de bug termina cuando se arregla. Una petición de funcionalidad es un dato: una de muchas personas que describen la misma carencia con palabras distintas. De ahí se siguen tres cosas:

  • La gente describe soluciones. "Añade un botón de CSV" puede significar "mi contable necesita las facturas del mes pasado". Pregunta por la tarea y podrás elegir una solución mejor que la pedida.
  • La misma necesidad llega muchas veces. Ikiform guarda cada envío y no fusiona repeticiones, y de todos modos dos personas rara vez usan las mismas palabras. Tú agrupas las peticiones por tema y cuentas personas distintas.
  • El valor está en el seguimiento. Decirle a alguien "lo pediste y lo hemos hecho" es la mejor razón para preguntar. Eso exige un email en cada petición, así que este formulario lo pide como obligatorio.
Cómo una petición de funcionalidad se convierte en un tema contado y en un email de lanzamientopeticiónenviadaproblema,área,impacto,emailfilaen tutablacon elid deenvíocomoclave¿encajaen untema?loetiquetascadasemanaconteodeltema +1personasdistintas,bloqueantesprimeroeltemaselanzaescribea todoslos quelopidierontemanuevoempiezacon unapersonawebhooksíbuildnoCómo una petición de funcionalidad se convierte en un tema contado y en un email de lanzamientopetición enviadaproblema, área,impacto, emailfila en tu tablacon el id de envíocomo clave¿encaja enun tema?lo etiquetascada semanaconteo del tema +1personas distintas,bloqueantes primeroel tema se lanzaescribe a todos losque lo pidierontema nuevoempieza conuna personawebhooksíbuildno
  1. Alguien describe lo que intenta hacer, elige un área y un impacto, y deja un email.
  2. Un relay de webhook inserta la petición en tu base de datos, una vez por id de envío.
  3. Cada semana etiquetas las nuevas peticiones con un tema, a mano o con un patrón.
  4. Si encaja en un tema existente, ese tema tiene ahora una persona más detrás.
  5. Si no, empieza un tema nuevo.
  6. Cuando sale un tema, seleccionas todos sus emails y mandas un solo mensaje.
La clasificación es personas por tema, no peticiones en bruto. Un cliente que pide lo mismo cinco veces sigue siendo un cliente.

Pregunta por el problema

Vista previa

Ejecuta el validador de Ikiform en tu navegador. No se envía nada.

¿Cuánto te importa?

Te escribiremos cuando se lance, o si tenemos alguna pregunta.

problem exige un mínimo de 30 caracteres, lo que convierte "modo oscuro porfa" en una frase con un motivo.
CampoPara qué sirve
problemLa tarea que quieren hacer, en sus palabras. Con diferencia, el campo más útil
workaroundTe dice cuánto duele. "Lo copio a mano en una hoja de cálculo" es una señal fuerte
areaUna primera clasificación gruesa, para etiquetar dentro de facturación y no entre todo
impactSepara a los clientes bloqueados de los curiosos, con opciones redactadas como resultados que la gente puede valorar
emailObligatorio. Sin él no puedes cerrar el ciclo ni hacer una pregunta

No incluyas un campo de prioridad ni de votos. Todo el mundo marca su propia solicitud como de alta prioridad, y el número de personas por tema es un voto mejor que cualquier botón.

El formulario

El formulario no necesita script: envía y redirige. Configura Redirect after submit con una página que explique qué pasa después. Para una versión que se quede en la página dentro de tu framework, toma el código del formulario de la guía principal y cambia los campos por estos.

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>

Guarda las solicitudes con un webhook

El panel y la exportación CSV sirven para las primeras docenas de solicitudes. Cuando ya etiquetas temas y cuentas personas cada semana, una tabla es más cómoda. Crea una en 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
);

Después añade un endpoint de webhook en Ikiform que apunte a esta ruta. Funciona en cualquier host de Node con el paquete pg, y tu propio servidor cubre el mismo patrón con más detalle:

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 rechaza todo lo que no esté firmado con el secreto whsec_ de tu endpoint.
  • on conflict (submission_id) do nothing hace que los reintentos sean seguros. Ikiform reenvía una entrega fallida con el mismo envío, y el segundo insert no hace nada.
  • Un error de la base de datos lanza una excepción, la ruta responde 500 e Ikiform lo reintenta más tarde. No pierdes solicitudes durante un reinicio de la base de datos.
  • El email se pasa a minúsculas al entrar, así que contar personas distintas funciona.
Ikiform no fusiona ni rechaza envíos repetidos por email. Una repetición idéntica en 24 horas solo suma a la puntuación de spam. Elimina duplicados donde vive la lista, o al exportar.

Etiqueta y ordena

Etiqueta las solicitudes nuevas

Una vez por semana, lee las filas sin etiquetar y asigna un tema a cada una. Los patrones resuelven las obvias; el resto léelas tú, porque las solicitudes interesantes no encajan en ningún patrón.

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

Ordena los temas

Esto ordena los temas por cuántas solicitudes dicen estar bloqueadas y luego por cuántas personas distintas lo pidieron:

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. Lee completos los textos de problem de los tres temas principales antes de diseñar nada.
  2. Fíjate en las soluciones provisionales. A menudo un cambio pequeño elimina lo peor.
  3. Contrasta la clasificación con tu plan; muestra demanda, no lo que conviene construir.

Cierra el ciclo

Cuando se lance un tema, saca a todos los que lo pidieron:

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

Envía un email breve desde tu herramienta de email o tu propia bandeja, contando qué se lanzó y con un enlace. Ikiform no envía emails a la persona que rellena el formulario. Las notificaciones por email solo os llegan a ti y a tu equipo, así que no hay respuesta automática, email de confirmación ni doble opt-in. Envíalos desde tu herramienta de email, activados por un webhook. Aquí pasa lo mismo: el email del lanzamiento sale de ti, no de Ikiform.

Comprueba que funciona

  1. Envía una solicitud con un problema de 10 caracteres. Ikiform responde 422 indicando problem.
  2. Envía una solicitud real. Aparece en Submissions y como fila en feature_requests.
  3. En Webhooks, reenvía la entrega. La tabla sigue teniendo una sola fila para ese envío.
  4. Envía dos veces con el mismo email en distinta combinación de mayúsculas. La consulta de clasificación cuenta una persona.

Errores comunes

  • Un tablero público de feedback, como Canny, añade votaciones y una página pública de estado. Si quieres que los clientes vean las solicitudes de los demás, esa es la mejor herramienta; esta configuración mantiene las solicitudes privadas.
  • Las solicitudes con varios enlaces pueden puntuar como spam. Revisa el filtro Spam cada semana mientras el volumen sea bajo.
  • Si más adelante añades una opción a area, añádela primero a la definición. Un valor que no esté en la lista se rechaza.
  • Guarda lo que necesitas y nada más. Una tabla de solicitudes con emails son datos personales; dilo en tu aviso de privacidad y borra las filas que ya no necesites.

Solución de problemas

ProblemaQué hacer
Faltan filas en la tablaAbre el endpoint en Webhooks. Un 400 significa que el secreto es incorrecto; un 500 con un error de base de datos significa que el insert falló e Ikiform lo está reintentando.
422 validation_failedEl envío no coincide con la definición de campos del formulario. error.fields indica cada campo y el motivo. Haz que el name del input coincida con el id del campo.
El envío está en el panel pero no llegó ningún email ni webhookSe guardó como spam. El spam no envía nada. Ábrelo en el filtro Spam para ver los motivos y márcalo como no spam para enviar su email y sus webhooks.

Preguntas

¿Te atascaste en un paso o quieres una guía para tu caso? Escribe a hello@ikiform.com.