TL;DR
- Usa versionado por URL para cambios mayores (v1 → v2), versionado por header para cambios menores—cada uno tiene diferentes implicaciones de caché
- Siempre soporta mínimo N-1 versiones; implementa actualización forzada solo para correcciones críticas de seguridad, no para nuevas funcionalidades
- Haz A/B testing de nuevas versiones de API con 10-20% de rollout primero—bucketing basado en hash asegura experiencia consistente por usuario
Para quién: Desarrolladores móviles, equipos backend que soportan clientes móviles, cualquiera gestionando ecosistemas API multi-versión
Puedes saltarlo si: Tu app tiene auto-actualizaciones obligatorias o controlas todos los deployments de clientes
Tiempo de lectura: 18 minutos
El versionado de API es crítico para aplicaciones móviles donde los usuarios no siempre actualizan a la última versión. Mientras que el testing de contratos API asegura la compatibilidad entre frontend y backend, la estrategia de versionado maneja la complejidad de soportar múltiples versiones de cliente en producción. Esta guía cubre estrategias de versionado, compatibilidad retroactiva, actualizaciones forzadas y testing de múltiples versiones de API simultáneamente.
Para una comprensión completa del testing API móvil, te recomiendo explorar también fundamentos del testing API, estrategias de caché API para optimización de rendimiento, y mock servers para desarrollo móvil que facilitan el testing de múltiples versiones.
Estrategias de Versionado
Versionado por URL
val BASE_URL_V1 = "https://api.example.com/v1/"
val BASE_URL_V2 = "https://api.example.com/v2/"
interface ApiServiceV1 {
@GET("users/{id}")
suspend fun getUser(@Path("id") id: String): UserV1
}
Versionado por Header
class VersionInterceptor(private val apiVersion: String) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request().newBuilder()
.addHeader("API-Version", apiVersion)
.build()
return chain.proceed(request)
}
}
Testing Compatibilidad Retroactiva
Testear múltiples versiones de API requiere cobertura completa en todas las versiones de cliente soportadas. Como se discute en dominio del testing API, una suite de tests bien estructurada valida tanto requisitos funcionales como no funcionales.
class MultiVersionApiTest {
@Test
fun testUserEndpointV1() = runTest {
val apiV1 = createApiClient(version = "v1")
val user = apiV1.getUser("123")
assertNotNull(user.id)
assertNotNull(user.name)
}
@Test
fun testUserEndpointV2() = runTest {
val apiV2 = createApiClient(version = "v2")
val user = apiV2.getUser("123")
assertNotNull(user.fullName) // renombrado de 'name'
assertNotNull(user.phoneNumber) // nuevo campo
}
}
Estrategia de Actualización Forzada
data class VersionResponse(
val minimumVersion: String,
val latestVersion: String,
val forceUpdate: Boolean,
val message: String
)
class VersionChecker(private val versionService: VersionCheckService) {
suspend fun checkForUpdate(): UpdateStatus {
val response = versionService.checkVersion("android", BuildConfig.VERSION_NAME)
return when {
response.forceUpdate -> UpdateStatus.ForceUpdate(response.message)
isNewerVersion(BuildConfig.VERSION_NAME, response.latestVersion) ->
UpdateStatus.OptionalUpdate(response.latestVersion)
else -> UpdateStatus.UpToDate
}
}
}
@Test
fun testForceUpdateDetection() = runTest {
mockService.respondWith(VersionResponse(
minimumVersion = "2.0.0",
forceUpdate = true,
message = "Por favor actualiza para continuar"
))
val status = VersionChecker(mockService).checkForUpdate()
assertTrue(status is UpdateStatus.ForceUpdate)
}
Degradación Graceful
class FeatureManager(private val apiVersion: String) {
fun isFeatureAvailable(feature: Feature): Boolean {
return when (feature) {
Feature.USER_PREFERENCES -> apiVersion >= "2.0"
Feature.PUSH_NOTIFICATIONS -> apiVersion >= "1.5"
else -> true
}
}
}
@Test
fun testFeatureAvailability() {
val managerV1 = FeatureManager("1.0")
assertFalse(managerV1.isFeatureAvailable(Feature.USER_PREFERENCES))
val managerV2 = FeatureManager("2.0")
assertTrue(managerV2.isFeatureAvailable(Feature.USER_PREFERENCES))
}
A/B Testing Diferentes Versiones de API
El A/B testing de versiones de API en aplicaciones móviles permite rollout gradual y comparación de rendimiento antes del despliegue completo.
class ApiVersionSelector(private val userId: String, private val rolloutPercentage: Int) {
fun selectApiVersion(): String {
val userHash = userId.hashCode().absoluteValue
val bucket = userHash % 100
return if (bucket < rolloutPercentage) "v2" else "v1"
}
}
@Test
fun testGradualRollout() {
val versions = (1..1000).map {
ApiVersionSelector("user$it", 20).selectApiVersion()
}
val v2Count = versions.count { it == "v2" }
val percentage = (v2Count.toDouble() / versions.size) * 100
assertTrue(percentage in 15.0..25.0) // Aproximadamente 20%
}
Estrategia de Deprecación
En arquitecturas de microservicios, trackear métricas entre versiones de API se vuelve aún más crítico para identificar cuellos de botella de rendimiento y problemas de compatibilidad.
class DeprecationMonitor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request())
val deprecationWarning = response.header("Deprecation-Warning")
if (deprecationWarning != null) {
log.warn("API Deprecación: $deprecationWarning")
analytics.logEvent("api_deprecation_warning")
}
return response
}
}
Mejores Prácticas
- Versión en URL para cambios mayores - Claro, amigable con caché
- Usa headers para cambios menores - Flexible, compatible retroactivamente
- Siempre soporta versiones N-1 - Da tiempo a usuarios para actualizar
- Implementa mecanismo de actualización forzada - Correcciones críticas
- Usa feature flags - Rollout gradual, A/B testing
- Monitorea distribución de versiones - Saber cuándo deprecar
Versionado de API Asistido por IA
Qué Hace Bien la IA
- Análisis de compatibilidad de versiones: Detectar cambios breaking entre versiones de API automáticamente
- Generación de scripts de migración: Crear código adaptador para mapear respuestas antiguas a nuevos modelos
- Generación de casos de prueba: Producir tests de compatibilidad para múltiples versiones de API
- Evaluación de impacto de deprecación: Analizar qué clientes serían afectados por sunset de versiones
Donde Se Necesitan Humanos
- Decisiones de negocio sobre timelines: Cuándo forzar actualizaciones vs extender soporte es una decisión de producto
- Comunicación con usuarios: Redactar mensajes de deprecación que no alarmen a usuarios
- Manejo de casos edge: El comportamiento real de clientes a menudo sorprende al análisis automatizado
- Decisiones de excepciones de seguridad: Determinar qué amerita una actualización forzada inmediata
Prompts Útiles
"Compara estos dos esquemas de respuesta API e identifica cambios
breaking que afectarían a clientes móviles. Considera renombres de
campos, cambios de tipo y campos eliminados: [schema v1] vs [schema v2]"
"Genera una clase adaptadora en Kotlin que mapee UserV1 a modelo
UserV2, manejando campos faltantes con valores por defecto sensatos
y logueando advertencias de deprecación."
"Crea una suite de tests que valide compatibilidad retroactiva entre
API v1 y v2, asegurando que toda funcionalidad v1 siga funcionando
para clientes que no han actualizado."
"Analiza este changelog de API y recomienda un timeline de deprecación
con umbrales de actualización forzada basados en la severidad de cambios."
Sistema de Decisión
Cuándo Invertir en Infraestructura de Versionado
| Factor | Alta Prioridad | Baja Prioridad |
|---|---|---|
| Diversidad de clientes | Múltiples versiones de app en producción | Deployment enterprise controlado |
| Frecuencia de actualización | Usuarios actualizan mensualmente o más lento | Auto-update habilitado |
| Tasa de cambios API | Cambios breaking frecuentes | Cambios estables, solo aditivos |
| Base de usuarios | Usuarios globales, conectividad variada | Red confiable, rollouts rápidos |
Cuándo NO Invertir Mucho
- Proyectos greenfield: La API cambiará rápidamente durante desarrollo inicial de todos modos
- Herramientas internas: Controlas todos los clientes y puedes sincronizar actualizaciones
- APIs stateless: CRUD simple sin transiciones de estado complejas
- Productos de corta vida: MVPs o experimentos con vida útil limitada
Medición del Éxito
| Métrica | Antes | Objetivo | Cómo Trackear |
|---|---|---|---|
| Tasa de adopción de versión | < 50% en última | > 80% en última en 30 días | Dashboard de analytics |
| Triggers de actualización forzada | 5+ por año | < 2 por año | Release notes, reportes de incidentes |
| Tiempo de ciclo de deprecación | 3+ meses | 6 semanas desde anuncio a sunset | Calendario de versionado |
| Incidentes de cambios breaking | Mensual | Trimestral o menos | Post-mortems |
| Tasa de error en versiones antiguas | > 5% | < 1% | Monitoreo de API |
Señales de Alerta
- Fragmentación de versiones: 5+ versiones activas indica pobre disciplina de deprecación
- Actualizaciones forzadas frecuentes: Usuarios deshabilitarán la app si las actualizaciones se sienten agresivas
- Fallos silenciosos: Clientes antiguos recibiendo respuestas inesperadas sin errores apropiados
- Sin monitoreo de deprecación: Volando a ciegas sobre quién usa qué versión
Conclusión
El versionado API para móviles requiere:
- Estrategia testing multi-versión
- Capas de compatibilidad retroactiva
- Mecanismos de actualización forzada
- Degradación graceful
- Infraestructura A/B testing
- Timelines de deprecación claros
El versionado apropiado asegura transiciones suaves mientras mantiene soporte para usuarios existentes, balanceando innovación con estabilidad.
Ver También
- Testing de Contratos API para Móviles - Asegura compatibilidad entre frontend y backend
- Dominio del Testing API - Fundamentos y mejores prácticas del testing API
- Estrategia de Caché API para Móviles - Optimización de rendimiento con caché
- Mock Servers para Desarrollo Móvil - Simula APIs para testing eficiente
- Testing Móvil 2025: iOS, Android y Más Allá - Tendencias actuales en testing móvil
