Preparación para entrega: mentalidad de “release”
El despliegue profesional no es solo “generar un APK/IPA”. Implica controlar configuración por entornos, variables sensibles, versionado, firma, builds reproducibles, verificación en dispositivos reales y un plan de monitoreo/actualizaciones que reduzca riesgos. En esta sección verás un flujo práctico para pasar de un proyecto listo para desarrollo a una app lista para tiendas y mantenimiento continuo.
Configuración por entornos (dev/staging/prod)
Objetivo
Separar endpoints, llaves, flags y comportamiento (logs, analítica, feature flags) según el entorno, sin tocar el código cada vez y sin exponer secretos en el repositorio.
Estrategia recomendada
- Un “contrato” de variables: define qué variables existen y qué significan.
- Archivos por entorno: por ejemplo
.env.development,.env.staging,.env.production. - Inyección en build: el build selecciona el archivo correcto.
- Validación en runtime: si falta una variable crítica, falla temprano con un error claro.
Paso a paso con variables de entorno
Una opción común es usar react-native-config para exponer variables nativas a JS.
# instalación (ejemplo con npm) npm i react-native-configCrea archivos:
# .env.development API_URL=https://dev.api.tuapp.com SENTRY_DSN=... FEATURE_PAYWALL=false # .env.production API_URL=https://api.tuapp.com SENTRY_DSN=... FEATURE_PAYWALL=trueEn JS, centraliza la lectura:
- Escuche el audio con la pantalla apagada.
- Obtenga un certificado al finalizar.
- ¡Más de 5000 cursos para que explores!
Descargar la aplicación
// src/config/env.ts import Config from 'react-native-config'; export const ENV = { API_URL: Config.API_URL, SENTRY_DSN: Config.SENTRY_DSN, FEATURE_PAYWALL: Config.FEATURE_PAYWALL === 'true', } as const; // Validación mínima (ejemplo) const required = ['API_URL']; required.forEach((k) => { if (!(ENV as any)[k]) throw new Error(`Missing env var: ${k}`); });Para evitar fugas de secretos: no guardes tokens privados en variables accesibles desde el cliente. En apps móviles, cualquier valor embebido puede ser extraído; usa el backend para operaciones sensibles.
Entornos en Android (productFlavors)
En Android es habitual usar productFlavors para generar builds separados (por ejemplo, dev, staging, prod) con applicationIdSuffix distinto, permitiendo instalar varias variantes en el mismo dispositivo.
// android/app/build.gradle android { flavorDimensions "env" productFlavors { dev { dimension "env" applicationIdSuffix ".dev" versionNameSuffix "-dev" } staging { dimension "env" applicationIdSuffix ".staging" versionNameSuffix "-stg" } prod { dimension "env" } } }Luego puedes apuntar cada flavor a su archivo .env correspondiente mediante scripts de build (por ejemplo, exportando ENVFILE antes de compilar si usas react-native-config).
Entornos en iOS (Schemes/Configurations)
En iOS lo más limpio es crear Schemes (Dev/Staging/Prod) y asociarlos a Build Configurations. Cada scheme puede usar un bundle identifier distinto, y puedes inyectar variables con archivos .xcconfig o con la integración de la librería de variables que elijas.
Manejo de versiones: versionName/versionCode y CFBundleShortVersionString/CFBundleVersion
Conceptos clave
- Versión visible (semver): lo que ve el usuario, p. ej.
1.8.0. - Build number: entero incremental obligatorio para tiendas. En Android es
versionCode; en iOSCFBundleVersion.
Reglas prácticas
- Incrementa build number en cada subida a Play Store / App Store, incluso si la versión visible no cambia.
- Usa SemVer para comunicar cambios:
MAJOR.MINOR.PATCH. - Automatiza el bump con CI o scripts para evitar errores manuales.
Dónde se configura
| Plataforma | Versión visible | Build number |
|---|---|---|
| Android | versionName | versionCode |
| iOS | CFBundleShortVersionString | CFBundleVersion |
Ejemplo Android:
// android/app/build.gradle defaultConfig { versionCode 10800 versionName "1.8.0" }Ejemplo iOS (Info.plist):
CFBundleShortVersionString = 1.8.0 CFBundleVersion = 10800Builds de Android: debug, release, AAB y verificación
Tipos de build y cuándo usarlos
- Debug: desarrollo local, logs completos, sin optimizaciones.
- Release: optimizado, minificado (si aplica), firmado, listo para distribución.
- AAB (Android App Bundle): formato recomendado para Play Store.
Paso a paso: generar AAB de release
- Asegura que el
applicationIdfinal sea el correcto (y que flavors no cambien el id de prod). - Configura firma (ver sección de firma).
- Compila el bundle.
# desde la carpeta android ./gradlew clean ./gradlew bundleReleaseEl resultado suele quedar en una ruta similar a:
android/app/build/outputs/bundle/release/app-release.aabVerificación en dispositivo (Android)
- Instala una variante firmada (si generas APK) o usa un “internal testing” en Play Console para instalar el AAB.
- Valida: arranque en frío, login, navegación crítica, permisos, notificaciones (si aplica), deep links, modo oscuro, rotación (si tu app lo soporta).
- Revisa logs de crash con herramientas de monitoreo (ver sección de monitoreo).
Builds de iOS: archive, IPA y distribución
Conceptos clave
- Archive: build empaquetado para distribución.
- Signing & Capabilities: permisos y capacidades (push, keychain, associated domains) deben coincidir con el perfil.
- Distribución: TestFlight para pruebas, App Store para producción.
Paso a paso: generar un Archive
- Selecciona el scheme de Prod en Xcode.
- Verifica el Bundle Identifier y el equipo (Team).
- Ejecuta Product > Archive.
- Distribuye a TestFlight o App Store desde el Organizer.
Recomendación: usa TestFlight como etapa obligatoria para validar en dispositivos reales antes de publicar.
Firma y gestión de llaves/certificados
Android: keystore
La firma en Android se basa en un .keystore. Si pierdes la llave de firma, puedes perder la capacidad de actualizar la app (salvo mecanismos específicos como Play App Signing). Buenas prácticas:
- Activa Play App Signing en Google Play para reducir riesgo.
- Guarda el keystore en un lugar seguro (gestor de secretos/almacenamiento cifrado) y controla accesos.
- No subas el keystore al repositorio.
Ejemplo de configuración típica (referencial):
// android/gradle.properties MYAPP_UPLOAD_STORE_FILE=my-release-key.keystore MYAPP_UPLOAD_KEY_ALIAS=my-key-alias MYAPP_UPLOAD_STORE_PASSWORD=*** MYAPP_UPLOAD_KEY_PASSWORD=***// android/app/build.gradle android { signingConfigs { release { storeFile file(MYAPP_UPLOAD_STORE_FILE) storePassword MYAPP_UPLOAD_STORE_PASSWORD keyAlias MYAPP_UPLOAD_KEY_ALIAS keyPassword MYAPP_UPLOAD_KEY_PASSWORD } } buildTypes { release { signingConfig signingConfigs.release } } }iOS: certificados y perfiles
En iOS, la firma depende de certificados y provisioning profiles. Buenas prácticas:
- Centraliza la gestión en la cuenta de Apple Developer del equipo.
- Evita certificados personales para releases de equipo.
- Documenta qué bundle id corresponde a cada entorno.
- Automatiza con CI (por ejemplo, usando gestión de certificados en pipelines) para builds reproducibles.
Verificación en dispositivos: matriz mínima de pruebas antes de publicar
Por qué no basta con emulador/simulador
Rendimiento real, permisos, notificaciones, cámara, biometría, comportamiento del teclado, redes inestables y restricciones del sistema se manifiestan mejor en dispositivos físicos.
Matriz recomendada
- Android: al menos 1 gama media (2–3 años), 1 gama baja si tu público lo requiere, y 1 gama alta; versiones Android actuales y una anterior relevante.
- iOS: al menos 1 dispositivo con iOS actual y, si es posible, otro con pantalla pequeña/mediana para validar layouts.
- Red: WiFi estable, 4G/5G, modo avión (para flujos offline si aplica), y red lenta (throttling).
Checklist de verificación rápida en dispositivo
- Instalación limpia y actualización sobre versión anterior.
- Arranque en frío y reanudación desde background.
- Navegación principal y rutas profundas (deep links si existen).
- Permisos: solicitar solo cuando se necesitan y con copy claro.
- Estados de error: sin conexión, timeouts, respuestas 401/403/500.
- Consumo de batería y calentamiento en uso típico.
Monitoreo de fallos (crash reporting) y observabilidad
Qué debes medir en producción
- Crashes: fallos fatales con stacktrace y contexto.
- Errores no fatales: excepciones controladas, fallos de API, timeouts.
- Rendimiento: tiempos de arranque, latencia de pantallas, tiempos de red.
- Eventos de negocio: embudo (onboarding, registro, compra), retención, uso de features.
Herramientas comunes
- Sentry: errores JS y nativos, breadcrumbs, releases.
- Firebase Crashlytics: crashes nativos, estabilidad, distribución con App Distribution.
- Firebase Analytics / Amplitude / Segment: eventos de producto.
Integración práctica: captura de errores y breadcrumbs
Independientemente de la herramienta, aplica estas reglas:
- Enriquece errores con contexto: usuario (id interno), entorno, versión, pantalla actual.
- Evita enviar PII sensible (emails, teléfonos, tokens).
- Registra breadcrumbs de navegación y acciones clave para reproducir.
Ejemplo conceptual (Sentry):
// src/observability/sentry.ts import * as Sentry from '@sentry/react-native'; import { ENV } from '../config/env'; export function initSentry() { if (!ENV.SENTRY_DSN) return; Sentry.init({ dsn: ENV.SENTRY_DSN, environment: __DEV__ ? 'development' : 'production', enableAutoSessionTracking: true, }); } export function setUserContext(userId?: string) { Sentry.setUser(userId ? { id: userId } : null); } export function captureError(err: unknown, extra?: Record<string, any>) { Sentry.captureException(err, { extra }); }Alertas y operación
- Define umbrales: crash-free users < 99.5% (ejemplo), p95 de arranque > X ms, aumento de errores 5xx.
- Configura alertas por canal (email/Slack) y un responsable de guardia si la app es crítica.
- Usa “releases” en la herramienta para correlacionar picos de fallos con una versión específica.
Registro de eventos (analytics) sin ensuciar el código
Principios
- Eventos con nombres consistentes y propiedades bien definidas.
- Evita disparar eventos desde componentes UI sin control; centraliza en una capa de tracking.
- Versiona el “tracking plan” para no romper dashboards.
Ejemplo de capa de tracking
// src/analytics/index.ts type EventName = 'screen_view' | 'login_success' | 'purchase_success'; type TrackProps = Record<string, string | number | boolean | null>; export function track(event: EventName, props: TrackProps = {}) { // Implementación según proveedor // analytics().logEvent(event, props) // o amplitude.track(event, props) } export function trackScreen(screenName: string) { track('screen_view', { screen: screenName }); }Con esto, si cambias de proveedor (o agregas uno nuevo), no reescribes toda la app.
Manejo de actualizaciones sin romper compatibilidad
Tipos de cambios y riesgos
- Cambios de API: endpoints, contratos, campos renombrados.
- Cambios de datos locales: migraciones de almacenamiento, caches, esquemas.
- Cambios de navegación: rutas removidas o deep links que dejan de existir.
- Feature flags: activar features en producción sin release puede romper si no hay compatibilidad.
Estrategias para compatibilidad hacia atrás
- Versionado de API: mantener endpoints antiguos por un tiempo o soportar múltiples versiones.
- Lectura tolerante: si falta un campo, usa defaults; si llega un campo nuevo, ignóralo.
- Migraciones: al cambiar estructuras locales, migra datos en el arranque de forma segura y con rollback si falla.
- Feature flags: el servidor controla activación; el cliente debe ser capaz de ignorar flags desconocidos.
- Kill switch: capacidad de desactivar remotamente una feature problemática.
Actualizaciones “over-the-air” (OTA) y límites
Soluciones como CodePush (en App Center) permiten actualizar JS sin pasar por tienda, pero:
- No pueden cambiar binarios nativos (nuevos permisos, nuevas dependencias nativas, cambios de Info.plist/AndroidManifest).
- Debes respetar políticas de las tiendas y usar OTA para correcciones y ajustes compatibles.
- Necesitas un control estricto de versiones: qué bundle JS corresponde a qué versión nativa.
Patrón recomendado para OTA
- Define “compatibility ranges”: por ejemplo, el bundle OTA solo se aplica a versiones nativas
>=1.8.0 <1.9.0. - Canales:
stagingpara QA interno,productionpara usuarios. - Rollout gradual: 5% → 25% → 100% con monitoreo de crashes.
Checklist técnica antes de publicar (rendimiento, estabilidad, permisos, navegación, UI)
1) Build y configuración
- Build Release generado para Android (AAB) e iOS (Archive) con configuración de Prod.
- Variables de entorno verificadas:
API_URL, DSN de monitoreo, flags de features. - Logs de debug desactivados o reducidos en producción.
- Versionado correcto:
versionName/versionCodeyCFBundleShortVersionString/CFBundleVersionincrementados. - Firma correcta: keystore/certificados válidos, Play App Signing habilitado (si aplica).
2) Estabilidad y manejo de errores
- Monitoreo activo (Sentry/Crashlytics): captura de crashes y errores no fatales funcionando.
- Errores de red: timeouts, sin conexión, 401/403/500 muestran UI adecuada y no bloquean la app.
- Estados vacíos y cargas: no hay pantallas “en blanco” sin explicación.
- Reintentos controlados: no hay loops infinitos de requests.
3) Rendimiento percibido
- Arranque en frío aceptable en dispositivos objetivo.
- Transiciones de navegación fluidas en release.
- Listas largas: scroll fluido y sin bloqueos.
- Uso de memoria estable tras navegar por flujos principales (sin degradación evidente).
4) Permisos y privacidad
- Permisos solicitados just-in-time (cuando el usuario ejecuta la acción) y con explicación clara.
- Si el usuario deniega permisos: la app ofrece alternativa o guía para habilitarlos.
- Revisión de datos enviados a analítica/monitoreo: sin PII sensible, sin tokens.
- iOS: descripciones en
Info.plistpara permisos usados (cámara, fotos, ubicación, etc.).
5) Navegación y enlaces
- Deep links (si existen): abren la pantalla correcta desde estado cerrado y desde background.
- Back behavior en Android: consistente y sin salidas inesperadas.
- Restauración: al volver desde background, no se pierde el estado crítico o se recupera correctamente.
- Rutas removidas: no quedan enlaces internos rotos.
6) Consistencia visual
- Tipografías, espaciados y colores consistentes en pantallas clave.
- Modo oscuro (si aplica): contraste correcto, sin textos invisibles.
- Accesibilidad básica: tamaños de toque, textos legibles, soporte de tamaño de fuente (si aplica).
- Safe areas: no hay contenido tapado por notch o barras del sistema.
7) Publicación y post-publicación
- Canales de distribución listos: Play Console (internal/closed testing) y TestFlight configurados.
- Notas de versión preparadas y alineadas con cambios reales.
- Alertas configuradas para crashes y degradación de métricas tras el release.
- Plan de rollback: versión anterior disponible y estrategia de desactivación (feature flags/kill switch).