03 — Desarrollo Móvil
iOS, Android, multiplataforma y los backends detrás de ellos
Apps móviles que se entregaron a usuarios reales — y se mantuvieron entregadas — durante más de once años.
iOS y Android nativos en sus propios lenguajes, React Native y Flutter donde encajan, y los backends, cachés y nubes que hacen de un teléfono más que una cáscara delgada. La trayectoria de once años es lo que hace creíble el resto.
He pasado más de once años construyendo y entregando aplicaciones móviles a usuarios reales — y ese solo hecho es el que hace que cada otra afirmación en esta página merezca ser leída.
Cualquiera puede listar frameworks. La credencial que importa es la longevidad: apps que personas reales instalaron, conservaron y usaron en iOS, Android y Windows, durante más de una década. La trayectoria de once años no es una línea en un currículum — es la evidencia de que las decisiones de abajo se tomaron bajo la presión de software que tenía que seguir funcionando después del lanzamiento.
Trabajo nativo primero. En iOS eso significa Swift y Objective-C con SwiftUI, Core Data, Core Animation y SiriKit; en Android significa Kotlin y Java contra el Android SDK en muchas versiones de SO. Esa fluidez nativa es exactamente lo que hace del trabajo multiplataforma — React Native y Flutter a nivel avanzado, Xamarin a nivel funcional — una elección deliberada en lugar de una forma de evitar una plataforma.
Y una app móvil nunca es solo el cliente. Detrás de ella se sientan backends en Node, Spring Boot, Django, Flask, Rails y Go, exponiendo REST y GraphQL, respaldados por SQLite en el dispositivo y Redis en el servidor, desplegados en Firebase, AWS y Google Cloud. La página que sigue es toda esa superficie, contada como realmente la construyo.
años entregando apps móviles a usuarios reales, en iOS, Android y Windows
plataformas nativas mantenidas en sus propios lenguajes — Swift/Objective-C y Kotlin/Java
stacks multiplataforma que opero a nivel avanzado — React Native y Flutter
frameworks de backend que emparejo con clientes móviles, de Node a Go
Dos plataformas nativas, dos stacks multiplataforma, una superficie Windows.
La primera decisión en cualquier proyecto móvil es sobre qué construir, y rara vez es la misma respuesta dos veces. La matriz de abajo es cómo lo razono: iOS y Android nativos cada uno en su propio lenguaje, dos stacks multiplataforma que opero a nivel avanzado, y una superficie Windows que mantiene honesta la lógica compartida. El diagrama mapea los lenguajes y frameworks sobre las plataformas en lugar de pretender que una herramienta lo cubre todo.
Lenguajes y frameworks, mapeados a plataformas
Una matriz, no un monolito — cada plataforma en el lenguaje que recompensa.
iOS recibe Swift y Objective-C; Android recibe Kotlin y Java; Windows recibe su propio cliente. React Native y Flutter abarcan las tiendas cuando la UI no pelea con la plataforma, y la columna de la derecha muestra dónde aterriza realmente cada herramienta.
Leer la matriz es el trabajo: es cómo decido si una función es nativa, multiplataforma o un cliente delgado sobre un backend compartido, antes de escribir una línea de UI.
- iOS — Swift, Objective-C, SwiftUI, Core Data
- Android — Kotlin, Java, Android SDK
- Multiplataforma — React Native, Flutter (avanzado)
- Windows — cliente de escritorio nativo
Lo que realmente escribo en cada plataforma.
Las plataformas no son intercambiables, y el utillaje lo refleja. Cada pestaña es una plataforma en la que he entregado — los lenguajes, los frameworks y el criterio sobre cuándo cada uno se gana su lugar.
iOS nativo en Swift, con Objective-C donde aún vive
En iOS escribo en Swift y leo Objective-C, porque las bases de código reales que llevan años entregándose rara vez son Swift puro. Construyo interfaces en SwiftUI para pantallas nuevas y mantengo UIKit donde un flujo existente depende de él, en lugar de reescribir código que funciona por el bien de un framework.
La persistencia es Core Data cuando la app posee un grafo de objetos real, y el movimiento es Core Animation cuando una transición necesita sentirse nativa en lugar de aproximada. SiriKit maneja intents de voz donde se ganan su lugar — no en cada pantalla, solo donde un atajo hablado es genuinamente más rápido que tocar.
- Swift primero, Objective-C leído y mantenido
- SwiftUI para pantallas nuevas, UIKit conservado donde funciona
- Core Data para el grafo de objetos, Core Animation para el movimiento
- Intents de SiriKit donde la voz es de verdad más rápida
Android en Kotlin y Java, en muchas versiones de SO
En Android trabajo en Kotlin y Java contra el Android SDK, y he entregado contra un amplio rango de versiones de SO — el trabajo práctico es soportar los dispositivos que los usuarios realmente cargan, no solo el último lanzamiento. Eso significa decisiones honestas de SDK mínimo y probar la fragmentación, no darla por superada.
Los mismos patrones se trasladan: una capa de datos clara, una UI consciente del ciclo de vida y el manejo explícito de las cosas que Android te obliga a manejar — muerte de proceso, cambios de configuración y los límites de fondo que se aprietan con cada lanzamiento.
- Kotlin y Java contra el Android SDK
- Entregado en muchas versiones de SO de Android
- UI consciente del ciclo de vida, manejo explícito de la muerte de proceso
- Decisiones de SDK mínimo guiadas por dispositivos reales
Clientes de Windows junto al trabajo móvil
También construyo para Windows, lo que mantiene la superficie honesta: una función que tiene que aterrizar en iOS, Android y Windows no puede apoyarse en las comodidades de una sola plataforma. Las partes compartidas se empujan al backend y a los contratos, y las partes específicas de cada plataforma quedan delgadas.
Esa disciplina es la razón por la que los stacks multiplataforma de abajo son una elección y no un valor por defecto — he mantenido los lados nativo y de escritorio, así que sé qué me está ahorrando realmente cada abstracción.
- Clientes de Windows entregados junto a lo móvil
- Lógica compartida empujada al backend y a los contratos
- Código por plataforma mantenido delgado y explícito
React Native y Flutter a nivel avanzado, Xamarin funcional
React Native y Flutter los opero a nivel avanzado. Recurro a ellos cuando un producto necesita que un equipo entregue dos tiendas con rapidez y la UI no pelea con la plataforma — y bajo a módulos nativos en cuanto una pantalla necesita Core Animation o un sensor que el puente no expone limpiamente.
Xamarin lo tengo a nivel funcional, lo que importa en organizaciones inclinadas a .NET donde el resto del stack ya es C#. El punto no es la lealtad a un framework; es ajustar el stack al equipo y al producto.
- React Native — avanzado, con módulos nativos donde haga falta
- Flutter — avanzado, una sola base de código para dos tiendas
- Xamarin — nivel funcional, para equipos inclinados a .NET
- Bajar a nativo cuando el puente se interpone
Swift como se lee en una pantalla real.
Un panel de código es más honesto que una lista de funciones — esta es la forma del trabajo en iOS, no una descripción de él.
En iOS me apoyo en SwiftUI para superficies nuevas y Core Data para el grafo de objetos detrás de ellas. El fragmento de abajo es del tipo que escribo a diario: un modelo pequeño y tipado, un fetch que la vista puede observar y un layout que se mantiene declarativo. Es deliberadamente ordinario — el punto de la fluidez nativa es que el código de rutina sea limpio, no ingenioso.
import SwiftUI
import CoreData
struct ContactList: View {
@FetchRequest(
sortDescriptors: [SortDescriptor(\.name)]
) private var contacts: FetchedResults<Contact>
var body: some View {
List(contacts) { contact in
VStack(alignment: .leading) {
Text(contact.name)
.font(.headline)
Text(contact.phone)
.font(.subheadline)
.foregroundStyle(.secondary)
}
}
.animation(.easeInOut, value: contacts.count)
}
} El conjunto de frameworks de iOS
SwiftUI arriba, Core Data debajo, SiriKit donde la voz es más rápida.
Los frameworks se componen: SwiftUI renderiza, Core Data persiste, Core Animation maneja el movimiento que SwiftUI no, y SiriKit expone el uno o dos intents donde un atajo hablado de verdad supera al toque. Objective-C sigue en escena porque las apps de larga vida todavía lo contienen, y leerlo es parte de mantenerlas.
Nada de esto se añade por sí mismo. Cada framework está en la app porque un problema específico — persistencia, movimiento, voz — lo pidió.
- SwiftUI para pantallas nuevas, UIKit conservado donde funciona
- Core Data para el grafo de objetos
- Core Animation para el movimiento que se siente nativo
- Intents de SiriKit solo donde la voz es más rápida
Kotlin contra el Android SDK, con fragmentación y todo.
Android recompensa la explicitud. El fragmento de abajo es un ViewModel de Kotlin que expone el estado de UI como un flow — consciente del ciclo de vida, sobrevive a cambios de configuración y hace su carga fuera del hilo principal. Habiendo entregado en muchas versiones de SO, escribo código Android que asume la muerte de proceso y los límites de fondo apretados en lugar de esperar evitarlos.
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
class ContactViewModel(
private val repo: ContactRepository
) : ViewModel() {
private val _state =
MutableStateFlow<UiState>(UiState.Loading)
val state: StateFlow<UiState> = _state.asStateFlow()
init {
viewModelScope.launch {
runCatching { repo.loadContacts() }
.onSuccess { _state.value = UiState.Ready(it) }
.onFailure { _state.value = UiState.Error }
}
}
} Muchas versiones de SO, dispositivos reales
Soporta los teléfonos que los usuarios cargan, no el del keynote.
La fragmentación de Android no es un problema del que quejarse — es el entorno. El trabajo es elegir un SDK mínimo honesto, probar el abanico de versiones y tamaños de pantalla que la base de usuarios real corre, y manejar las reglas de fondo y permisos que se aprietan con cada lanzamiento.
Java sigue en el utillaje junto a Kotlin por la misma razón que Objective-C en iOS: el código entregado vive mucho tiempo, y mantenerlo significa leerlo con fluidez.
- Kotlin y Java contra el Android SDK
- Elecciones honestas de SDK mínimo
- Probado en las versiones que los usuarios corren
- Manejo explícito de fondo y permisos
La forma de una app, dibujada de la UI al almacén de datos.
Una app móvil es una pila de capas claras — y la disciplina es mantenerlas claras para que cada una pueda probarse y reemplazarse por sí sola.
Sea cual sea la plataforma, la arquitectura a la que recurro es la misma en espíritu: una capa de UI que solo renderiza estado, una capa de dominio que sostiene la lógica, una capa de datos que habla con la red y el almacén del dispositivo, y una frontera de sincronización en medio. El diagrama muestra las capas y la única dirección a la que apuntan las dependencias.
Mantener las flechas apuntando en un solo sentido es lo que hace mantenible la app un año después — la UI no alcanza la red, y la red no sabe de la UI. La misma forma se mantiene tanto si el cliente es Swift, Kotlin, React Native o Flutter.
UI → dominio → datos → almacén
Cuatro capas, dependencias apuntando en un solo sentido.
La UI observa el estado y emite intents; la capa de dominio decide; la capa de datos obtiene y persiste; SQLite sostiene la verdad en el dispositivo y la red lleva el resto. Cada frontera es un lugar donde puedo poner un test o intercambiar una implementación sin que las capas de arriba lo noten.
No es arquitectura por sí misma. Es la estructura que dejó que once años de apps siguieran aceptando funciones sin colapsar bajo ellas.
- La UI renderiza estado, nunca alcanza la red
- La capa de dominio sostiene la lógica y las reglas
- La capa de datos posee la red y la persistencia del dispositivo
- SQLite como fuente de verdad en el dispositivo
El lado servidor de cada app móvil.
Un cliente móvil es tan bueno como el backend con el que habla — así que construyo ese backend también, en el framework que encaje con el equipo.
Detrás de las apps he entregado Node/Express, Spring Boot, Django, Flask, Ruby on Rails y Go con Gin y Gorm. El framework se elige por el equipo y el presupuesto de latencia, no por costumbre: Node donde el equipo es fluido en JavaScript, Spring donde es una casa JVM, Django o Flask para backends pesados en Python, Rails para CRUD guiado por convención, y Go donde el presupuesto por solicitud es ajustado.
Cada uno expone un contrato — REST o GraphQL — contra el que se construye el cliente móvil, persiste a través de ORMs como Sequelize y Mongoose, y cachea la ruta caliente en Redis. El proceso de abajo es el mismo sin importar qué framework se sienta en medio.
Del contrato a un dispositivo sincronizado
- 01 Definir el contrato Esquema REST o GraphQL acordado primero — el cliente y el servidor se construyen contra un contrato, no el uno contra el otro.
- 02 Elegir el framework Node/Express, Spring Boot, Django/Flask, Rails o Go (Gin/Gorm) — elegido para encajar con el equipo y el presupuesto de latencia.
- 03 Modelar los datos ORMs — Sequelize o Mongoose — sobre el almacén relacional o documental que el dominio realmente necesita.
- 04 Cachear la ruta caliente Redis delante de las lecturas costosas, con invalidación explícita en lugar de TTLs esperanzados.
- 05 Sincronizar el dispositivo SQLite en el dispositivo, un protocolo de sincronización sobre el contrato, resolución de conflictos definida antes de necesitarla.
- 06 Entregar el pipeline Builds firmados, tests automatizados, entrega a tiendas — la CI que convierte un commit en un build que un usuario puede instalar.
func GetContact(c *gin.Context) {
id := c.Param("id")
if cached, err := rdb.Get(ctx, id).Result();
err == nil {
c.Data(200, "application/json",
[]byte(cached))
return
}
var contact Contact
if err := db.First(&contact, id).Error;
err != nil {
c.JSON(404, gin.H{"error": "not found"})
return
}
c.JSON(200, contact)
} Un handler en Go, como se entrega
REST y GraphQL, servidos rápido y tipados.
El fragmento es un handler de Gin que lee a través de Gorm con una caché Redis delante — la forma pequeña y repetible de un endpoint en el que un cliente móvil puede confiar: una respuesta tipada, un error predecible y una caché que quita carga de la base de datos en la ruta caliente.
Sea el framework Go, Node o Spring, el contrato que ve el teléfono es el mismo. Ese es el punto de acordarlo primero.
- Go con Gin y Gorm en las rutas de presupuesto ajustado
- Redis cacheando las lecturas costosas
- Respuestas tipadas, formas de error predecibles
- Mismo contrato sin importar el framework
El stack móvil completo — del cliente a la nube
- Lenguajes iOS
- Swift · Objective-C
- Frameworks iOS
- SwiftUI · UIKit · Core Data · Core Animation · SiriKit
- Lenguajes Android
- Kotlin · Java
- Objetivo Android
- Android SDK, muchas versiones de SO
- Multiplataforma
- React Native · Flutter (avanzado) · Xamarin (funcional)
- Escritorio
- Clientes de Windows
- Backends
- Node/Express · Spring Boot · Django/Flask · Rails · Go (Gin/Gorm)
- APIs
- REST · GraphQL
- En dispositivo / datos
- SQLite · Redis · Sequelize · Mongoose
- Nube móvil
- Firebase · AWS · Google Cloud
- Oficio de frontend
- HTML5 · CSS3 · SASS/SCSS · JS ES6+ · TypeScript
Cuando la red falla — y en un teléfono, lo hará.
Un teléfono se mueve por túneles, ascensores y zonas muertas. Una app que asume conectividad es una app que se cuelga en un spinner frente a un usuario real.
Construyo apps móviles offline-first: el dispositivo escribe en SQLite de inmediato y encola el cambio para sincronizar, de modo que el usuario nunca espera a la red para ver surtir efecto su propia acción. Cuando la conectividad vuelve, la cola se drena sobre el contrato REST o GraphQL, y los conflictos se resuelven por una política decidida por entidad antes de necesitarla.
El diagrama traza ese bucle — escritura local, cola, sincronización, reconciliación — y el modo de fallo es la parte que importa: la app degrada a solo lectura en lugar de perder una escritura en silencio. Esa es la diferencia entre una app en la que los usuarios confían y una que abandonan.
Escritura local → cola → sync → reconciliar
El dispositivo es la fuente de verdad hasta que el servidor está de acuerdo.
Cada escritura aterriza primero en SQLite y se refleja en una cola de sincronización. Una sincronización en segundo plano drena la cola contra el servidor, el servidor reconcilia usando lecturas respaldadas por Redis y el almacén mapeado por ORM, y el estado resuelto fluye de vuelta al dispositivo. El usuario ve feedback local instantáneo y consistencia eventual, no un spinner.
Diseñar la política de conflictos de antemano — last-write-wins, merge o manual — es lo que mantiene honesto el bucle cuando dos dispositivos editan el mismo registro.
- Escritura local en SQLite antes de cualquier llamada de red
- Cambio encolado, drenado cuando vuelve la conectividad
- Política de conflictos definida por entidad por adelantado
- Degradar a solo lectura, nunca perder una escritura
Offline-first — el contrato con la red
- Almacén en dispositivo
- SQLite — la fuente de verdad mientras está offline
- Ruta de escritura
- Escritura local primero, encolada para sincronizar
- Transporte de sync
- REST o GraphQL sobre el contrato acordado
- Política de conflictos
- Definida por entidad antes de necesitarla
- Caché de servidor
- Redis delante de las lecturas costosas
- Almacén de servidor
- Relacional o documental, vía Sequelize o Mongoose
- Modo de fallo
- Degradar a solo lectura, nunca perder una escritura en silencio
El usuario nunca debería esperar a la red para ver su propia acción. Escribe localmente, sincroniza en segundo plano y nunca pierdas una escritura — esa es toda la disciplina offline-first.
Firebase, AWS y Google Cloud — elegidas por encaje.
La nube no es una sola decisión. Un equipo pequeño necesita un backend para ayer; un producto que escala necesita control sobre su cómputo. Cada pestaña es una plataforma a la que despliego backends móviles, y el criterio sobre cuándo cada una es la respuesta correcta.
Firebase cuando un equipo pequeño necesita un backend para ayer
Firebase se gana su lugar cuando un producto móvil necesita autenticación, un almacén de datos sincronizado y push sin levantar infraestructura primero. Uso Auth para el inicio de sesión, Firestore para los datos estructurados y consultables, y la Realtime Database donde el patrón de acceso es genuinamente un árbol en vivo en lugar de documentos.
La disciplina es saber dónde Firebase deja de ser la respuesta correcta — reglas de seguridad que tienen que codificar autorización real, y costes de lectura que crecen con un modelo de datos ingenuo. Diseño los documentos en torno a las consultas, no al revés.
- Auth para el inicio de sesión, Firestore para datos estructurados
- Realtime Database donde un árbol en vivo encaja con el patrón de acceso
- Reglas de seguridad tratadas como autorización real
- Documentos diseñados en torno a las consultas
AWS para los backends que superan a un BaaS
Cuando un producto necesita control sobre su cómputo, lo corro en AWS — Lambda para cargas dirigidas por eventos y con picos, EC2 donde se requiere un proceso de larga vida o un runtime específico, y S3 para los medios y las cargas estáticas que un cliente móvil descarga.
Lo móvil cambia las restricciones: los arranques en frío se sienten en un teléfono, los reintentos de subida tienen que sobrevivir a un enlace celular inestable, y el dispositivo no puede mantener una conexión abierta como puede un servidor. La arquitectura tiene en cuenta la red en la que el dispositivo realmente está.
- Lambda para cargas dirigidas por eventos y con picos
- EC2 para procesos de larga vida y runtimes específicos
- S3 para medios y cargas descargables
- Diseñado para una red celular inestable, no una LAN de centro de datos
Google Cloud donde la plataforma encaja con la carga
En Google Cloud uso App Engine para servicios gestionados que escalan sin que yo cuide instancias, y Compute Engine donde una carga necesita una VM completa. Se empareja naturalmente con Firebase cuando parte de un producto es un almacén de datos gestionado y parte es cómputo a medida.
La elección entre nubes rara vez es ideológica. Es qué servicios gestionados quitan más trabajo operativo a este equipo mientras mantienen dentro de presupuesto la latencia que el cliente móvil ve.
- App Engine para servicios gestionados de autoescalado
- Compute Engine donde se requiere una VM completa
- Se empareja con Firebase para backends mixtos gestionados/a medida
- Elegido por encaje operativo, no por lealtad
Dónde corre realmente el backend
Gestionado donde ahorra trabajo, controlado donde tiene que estarlo.
Firebase Auth y Firestore levantan un backend sincronizado sin infraestructura; Lambda y EC2 dan cómputo serverless y de larga vida en AWS; App Engine y Compute Engine hacen el equivalente en Google Cloud; S3 sostiene los medios que un teléfono descarga. El diagrama mapea el cliente sobre esas opciones.
La restricción móvil corre por todo ello: los arranques en frío se sienten en un teléfono, y las subidas tienen que sobrevivir a un enlace celular. La nube se elige para que la latencia que el usuario ve se mantenga dentro de presupuesto.
- Firebase — Auth, Firestore, Realtime Database
- AWS — Lambda, EC2, S3
- Google Cloud — App Engine, Compute Engine
- Elegido por encaje operativo, latencia en presupuesto
React Native y Flutter — una elección, no un valor por defecto.
Los stacks multiplataforma dejan que un equipo entregue ambas tiendas desde una sola base de código, y opero React Native y Flutter a nivel avanzado. Pero la razón por la que confío en ellos es que he entregado nativo primero: sé exactamente qué me ahorra el puente y dónde se interpone. Cuando una pantalla necesita Core Animation, un sensor que el puente no expone limpiamente, o un comportamiento de plataforma que la abstracción tapa, bajo a un módulo nativo sin dudar.
Xamarin se sienta a nivel funcional en el utillaje, que es la respuesta correcta en organizaciones inclinadas a .NET donde el resto del stack ya es C#. La decisión entre ellos nunca es sobre lealtad — es sobre ajustar el stack al equipo y al producto, con nativo siempre disponible debajo.
Una base de código, dos tiendas, escotilla de escape nativa
Lógica de negocio compartida arriba, módulos nativos donde el puente se detiene.
En una app multiplataforma la lógica de negocio y la mayor parte de la UI viven en una sola base de código compartida — React Native o Flutter — que renderiza tanto a iOS como a Android. Las partes que la abstracción no puede servir bajan a módulos nativos en Swift y Kotlin, de modo que la app nunca cambia capacidad por portabilidad.
Esa escotilla de escape es toda la razón por la que la fluidez nativa importa incluso en un proyecto multiplataforma. La capa compartida cubre el terreno común; los módulos nativos cubren los bordes.
- Lógica y UI compartidas en una sola base de código
- Renderiza tanto a iOS como a Android
- Módulos nativos en Swift / Kotlin en los bordes
- La capacidad nunca se cambia por portabilidad
El pipeline que convierte un commit en una instalación.
La CI móvil es más dura que la CI web — hay dos cadenas de herramientas, firma de código y dos procesos de revisión de tienda entre el commit y el usuario.
Un release móvil no es un deploy; es un build, una firma y un envío. El pipeline que corro hace lint y type-check primero, luego construye el archive de iOS con Xcode y lo firma, construye el APK o AAB de Android firmado con Gradle, y envía artefactos versionados a TestFlight y al canal interno antes de la tienda. La regla es simple: sin pipeline en verde, no hay release.
El diagrama de abajo muestra ese reparto — un commit, dos cadenas de herramientas, dos tiendas — porque el coste de equivocarse en la firma de código o en un bump de versión es un envío rechazado y un día perdido, no un re-deploy rápido.
Commit → comprobaciones → dos builds → dos tiendas
Un commit se reparte en dos builds firmados.
El pipeline corre las comprobaciones estáticas una vez, luego se divide: una ruta Xcode que construye, firma y archiva para iOS, y una ruta Gradle que ensambla y firma para Android. Cada una produce un artefacto versionado que va al canal de pruebas de su tienda antes del release.
Tratar el build firmado como lo único que se entrega — nunca un archive local de la máquina de alguien — es lo que mantiene reproducibles los releases a lo largo de once años de apps.
- Lint, type-check y tests unitarios como compuerta
- Xcode firma y archiva para iOS
- Gradle ensambla APK/AAB firmado para Android
- Artefactos versionados a canales de prueba, luego tienda
CI móvil — etapa por etapa
- Disparador
- Commit a una rama de release
- Comprobaciones estáticas
- Lint, type-check (TypeScript), tests unitarios
- Build iOS
- Build con Xcode, firma de código, archive
- Build Android
- Gradle assemble, APK/AAB firmado
- Artefacto
- Build versionado por plataforma
- Entrega
- TestFlight / canal interno, luego tienda
- Compuerta
- Sin pipeline en verde, no hay release
Las superficies web que se sientan junto a la app.
Un producto móvil rara vez es solo la app. Hay páginas de onboarding, superficies de marketing, web views embebidas y lógica compartida que quieren el mismo cuidado que el código nativo. Construyo esas en HTML5 y CSS3 limpios, organizo los estilos con SASS/SCSS para que el sistema de diseño siga siendo un sistema, y escribo la lógica en JavaScript moderno — ES6+ y TypeScript dondequiera que el proyecto tolere un sistema de tipos.
TypeScript en particular se paga solo en móvil: un contrato tipado atrapa el desajuste entre cliente y servidor antes de que un usuario lo vea, y los mismos tipos pueden fluir desde el esquema GraphQL hasta el cliente React Native.
HTML5 y CSS3
Las superficies web que se sientan junto a una app móvil — páginas de onboarding, marketing, web views embebidas — construidas en HTML5 y CSS3 limpios en lugar de volcadas en un framework por reflejo.
SASS/SCSS
Hojas de estilo organizadas con SASS/SCSS para que el sistema de diseño siga siendo un sistema: variables, partials y mixins en lugar de declaraciones copiadas.
JavaScript ES6+
JavaScript moderno para la lógica compartida y la capa web — módulos, async/await y las características del lenguaje que hacen legible una base de código años después.
TypeScript
TypeScript dondequiera que el proyecto lo tolere, porque un contrato tipado atrapa el desajuste entre cliente y servidor antes de que un usuario lo vea.
APIs REST
REST para los endpoints sencillos con forma de recurso, con versionado y formas de error predecibles en las que el cliente móvil puede confiar.
APIs GraphQL
GraphQL donde una pantalla móvil necesita exactamente sus datos en un solo viaje de ida y vuelta — el viaje de red que un teléfono paga es el que vale la pena ahorrar.
Once años, una decisión de plataforma a la vez.
Cimientos, nativo, multiplataforma, backend, nube — el mismo ingeniero, siguiendo el trabajo mientras los productos crecían más allá de sus primeros mil usuarios.
Leído en secuencia, el trabajo móvil es una sola trayectoria continua en lugar de una lista de frameworks. Empieza con once años entregando a usuarios reales, se profundiza en iOS y Android nativos, se extiende por React Native y Flutter donde encajan, alcanza de vuelta los backends que hacen de un cliente más que una cáscara, y llega a las nubes que lo escalan.
Lo que atraviesa todo ello es la misma negativa que recorre el resto de mi trabajo: la app y el sistema detrás de ella son un solo problema, no dos repartidos entre dos personas.
- Cimientos Once años entregando a usuarios reales La trayectoria que ancla todo lo demás: más de once años construyendo y entregando apps móviles que personas reales instalaron y usaron, en iOS, Android y Windows.
- Nativo iOS y Android en sus propios lenguajes Swift y Objective-C en iOS con SwiftUI, Core Data, Core Animation y SiriKit; Kotlin y Java en Android en muchas versiones de SO. La fluidez nativa que hace informadas las elecciones multiplataforma.
- Multiplataforma React Native y Flutter a nivel avanzado Un equipo entregando dos tiendas donde encaja, bajando a módulos nativos donde no — más Xamarin a nivel funcional para equipos inclinados a .NET.
- Backend El lado servidor de cada app Node/Express, Spring Boot, Django/Flask, Rails y Go (Gin/Gorm), exponiendo REST y GraphQL, respaldados por SQLite, Redis y ORMs — los backends que hacen de un cliente móvil más que una cáscara delgada.
- Nube Firebase, AWS y Google Cloud Almacenes de datos gestionados, cómputo serverless y almacenamiento elegidos por encaje operativo — la infraestructura que escala una app más allá de sus primeros mil usuarios.
Los principios bajo las plataformas.
Las plataformas y frameworks cambian con el producto; los principios no. Estas son las reglas que aplico tanto si la app es Swift nativo, Kotlin nativo, React Native o Flutter — la parte que convirtió once años de trabajo móvil en apps que siguieron funcionando en lugar de un portafolio de lanzamientos.
La fluidez nativa gana la abstracción
Porque he entregado Swift y Kotlin directamente, elegir React Native o Flutter es un intercambio medido, no una forma de evitar aprender una plataforma. Sé qué me ahorra el puente y qué me oculta.
El contrato va primero
Cliente y servidor se construyen contra un contrato REST o GraphQL acordado, idealmente tipado de principio a fin. La integración se diseña antes de escribir cualquiera de los dos lados, no se negocia después de que ambos se rompan.
Asume que la red fallará
Un teléfono se mueve por zonas muertas. La app escribe localmente en SQLite primero, encola para sincronizar y degrada a solo lectura en lugar de perder una escritura o colgarse en un spinner.
Respeta las reglas de la plataforma
Límites de fondo, muerte de proceso, revisión de tienda, firma de código — no son obstáculos que rodear. Trabajar con ellos es lo que mantiene una app instalada en lugar de desinstalada.
Prueba la fragmentación
Muchas versiones de SO de Android y un abanico de dispositivos iOS significa probar lo que los usuarios realmente cargan, no afirmar que el último lanzamiento cubre a todos.
Once años son la credencial
Cada afirmación aquí está respaldada por apps que se entregaron y se mantuvieron entregadas. La longevidad es la evidencia — no un framework en una diapositiva, sino software que sobrevivió al contacto con usuarios reales.
Once años de apps que se entregaron y se mantuvieron entregadas es la credencial. Todo lo demás en esta página es verdad porque esa única cosa lo es.
Open to the right work
Si tu producto es una app móvil y el backend, la nube y el comportamiento offline que la hacen confiable, ese es todo el problema que quiero.
If you are holding a problem that doesn't fit inside one field, that is the conversation I want.