Elegir para tu equipo: el criterio honesto
La arquitectura correcta no es una propiedad del código sino del sistema formado por el código y las personas que lo escriben. Esta lección propone tres variables medibles (tamaño de la app, rotación del equipo y apetito real por la ceremonia), las cruza en una matriz de decisión, expone los costes que casi nadie mete en la ecuación y define cuándo y cómo se revisa una elección sin convertirla en identidad.
Las cuatro lecciones anteriores han descrito herramientas: el patrón MV, el modelo de vista moderno con @Observable, las arquitecturas unidireccionales como TCA y la separación en capas con un repository como aduana. Ninguna de ellas es la respuesta, porque la pregunta nunca fue cuál es mejor en abstracto. Una arquitectura la ejecutan personas concretas, con una antigüedad concreta en el proyecto, bajo una presión de calendario concreta, y esas variables pesan más en el resultado que cualquier propiedad formal del patrón elegido. Un diseño impecable que el equipo no sostiene se degrada en meses hasta quedar peor que el diseño mediocre que todos entendían. Este cierre trata de eso: de elegir mirando al equipo y no al blog de moda, y de escribir la decisión de forma que pueda revisarse sin que nadie pierda la cara.
- Medir las tres variables que deciden: tamaño y vida esperada de la app, rotación de personas y apetito real por la ceremonia.
- Cruzar esas variables en una matriz de decisión que produzca una recomendación defendible.
- Contabilizar los costes ocultos: formación, compilación, dependencias externas y coste de reversión.
- Documentar la decisión con criterios de revisión y fecha, para que deje de ser una cuestión de identidad.
Las tres variables que deciden
El tamaño no se mide en pantallas sino en superficie de estado compartido: cuántas entidades vive el usuario a la vez, cuántas pantallas escriben sobre las mismas y cuánta asincronía las cruza. Diez pantallas independientes son un proyecto pequeño; tres pantallas que comparten una sesión, un carrito y una sincronización en segundo plano son un proyecto grande.
Esa distinción explica por qué las comparativas basadas en el número de pantallas no predicen nada. La complejidad de una app no crece con la cantidad de vistas sino con la cantidad de pares de vistas que pueden interferir entre sí, y esa magnitud crece mucho más deprisa. Añadir la cuarta pantalla que escribe sobre el carrito no suma una unidad de dificultad: suma tres relaciones nuevas que alguien tendrá que mantener coherentes.
La rotación importa porque una arquitectura es también un mecanismo de transmisión de conocimiento. Si cada seis meses entra gente nueva, el patrón explícito y verificado por el compilador vale mucho más que el patrón implícito que se aprendía leyendo a quien ya se fue. La pregunta operativa es incómoda pero útil: si mañana desaparecieran las dos personas con más antigüedad, cuánto del diseño seguiría siendo evidente a partir del código, y cuánto era en realidad tradición oral sostenida por su presencia en las revisiones.
El apetito por la ceremonia es la variable que casi nadie mide y la que más predice el fracaso. Se mide con hechos, no con intenciones: cuántas pruebas escribe hoy el equipo sin que nadie las pida, si las revisiones de código discuten diseño o solo estilo, si la última convención acordada sigue viva seis meses después.
Conviene ver el mismo requisito escrito en los tres regímenes, porque la diferencia de coste se hace visible de golpe. El requisito es trivial a propósito: no permitir el envío mientras el campo esté vacío.
// Regimen 1: estado suelto en la vista
struct EnvioView: View {
@State private var texto = ""
var body: some View {
Button("Enviar") { }.disabled(texto.isEmpty)
}
}
// Regimen 2: modelo de vista con la regla nombrada
@Observable final class EnvioViewModel {
var texto = ""
var puedeEnviar: Bool { !texto.isEmpty }
}
// Regimen 3: la regla vive en un reducer y la mutacion es un dato
@Reducer struct Envio {
@ObservableState struct State: Equatable {
var texto = ""
var puedeEnviar: Bool { !texto.isEmpty }
}
enum Action: Equatable { case textoCambio(String), enviarTocado }
}
Ninguno de los tres es incorrecto y los tres expresan la misma regla. Lo que cambia es cuánto contexto hay que sostener para leerlos y cuánto se gana cuando el requisito deja de estar solo.
Con un solo requisito, el tercer régimen parece un despilfarro evidente. Con cuarenta requisitos que se cruzan entre sí y efectos que llegan tarde, la relación se invierte y el primero se vuelve insostenible. El error consiste en decidir mirando un requisito cuando vas a vivir con cuarenta, o en decidir mirando cuarenta cuando solo vas a tener uno.
Superficie de estado
No cuentes pantallas: cuenta entidades vivas a la vez y cuántas pantallas las escriben.
Rotación
Cuanta más gente entra y sale, más valor tiene lo que el compilador enseña sin necesidad de tutor.
Apetito medido
Mira el historial de pruebas y convenciones. Las intenciones declaradas no predicen nada.
Vida esperada
Una app de campaña que muere en seis meses y un producto de diez años no admiten el mismo cálculo.
La matriz honesta
Con las tres variables sobre la mesa, la recomendación deja de ser opinión. Esta tabla no sustituye al juicio, pero obliga a nombrar por qué te apartas de ella.
| Superficie de estado | Rotación | Apetito por la ceremonia | Recomendación |
|---|---|---|---|
| baja | baja | bajo | patrón MV puro, capas mínimas |
| baja | alta | bajo | MV con convenciones escritas y previews como documentación |
| media | baja | medio | MVVM con @Observable en las pantallas complejas |
| media | alta | medio | MVVM más repository explícito y protocolos en el dominio |
| alta | baja | alto | arquitectura unidireccional en las islas críticas |
| alta | alta | alto | arquitectura unidireccional generalizada y módulos separados |
| alta | alta | bajo | capas y repository primero; formar antes de formalizar |
Hay dos formas de usar mal esta tabla. La primera es tratarla como un veredicto y saltarse la medición, con lo que solo has cambiado una opinión por otra con formato de tabla. La segunda es leer solo la fila que confirma lo que ya querías hacer, que es el uso más frecuente de cualquier matriz de decisión publicada. El valor de la tabla no está en la recomendación sino en la obligación de escribir tres números antes de opinar.
La última fila es la más importante y la que más se ignora. Cuando la app pide formalidad y el equipo no tiene apetito, adoptar la arquitectura formal produce lo peor de ambos mundos: la ceremonia se escribe mal, las acciones degeneran en setters, las pruebas exhaustivas se desactivan una a una y al cabo de un año queda un patrón caro que ya no garantiza nada.
flowchart TD
A[Decision de arquitectura] --> B{superficie de estado alta}
B -->|no| MV[Patron MV con capas minimas]
B -->|si| C{el equipo escribe pruebas hoy sin que se lo pidan}
C -->|no| F[Formar y separar capas antes de formalizar]
C -->|si| D{la rotacion es alta}
D -->|no| ISLA[Arquitectura formal solo en las islas criticas]
D -->|si| TODO[Arquitectura formal generalizada y modulos]
style MV fill:#a6e3a1,color:#11111b
style F fill:#f9e2af,color:#11111b
style TODO fill:#89b4fa,color:#11111bLos costes que nadie mete en la ecuación
Las comparativas suelen contar líneas de código y detenerse ahí, que es la parte más barata. La formación cuesta semanas de productividad reducida por persona, y se paga de nuevo con cada incorporación. Los tiempos de compilación de una arquitectura con tipos genéricos anidados degradan el bucle de trabajo diario, y ese bucle es lo que determina cuántas veces al día alguien prueba una idea. Una dependencia externa impone su calendario de migraciones al tuyo. Y el coste que casi nunca se estima es el de reversión: cuánto costaría volver atrás si la decisión sale mal, porque una decisión con reversión barata puede tomarse con la mitad de certeza que una irreversible.
| Coste | Cómo se manifiesta | Cómo se estima antes de decidir |
|---|---|---|
| Formación | semanas de menor rendimiento por persona | tiempo hasta la primera pull request en solitario |
| Compilación | menos iteraciones al día por cada cambio | mide el módulo antes y después de una isla piloto |
| Dependencia externa | migraciones impuestas por otro calendario | revisa el ritmo de versiones mayores del paquete |
| Reversión | pantallas a reescribir para volver atrás | cuenta las pantallas convertidas y multiplícalas |
| Erosión | la forma sobrevive, la garantía se apaga | cuenta pruebas exhaustivas desactivadas por trimestre |
Casi todas estas partidas se abaratan si la adopción es local en lugar de global. Convertir una sola pantalla convierte una apuesta en un experimento: la formación se concentra en dos personas, la compilación se mide con un contrafactual real y la reversión cuesta una pantalla en vez de una app. La pregunta no debería ser qué arquitectura adoptamos, sino en qué isla la probamos y con qué criterio la juzgaremos.
La señal de alarma más fiable no es técnica sino social: cuando la discusión sobre el patrón deja de citar síntomas del proyecto y empieza a citar autoridades, conferencias o lo que hace una empresa admirada, la decisión ya se tomó por pertenencia. A partir de ahí, cualquier evidencia contraria se interpretará como falta de dominio del patrón, nunca como límite del patrón.
Cómo se revisa sin drama
Una decisión de arquitectura debería escribirse en una página con cuatro apartados: los síntomas que la motivan, la opción elegida, las opciones descartadas con su motivo, y los criterios objetivos que obligarían a revisarla, con fecha. Ese último apartado es el que convierte una creencia en una hipótesis. Criterios útiles: que las incidencias por estados imposibles no bajen en dos ciclos, que el tiempo de compilación supere un umbral acordado, que una incorporación tarde más de un mes en abrir una pull request en solitario. Cuando el criterio se cumple, la revisión no es una derrota de nadie: es el plan funcionando.
Ese documento tiene además una función que no es técnica y que justifica por sí sola escribirlo: transfiere la decisión de las personas al proyecto. Quien entre dentro de un año no tendrá que reconstruir el razonamiento preguntando a quien quede, ni deducirlo del código, ni volver a discutir desde cero una cuestión ya resuelta. Y quien defendió la opción descartada puede señalar el criterio de revisión en lugar de reabrir el debate cada trimestre, que es la forma más silenciosa en que un equipo pierde tiempo.
Existe una tentación profesional que consiste en confundir la arquitectura con su descripción, y por tanto en creer que elegir bien es un acto de juicio técnico que ocurre una vez y se documenta. Pero una arquitectura no es un plano: es una práctica, y una práctica solo existe mientras se repite. Lo que define su valor no es la calidad del diseño en el pizarrón sino su tasa de supervivencia bajo condiciones adversas, es decir, la semana de la entrega, la incorporación que aún no entiende el patrón, la corrección urgente a las once de la noche. Todo diseño se degrada en esos momentos; la única pregunta relevante es hacia dónde se degrada. Un patrón MV mal ejecutado bajo presión produce vistas gordas, que son un problema legible, localizado y reparable en una tarde. Una arquitectura formal mal ejecutada bajo presión produce algo mucho peor: la forma intacta y las garantías vacías, acciones que son asignaciones, reducers que llaman a servicios por su cuenta, pruebas exhaustivas desactivadas con un comentario. Y ese estado es más caro que no haber formalizado nunca, porque el equipo sigue pagando la ceremonia mientras confía en unas propiedades que dejaron de cumplirse hace meses sin que nada lo señalara. De ahí el criterio que cierra el nivel, y que no es una recomendación sino una restricción: elige la arquitectura más exigente que tu equipo mantenga sin necesidad de vigilancia, y ni un grado más. La sofisticación que requiere un guardián no está protegiendo tu app; está esperando el día en que el guardián se vaya.
- Mide las tres variables con datos: entidades compartidas vivas a la vez, incorporaciones del último año y número de pruebas escritas sin que nadie las pidiera.
- Sitúate en la matriz y anota en qué fila caes. Si tu práctica actual no coincide con la recomendación, escribe el motivo concreto en una frase.
- Estima el coste de reversión de tu opción preferida: cuántas pantallas habría que reescribir para volver atrás dentro de seis meses.
- Redacta la decisión en una página con síntomas, elección, descartes y criterios de revisión objetivos con fecha.
- Pon la revisión en el calendario del equipo y, llegado el día, ejecútala con los datos delante aunque el resultado te obligue a cambiar de opinión en público.