Lista de verificación de reglas del proyecto Expo
Veinticinco reglas innegociables para aplicaciones Expo mantenibles en React Native 0.86 y Expo SDK 57. Estas reglas cubren scaffolding de proyecto, configuración, diseño de carpetas, Continuous Native Generation (CNG), higiene de dependencias y convenciones de equipo. Trata las violaciones como bloqueadores de merge a menos que un ADR documente una excepción.
- Ejecuta Tier 1 cuando hagas scaffold de una nueva app o inmediatamente después de una actualización de SDK - los errores de configuración y dependencias se componen cada sprint.
- Aplica Tiers 2-4 durante la revisión de PR; los archivos de rutas, las carpetas nativas y el manejo de secretos son las áreas de mayor riesgo.
- Registra las excepciones intencionales en
docs/adr/ - "lo arreglaremos después" sin un ADR se convierte en deuda permanente.
- Revisita la lista completa trimestralmente y después de cada bump de SDK mayor - RN 0.86 y los valores predeterminados de New Architecture cambian lo que "verde" significa.
- Empareja esta lista con Expo Rules Best Practices para un resumen de cumplimiento condensado en standups.
-
Fija el SDK en el momento del scaffold: Comienza con npx create-expo-app@latest --template default@sdk-57 para que expo se resuelva a ~57.0.4 - actualizar tres SDKs en un PR es una fábrica de conflictos de merge.
- Verifica:
npx expo-doctor pasa antes de que se fusione la primera rama de feature.
- Rechaza:
latest sin un pin de SDK en CI o en documentos de onboarding de README.
-
app.config.ts es la única fuente de verdad: La configuración dinámica soporta ambientes, helpers tipados y plugins condicionales; un app.json estático solitario no escala más allá de un sabor de compilación.
- Patrón: Exporta un
ExpoConfig tipado desde app.config.ts y elimina campos duplicados de app.json.
- Auditoría:
npx expo config --type public antes de cada envío a la tienda.
-
Nunca confirmes secretos a extra o EXPO_PUBLIC_*: Los valores con prefijo EXPO_PUBLIC_ se insertan en línea en el momento de la compilación - las claves de API, secretos de firma y tokens de admin pertenecen a EAS Secrets, no a git.
- Solo no-secretos en runtime: Pasa banderas de feature y URLs de API pública a través de
extra y léelas con Constants.expoConfig.
- Rechaza:
process.env.STRIPE_SECRET_KEY en cualquier parte del código fuente de la app o configuración.
-
Confirma lockfiles con la app: package-lock.json, pnpm-lock.yaml o yarn.lock deben coincidir con lo que EAS Build y CI resuelven - las instalaciones flotantes ocultan el sesgo de nativo/JS hasta TestFlight.
- CI: Falla las compilaciones cuando falta el lockfile o está fuera de sincronización con
package.json.
- Monorepo: Un lockfile en la raíz del workspace; las apps no mantienen árboles flotantes independientes.
-
Usa npx expo install para cada paquete Expo: npm install expo-camera@latest manual puede extraer stubs de JS que no coinciden con los binarios nativos de SDK 57 - TypeScript compila mientras que la producción falla.
- Ruta de actualización:
npx expo install --fix después de bumps de SDK, luego expo-doctor.
- Documenta: Cualquier paquete que requiera una anulación de versión personalizada obtiene un ADR.
-
Mapea perfiles de compilación de EAS a ambientes: Los perfiles eas.json declaran environment: development | preview | production para que las credenciales y variables de env se resuelvan igual en CI y en laptops.
- Sincronización local:
eas env:pull --environment development para onboarding - no copiar-pegar de Slack.
- Rechaza: URLs de API codificadas en fuente que difieren del perfil usado en EAS Build.
-
Directorio app/ delgado: Los archivos de Expo Router re-exportan pantallas de feature - la lógica de negocio, hooks y clientes de API no viven junto a _layout.tsx.
- Patrón:
app/(tabs)/orders/index.tsx exporta OrdersScreen desde features/orders.
- Lint: Regla ESLint opcional o CODEOWNERS en
app/ para mantener archivos bajo ~20 líneas.
-
Organiza por feature, no solo por capa: features/orders/ posee pantallas, hooks, presenters y tipos específicos de feature - la components/ global es solo para primitivos de design-system.
- Rechaza: Árboles
screens/, hooks/ y services/ donde cada feature toca cada carpeta.
- Disparador de escala: Revisita a ~15 ingenieros antes de que los ciclos de importación fuercen una reescritura.
-
Una exportación pública por feature: Los archivos de ruta importan desde features/orders barrel - sin importaciones profundas entre features en features/billing/hooks/useInvoice.ts.
- Refuerza: Límites de módulos Nx o regla ESLint personalizada
no-restricted-imports en monorepos.
- Excepción: APIs compartidas
packages/core documentadas en README.
-
Mantén los scripts de package.json aburridos: start, ios, android, lint, typecheck, test - cada ingeniero no debe memorizar banderas Metro de una sola vez.
- CI refleja local: Los mismos nombres de script se ejecutan en GitHub Actions y EAS hooks.
- Documenta: Scripts no obvios (
postinstall, eas-build-pre-install) en README.
-
Extiende expo/tsconfig.base: TypeScript estricto de la plantilla - la deriva personalizada tsconfig rompe Expo Router typed routes y tipos de autolinking.
- Rutas: Refleja aliases
@/* en Jest moduleNameMapper cuando uses aliases de ruta.
- Rechaza:
skipLibCheck: false pelea con tipings de RN de terceros - sigue los valores predeterminados de Expo.
-
Coloca pruebas junto al código fuente: Button.test.tsx junto a Button.tsx o bajo features/orders/__tests__/ - __tests__ distantes en la raíz del repo dejan de actualizarse.
- Excluye: Mantén pruebas fuera de archivos de ruta
app/ - usa __tests__ o *.test.tsx en src/ y features/.
- CI:
npm test -- --ci en cada PR.
-
Gitignore ios/ y android/ bajo CNG: Las carpetas nativas confirmadas de un SDK anterior bloquean plantillas prebuild de RN 0.86 - regenera en EAS Build o npx expo prebuild --clean.
- Excepción de brownfield: Documenta en ADR si las carpetas nativas son intencionalmente confirmadas.
- Ritual de actualización: Elimina carpetas nativas antiguas antes de
expo prebuild después de bumps de SDK.
-
Expresa cambios nativos como plugins de configuración: Editar manualmente Info.plist o AndroidManifest.xml se pierde en npx expo prebuild --clean - el predeterminado de SDK 57.
- Plugins idempotentes: Seguros en ejecuciones de prebuild repetidas - los plugins que se añaden dos veces corrompen proyectos.
- Mira: Native Module Rules para cuándo el código nativo personalizado está justificado.
-
Comienza con Expo Go, graduate a dev builds: Expo Go es adecuado para aprender; agrega expo-dev-client tan pronto como dependas de módulos nativos no incluidos en Expo Go.
- Regla de reconstrucción: Reconstruye el dev client después de cada bump de SDK - las actualizaciones OTA solo cambian JavaScript.
- CI: Un perfil
development de EAS produce dev clients instalables para QA.
-
Una instancia react-native en el gráfico de dependencias: Las copias nativas duplicadas causan "Invalid hook call" y redboxes oscuras - npx expo-doctor y npm why react-native en CI.
- Monorepo:
npx expo install desde el directorio de la app; eleva consciente con pnpm nodeLinker.
- Rechaza:
node_modules/react-native anidado de paquetes de workspace conflictivos.
-
Deja que SDK 57 configure Metro primero: La detección automática de monorepo vence watchFolders escrito manualmente - personaliza solo cuando doctor y compilaciones prueban una brecha.
- Documenta: Cualquier cambio personalizado
metro.config.js enlaza al error que arregla.
- Rechaza: Copiar-pegar configuración Metro de una entrada de blog pre-SDK-54.
-
Registra la raíz con registerRootComponent: Los puntos de entrada bare o brownfield que omiten bootstrap de Expo pierden inicialización de autolinking y fallan misteriosamente cuando llaman módulos nativos.
- Verifica:
MainComponent / moduleName coincide con registro de JS en embeds de brownfield.
- Mira: Documentos de Expo Platform para entrada
expo en package.json main.
-
Establece owner para proyectos de organización: Omitir "owner": "org-slug" en app.config enruta compilaciones y credenciales a una cuenta personal en lugar de al equipo.
- Un
projectId por binario de app: Reutilizar un proyecto EAS en bundle IDs no relacionados colisiona credenciales y canales de actualización.
projectId estable: Nunca rota - los servicios EAS usan la clave UUID para la vida útil de la app.
-
Tokens de robot para CI, no contraseñas personales: Los usuarios de robot con alcance de organización sobreviven salidas de empleados y proporcionan una ruta de revocación auditable.
- Alcance: Permisos mínimos por workflow - compilación, envío, actualización.
- Rechaza: Cuentas humanas compartidas "ci@company" con rol Owner.
-
Ignora .env.local, confirma .env.example: Los secretos específicos de máquina permanecen locales - env de ejemplo documenta claves EXPO_PUBLIC_* requeridas sin valores.
- Onboarding: README enumera
eas env:pull como paso dos después de npm install.
- Rechaza:
.env.production con secretos reales en historial de git.
-
Ejecuta expo-doctor en CI en cada PR: Detecta versiones de módulos nativos no coincidentes, campos de configuración inválidos y sesgo de dependencias antes de merge.
- Ramas de actualización: Doctor debe pasar antes de que se fusione el PR de bump de SDK - no como un ticket de seguimiento.
- Empareja con:
tsc --noEmit y expo lint.
-
Documenta verificación de primera ejecución: Un script que ejecuta expo-doctor, tsc --noEmit y opcionalmente una compilación de dev-client - los nuevos empleados prueban la cadena de herramientas en un comando.
- Nómbralo:
npm run verify o scripts/verify-toolchain.sh.
- Actualiza: Después de cada actualización de SDK, antes de anunciar "actualización completa".
-
ADR para cada elección arquitectónica no predeterminada: Configuración Metro personalizada, carpetas nativas confirmadas, navegación no Expo o almacenamiento seguro omitido - registra contexto y fecha de revisión.
-
Actualiza SDKs secuencialmente: Saltar múltiples versiones de SDK en un PR compone cambios que rompen - pasa a través de changelogs entre fusiones incluso si el objetivo final es SDK 57.
- Lee: Notas de RN 0.86, Hermes, Reanimated y New Architecture antes de fusionar.
- Plan de reversión: Sabe qué compilación de tienda y canal OTA promocionar si la rama de actualización falla QA.
- Tier 1 (1-6): Scaffold y configuración - arregla antes de escribir features; el pin de SDK incorrecto o los secretos filtrados son costosos de desentrañar.
- Tier 2 (7-12): Diseño y límites - previene pantallas divinas y espagueti de importación a medida que crece el personal.
- Tier 3 (13-18): Flujo nativo - costo más alto cuando se viola; errores de prebuild y Metro bloquean lanzamientos.
- Tier 4 (19-25): Gobernanza y actualizaciones - fija convenciones antes de que el equipo se duplique.
¿Deberíamos confirmar las carpetas ios/ y android/?
No para aplicaciones Expo estándar de CNG - gitignore y déjales regenerar por EAS Build o expo prebuild. Las aplicaciones brownfield con código nativo mantenido a mano son la excepción; documenta esa elección en un ADR.
¿app.json o app.config.ts?
Prefiere app.config.ts para cualquier app con más de un ambiente. Mantén un app.json mínimo solo si las herramientas lo requieren, pero evita duplicar campos en ambos archivos.
¿Cuándo es suficiente Expo Go?
Aprendizaje, demos y trabajo temprano de UI sin módulos nativos personalizados. Muévete a expo-dev-client antes de integrar pagos, push, tareas de fondo o cualquier paquete que no esté en Expo Go.
¿Cómo reforzamos la regla de app/ delgado?
Revisión de código, max-lines ESLint opcional en app/** y barrels de feature que las pantallas deben importar. Los archivos de ruta deberían leer como una tabla de contenidos, no una novela.
¿Qué pertenece a EXPO_PUBLIC_ vs EAS Secrets?
EXPO_PUBLIC_* solo para valores seguros para enviar en el bundle del cliente (URLs de API pública, claves de analytics destinadas al cliente). Los secretos del servidor, claves de firma y tokens de admin permanecen en EAS Secrets y código del lado del servidor.
¿Los monorepos necesitan reglas diferentes?
Las mismas reglas aplican; agrega: un lockfile, una instancia react-native, npx expo install desde cada directorio de app, y aplicación de límites Nx o Turborepo para importaciones entre features.
¿Con qué frecuencia deberíamos ejecutar expo-doctor?
En cada PR en CI, localmente después de npm install e imperativamente antes de fusionar cualquier rama de actualización de SDK.
¿Podemos omitir lockfiles en paquetes de solo librerías?
Las apps siempre confirman lockfiles. Los paquetes de workspace interno consumidos solo a través de workspace:* siguen el lockfile raíz - no flotar versiones dentro de packages/ui.
¿Qué scripts debe tener cada app Expo?
Como mínimo: start, objetivos de plataforma (ios/android o expo run:*), lint y typecheck. Agrega test una vez que Jest esté configurado. CI debe llamar a los mismos nombres que los desarrolladores usan localmente.
¿Cuándo necesitamos un ADR en lugar de una nota de README?
Cuando la elección afecta código nativo, canales de lanzamiento, postura de seguridad o es difícil de revertir - módulos nativos personalizados, ios/ confirmado, fijación de certificados u omisión de expo-secure-store para tokens.
¿Cómo manejamos aplicaciones de etiqueta blanca?
Variantes app.config separadas por marca (APP_VARIANT), bundle IDs distintos, projectId EAS separado por binario, packages/core compartido - no código de feature bifurcado por cliente.
¿New Architecture cambia estas reglas del proyecto?
RN 0.86 favorece New Architecture por defecto - las reglas del proyecto siguen aplicándose. Las actualizaciones de SDK requieren leer elementos del changelog nativo; CNG y expo-doctor importan más, no menos.
¿Cuál es la puerta CI mínima para un nuevo repo Expo?
expo-doctor, tsc --noEmit, expo lint y pruebas unitarias (jest --ci). Agrega EAS Build en main una vez que los módulos nativos excedan Expo Go.
¿Las features deberían importar de otras features?
Solo a través de barrels públicos o paquetes compartidos - nunca importa profundamente los hooks internos de otra feature. El acoplamiento entre features pertenece a packages/core con APIs explícitas.
Versiones de stack: Esta página fue escrita para React 19.2.3, React Native 0.86.0 y Expo SDK 57 (expo ~57.0.4).