# La triste historia de desarrollar con IA

Desarrollo e IA. Si me dicen hace años que pasaría de escribir la mayoría de mi código a pedirle a "algo" (un programa) que simula inteligencia (repito: simula) que haga el código por mi lo hubiera llamado loco.

## ¿Cómo funciona la IA?

Hace años, en nuestro ahora en pausa indefinida podcast "Nebuchadnezzar", mi amigo Óliver Nabani y yo teorizábamos sobre qué haría falta para ser Tony Stark. Es un episodio muy interesante que tienen en Be Native, por cierto, y que está de actualidad ahora más que nunca. En él pensábamos qué haría falta para emular al genio, billonario, _playboy_ y filántropo. Emularlo en la forma de trabajar que tenía cuando creó el _Mark-II_ o cuando diseñó la cinta de Moebius que aplicada al universo cuántico permitía los viajes temporales.

**La gran conclusión es que el elemento fundamental para todo ello era la aplicación del conocimiento que Stark tiene. La IA está claro que tiene el conocimiento, pero lo tiene desordenado y es una como una biblioteca sin etiquetas en las estanterías.** La IA, por sí sola, es una herramienta con acceso a mucho conocimiento. Pero un conocimiento que está ordenado por asociación de palabras y conceptos. Una forma similar a como funciona la mente humana, pero el humano tiene una capacidad de "validación de la verdad" a través de fuentes externas que la IA no tiene.

Me explico: **una IA (un modelo _LLM_ para ser más exactos) se entrena "entendiendo" cómo unas palabras se relacionan con otras. Y por eso aprende cuál es la palabra más probable que seguirá a otra en una frase inacabada.** Imagina a un niño de 3 años que escucha constantemente a sus padres hablar. Cuando oye "Me voy a dormir porque tengo mucho…", el niño aprende por repetición y contexto que la palabra más probable que sigue es "sueño", no "hambre" o "frío". Cuantas más veces escucha frases similares, más fuerte se vuelve esa asociación en su cerebro. El _LLM_ funciona exactamente igual: lee millones de textos y cuando ve la secuencia "Me voy a dormir porque tengo mucho…", ajusta internamente unos pesos (como conexiones neuronales) que le indican qué tan probable es cada palabra como siguiente en la frase.

Si en los textos de entrenamiento "sueño" aparecía el 85% de las veces después de esa frase, "cansancio" el 10%, y "hambre" solo el 5%, el modelo asigna probabilidades similares. Así es como una IA aprende (un _LLM_) y cómo nos contesta. **Por lo que su conocimiento depende en toda su medida de ese entrenamiento que se le de. De ese contenido que usará para aprender las asociaciones entre palabras.** Eso, que es como aprende un niño, le permite "saber" idiomas, lenguajes de programación y hacer cosas para las que (y esto es verídico) ni quien lo hizo pensó que podría ser capaz de hacer.

**Por lo tanto, un _LLM_ que usa la tecnología _transformer_ NO es inteligente (solo lo simula), no es capaz de razonar (de nuevo, parece que lo hace, pero no) y "solo" (que no es poco) es una fuente inmensa de "conocimiento" donde su forma de conectar los puntos es a través de palabras (_tokens_) inconexos donde la asociación se busca por el "recuerdo" de cuántas veces la palabra A y la B aparecieron juntas.** Por eso un _LLM_ tiene siempre un sesgo positivo que busca la respuesta y si no encuentra las palabras apropiadas, como haría ese niño de 3 años, se las inventa y aquí no ha pasado nada. Nosotros hemos de ser el padre que detecte la "alucinación" de nuestro pequeño.

![Swifty Stark está creando la pulsera para viajar por el mundo cuántico](https://image.service.eu.axinom.net/transform/72a20w8irkd812lj5hqycl0ft-3dl5y1dj0seejmhh8wynzscv2/HCRDR2qXL1tvRRWcXjEhVk.jpg)

## La IA nunca podrá ir sola, mientras sea basada en _transformers_

Sé que es duro hacer esta afirmación, pero la hago tras años de investigación, lectura de _papers_, estudios científicos y entender a la perfección (en lo más perfecto que se puede hoy día porque nadie ha podido sacar el mapa completo de un modelo actual porque es inabarcable) cómo funciona la IA. La IA no: un _LLM_. **Un _LLM_ siempre va a necesitar supervisión detrás.** Un _LLM_ basado en _transformers_ que funciona como ya he explicado, es increíble generando "texto" y se ha convertido en un herramienta con la que ya no podemos vivir: yo no puedo vivir sin ella. **Pero hay que saber para lo que sirve, sus limitaciones y cómo usarla para sacar lo mejor de ella. Y seamos francos: muy pocos saben hacer esto y tienen la paciencia suficiente para llegar a ese niño de 3 años de la mano y que no salga corriendo y cruce la carretera sin mirar.**

Les cuento un caso empírico: en estos últimos 3 días he estado trabajando intensamente en la creación de test unitarios con _Swift Testing_ para asegurar los procesos de arranque de Be Native (sí, ya lo sé, los tests se hacen antes… no me juzguen… tú también lo hiciste alguna vez) y me he encontrado con muchas lecciones a aprender sobre el uso de los agentes de IA e incluso de los motores de búsqueda basados en _LLM_ como Perplexity.

Si queremos hacer tests unitarios lo primero que hemos de hacer es crear capas que "mockeen" (qué mal suena la expresión) los componentes reales mediante inyección de dependencias con protocolos. En Swift, obviamente. Me explico: imaginen que yo tengo que llamar a la red para enviar los IDs que he generado en el dispositivo y que el servidor los valide. Mi código tiene una función `validateID` que llama al servidor y recibe un respuesta en la forma de otro _struct_ llamado `serverResponse` con una serie de propiedades que me dicen si la validación ha ido bien o no. Eso hará que mi _app_ tome un camino A, B o C dependiendo de la respuesta del servidor. ¿Cómo pruebo que mi código hace lo que tiene que hacer y que cuando suceden todas las posibles respuestas del servidor (incluso que no funcione) la _app_ es capaz de reaccionar como debe? Obviamente, no voy a hacer que el servidor real de la _app_ haga comportamientos que no son. Tampoco puedo modificar el código real porque entonces estaría falseando las pruebas. **La premisa de un test unitario es que pruebe el código real, los flujos reales, pero con "componentes" (partes) que no sean las reales pero "simulen" su comportamiento.**

Por este motivo, uso protocolos. Si mi función `validateID` se llama así, recibe el dato `sourceID` de tipo `Data` y devuelve un tipo `serverResponse`. Yo creo un protocolo con esa exigencia: con ese contrato. De esta forma, el tipo REAL (el que hace la funcionalidad en la _app_ cuando la ejecutas) usará este protocolo y mediante acoplamiento leve, perderá su propia identidad y se convertirá en el tipo en sí del protocolo permitiendo que a través de este, que pueda pasar cualquier tipo que tenga esa función, con independencia de qué hace para resolver el problema de su definición. Al hacerlo, puedo crear otro tipo distinto con la misma función, y hacer que esa función tenga un comportamiento controlado que en realidad no conecte al servidor pero sí envíe respuesta como si fuera el servidor para generar artificialmente todas las posibles respuestas del servidor y probar cómo reacciona el código de producción ante esos casos que he creado de forma artificial. ¿Complejo, verdad? No tanto: **pero la clave de todo esto es un componente clave… la abstracción.**

**Pues bien: esto que te acabo de explicar la IA no lo entiende.** Tanto _Claude Code_ (el agente que uso ahora mismo principalmente), como _Codex_ e incluso Perplexity como buscador, no lo entienden. Si buscas te dan la información, te dicen cómo hacerlo (más o menos) pero no son capaces de aplicarlo. ¿Sabéis qué hizo Claude de primeras tras un análisis que le di sobre qué hace la capa de entrada de Be Native para que la validara? Crear funciones específicas que probaran esa funcionalidad recreada al 100%. Me explico: imagina que tengo una función que suma dos números y devuelve el resultado: para probarla tendré que mandarle operaciones de las que yo ya conozco el resultado y si 2 + 2 devuelve 4, es que la función funciona (valga la redundancia). Pues la IA creó otra función que devolviera siempre 4 cuando recibe 2 + 2, pero no usó para nada la que quería probar. Como entenderás, la validez de esa prueba es ridícula. No tiene sentido. Un _test_ está hecho para detectar cuando algo deja de funcionar y se valida cuando funciona. Hacer un _test_ que replique una funcionalidad es absurdo. Pues eso hizo: **¿por qué? Porque no sabía hacerlo de otra forma. Porque no razona. Porque no abstrae. Porque no entiende (a código hecho) qué debe hacer.** En vez de probar si mi calculadora funciona, creo una nueva para ver si como he creado mi calculadora, una calculadora funcionaría. De locos.

2 días he tardado (sí, hubiera tardado yo menos en hacerlo desde 0 sin IA, lo sé) en que el modelo sea capaz de hacerlo. Pero ha sido un reto para entender los límites de los modelos y hasta dónde puedo pedirle a los agentes. Cuál es su capacidad real y para qué puedo o no usarlo con eficiencia. Está claro que para esto, mejor hacerlo yo. **Porque todo lo que ha "aprendido" en estos días, se le olvidará en gran medida (casi todo) cuando inicie una nueva sesión y será un "volver a empezar".** Puedo crear resúmenes de implementación (lo hago) para darlos como contexto al iniciar una nueva sesión, pero todo el "aprendizaje" que ha obtenido no puede estar (no estará) en un documento porque es abstracción. Y es que realmente solo ha seguido mis instrucciones, sin "entender" nada. Ha hecho un _copy+paste_ de mí. Irónico. El modelo debería entender que si tengo un módulo de red, un módulo de grabación en el _Keychain_ del dispositivo y un módulo de generación de claves con el chip _Secure Enclave_… tengo que crear protocolos que "mockeen" (otra vez la palabra) esos comportamientos para que producción no se vea afectada pero desde el _test_ pueda darle esos entornos controlados que devuelvan las respuestas que yo busco para que el mismo exacto código de producción funcione y pueda validar todos los posibles casos que deberá controlar de forma artificial. Algo complejo, abstracto, difícil… que la IA no sabe hacer. Validar el código real, pero con "almacenes" falsos que generen respuestas artificiales que permiten "replicar" todos los posibles casos que dicho código debería controlar. Así de simple y así de sencillo.

![Swifty Stark meme](https://image.service.eu.axinom.net/transform/72a20w8irkd812lj5hqycl0ft-3dl5y1dj0seejmhh8wynzscv2/2XcTAKXLTKmGbk3iKDeYWK.jpg)

## El problema del conocimiento

Y por si fuera poco, y con esto vamos terminando, tenemos el otro problema. ¿Han probado ustedes ha preguntar por algo sobre iOS 26 a un _LLM_? Puede que les diga que iOS 26 no existe, que la próxima versión será la 19 y que la 26 saldrá en 2032. **No "sabe" (no tiene ese conocimiento en su entrenamiento) que iOS 26 es la versión actual porque Apple anunció en junio de 2025 este cambio.** Sí lo sabe (a fecha de hoy, 18 de noviembre), GPT 5.1 porque su fecha límite es posterior. Pero Claude, Perplexity con algunos de los modelos, Grok, etc… no lo saben. Te dicen que te equivocas. Que iOS 26 no existe y es una versión futura. Que el _iPhone_ 17 todavía no ha salido y que todos son rumores… y se queda tan pancha. Porque no es capaz de ver más allá de su propio entrenamiento y de la cantidad de fuentes de datos que usó para "aprender".

Es ridícula "la pelea" que tengo de vez en cuando (ahora ya no porque lo he puesto en instrucciones del agente) diciendo: "para iOS 26" y me contesta: "De acuerdo, lo haremos para iOS 16". Contesto: "que no. He dicho 26". Respuesta: "Sí, la versión 16. Lo he entendido". Contrarespuesta: "Que 16 no, que te he dicho 26". Respuesta: "Entendido, preparemos la funcionalidad para iOS 16". Os lo prometo, es verídico. ¿Por qué? **Porque en su "conocimiento", 26 no existe. Y como no existe estás cometiendo una errata. Quieres escribir 16 pero se ha escapado el dedo. Pobre humano.**

Lo mismo pasa con el protocolo `TestScoping`, una funcionalidad apenas documentada que permite en _Swift Testing_ desde Swift 6.1, crear un flujo de inicialización y liberación de elementos durante el lanzamiento de tests. Imagina que tienes que crear un fichero temporal con información para lanzar un _test_. Como los _test_ son unitarios, deberás borrar esa información cuando acabes. Este protocolo permite crear un flujo que se ejecutará antes y después de cada _test_. Pero apenas hay documentación y la poca que hay es muy actual. Conclusión: la IA no sabe nada de esto. Pero es que incluso Perplexity (que debería buscar en internet) no fue capaz de crearme un ejemplo ni buscarlo porque interpretó que quería hacer un ámbito de _test_ (_test scope_) y creó un código absurdo e inusable porque sí, señores, tampoco saben nada de _Swift Testing_ porque es muy actual. No hablemos de concurrencia estricta. **Intentar resolver problemas de esos conceptos tan abstractos que obligan a entender el código es un imposible. Olvídalo. No podrás hacerlo como no seas tú quien le expliques con puntos y comas qué ha de hacer… casi como hacer tú mismo el código.** Tiene que hacerte _copy paste_ de ti y para eso, mejor escribe tú el código.

![Swifty Stark dándolo todo](https://image.service.eu.axinom.net/transform/72a20w8irkd812lj5hqycl0ft-3dl5y1dj0seejmhh8wynzscv2/XvCRRkd92pyTGWvsRrA1ND.jpg)

## Conclusión: IA sí, pero Tony Stark es quien la maneja

**Hay que ser Tony Stark.** Tal vez no tan _playboy_, desde luego no billonario (y por ende, filántropo poco…) pero sí un genio. **Un genio que sepa no cómo se hace algo con código si no como se hace algo de forma conceptual.** Yo en este artículo os he explicado cómo se hacen muchas cosas y no he usado una sola línea de código. He usado análisis. Conceptualización… he usado la palabra escrita, el lenguaje natural, para explicar conceptos porque los entiendo más allá del código. **El código es la forma de representación en la máquina pero lo que de verdad importa es el conocimiento que luego se traduce a código cuando es necesario.**

Y si ese conocimiento se puede traducir a código por una IA para ahorrarnos tiempo y ser más productivos, adelante con ello. **La productividad que he ganado no es medible.** Este artículo no pretende desanimaros: pretende daros el ámbito más preciso de cuándo o no usar la IA. Está claro que podía haber tardado mucho menos haciendo yo los tests sin contar con la IA, pero la experiencia ha sido enriquecedora para medir los límites de los agentes de desarrollo y qué pueden o no pueden hacer. O qué les cuesta más o menos. Y cómo no tienen (obvio) capacidad de razonamiento y abstracción real. Ha sido una investigación en toda regla.

**La IA es y será siempre (cuanto más evolucione) el compendio infinito de todos los botones que permiten hacer infinitas cosas, pero quien "sobrevivirá" a ellas es quien sabe qué combinación, en qué orden y qué botones hay que pulsar para hacer las distintas cosas… porque ella no lo sabrá nunca.** Porque hasta para combinaciones concretas que sea un comando de una palabra… se necesitará una persona para decirle: "activar comando" porque es el momento y los datos precisos que necesitan lanzar ese comando. 

**Así que tú eliges, si quieres ser un Maestro o dejar que la IA que crea código más rápido que tú te quite el trabajo. Está en tu mano.**
