tl;dr
El lenguaje natural, el pseudocódigo y el código no son herramientas competidoras. Son lenguajes diferentes para distintos niveles de precisión.
En la era de la programación asistida por IA, aprender a programar no se trata solo de escribir cada línea de código a mano. Se trata de saber cuándo una idea necesita volverse más precisa, cómo controlar esa traducción y cómo diferenciar entre decisiones tomadas por un humano y decisiones llenas por un sistema de IA.
Una cita de Richard Feynman
Para entender por qué la programación no desaparece simplemente con la IA, ayuda empezar con una distinción más general: la diferencia entre claridad y precisión.
Richard Feynman explicó esta idea una vez hablando sobre matemáticas:
El problema real en la lengua escrita no es el lenguaje preciso. El problema es el lenguaje claro. El deseo es que la idea sea claramente comunicada a la otra persona. Solo es necesario ser preciso cuando haya duda sobre el significado de una frase, y entonces la precisión debe colocarse en el lugar donde existe la duda.
Es realmente imposible decir algo con precisión absoluta, a menos que esa cosa esté tan abstracta del mundo real como para no representar ningún objeto real. La matemática pura es una abstracción del mundo real, y la matemática pura tiene un lenguaje especial y preciso para manejar sus propias materias especiales y técnicas.
Pero este lenguaje preciso no es preciso en ningún sentido si se trata de objetos reales del mundo, y es solo pedante y bastante confuso usarlo a menos que haya algunas sutilezas especiales que tengan que distinguirse cuidadosamente.
La parte interesante es que Feynman no está criticando las matemáticas. Está criticando la idea de que más formalismo siempre significa más comprensión.
Su punto es simple:
- la claridad importa más que la precisión;
- la precisión debe introducirse solo donde haya una ambigüedad real;
- la matemática pura puede ser perfectamente precisa porque trata con abstracciones definidas por las matemáticas mismas;
- cuando hablamos del mundo real, siempre hay aproximaciones, suposiciones y definiciones imperfectas.
Aunque Feynman hablaba de matemáticas, la distinción es útil para pensar en programación también. No porque la programación sea la misma que la matemática pura, sino porque la programación también introduce precisión formal donde el lenguaje natural es demasiado ambiguo.
La idea central de este artículo es esta:
Aprender a programar aún importa porque el problema difícil no es solo producir código. El problema difícil es controlar la traducción entre una intención humana ambigua y un sistema formal que ejecuta instrucciones sin sentido común.
El lenguaje de las personas y el lenguaje de las computadoras
La programación es una forma de comunicar un procedimiento. Es una forma de decir a una máquina lo que queremos que haga.
Una CPU, o Unidad Central de Proceso, no entiende inglés, español, Python o C++ de la misma manera que una persona. En el nivel más bajo, ejecuta instrucciones muy simples: leer números, compararlos, sumarlos, copiar datos de un lugar en la memoria a otro y saltar de una instrucción a otra.
Los lenguajes de programación son una capa mucho más amigable para describir procedimientos que luego se interpretan o compilan en instrucciones que la máquina puede ejecutar.
En el lenguaje natural, una oración como:
Ordena esta baraja de cartas.
deja muchas cosas abiertas a la interpretación.
Asume que la persona que recibe la instrucción sabe qué es una carta, qué significa ordenar, cuál criterio de ordenación usar, cuáles cartas son más altas que otras, si las suits importan y cuánto tiempo o esfuerzo es aceptable.
En programación, esas decisiones deben definirse en algún lugar del sistema.
No siempre escribimos cada detalle a mano, porque utilizamos lenguajes de programación, bibliotecas, marcos y abstracciones que ya encapsulan muchas decisiones. Pero alguien, en algún nivel, tuvo que definir esas decisiones con precisión.
El lenguaje natural y los lenguajes de programación no son versiones mejores o peores de lo mismo. Son herramientas diferentes. Preguntar si la inteligencia artificial significa que ya no necesitamos aprender programación es algo similar a preguntar si, porque sabemos cómo usar un lente de aumento, ya no necesitamos entender para qué sirve un microscopio.
Cómo se crea software en una empresa de software
En una empresa de software, casi todo comienza con el lenguaje natural.
Un cliente explica lo que quiere:
Necesito una aplicación para gestionar pedidos.
O:
Necesito un sitio web para vender productos.
O:
Necesito un sistema para gestionar citas.
Esa primera descripción rara vez es precisa. No porque el cliente se exprese mal, sino porque están intentando comprimir una gran cantidad de conocimiento del dominio en unas pocas frases.
Ese conocimiento puede incluir hábitos, excepciones, reglas de negocio, preferencias, casos extremos, problemas anteriores y decisiones implícitas.
Ese es el motivo por el que existen capas intermedias.
Un product owner, analista de negocios o analista funcional habla con el cliente, hace preguntas, detecta ambigüedades y transforma la solicitud inicial en requisitos más claros.
Luego, un líder técnico, arquitecto o programador traduce esos requisitos en decisiones técnicas cada vez más específicas, hasta que finalmente haya código.
Cada capa puede descubrir problemas o proponer cambios. Pero ninguna capa debe silenciarmente sobrescribir una decisión tomada en un nivel superior.
Si un programador encuentra una contradicción, debe señalarla. Si un product owner reformula un requisito, esa reformulación debe permanecer trazable a la solicitud original del cliente.
Tres formas de traducir una idea en código
El siguiente diagrama compara tres flujos de trabajo: desarrollo de software tradicional, generación directa de código basada en LLM y un flujo de trabajo propuesto con una capa intermedia explícita.
Proceso tradicional
LLM sin metodología
Aproach en caja negra
¿Qué ocurre cuando una persona utiliza un LLM para generar código?
Cuando una persona interactúa directamente con un LLM para generar código, la capa intermedia no desaparece completamente. Se vuelve implícita.
El modelo interpreta el prompt, completa los detalles faltantes, prioriza las instrucciones y toma decisiones de implementación. Pero muchas de esas decisiones no se registran en ningún lugar estable.
Es por eso que muchas herramientas de codificación basadas en IA tienen algo como un Modo de Plan. La idea es pedir al modelo que piense antes de implementar.
El problema es que el plan a menudo es temporal. Puede quedar atrapado en una conversación, desconectado del código final, o ignorado por el usuario.
Cuando el usuario es un programador, suele pasar mucho tiempo escribiendo especificaciones, documentos de requisitos, notas de arquitectura y archivos de contexto para guiar al LLM. Pero esto crea otro problema: decenas de archivos dispersos, cada uno con diferentes niveles de autoridad, prioridad y frescura.
Incluso cuando las especificaciones están cuidadosamente escritas, el problema no desaparece.
El LLM puede encontrar contradicciones, llenar vacíos o tomar decisiones que no se ajusten exactamente a los requisitos. A veces ocurre esto porque el lenguaje natural es ambiguo. A veces ocurre porque el modelo lee los archivos en un orden específico. A veces ocurre porque las instrucciones son contradictorias. Y a veces ocurre porque los LLM son sistemas probabilísticos.
El problema no es que el LLM tome decisiones.
El problema es no saber qué decisiones tomó, por qué las tomó, o si contradicen algo que ya decidió una persona.
Pseudocódigo como capa intermedia
El pseudocódigo puede jugar el papel que, en el desarrollo tradicional de software, está distribuido entre conversaciones entre clientes, gerentes, arquitectos y programadores.
No es un lenguaje natural puro, porque tiene estructura.
Pero tampoco es código ejecutable, porque aún permite hablar en términos de intención, módulos y responsabilidades.
Esta capa intermedia es especialmente útil con LLMs porque separa dos momentos que a menudo se colapsan demasiado rápido:
- decidir qué debe hacer el sistema;
- decidir cómo implementarlo.
Por ejemplo:
IF user selects a subscription plan
CALL create_checkout_session
IF payment succeeds
CALL activate_subscription
CALL send_confirmation_email
ELSE
CALL show_payment_error
Este pseudocódigo aún no decide si utilizaremos Stripe, Paddle, Mercado Pago, una API personalizada o un biblioteca específica.
Pero sí define una estructura:
- existe un plan;
- existe una sesión de pago;
- existe un paso de activación de suscripción;
- existe un camino de pago exitoso;
- existe un camino de error.
Eso ya es mucho más preciso que una solicitud vaga como:
Agregar pagos a la aplicación.
Pero aún no es tan rígido como el código de implementación final.
La propuesta: el enfoque de la caja negra
El método propuesto es simple:
Tratar cada parte del sistema como una caja negra refinable, con límites claros, pseudocódigo intermedio y proveniencia explícita.
Cada proyecto de aplicación recibe una carpeta blackboxes/ que contiene:
- cajas en YAML;
- estructuras de datos compartidas;
- un archivo de habilidad que debe seguir el LLM cuando se invoca explícitamente;
- un registro de proveniencia;
- opcionalmente, un visualizador que muestre el árbol de cajas.
El objetivo no es reemplazar el código.
El objetivo es hacer visible, editable y auditable la traducción desde la intención humana al código.
Lo que intenta resolver
1. Decisiones humanas vs decisiones de IA
Cada caja y cada campo de datos sigue la proveniencia:
human_definedai_definedmixed
Una vez que un humano confirma algo, la IA no puede silenciosamente descenderlo de nuevo a una suposición.
Por ejemplo, si el LLM decide utilizar Stripe, esa decisión puede marcarse inicialmente como ai_defined.
Si un humano confirma esa decisión, se convierte en aprobada por el humano. Desde entonces, el modelo no debe reemplazar Stripe con otro proveedor a menos que explícitamente se indique ese cambio.
2. Pseudocódigo como capa intermedia
Las cajas utilizan pseudocódigo híbrido:
CALL create_checkout_session
IF payment succeeds
WHILE there are pending invoices
Esto proporciona suficiente estructura para descomponer un sistema, pero no tanto como para ahogarse en detalles de implementación antes de estar listos.
3. Límites modulares
El sistema se refina un caja negra a la vez.
Los antecesores son contexto de solo lectura. Solo puede cambiar la subárbol seleccionada.
Esto importa porque una solicitud como:
Corrija el flujo de pago.
no debe permitir que el LLM reescriba la capa de autenticación, el esquema de la base de datos y el sistema de notificaciones a menos que esos cambios estén explícitamente conectados y aprobados.
4. Una vista legible para humanos
Un visualizador puede mostrar el árbol de cajas en una forma mucho más simple que cincuenta archivos de especificación dispersos.
El punto no es solamente documentación. El punto es preservar los límites de decisión.
Un ejemplo mínimo
Aquí está un ejemplo simplificado de una caja negra para un flujo de pago:
box: payment_flow
goal: allow the user to pay for a subscription
provenance: human_defined
inputs:
- selected_plan
- user_id
pseudocode:
- IF user selects plan
- CALL create_checkout_session
- IF payment succeeds
- CALL activate_subscription
- ELSE show_error_message
decisions:
payment_provider:
value: Stripe
provenance: ai_defined
status: needs_human_confirmation
constraints:
- Do not modify auth_flow without human approval
- Do not store sensitive card data
La parte interesante no es el propio YAML.
La parte interesante es que cada decisión tiene un lugar. El sistema sabe qué ha sido especificado por un humano, qué ha sido inferido por la IA y qué aún necesita confirmación.
La capa intermedia no es burocracia. Es una forma de prevenir que la IA convierta la ambigüedad en decisiones invisibles.
Entonces, ¿qué significa aprender programación ahora?
Quizás aprender programación ya no significa memorizar sintaxis o escribir cada función desde cero.
Quizás significa aprender a pensar en niveles de precisión.
Significa saber cuándo basta con el lenguaje natural, cuándo se necesita pseudocódigo, cuándo se necesita código ejecutable y cuándo una decisión debe registrarse porque afectará al sistema en su totalidad.
Aprender programación también enseña:
- cómo descomponer problemas;
- cómo identificar casos límite;
- cómo modelar datos;
- cómo leer código generado críticamente;
- cómo depurar;
- cómo evaluar trade-offs;
- cómo razonar sobre restricciones como memoria, latencia, seguridad, concurrencia, permisos y costo.
La inteligencia artificial puede acelerar significativamente la generación de código.
Pero a medida que más código genera la inteligencia artificial, cada vez más importante es leerlo, cuestionarlo y vincularlo nuevamente con las decisiones humanas que se supone que implementa.
Conclusión
La pregunta "¿Aún necesitamos aprender programación?" puede estar mal formulada.
Si aprender programación significa memorizar sintaxis y escribir manualmente cada línea de código, probablemente necesitaremos hacerlo menos con el tiempo.
Pero si aprender programación significa comprender cómo una intención ambigua se convierte en un sistema formal que toma decisiones y ejecuta acciones, entonces la respuesta es claramente sí.
La inteligencia artificial puede escribir código.
Pero alguien aún debe controlar la traducción.
Y para controlar una traducción, se necesita entender al menos parte de ambos idiomas.
El Enfoque de la caja negra descrito en este artículo es un experimento de prueba de concepto. github.com/amiune/theblackboxapproach