La IA abarata la ejecución. El criterio sigue siendo escaso.
Contratar más desarrolladores o sustituirlos por agentes de IA deja sin respuesta la misma pregunta: ¿quién tomará la...
11 min de lectura
17.10.2026, Por Stephan Schwab
Una aplicación construida con IA puede parecer casi terminada cuando las preguntas difíciles apenas empiezan. Desarrollar Nilo, nuestro SaaS, con ayuda de IA vuelve esa distancia tangible. Las pantallas funcionan. Luego dos versiones discrepan sobre un evento, una renovación olvida el plan elegido por el cliente o el fallo de una base de datos opcional inutiliza una página útil. El trabajo restante se esconde en interacciones que ninguna demo pone a prueba. Detectarlas y cuestionar soluciones plausibles exige más que saber pedir otra función.
En Cuando el vibe coding se encuentra con un desarrollador de verdad, describí aplicaciones que sus creadores habían construido con IA antes de pedir a un desarrollador experimentado que las revisara. Habían llegado sorprendentemente lejos. Sus aplicaciones también contenían problemas que no tenían motivos para reconocer.
Nilo me permite ver el otro lado. También usamos IA para desarrollarlo. Estamos en 2026. Hacer que un desarrollador teclee todo laboriosamente demostraría poco más que una afición por teclear.
Las pruebas interesantes están en mis conversaciones con Codex: las preguntas después de implementar, las objeciones después de explicar y los cambios aparentemente pequeños que atraviesan todo el producto.
El «último 20 %» es una forma de hablar, no una medida del progreso de Nilo. Describe el trabajo que se descubre cuando la aplicación ya resulta convincente. Nadie promete que exija solo el 20 % del esfuerzo. Los porcentajes tranquilizan maravillosamente cuando nadie ha medido el denominador.
El 3 de octubre aparecieron más errores después de un despliegue. Pedí a Codex que investigara los logs centralizados, donde se reúnen los mensajes de las instancias de la aplicación en ejecución.
Ya teníamos despliegue blue/green. La explicación habitual dice que una versión atiende a los clientes mientras otra arranca a su lado. Comprobar la candidata, cambiar el tráfico y retirar la anterior. Muy ordenado. Dos colores, una flecha y una diapositiva excelente.
La investigación encontró que la nueva versión de Nilo publicaba un nuevo tipo de evento interno, tasks.board. La versión anterior no reconocía ese tipo de evento. Ambas compartían la infraestructura de eventos, así que las instancias antiguas recibían los nuevos eventos y los rechazaban.
Ambas versiones podían seguir superando sus comprobaciones de salud. La nueva había arrancado correctamente. La anterior seguía respondiendo a las peticiones. Esos hechos no demostraban que pudieran convivir de forma segura.
Los errores cesaron cuando se apagaron las instancias antiguas. Codex lo presentó inicialmente como algo tranquilizador: la ráfaga de errores había terminado y las instancias actuales funcionaban.
Pregunté:
Así no se supone que debe funcionar un despliegue blue/green, ¿verdad?
Esa pregunta cambió la conclusión. La desaparición de los errores demostraba que había terminado la convivencia de versiones. No demostraba que la transición fuera correcta.
Nuestra implementación controlaba el tráfico web entrante. No controlaba adecuadamente la actividad en segundo plano que conectaba las dos versiones. Arrancar la candidata también iniciaba trabajo capaz de afectar al sistema en funcionamiento.
Alguien que solo mire el sitio web quizá nunca lo vea. Un desarrollador centrado en cambiar el tráfico también puede pasarlo por alto. El fallo cruzaba una frontera entre el despliegue, el procesamiento en segundo plano y los eventos de la aplicación.
Después pregunté cómo mejorar la transición sin convertir los despliegues frecuentes y el desarrollo rápido en una tortura.
Eso nos llevó a completar el trabajo en curso antes de parar, lo que se conoce como draining: dejar de iniciar trabajo nuevo, permitir que el ya iniciado llegue a un resultado conocido y después retirar el proceso.
Para un sitio web sencillo, contar las peticiones activas podría resolver buena parte del problema. Nilo también tiene tareas programadas, actividad de buzones, operaciones asíncronas y conexiones persistentes. Una petición puede terminar mientras el trabajo que inició continúa en otro lugar.
Pensemos en una operación que envía un correo. El servicio externo puede aceptarlo justo antes de que se detenga la aplicación, cuando esta todavía no ha registrado el resultado. Reiniciar y volver a intentarlo podría enviar un duplicado. Matar el hilo no dice nada sobre si el destinatario recibirá el mensaje.
El fallo observado era el evento incompatible. El correo es un escenario de fallo que la transición debe resolver: determinar si una operación interrumpida tuvo éxito antes de reintentarla. El enrutamiento del tráfico, por sí solo, no puede responder a eso.
La candidata debe arrancar sin realizar prematuramente trabajo de producción. El proceso anterior debe dejar de aceptar trabajo y terminar el que ya aceptó. Los consumidores de eventos y las conexiones persistentes necesitan un cierre ordenado. Los tiempos de espera necesitan una consecuencia explícita.
Una espera fija no demuestra esos hechos. Esperar un minuto prueba que pasó un minuto. La aplicación puede haber terminado en dos segundos o seguir esperando a un servicio externo.
La etiqueta blue/green no vuelve segura la convivencia de versiones incompatibles. Hace falta compatibilidad entre eventos durante la convivencia o una transición controlada que impida que ambas versiones los procesen a la vez. Una pausa breve en las escrituras puede ser el precio honesto de mantener fáciles de modificar los contratos internos.
Son decisiones de producto y operación escondidas en algo que parecía un script de despliegue.
Codex propuso estados explícitos de las versiones, permisos para ejecutar trabajo y un registro de la generación del despliegue en Mongo. La explicación era coherente. También introducía la orquestación del despliegue en la aplicación.
Pregunté:
¿Entonces la aplicación conoce el proceso de despliegue? Eso no suena bien.
Después:
Esto no es nada nuevo. Supongo que en el ecosistema de Spring ya se ha resuelto.
La investigación se orientó hacia el soporte de ciclo de vida que ya ofrece Spring, el framework que utiliza Nilo. La frontera útil quedó más clara: la infraestructura de despliegue coordina las versiones y el enrutamiento; los componentes de la aplicación participan en el arranque y la parada ordenados.
Eso sigue exigiendo integración. Un listener en segundo plano que arranca automáticamente debe participar en el ciclo de vida. El soporte del framework no se conecta por sí solo a cada componente propio. Pero examinarlo primero cambia cuánta maquinaria necesitamos inventar y dónde debe estar.
El 3 de octubre, el trabajo seguía en curso. Una dirección mejor todavía necesitaba implementación y verificación en producción.
Este intercambio importa porque la IA fue útil durante todo el proceso. Rastreó el fallo y desarrolló propuestas concretas. También ofreció una interpretación inicialmente tranquilizadora y luego una solución cuyas consecuencias arquitectónicas había que cuestionar.
El trabajo incluía evaluar la explicación, no simplemente aceptar el parche.
Una conversación anterior empezó con la idea de hacer que algunos créditos no caducaran. Incluso lo llamé «probablemente un cambio muy pequeño». Otra frase para enmarcar.
Nilo tiene créditos pagados, créditos concedidos por el operador y servicios recurrentes que consumen créditos. Un cliente de prueba puede recibir créditos gratuitos y después convertirse en cliente de pago. El saldo mostrado, por sí solo, no explica qué debe caducar, qué puede revocar un operador o qué debe proporcionar una renovación.
Eso exigía distinguir los orígenes y registrar los créditos concedidos y su uso. Después llegó otra pregunta:
¿Hay algo que evite quedarnos prácticamente sin créditos? Tenemos cargos recurrentes.
Y la regla de negocio:
No debería ser posible elegir una suscripción demasiado pequeña mientras haya productos con cargos recurrentes activos.
Desactivar un botón ayuda a quien utiliza la página de facturación. La regla también debe cumplirse en el servicio que modifica la suscripción, antes de cambiar nada en el proveedor de pagos.
La investigación descubrió un problema relacionado con la renovación: había que guardar el plan elegido por el cliente y usarlo al renovar, en lugar de volver al plan más bajo.
Una demostración del registro no pone a prueba la renovación del próximo mes. Un pago puede tener éxito mientras el producto incumple el compromiso que el cliente creía haber comprado.
La página de precios era visible. La promesa se repartía entre el modelo de créditos, los cambios de suscripción, las notificaciones de pago y el procesamiento de las renovaciones. Ahí estaba el trabajo restante.
Un problema de la página de contactos surgió por la falta de disponibilidad del almacenamiento personal. Nilo guarda los datos personales relacionados con contactos separados de los contactos de la empresa. Los contactos de la empresa podían seguir disponibles aunque la base de datos personal no lo estuviera.
Durante la investigación, Codex tomó una dirección de reparación que rechacé. La base de datos de desarrollo contenía datos mezclados y referencias inconsistentes. Lo necesario era contener el fallo de la dependencia, no reescribir otros registros almacenados para que desapareciera la excepción.
Mi corrección incluía:
Pero NUNCA alterar la base de datos de Mongo.
Desde la perspectiva del cliente, el comportamiento requerido era sencillo: mostrar los contactos de la empresa, explicar que la información personal no estaba disponible y mantener operativas las funciones independientes.
Llegar ahí exigía preservar la separación del almacenamiento, gestionar el fallo concreto donde la página consumía datos opcionales y comprobar también las páginas de importación y detalle. Arreglar una página visible no cubría todos los lugares que consumían esos datos.
La ausencia de una membresía no puede convertirse en una excusa para inventar acceso a otra base de datos. Del mismo modo, la falta de datos opcionales no debería impedir al cliente utilizar datos independientes de la empresa.
El impulso de «reparar» puede resultar perjudicial cuando el sistema está denegando correctamente el acceso. La experiencia ayuda a distinguir una referencia rota, una dependencia no disponible y una barrera que debe mantenerse intacta. Todas pueden presentarse con el mismo mensaje de error rojo.
El seguimiento de campañas produjo otra función engañosamente sencilla: enviar un enlace personalizado y mostrar actividad en el historial del contacto.
Pregunté qué ocurre cuando un escáner de seguridad del correo sigue el enlace para comprobar si la página contiene malware. Si lo registramos como una lectura del destinatario humano, nuestro CRM cuenta una historia que las pruebas no respaldan.
Exigir una acción deliberada antes de registrar nada evitaría parte de esa ambigüedad, pero frustraría el propósito de observar visitas. También me opuse a eso.
Cambiamos el modelo para conservar observaciones separadas: una visita y después el desplazamiento, los clics u otra actividad reportada. Las señales posteriores pueden añadir contexto sin reescribir la primera observación como una afirmación más fuerte.
Siguen siendo observaciones. Ni siquiera un clic reportado nos da certeza mágica sobre las intenciones de una persona.
Después, el historial del contacto seguía vacío. Pedí a Codex que rastreara lo que debería haber llegado.
La investigación comparó los logs de producción, la API documentada y el código que realmente se ejecutaba en la página externa. Ese cliente utilizaba un endpoint diferente y omitía partes de la petición y la autenticación exigidas por Nilo. Nilo podía tener una API funcional y el otro sistema podía tener código de seguimiento, mientras la integración entre ambos seguía incompleta.
Que una página diga que envía eventos es una prueba débil. Un log que mencione el dominio de la campaña también lo es: puede describir el correo saliente. La prueba significativa es que la petición llegue al servicio correcto, supere las comprobaciones previstas, produzca un evento almacenado y aparezca en el contacto correcto.
«Ambas partes lo implementaron» deja bastante espacio entre las partes.
Ninguno de estos ejemplos exigía abandonar la IA. La usamos para investigar y modificar el sistema. La velocidad es real.
También lo es la necesidad de cuestionar el resultado. El despliegue terminó; ¿trabajaron juntas versiones incompatibles? El pago tuvo éxito; ¿la renovación conservará el plan elegido? La página carga; ¿la solución alteró silenciosamente datos que no debía tocar? Aparece un evento; ¿qué demuestra sobre una persona?
Quien no es desarrollador tiene pocos motivos para anticipar estas preguntas. Un desarrollador capaz, con experiencia concentrada en una sola capa, también puede pasarlas por alto. Años trabajando en pantallas no enseñan automáticamente los ciclos de los pagos. Saber desplegar contenedores no explica automáticamente el trabajo asíncrono dentro del contenedor.
La amplitud de experiencia importa porque los fallos atraviesan capas. Reconocer una señal de alarma conocida da un punto de partida para investigar; no exime de demostrar el diagnóstico.
Las preguntas pueden convertirse en comprobaciones ejecutables: el plan elegido sobrevive a la renovación; una suscripción inadecuada no provoca cambios en el proveedor de pagos; la falta de almacenamiento personal deja intactos los contactos de la empresa; una versión candidata no realiza trabajo de producción antes de que la transición lo permita.
Entonces el siguiente cambio asistido por IA tiene algo concreto que cumplir. Los tests dan a esas decisiones una memoria que se ejecuta. Las comprobaciones del despliegue y las pruebas de producción cubren lo que los tests aislados no pueden.
Así es el «último 20 %». Parte es código que falta. Mucho consiste en encontrar una promesa que la función visible todavía no cumple y seguirla a través de todo el sistema.
La interfaz quizá apenas cambie. El cliente obtiene un servicio que sigue funcionando durante las renovaciones, los fallos y la siguiente versión. Bastante producto para algo que la demo no podía mostrar.
Cuénteme qué está pasando. Yo escucho, hago algunas preguntas prácticas y le devuelvo lo que veo: dónde puede estar el riesgo, qué puede estar bloqueando la entrega y qué parece valer la pena revisar después. Sin discurso comercial, sin compromiso. Confidencial y directo.
Iniciar una conversaciónUn desarrollador experimentado para tu equipo
Nuestro Developer Advocate programa código productivo con tu equipo, mejora el pipeline y acelera la entrega. 60-70% código, 30-40% coaching. Un compañero de equipo temporal que entrega desde el primer día.