Muchos desarrolladores escriben prompts igual que escriben scripts. Paso uno, paso dos, paso tres. Es natural: llevamos casi toda la carrera pensando en términos imperativos.
Pero un LLM no es un intérprete. Se parece más a un runtime inteligente que configuras con contexto. Los prompts que funcionan de verdad leen como una especificación, no como una receta. Y aun así, muchos seguimos cayendo en el reflejo imperativo.
Un recordatorio rápido
El código imperativo le dice a la máquina cómo hacer algo: recorre este array, muta esta variable, bifurca si se cumple esta condición.
El código declarativo le dice qué quieres conseguir: dame los usuarios que se registraron la semana pasada, renderiza un árbol de componentes con esta forma, mantén tres réplicas vivas de este pod.
SQL es declarativo. Terraform es declarativo. Kubernetes es declarativo. Muchas de las herramientas que han reducido carga mental en la última década se han movido en esa dirección. No es casualidad.
La trampa del prompt imperativo
Un ejemplo típico:
Primero lee config/routes.rb. Después abre ArticlesController.
Busca la acción show. Añade un before_action de autenticación.
Después actualiza la vista para ocultar el botón de editar si el usuario
no es administrador.
Eso no es un buen prompt. Es un commit message escrito antes de mirar el código. Suena a alguien narrando lo que haría si estuviera llevando el teclado.
El problema aparece en cuanto la realidad no encaja con el guion. ¿Y si el before_action ya existe? ¿Y si la vista usa un parcial? ¿Y si "administrador" no es un booleano, sino un rol en otro modelo o un método en Current.user que la aplicación lleva meses usando?
Has escrito un script. Y los modelos son bastante buenos siguiendo scripts, incluso cuando el script es incorrecto.
Cómo se ve un prompt declarativo
Un buen prompt declara el resultado, las restricciones y qué significa haber terminado:
Solo los administradores deben poder editar un artículo. Los usuarios
no administradores deben poder ver la página, pero no el botón de editar.
Mantén la solución consistente con cómo esta aplicación gestiona la
autenticación y la autorización en el resto del código.
No hay pasos. No hay rutas de archivo obligatorias. No hay secuencia. El modelo decide el camino. Leerá routes.rb si lo necesita, no porque se lo hayas ordenado. Encontrará el parcial, el concern o el helper que tú no sabías que existía porque le has dicho cómo se ve el resultado, no cómo llegar a él.
No estás programando el modelo. Estás configurando el contexto en el que razona.
Ese cambio de marco mejora más prompts que cualquier truco superficial de "prompt engineering".
CLAUDE.md, skills, MCP: la misma historia
Todo lo que escribes alrededor del modelo sigue la misma regla.
Un buen CLAUDE.md es una especificación: "Rails 8.1, Ruby 3.4, SQLite en producción, modelos con lógica de negocio, controladores finos, Minitest con fixtures, autenticación por sesión sin Devise". El modelo lee eso, más el código, más la petición actual, y decide qué hacer.
Un mal CLAUDE.md es un runbook: "Primero revisa el Gemfile. Luego abre config/database.yml. Después mira el directorio de modelos". Cada línea desperdicia contexto, porque el modelo habría leído esos archivos de todas formas en cuanto los necesitara.
Con las skills pasa lo mismo. Las mejores describen el trabajo: este es el objetivo, esta es la voz, esto significa terminado, esto hay que evitar. No describen una secuencia mecánica. Una skill que dice "primero haz un esquema, luego escribe la introducción, luego el cuerpo y luego el resumen" está escrita para un compilador, no para un escritor.
Las descripciones de subagentes, la documentación de servidores MCP o los prompts dentro de una canalización caen en la misma categoría. Le dices al modelo hacia dónde apuntar, no cómo aterrizar.
Cuándo sigue ganando lo imperativo
Lo imperativo no está mal. Simplemente pertenece a otra capa.
El arnés es imperativo: el hook que se ejecuta en PreToolUse, la herramienta que abre un navegador y rellena un formulario, el chequeo de pre-commit que corre RuboCop y bloquea el commit si falla. Eso es código. Debe ser determinista, paso a paso, testeable y aburrido.
La separación es limpia:
- Arnés: imperativo. Código, hooks, herramientas y flujos deterministas.
- Contexto: declarativo. Prompts,
CLAUDE.md, skills y descripciones de subagentes.
Cuando mezclas las capas, todo se vuelve raro. Un prompt imperativo convierte al modelo en un robot terco que sigue un guion malo. Un hook declarativo convierte tu arnés en una máquina de adivinar que interpreta silenciosamente lo que querías decir. Conviene mantener cada cosa en su sitio.
Resumen
El pensamiento imperativo cuesta soltarlo porque llevamos años escribiendo scripts. Es lógico que nuestros prompts también parezcan scripts. Pero el modelo es otro tipo de runtime. Quiere una especificación, no una receta.
Describe el resultado, las restricciones y la definición de terminado. Deja que el runtime encuentre el camino. Guarda lo imperativo para el arnés, donde la determinación sí paga.
En Laboratorio Web lo vemos cada semana: cuanto más claro es el resultado que pedimos y más explícitas son las restricciones, menos necesitamos dirigir cada movimiento del modelo. La calidad no viene de micromanagement. Viene de darle un buen espacio de decisión.