Descúbrelo sobre la marcha: diseñar software en la era de la IA
Ensayo

Descúbrelo sobre la marcha: diseñar software en la era de la IA

Osama Alghanmi · 21 de febrero de 2026

No necesitas un diseño perfecto desde el principio. Necesitas una dirección y las ganas de seguir refinando hasta que las cosas converjan.

Esto es lo que aprendí después de construir la misma aplicación una docena de veces, quemarla y reconstruirla, hasta que el sistema me enseñó lo que quería ser.

El mito del diseño perfecto desde el principio

Todo proyecto de software empieza igual: alguien dibuja cajas en una pizarra y declara: «Esta es la arquitectura». Suele haber muchos asentimientos. A veces hay una segunda pizarra.

Entonces empiezas a construir de verdad, y la realidad aparece sin que nadie la invite.

Las cajas no encajan. Los datos fluyen en la dirección equivocada. Ese servicio que creías sencillo tiene diecisiete casos límite. Hacia la tercera semana estás escribiendo código que pide disculpas en voz baja a la pizarra.

La dura verdad es esta: no puedes conocer la forma completa de tu sistema antes de haber construido partes de él. Los requisitos mienten. Los usuarios te sorprenden. La escala revela cosas que ningún diagrama predijo.

Y eso está bien. El problema no es que tu diseño estuviera equivocado. El problema es tratar el diseño original como sagrado.

Consistencia eventual, pero para tu código

Si has trabajado con sistemas distribuidos, conoces el concepto de consistencia eventual: distintos nodos de un sistema pueden tener vistas diferentes de los datos en un momento dado, pero con el tiempo y la comunicación suficientes, todos convergen al mismo estado.

El diseño de tu software funciona igual.

Empiezas con información incompleta. Tu arquitectura tiene huecos. Tu comprensión del dominio es parcial. Pero si sigues iterando, y cada ciclo hace el diseño un poco más honesto, un poco más alineado con la realidad, convergerás hacia algo que de verdad encaja.

El objetivo no es acertar a la primera. El objetivo es seguir equivocándote cada vez menos.

Llámalo desarrollo Figure-It-Out-As-You-Go (FIAYG), es decir, «ya lo averiguarás sobre la marcha». No es tan pegadizo como TDD o DDD, pero es bastante más honesto sobre cómo se construye realmente el software.

La clave es esta: cada vez que aprendes algo nuevo, de un error, del feedback de los usuarios, de chocar con un muro de escala, codificas ese aprendizaje en la arquitectura. No arreglas solo el síntoma. Arreglas el sistema para que el síntoma no pueda repetirse.

Cómo construimos Almadar (o: una historia de caos productivo)

Almadar es un marco declarativo que compila esquemas de máquinas de estados en aplicaciones full-stack. Un solo archivo .orb describe tus entidades, comportamientos e interfaz, y el compilador genera un frontend en React que funciona, un backend en Express y los modelos de la base de datos.

Eso es lo que es ahora. Así llegamos hasta aquí.

Etapa 1: El proyecto único

Empezó de forma sencilla. Teníamos un proyecto, un cliente que necesitaba una aplicación de negocio con mucho CRUD. La programamos a mano, de la forma normal: componentes de React, rutas de Express, entidades de TypeORM. Código repetitivo por todas partes.

Notamos que escribíamos los mismos patrones una y otra vez: una tabla para listar registros, un modal para crearlos, un formulario para editarlos y una confirmación para borrarlos. Copiar. Pegar. Renombrar. Repetir.

Tiene que haber una forma mejor.

Así que probamos algo: ¿y si describíamos los datos y el comportamiento en un esquema JSON y generábamos el código repetitivo a partir de él?

Fue tosco. El esquema apenas tenía estructura. El generador era una plantilla de cadenas con llamadas a fs.writeFileSync. Pero funcionó. El cliente estaba contento. Entregábamos más rápido.

Lo que aprendimos: el 80 % de toda aplicación que es código repetitivo se puede describir de forma declarativa. El 20 % que es único es en lo que de verdad quieres invertir tu tiempo.

Etapa 2: El segundo cliente lo rompe todo

El segundo cliente tenía requisitos distintos. Nuestro generador estaba demasiado acoplado a los patrones del primer cliente. Habíamos fijado suposiciones por todas partes: convenciones de nombres, estructura de archivos, jerarquía de componentes.

Podríamos haberlo parcheado. En cambio, nos detuvimos y preguntamos: ¿Cómo sería una versión general de esto?

Aquí la arquitectura empezó a cristalizar. Introdujimos el concepto de Orbitals, módulos de funcionalidad autocontenidos que combinan una entidad (la forma de los datos), un trait (comportamiento como máquina de estados finitos) y una página (la ruta). Separamos la definición del esquema de la generación de código.

Lo que aprendimos: en el momento en que tienes un segundo caso de uso, tus abstracciones se revelan. Si no se generaliza, no era una abstracción. Era una coincidencia.

Etapa 3: El monorepo y las restricciones

A medida que crecía el número de proyectos, crecía también el caos. Cada proyecto de cliente tenía su propio sistema de diseño, sus propias variaciones de componentes, su propia idea de cómo era un «botón». Manteníamos diez sabores del mismo componente.

Así que reestructuramos todo en un monorepo con reglas explícitas:

  • Los componentes básicos viven en @almadar/ui, compartidos, versionados, nunca duplicados
  • Los sistemas de diseño de los clientes viven en projects/{name}/design-system/, extienden el núcleo, no lo reemplazan
  • El compilador está en orbital-rust/, sagrado, nunca se modifica sin discutirlo
  • El código generado va en projects/{name}/app/, nunca se edita directamente

Más importante aún, añadimos restricciones. Reglas duras que el equipo codifica en un archivo CLAUDE.md y que todo desarrollador (humano y de IA) debe seguir:

  • Nunca uses elementos HTML en bruto, usa siempre componentes del sistema de diseño
  • Primero el esquema, edita el archivo .orb, recompila, nunca toques el código generado
  • El compilador es sagrado, añadir una regla requiere aprobación explícita

Y hicimos cumplir esas restricciones con un plugin de ESLint propio (@almadar/eslint). No eran pautas opcionales, sino errores de verdad que bloquean el hook de pre-commit. Cero advertencias permitidas. Algunas de las reglas que construimos:

ReglaQué detecta
no-raw-dom-elementsBloquea <div>, <span>, <button>, etc. Usa VStack, Typography, Button en su lugar
no-as-anyBloquea los casts de tipo as any. Arregla el problema de tipos real
no-import-generatedBloquea importar desde projects/*/app/. El código generado no es una API
require-closed-circuit-propsTodo componente debe aceptar className, isLoading, error, entity
require-event-busLas interacciones deben usar useEventBus(), no onClick en bruto
require-display-nameTodo componente debe definir displayName para depurar
require-storiesTodo componente nuevo debe entregarse con un archivo .stories.tsx
event-naming-conventionLos eventos de la UI deben seguir el patrón UI:EVENT_NAME
template-no-hooksLos componentes plantilla son puros. Sin useState, sin useEffect
template-no-iterationLas plantillas no hacen bucles. La iteración la hace el organismo
organism-no-callback-propsLos organismos se comunican por el bus de eventos, no con callbacks en las props

El hook de pre-commit ejecuta ESLint sobre cada archivo .ts/.tsx preparado. Si salta cualquier regla, el commit se bloquea. Sin excepciones. Toda regla tiene severidad 2 (error), no advertencia. Las advertencias son aspiracionales. Los errores son arquitectura.

El resultado: un desarrollador nuevo, humano o de IA, no puede violar por accidente los principios de diseño. El linter dice que no antes de que se abra siquiera la pull request.

Las restricciones suenan limitantes. En realidad liberan. Cuando las reglas son claras, las decisiones se toman más rápido, las inconsistencias se detectan antes y los miembros nuevos del equipo pueden contribuir sin romper nada.

Lo que aprendimos: un monorepo sin restricciones es solo un monolito educado. Las restricciones son la arquitectura, y el linting es cómo las haces reales.

Etapa 4: La IA cambia las reglas

Aquí es donde se pone interesante.

Cuando llegaron los asistentes de programación con IA, nuestra arquitectura basada en restricciones resultó ser exactamente lo que necesitaban para trabajar bien. ¿Un modelo de lenguaje grande (LLM) con un esquema claro que editar, un registro de patrones contra el que validar, un linter que bloquea los malos patrones y un compilador que dice «no» a la salida rota? Eso es un agente de IA productivo.

¿Un LLM trabajando en una base de código libre y sin reglas? Eso es un agente muy seguro de sí mismo que hace lo incorrecto con total seguridad.

Lo formalizamos: la IA recibe las restricciones en CLAUDE.md, consulta el registro de patrones antes de añadir componentes, ejecuta ESLint sobre cada archivo que cambia y valida el esquema con el compilador de Orbital antes de dar nada por terminado. El compilador es el revisor de código de la IA, nunca se cansa, nunca se le pasa un caso y se ejecuta en menos de 50 ms.

La arquitectura con la que tropezamos a base de iterar resultó ser la arquitectura correcta para el desarrollo asistido por IA. No lo planeamos. Convergimos hacia ella.

Lo que aprendimos: la IA amplifica cualquier estructura (o caos) que ya exista en tu base de código. Las restricciones no son burocracia. Son la diferencia entre una IA que ayuda y una IA que rompe cosas con total seguridad.

El manual de FIAYG

Si eres principiante, así puedes aplicar esto sin construir un marco desde cero:

1. Empieza simple. Vergonzosamente simple.

Tu primera versión debe hacer una sola cosa. No la cosa, más manejo de errores, más caché, más un sistema de configuración. Solo la cosa. Aprenderás más entregando algo pequeño que diseñando algo grande.

2. Fíjate en la repetición, es un mensaje

Cuando escribes el mismo código por tercera vez, la base de código te está diciendo que algo quiere abstraerse. No abstraigas en la primera repetición (prematuro). No ignores la tercera (negligencia). La segunda vez es la señal, la tercera es la confirmación.

3. Codifica lo que aprendes como restricciones

Cuando encuentres un patrón de error, como un modal que puede quedarse abierto atascado, no arregles solo ese modal. Averigua qué regla evitaría por completo esa clase de error. Escríbela. Hazla cumplir. En el caso de Almadar, convertimos «todo modal debe tener una transición CANCEL» en una regla de validación del compilador, y «nunca uses elementos HTML en bruto» en un error del linter. En tu proyecto puede ser una regla de ESLint propia, un hook de pre-commit o una lista de revisión de código. El medio no importa. Lo que importa es que la regla sea automática. Si requiere que un humano la recuerde, acabará olvidándose.

4. Tu arquitectura debería hacerse más pequeña con el tiempo

Cada vez que detectas un patrón y lo generalizas, deberías escribir menos código para lograr lo mismo. Si tu base de código sigue creciendo al mismo ritmo para siempre, estás acumulando complejidad, no gestionándola. El objetivo es más funcionalidad por línea, no más líneas.

5. No temas al giro

Renombramos cosas. Borramos paquetes enteros. Movimos el sistema de diseño dos veces. Cada vez que lo hicimos, el sistema quedó más limpio y las reglas más claras. Un giro no es un fracaso. Es la prueba de que tu comprensión ha mejorado.

Cómo se ve la convergencia

Después de varios años y muchos proyectos de clientes, esto es a lo que convergimos:

  • Un archivo de esquema por proyecto, la única fuente de verdad
  • Un compilador, determinista, que valida más de 50 reglas y genera todo el código repetitivo
  • Un sistema de diseño con extensiones por proyecto
  • Un plugin de ESLint propio, 17 reglas aplicadas en cada commit
  • Una biblioteca estándar de comportamientos reutilizables (CRUD, asistentes, maestro-detalle, paginación)
  • Una restricción que sigue todo desarrollador y todo agente de IA

La conclusión

La mejor arquitectura de software no es la que diseñas antes de empezar. Es la que surge de escuchar lo que tu sistema intenta decirte, a través de los errores, de la repetición, de la fricción que sientes cuando algo va mal.

Averígualo sobre la marcha. Codifica lo que aprendes. Sigue equivocándote cada vez menos.

La consistencia es eventual. Entrega de todos modos.

Publicado por primera vez en Dev.to.

Osama Alghanmi

El conocimiento es libertad. Los sistemas deben estar abiertos a las personas a las que gobiernan.

© 2026 Osama Alghanmi. Liubliana, Eslovenia.