Piensa como un ingeniero senior de Flutter en la era de la IA

Piensa como un ingeniero senior de Flutter en la era de la IA

Piensa como un ingeniero senior de Flutter en la era de la IA

Hay un secreto sucio en la comunidad Flutter: cuando decimos “diseño de sistemas”, la mayoría de los ingenieros empieza a dibujar diagramas de balanceadores de carga y colas de mensajes. Y ojo, lo entiendo, es lo que nos enseñaron los libros de preparación de entrevistas. Pero en el momento en que te piden diseñar los sistemas *frontend* —el árbol de widgets, el flujo de estado, la capa de datos de una app Flutter—, la sala se queda en silencio.

Y es que el diseño de sistemas frontend es real, es difícil, y es exactamente donde las herramientas de IA van a hacerte o mucho más productivo o peligrosamente descuidado. No hay punto medio.

He pasado el último año viendo a agentes de IA generar apps Flutter a una velocidad aterradora. Parte de ese código es precioso. La mayoría es una pila de deuda técnica sostenida por un `StatefulWidget` que toca tres APIs distintas. Eso no es un problema de herramientas. Es un problema de pensamiento.

Vamos a arreglarlo.

Qué significa realmente “diseño de sistemas” para Flutter

Cuando un ingeniero senior escucha “diseña una app de chat”, no empieza por `ListView.builder`. Empieza por los límites.

Los ingenieros de backend piensan en servicios, bases de datos y colas. Los ingenieros de Flutter tenemos que pensar en:

- **Descomposición de widgets**: ¿cuándo un widget de 600 líneas se convierte en una carpeta de widgets enfocados?
- **Propiedad del estado**: ¿qué estado vive en el árbol de widgets, qué vive en un controlador, qué vive en un repositorio?
- **Flujo de datos**: ¿cuál es la dirección única de los datos desde la red hasta los píxeles?
- **Gestión de fallos**: ¿dónde viven los reintentos, los fallbacks y los estados de error?

Un ingeniero senior de Flutter ve una app como una cebolla. La capa externa es el árbol de widgets (presentación). La capa del medio es la gestión de estado (lógica de aplicación). El núcleo es el acceso a datos (repositorios, servicios, almacenamiento local). El trabajo del diseño de sistemas es evitar que esas capas se derritan unas sobre otras.

La regla que te salva la cordura: los widgets no hacen negocio

Hay una regla que le doy a cada mentee: **tus widgets no deberían conocer tu lógica de negocio, y tu lógica de negocio no debería conocer tus widgets.**

Si un `TextFormField` está llamando a un repositorio directamente, rompiste el límite. Si tu repositorio sabe lo que es un `TextEditingController`, lo rompiste en la dirección opuesta. Esa separación es lo que hace que el código sea testeable, reemplazable y, lo más importante en la era de la IA, hace posible que un agente edite código sin hacer explotar toda tu app.

Cómo cambia la IA el juego

Herramientas como GitHub Copilot, Claude Code y Cursor cambiaron fundamentalmente la forma en que se escribe código. Puedo montar una app CRUD completa en una tarde. No es una exageración; es un jueves cualquiera.

Pero esto es lo que aprendí después de decenas de proyectos Flutter asistidos por IA: **los agentes son excelentes escribiendo código y pésimos tomando decisiones arquitectónicas.**

Dale a un agente una carpeta con un widget incompleto y una instrucción clara, y lo va a clavar. Dale un prompt vago como “haz que esta app sea mejor”, y va a tomar silenciosamente cuarenta decisiones no relacionadas, algunas de ellas catastróficamente equivocadas.

Esto es exactamente por qué el agente OpenClaw que hackeó el sistema de reservas de un gimnasio se hizo viral. No porque el código fuera inteligente, sino porque al agente se le dio un *objetivo* sin *restricciones*. Ese mismo patrón lo veo en bases de código Flutter donde se dejó sueltos a los agentes sin barreras arquitectónicas.

Escenario uno: el desastre generado por IA

El mes pasado, un desarrollador al que mentorizo abrió su proyecto y me enseñó lo que había producido un agente. La tarea era simple: “Añade soporte offline a la pantalla de pedidos”.

Lo que hizo el agente:

- Añadió `path_provider` y `sqflite` al `pubspec.yaml`
- Creó una base de datos SQLite dentro del `initState` de un widget
- Envolvió un cliente HTTP de terceros en una clase personalizada sin interfaz
- Cacheó respuestas JSON completas en una variable estática global

Técnicamente, “funcionaba”… en el camino feliz. Pero la conexión a la base de datos filtraba memoria, la caché no tenía estrategia de invalidación, y la interfaz se congelaba en dispositivos lentos porque las operaciones SQLite corrían en el mismo isolate principal.

La solución no eran mejores prompts. La solución era **mejor diseño de sistemas**: decidir *antes* de que el agente escribiera código que la persistencia viviría detrás de un repositorio abstracto, que usaría almacenamiento seguro en tiempo de compilación como [drift](https://drift.simonbinder.eu), y que expondría un stream de estado que la UI solo observa.

Patrones de diseño de sistemas que todo ingeniero Flutter debería conocer

Te voy a dar el manual que yo uso cuando me piden diseñar una funcionalidad Flutter, ya sea un humano, un agente o una combinación de ambos.

1. El pastel de tres capas (Presentación / Aplicación / Datos)

- **Capa de presentación**: widgets, animaciones, routing. Solo consume estado y emite intenciones.
- **Capa de aplicación**: gestión de estado (providers de Riverpod, cubits de Bloc, ChangeNotifiers). Transforma intenciones en cambios de estado.
- **Capa de datos**: repositorios, servicios, bases de datos locales, clientes de red. Se encarga del trabajo sucio de hablar con el mundo exterior.

Cada capa solo habla con la capa directamente inferior. Los widgets nunca tocan `Dio`. Los repositorios nunca devuelven un `BuildContext`.

Si te preguntas “¿dónde vive la validación de formularios?” o “¿debería poner la API key en el Provider?”, la respuesta es siempre “en la capa que es dueña de esa preocupación”.

2. Gestión de estado como flujo unidireccional

No me importa qué librería de gestión de estado elijas —[Riverpod](https://riverpod.dev), [Bloc](https://bloclibrary.dev), o incluso un `InheritedWidget` pelado—, siempre que el flujo sea unidireccional:

```
Stream de Estado -> Los Widgets Renderizan -> Intenciones del Usuario -> Mutaciones de Estado -> Repetir
```

En el momento en que tienes bindings bidireccionales, tienes bugs. En el momento en que tienes widgets mutando estado directamente, tienes bugs que no puedes reproducir. En el momento en que tienes un `static var` global guardando datos de usuario, tienes una revisión de seguridad en tu futuro.

3. Límites de error y degradación elegante

Un ingeniero senior diseña primero para el fallo. Cada pantalla responde estas preguntas:

- ¿Qué pasa si la API está caída?
- ¿Qué pasa si el usuario no tiene internet?
- ¿Qué pasa si la forma del JSON cambia?
- ¿Qué pasa si el dato es null?

Esto no se trata de envolver todo en try-catch. Se trata de diseñar *tipos de estado* que incluyan estados `loading`, `loaded`, `error` y `empty`, y luego asegurarte de que cada widget sepa renderizar los cuatro. Te prometo que esto vale más que cualquier paquete oscuro.

El enfoque “especificación primero” para Flutter asistido por IA

Ya dije que los agentes de IA se desvían sin restricciones. La solución que a mí me funciona de forma consistente es el **desarrollo guiado por especificación**. Escribe la especificación *antes* de que el agente empiece a codear.

Este es el flujo de trabajo:

1. **Escribe un Registro de Decisión de Arquitectura (ADR) de una página** para la funcionalidad. Que sea de menos de 500 palabras. Incluye la estructura del árbol de widgets, los puntos de integración con la gestión de estado y los contratos de datos.
2. **Define los límites de archivos**. Nombra las carpetas: `features/checkout/widgets/`, `features/checkout/logic/`, `features/checkout/data/`. Dale al agente rutas de archivo explícitas.
3. **Indícale al agente que te pregunte a ti por las decisiones arquitectónicas, no que las tome él**. Esta sola frase previene el 90% de las historias de terror arquitectónicas impulsadas por IA.
4. **Revisa el código del agente contra el ADR antes de ejecutarlo**. La revisión de código sigue siendo tu mejor prueba.

Esto no es burocracia. Es asegurarte de que las 500 líneas de código del agente caigan en el lugar correcto. Porque la alternativa es refactorizar, y refactorizar código generado por IA es peor que refactorizar código humano, porque *nadie* lo escribió con intención.

Escenario dos: respuestas de IA en streaming (sí, estás construyendo una funcionalidad de IA)

Estás construyendo una funcionalidad de chat con IA. El agente puede generar una pantalla de respuestas en streaming con animaciones hermosas en minutos. Pero da un paso atrás y piensa a nivel de sistemas:

- ¿Dónde vive el historial de la conversación? ¿En memoria, en almacenamiento local, en el servidor?
- ¿Qué pasa cuando el stream HTTP se corta a mitad de una respuesta?
- ¿Cómo limitas la tasa de peticiones y manejas los errores 429?
- ¿Cómo manejas contenido inseguro o alucinado en la interfaz?

Un ingeniero senior diseña el estado del streaming (`StreamBuilder`, estado respaldado por buffer, lógica de reintento) antes de escribir un solo widget. El agente puede llenar el resto.

Escenario tres: una app de tareas offline-first

Crea una app donde los usuarios gestionen tareas en un avión. La pregunta de diseño de sistemas es: ¿optimizas para no perder datos nunca, o para la velocidad?

Con una base de datos local basada en `drift`, un repositorio que sincronice con una API REST, y un provider de conectividad que rastree el estado de la red, el diseño se mantiene limpio. El agente escribe la tubería; el ingeniero senior diseña la *forma* de esa tubería.

Lo que la IA vuelve “innecesario” (y lo que no)

Hay una ola de artículos de opinión que afirman que la IA eliminará la necesidad de desarrolladores humanos. El caso alcista es que le da más autonomía a los creadores; el caso bajista es que expone las grietas que la autonomía hace más difíciles de ocultar. Andy Budd escribió sobre esto en diseño digital, y es exactamente cierto en Flutter: la IA elimina la *fricción* de escribir código, pero no la *responsabilidad* de saber qué escribir.

La IA puede:

- Generar código boilerplate de widgets más rápido de lo que tú lo escribes
- Traducir un mock de diseño a un árbol de widgets en segundos
- Escribir unit tests para tus repositorios

La IA no puede:

- Decidir dónde vive el estado
- Decidir el trade-off entre frescura de caché y costo de API
- Decidir cómo debería verse el skeleton de carga cuando la API va lenta

Esas son decisiones de diseño. Son *tu* trabajo, y lo seguirán siendo dentro de diez años.

Checklist rápida para tu próximo diseño de sistemas Flutter

Cuando alguien te pida diseñar una app Flutter —en una entrevista, en un documento de diseño, o solo en tu cabeza—, recorre esta lista:

1. **¿Cuáles son los átomos funcionales?** Divide la app en funcionalidades. Cada una debería poder construirse, probarse y eliminarse de forma independiente.
2. **¿Cuáles son los átomos de estado?** Para cada funcionalidad, identifica: qué estado es local, qué es compartido y qué es persistido.
3. **¿Cómo entran y salen los datos?** Cada fuente de datos tiene un repositorio. Cada repositorio devuelve objetos tipados, no JSON crudo.
4. **¿Qué pasa cuando se rompe?** Estados de fallo para cada llamada asíncrona. Estados de carga para cada stream.
5. **¿Cómo encontraría el código un desarrollador nuevo (o un agente de IA)?** La estructura de carpetas debería auto-documentarse.

Siendo realistas: el principio de “suficientemente bueno”

No quiero que termines este post pensando que tu app necesita seis capas de abstracción antes de poder añadir un botón de login. El diseño de sistemas se trata de *proporción*.

Una app utilitaria de dos pantallas no necesita Riverpod con code generation. Necesita un `StatefulWidget` y un repositorio. Una app de e-commerce en producción no necesita treinta providers para renderizar una lista de productos. Necesita límites claros, propiedad deliberada del estado, y una capa de datos que no se filtre en la UI.

Los buenos ingenieros senior saben cuándo *no* aplicar complejidad. Los agentes de IA no lo saben. Felizmente generarán un patrón factory para una funcionalidad que podría tener cuarenta líneas. Tu trabajo es ser la barrera.

Reflexiones finales

Esto es lo que separa a un ingeniero Flutter junior de uno senior en la era de la IA: **los juniors le piden a la IA que construya la app, y luego intentan arreglar lo que se rompe. Los seniors diseñan los límites de la app, y luego dejan que la IA construya dentro de ellos.**

Adopta la mentalidad de tres capas. Escribe especificaciones cortas antes de soltar a los agentes. Revisa con intención. Diseña para el fallo primero. Y recuerda: el sistema se trata de *lo que estás construyendo*, no de la IA que lo escribe.

Porque ya sea un humano o un modelo de lenguaje escribiendo el código, alguien tiene que decidir dónde vive el estado. Ese alguien deberías ser tú.

FAQ

1. ¿De verdad necesito “diseño de sistemas” para Flutter? ¿No es eso cosa del backend?

El diseño de sistemas frontend es absolutamente real. Se trata de decidir dónde vive el estado, cómo fluyen los datos y dónde poner los límites entre widgets, lógica y capas de datos. El diseño de backend maneja escala; el diseño frontend maneja *complejidad*. Una app Flutter con cincuenta widgets interdependientes y sin límites es tan difícil de mantener como un backend con servicios espagueti.

2. ¿Cómo debería enfocar el diseño de sistemas en una entrevista de Flutter?

Enfócate en separar responsabilidades. Los entrevistadores quieren escucharte hablar sobre descomposición del árbol de widgets, propiedad de estado (qué es local vs. global), contratos de datos (¿qué devuelve el repositorio?) y gestión de fallos. Mencionar herramientas como Riverpod o Bloc está bien, pero nombrar *cuándo usarías cada una* es mejor. Y no corras a escribir código. Habla primero de los trade-offs.

3. ¿Debería dejar que los agentes de IA diseñen mi arquitectura Flutter desde cero?

No. Los agentes son excelentes implementando dentro de límites, pero pondrán felizmente una llamada a la base de datos dentro de un widget si el prompt es vago. Escribe una especificación corta —aunque sean bullets— definiendo carpetas, puntos de integración con el estado y contratos de datos *antes* de que el agente empiece. Luego revisa el resultado contra ella. Tu especificación es la palanca de control más importante.

4. ¿Es mejor Riverpod o Bloc para apps Flutter grandes?

Ambos están listos para producción. Esto es más una pregunta de filosofía que de tecnología. Riverpod te da seguridad en tiempo de compilación, providers con alcance y un estilo más funcional. Bloc impone un patrón estricto de eventos/estados que es más fácil de documentar, a costa de verbosidad. Para apps grandes, elige según la comodidad de tu equipo y luego sé consistente. Aplicar cualquiera de los dos de forma consistente es mejor que cambiarlo a mitad de proyecto. Lee ambos docs — [riverpod.dev](https://riverpod.dev) y [bloclibrary.dev](https://bloclibrary.dev) — y toma la decisión con tu equipo.

Comments (0)

No comments yet. Be the first to comment!

Leave a Comment