Despliegue y mantenimiento de una app React Native profesional

Capítulo 12

Tiempo estimado de lectura: 11 minutos

+ Ejercicio

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-config

Crea 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=true

En JS, centraliza la lectura:

Continúa en nuestra aplicación.
  • Escuche el audio con la pantalla apagada.
  • Obtenga un certificado al finalizar.
  • ¡Más de 5000 cursos para que explores!
O continúa leyendo más abajo...
Download App

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 iOS CFBundleVersion.

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

PlataformaVersión visibleBuild number
AndroidversionNameversionCode
iOSCFBundleShortVersionStringCFBundleVersion

Ejemplo Android:

// android/app/build.gradle defaultConfig { versionCode 10800 versionName "1.8.0" }

Ejemplo iOS (Info.plist):

CFBundleShortVersionString = 1.8.0 CFBundleVersion = 10800

Builds 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 applicationId final 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 bundleRelease

El resultado suele quedar en una ruta similar a:

android/app/build/outputs/bundle/release/app-release.aab

Verificació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: staging para QA interno, production para 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/versionCode y CFBundleShortVersionString/CFBundleVersion incrementados.
  • 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.plist para 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).

Ahora responde el ejercicio sobre el contenido:

¿Cuál es una práctica recomendada para gestionar configuraciones por entornos (dev/staging/prod) sin modificar el código cada vez y reduciendo el riesgo de exponer secretos?

¡Tienes razón! Felicitaciones, ahora pasa a la página siguiente.

¡Tú error! Inténtalo de nuevo.

La estrategia recomendada separa la configuración por entorno con archivos específicos, los inyecta durante el build y valida en ejecución variables obligatorias. Además, evita exponer secretos en el cliente.

Portada de libro electrónico gratuitaReact Native desde Cero a App Profesional
100%

React Native desde Cero a App Profesional

Nuevo curso

12 páginas

Descarga la aplicación para obtener una certificación gratuita y escuchar cursos en segundo plano, incluso con la pantalla apagada.