El criterio: cuándo Orbit compensa y cuándo bastan cincuenta líneas
Cierre del nivel con lo único que no se puede copiar de nadie: el criterio para decidir. Esta lección propone cuatro ejes medibles —número de pantallas, tamaño y rotación del equipo, horizonte de vida del proyecto y rareza de los requisitos—, enuncia las condiciones bajo las cuales la librería devuelve con creces su coste y aquellas en que cincuenta líneas propias son objetivamente mejores, y termina con lo que hace segura cualquiera de las dos decisiones: que la frontera entre ambas se pueda cruzar en las dos direcciones sin reescribir la aplicación.
La pregunta con la que se cierra este nivel no tiene una respuesta correcta universal, y esa es exactamente la razón por la que merece una lección entera en vez de una nota al pie. Quien responde siempre lo mismo —siempre la librería, siempre a mano— no ha tomado una decisión, ha adoptado una postura, y las posturas son baratas de sostener y caras de mantener. Lo que sí existe es un conjunto reducido de variables que determinan la respuesta en cada contexto concreto, y son suficientemente pocas como para enumerarlas y suficientemente observables como para medirlas antes de decidir. Este cierre las expone, las convierte en condiciones aplicables y termina en el punto que hace tolerable equivocarse: que la decisión sea reversible en ambos sentidos y que hayas escrito el código de forma que cruzar la frontera cueste días y no trimestres.
- Evaluar la decisión sobre cuatro ejes observables en vez de sobre preferencias estéticas.
- Enunciar las condiciones concretas bajo las cuales una librería devuelve su coste con creces.
- Reconocer los contextos en que la implementación artesanal es objetivamente la elección correcta.
- Diseñar la frontera para que migrar en cualquiera de las dos direcciones sea barato.
Los cuatro ejes que deciden
El primer eje es el número de pantallas con estado no trivial. Con dos o tres, la duplicación es visible y controlable y cualquier abstracción es prematura. A partir de más o menos una decena, el coste de mantener consistencia entre implementaciones caseras crece más deprisa que el de aprender una API ajena, sencillamente porque cada pantalla nueva es otra oportunidad de desviarse.
El segundo es el tamaño y sobre todo la rotación del equipo. Una persona sola que escribe su contenedor lo conoce entero y siempre; cinco personas necesitan un acuerdo, y un acuerdo no escrito es una convención que se erosiona. La variable crítica no es cuánta gente hay hoy sino cuánta habrá pasado por el código en dos años, porque cada incorporación paga de nuevo el coste de reconstruir tus decisiones tácitas.
El tercero es el horizonte de vida. Un prototipo que se enseña en tres semanas y se tira no tiene ningún interés en propiedades a largo plazo; una aplicación que se mantendrá una década con equipos intermitentes tiene un interés enorme, pero cuidado con la conclusión fácil, porque ese mismo horizonte también es el que hace pesar el riesgo de que la dependencia se abandone.
El cuarto es la rareza de los requisitos. La mayoría de las pantallas necesitan exactamente lo que la librería ofrece. Algunos dominios no: transiciones que deben ser transaccionales, contenedores que viven fuera del ciclo de vida de Android, un modelo de concurrencia que la librería no contempla. Cuando el requisito raro es central y no periférico, adaptar la librería puede costar más que no haberla usado.
flowchart TD A[Decision sobre libreria] --> B[Pantallas con estado no trivial] A --> C[Tamano y rotacion del equipo] A --> D[Horizonte de vida del proyecto] A --> E[Rareza de los requisitos] B --> F[Muchas y estandar lleva a libreria] C --> F D --> G[Corto o muy especial lleva a artesanal] E --> G style F fill:#a6e3a1,color:#11111b style G fill:#f9e2af,color:#11111b
Los cuatro ejes son observables con datos que ya tienes: cuenta las pantallas con estado, mira el historial del repositorio para estimar rotación, pregunta por el horizonte de mantenimiento y enumera por escrito los requisitos que crees especiales. Ese último ejercicio es el más revelador, porque la mayoría de los requisitos que un equipo cree únicos resultan, al escribirlos, ser el caso normal con otro nombre.
Cuando la librería devuelve su coste
Hay condiciones bajo las cuales la balanza se inclina de forma bastante inequívoca hacia adoptar una librería madura, y conviene enunciarlas como condiciones y no como preferencias.
Equipo con rotación
Varias personas y entradas o salidas previsibles. El vocabulario público y su documentación ahorran una reconstrucción por incorporación.
Testing serio
Si se afirma sobre secuencias de estados y efectos, las utilidades específicas ahorran más infraestructura que todo el resto junto.
Multiplataforma
Compartir la capa de presentación entre Android e iOS multiplica el valor de un contenedor probado en ambos destinos.
El caso de los tests merece subrayarse porque suele decidir la discusión y casi nunca se menciona al principio. Un equipo que solo comprueba el estado final apenas nota la diferencia. Un equipo que verifica que aplicar un cupón produce exactamente la secuencia de estado en validación, estado con descuento y efecto de aviso, escribe esa afirmación en tres líneas con herramientas específicas y en quince sin ellas, multiplicado por cada test de cada pantalla durante todo el proyecto. Eso ya no es ceremonia: es la mayor parte del coste.
// Con utilidades especificas la afirmacion es la secuencia esperada
@Test
fun aplicarCupon() = runTest {
CarritoViewModel(repoFalso).test(this) {
expectInitialState()
containerHost.aplicarCupon("VERANO")
expectState { copy(validando = true) }
expectState { copy(validando = false, descuento = 10) }
expectSideEffect(CarritoEfecto.AvisarCuponAplicado)
}
}
Hay además un factor que no aparece en ninguna hoja de cálculo y que pesa: la fatiga de decisión. Un contenedor propio obliga a decidir, en cada pantalla y en cada revisión, cosas que la librería ya decidió. Ninguna de esas microdecisiones es difícil; todas consumen atención, y la atención es el recurso realmente escaso de un equipo.
El caso multiplataforma merece su propio matiz. Compartir la capa de presentación entre Android e iOS multiplica el valor de un contenedor probado, pero también multiplica el coste de haberse equivocado, porque la dependencia pasa a estar en el código común y su abandono afectaría a dos plataformas a la vez. La pregunta que hay que hacerse ahí no es si la librería funciona hoy en ambos destinos, sino qué harías si dejara de publicarse para uno de ellos.
Cuando cincuenta líneas propias ganan
La dirección contraria también tiene condiciones claras. Una aplicación con tres o cuatro pantallas mantenida por una persona no recupera nunca el coste de aprender, integrar y actualizar una dependencia; ahí el contenedor artesanal no es una postura purista sino la respuesta proporcionada al problema.
Una librería de terceros que expone su propia interfaz pública tampoco quiere arrastrar dependencias arquitectónicas a quien la consuma, y esa es una razón técnica sólida para implementar el patrón internamente. Lo mismo vale para un proyecto que forma parte de un producto mayor con una política estricta sobre dependencias externas.
El caso más interesante es el pedagógico y el de los requisitos verdaderamente atípicos. Un contenedor que debe sobrevivir fuera del ciclo de vida de Android, uno que necesita transiciones con confirmación en dos fases, uno que integra un modelo de concurrencia impuesto por otra capa: en todos ellos, doblegar la librería hasta que encaje suele producir un código peor que el que se habría escrito partiendo de cero, y además dependiente de detalles internos que la próxima versión puede cambiar.
Proyecto pequeño
Pocas pantallas y una persona. El coste de aprender e integrar no se amortiza nunca.
Librería propia
Si tu código lo consumen terceros, no les impongas tus decisiones arquitectónicas por transitividad.
Requisito central atípico
Cuando lo raro está en el núcleo y no en el margen, adaptar la librería cuesta más que prescindir de ella.
Hay un cuarto caso que se menciona poco y es legítimo: el proyecto que aprende. Un equipo que implementa el patrón a mano durante un trimestre y después adopta la librería tiene, a partir de entonces, una comprensión del comportamiento por defecto que ningún tutorial le habría dado. El coste de esa inversión es real y su rendimiento también, siempre que la decisión sea consciente y esté puesta por escrito en lugar de disfrazarse de argumento técnico.
El escenario más caro no es elegir mal, sino no elegir: proyectos con dos pantallas que usan la librería, tres con una clase base propia y una que hace las dos cosas porque se migró a medias. Cada estilo es defendible por separado; la mezcla no lo es, porque multiplica el coste de comprensión sin aportar ninguna de las ventajas de ninguno. Si decides migrar, migra del todo o deja escrito por qué la frontera está donde está.
La frontera que se cruza en los dos sentidos
Lo que hace segura cualquiera de las dos decisiones es que se pueda deshacer. Y se puede, siempre que la interfaz pública de tus pantallas sea la misma en ambos mundos: un estado observable, un flujo de efectos y una función de entrada. Si la interfaz coincide, la implementación interna es sustituible sin tocar una sola línea de la interfaz de usuario ni de los tests que afirman sobre comportamiento observable.
interface Presentador<S : Any, I : Any, E : Any> {
val estado: StateFlow<S>
val efectos: Flow<E>
fun procesar(intent: I)
}
Programar la interfaz de usuario contra ese contrato en vez de contra la clase concreta convierte la migración en un ejercicio de sustituir implementaciones, pantalla a pantalla, con ambas conviviendo mientras dure. Es una capa de indirección barata que compra opcionalidad real, y su mera existencia cambia la naturaleza de la decisión: deja de ser una apuesta a diez años y pasa a ser una elección revisable con información nueva.
Conviene ser exacto sobre lo que esta interfaz protege y lo que no. Protege la superficie observable: qué publicas, qué aceptas y qué provocas. No protege la semántica: si tu contenedor artesanal permitía intents concurrentes y el nuevo los serializa, o si el orden entre reducción y efecto cambia, ninguna interfaz común te avisará. Por eso la migración real no consiste en sustituir tipos sino en tener tests que afirmen sobre secuencias, porque son los únicos que detectan un cambio de semántica que compila perfectamente.
Que la frontera se pueda cruzar no convierte la decisión en irrelevante, solo en asumible. Migrar veinte pantallas cuesta semanas aunque cada una cueste horas, y durante ese tiempo el proyecto vive en el estado mixto que la lección advertía. La conclusión práctica es doble: mantén la interfaz común desde el primer día porque su coste es casi nulo, y aun así toma la decisión inicial en serio, porque la reversibilidad reduce el riesgo pero no lo elimina.
Si algo debe quedar de este nivel no es una preferencia sino una capacidad: la de mirar una dependencia y saber exactamente qué hace por ti, porque lo has escrito tú al menos una vez. Esa capacidad reordena por completo la relación con el ecosistema. Quien nunca implementó el patrón a mano vive las librerías como magia y tiende a dos errores gemelos: adoptar la que está de moda sin entender qué problema resuelve, y quedarse paralizado cuando el comportamiento por defecto no encaja con su caso. Quien lo escribió una vez ve la librería como lo que es —una condensación de decisiones que él mismo tendría que tomar— y puede evaluarla decisión por decisión: esta la necesito, esta no, esta la habría tomado distinta y aquí está el motivo. Ese es el sentido profundo de haber dedicado un nivel entero a reconstruir lo que ya existe. No era para que lo uses en producción, aunque a veces convenga; era para que la próxima vez que abras una discusión sobre adoptar algo, la abras desde el conocimiento del problema y no desde la confianza en la solución. Y hay un corolario que se extiende mucho más allá de MVI: el criterio técnico no se hereda leyendo comparativas ni escuchando a quien tiene más seguidores, se construye implementando una vez, con cuidado, aquello que después decidirás no implementar. Ese trabajo aparentemente redundante es la única forma conocida de convertir opiniones prestadas en juicio propio, y el juicio propio es lo último que se automatiza.
- Puntúa tu proyecto real en los cuatro ejes con números concretos y no con adjetivos.
- Escribe el documento de decisión: contexto, opciones consideradas, elección y las condiciones observables que te harían revisarla.
- Introduce la interfaz común de presentador y adapta dos pantallas para que dependan de ella y no de la clase concreta.
- Migra una sola pantalla en la dirección contraria a tu elección actual y cronometra cuánto cuesta de verdad.
- Enumera los tres requisitos de tu dominio que crees atípicos y comprueba, uno por uno, si la librería ya los cubre.