Skip to main content

Command Palette

Search for a command to run...

Tres años en producción, 71 dólares al mes y ahora veo qué hacen mis usuarios

Published
9 min read

Smart Tickets, una de mis apps, lleva en producción desde 2023. Tres años. Hoy tiene 28 suscripciones activas, 7 trials abiertos y 71 dólares de MRR.

La gente paga por ella. La idea está validada. La app funciona.

Y aun así, hasta hace una semana, no podía responder a una pregunta básica:

¿Cuántos de los nuevos usuarios escanean su primer ticket?

Tres años manteniendo una app que factura, sin saber qué hace la gente dentro de ella. Estaba construyendo a ciegas.

En este post cuento lo que cambió cuando metí PostHog, qué eventos de verdad mueven decisiones (con datos reales) y por qué creo que cualquier app con tracción debería tener analítica, aunque haya tardado tres años en hacerlo.


1. La trampa de "ya está validada"

Durante tres años Smart Tickets fue una app que iba a más. Cada mes salía una versión nueva, cada release añadía una funcionalidad, cada usuario nuevo me daba una pista del siguiente paso. La señal de que iba bien era simple: la gente seguía pagando.

Es una trampa cómoda. Mientras los números no caen, parece que sabes lo que haces.

El problema es que esa señal solo te dice una cosa: que la app vale lo suficiente para que alguien suelte 29,99 al año. No te dice nada sobre qué parte de la app vale. No te dice por qué un nuevo usuario abandona en el día dos. No te dice si tu paywall convierte mejor en iOS o en Android. No te dice nada del 90% de los usuarios que se descargan la app y nunca la usan dos veces.

Cuando llevas tres años en este modo, te das cuenta de algo:

La validación es solo el primer juego. El segundo —el que separa una app que factura 70 al mes de una que factura 700— se juega entendiendo a los usuarios que ya tienes.

Y para eso necesitas datos.


2. Por qué PostHog y no Mixpanel o Amplitude

No voy a meterme en una comparativa larga. Tres razones por las que elegí PostHog para mis apps en Expo:

  • Free tier honesto. Un millón de eventos al mes gratis. Para una indie app eso es muchísimo.

  • Host en Europa. Importa si tus usuarios están en España o la UE. PostHog deja elegir el host EU al crear el proyecto y te ahorra documentar transferencias internacionales en tu política de privacidad.

  • SDK de React Native decente y mantenido. Tiene PostHogProvider, captura automática de pantallas para Expo Router y feature flags integrados.

  • Conexión con IA de serie. La IA ya forma parte de mi día a día y necesito que las herramientas que uso se dejen integrar con ella. PostHog tiene MCP server oficial, así que desde Claude o Cursor pregunto "cuál es mi tasa de activación esta semana" y me responde contra los datos reales sin abrir el dashboard. Y para el setup tienen un wizard que te configura el SDK, los eventos base y un dashboard inicial guiado, sin pelearte con la doc.

Si más adelante quieres replays, encuestas o A/B tests, todo eso vive en el mismo dashboard sin tener que añadir más herramientas. Para mi caso, donde lo importante es no fragmentar la stack, fue determinante.


3. La instalación: diez minutos, sin dolor

Empiezas instalando el SDK con sus dependencias de Expo:

npx expo install posthog-react-native expo-application expo-localization expo-device expo-file-system

Después creas un cliente PostHog en lib/posthog.ts. Esta es la parte que muchos tutoriales hacen mal: la versión que yo uso lee las claves de variables de entorno y se desactiva limpiamente si no están definidas, en lugar de petar.

// lib/posthog.ts
import PostHog from 'posthog-react-native';

const apiKey = process.env.EXPO_PUBLIC_POSTHOG_KEY;
const host = process.env.EXPO_PUBLIC_POSTHOG_HOST;
const isPostHogConfigured = !!apiKey && !!host;

export const posthog = new PostHog(apiKey || 'placeholder_key', {
  host,
  disabled: !isPostHogConfigured,
  captureAppLifecycleEvents: true,
  flushAt: 20,
  flushInterval: 10000,
  preloadFeatureFlags: true,
});

Después lo envuelves en tu root layout:

// app/_layout.tsx
import { PostHogProvider } from 'posthog-react-native';
import { posthog } from '@/lib/posthog';

export default function RootLayout() {
  return (
    <PostHogProvider client={posthog}>
      {/* tu Stack y tus providers */}
    </PostHogProvider>
  );
}

Y un .env local con:

EXPO_PUBLIC_POSTHOG_KEY=phc_...
EXPO_PUBLIC_POSTHOG_HOST=https://eu.i.posthog.com

4. La pregunta clave a la hora de decidir qué medir

Cuando llevas años sin analytics y por fin te decides a meterlos, la tentación es trackear todo lo que se mueve. Cada tap, cada scroll, cada cambio de input. El resultado: una colección de gráficas que nadie mira, porque nadie sabe qué hacer con ellas.

Para no caer ahí, antes de decidir si trackear un evento, hazte una pregunta sencilla:

Si este número se mueve, ¿voy a tomar una decisión distinta?

Si la respuesta es no, no lo trackees. Te ahorra ruido en el dashboard, ahorra eventos del free tier y te ahorra el cansancio mental de mirar dashboards llenos de datos irrelevantes.

Lo que te mueve decisiones en una app indie con tracción:

  • El AHA moment del producto. Para Smart Tickets es escanear el primer ticket. Para una app de tracking es registrar la primera entrada. Para un journaling es escribir la primera reflexión. Si los nuevos usuarios no llegan ahí, no hay forma de monetizar lo demás.

  • El funnel del paywall. paywall_viewedpurchase_startedsubscription_started. Tres eventos. Te dicen si el paywall convence, si la pasarela funciona y si la conversión real ocurre.

  • Los errores en flujos críticos. No todos los errores. Solo los que rompen experiencia: scan fallido, login fallido, sync con backend caído.

  • El lifecycle. PostHog lo captura solo si lo activas con captureAppLifecycleEvents: true. Te da Application Installed, Application Opened, Application Updated y Application Backgrounded. La cuarta llave para entender retención.

Lo que no necesitas trackear como evento custom:

  • Cada pantalla por separado: PostHog ya saca $screen con autocaptura.

  • Cada tap a un botón secundario.

  • Cambios de inputs, ordenaciones de listas, scrolls.

  • Casos felices irrelevantes (que el usuario abra el menú casi nunca da una pista accionable).


5. La taxonomía mínima viable que uso

Esta es la lista que me funciona. La adapto a cada app, pero el esqueleto se repite. Convención: snake_case, formato objeto_acción en pasado.

// Auth / Adquisición
'user_signed_up'        { auth_provider }
'user_signed_in'        { auth_provider }
'auth_failed'           { auth_provider, error_code }

// Activación (el bloque más importante)
'first_<core_action>_completed'   // ej. first_ticket_created
'aha_moment_reached'    { time_to_aha_ms }

// Producto (el bucle del valor)
'<core_action>_started'
'<core_action>_completed'    { processing_time_ms, items_detected }
'<core_action>_failed'       { error_code }

// Revenue (cliente)
'paywall_viewed'        { trigger_source }
'paywall_dismissed'     { trigger_source, time_on_paywall_ms }
'purchase_started'      { package_id, price, currency }

// Fricción
'error_occurred'        { screen, operation, error_code }

Tres notas que importan más de lo que parece:

  • Identifica al usuario en el login con posthog.identify(userId, { email }). Y haz posthog.reset() en el logout. Sin esto, cada login crea un usuario nuevo y los funnels se rompen.

  • Pasa siempre trigger_source en el paywall. Saber si el usuario llega al paywall por intentar usar una feature premium o por un banner cambia toda la conversión.

  • No metas datos personales en propiedades. Nada de email en plain text, nada de nombres. Si tienes que cruzarlos, ahí está el identify.


6. El primer dato que te abre los ojos

Cuando los eventos empezaron a llegar a PostHog, miré el dashboard y la primera semana de Smart Tickets contaba esto:

  • 38 nuevos usuarios instalaron la app.

  • 2 llegaron al AHA moment. Un 5% de activación. Pésimo.

  • 23 vieron el paywall, 0 iniciaron una compra. Sospechoso.

  • 85 auth_failed en 7 días, repartidos entre 15 usuarios. Mucha gente reintentando el login.

Tres cosas accionables salieron de ahí en cinco minutos:

  1. El onboarding no enseña a escanear el primer ticket. Si solo el 5% de los nuevos llegan al AHA, es que el flujo del primer día no les guía a la acción que da valor. Eso es prioridad uno del próximo sprint.

  2. El evento purchase_started está mal cableado. RevenueCat me confirma que sí hay subs nuevas la última semana, así que el evento simplemente no se dispara bien en el callback de mi paywall. Bug, no problema de producto.

  3. Algún proveedor de auth está fallando. No sé aún si es Apple, Google o credenciales, pero son cinco fallos por usuario que llega a probar. Toca filtrar por auth_provider y ver qué pasa.

Sin esos números, esto era invisible. Yo creía que la app necesitaba más features. La realidad es que necesitaba que menos gente se cayera en el primer paso.


7. Qué he aprendido (y qué tú deberías hacer hoy)

Si has llegado hasta aquí, estas son las tres cosas concretas con las que cerrar el post:

  1. Si tienes una app sin analítica y con tracción, mete PostHog ya. Diez minutos de set-up. Vale más tener datos imperfectos esta tarde que datos perfectos dentro de tres meses.

  2. Si tienes analíticas pero trackeas todo, dedica una hora a podar la lista. Quédate con 8-12 eventos que muevan decisiones. Borra el resto.

  3. Si ya lo tienes en su sitio, abre el dashboard ahora mismo y mira los tres números que apuntan a tu próximo sprint: tasa de activación, conversión del paywall y errores que más se repiten. La decisión está ahí, no en tu intuición.

La parte más incómoda de todo este proceso fue darme cuenta de que llevaba tres años construyendo a ciegas. No es que tuviera un problema técnico. Tenía un problema de feedback loop: estaba tomando decisiones sin información. Y a esto lo arregla un dashboard, no más código.

Si te apetece seguir el viaje —yo voy a ir contando aquí lo que aprenda al iterar sobre estos tres números—, suscríbete o sígueme por aquí.

More from this blog

G

gartnerleandro - Blog

11 posts

Programador full stack autodidacta, entusiasta de la informática y proactivo.

Me gusta programar y aprender cosas nuevas para poder mejorar día a día mi trabajo y mis proyectos.