Volver al Blog
AI & Development14 de mayo de 202610 min de lectura

Prompt Engineering para Calidad de Código Angular

DR

Doug Ribeiro

10 min de lectura

Después de un largo tiempo iterando sobre cómo uso asistentes de IA en mi workflow Angular, he llegado a una conclusión: la calidad del código que recibes está casi enteramente determinada por la calidad del prompt que escribes. Suena obvio, pero la mayoría de los desarrolladores tratan a la IA como un colega senior que ya conoce su codebase, sus convenciones y sus restricciones. No es así. Tienes que enseñarle cada vez, y la forma en que le enseñas es a través del prompt.

Nada de lo que sigue es el consejo genérico de 'sé específico, da contexto'. Son los prompts que realmente uso cuando genero componentes, servicios, tests y decisiones de arquitectura en Angular, mostrados antes y después, porque la diferencia entre un prompt vago y uno preciso es más fácil de ver que de describir.

Definir el contextoDile al modelo el stack, las restricciones y el objetivo 1 Instrucciones específicasSé explícito: TypeScript, Angular, sin any, usa signals 2 Guiado por ejemplosDa ejemplos de entrada y salida o patrones existentes 3 Refinamiento iterativoRevisa el resultado, corrige errores y vuelve a promptear con las corre… 4

Por Qué Angular Necesita Prompts Diferentes

Angular es opinionado. Tiene una forma específica de hacer inyección de dependencias, un sistema de módulos (o componentes standalone, que entraron en developer preview en v14.0 y se volvieron estables en v15), convenciones de RxJS, expectativas fuertes de TypeScript, y una estructura de archivos muy particular. Los modelos de IA genéricos conocen Angular, pero no saben cuál Angular. Podrían generar código usando NgModules cuando estás en un proyecto standalone, o usar componentes basados en clases con lifecycle hooks cuando has adoptado signals. Necesitas decírselo.

Además de eso, los proyectos Angular empresariales tienen sus propias capas de convención: bibliotecas compartidas, componentes de design system, patrones específicos de gestión de estado, convenciones de nomenclatura para services vs facades vs resolvers. Nada de eso está en los datos de entrenamiento del modelo. Así que la responsabilidad está en ti de proporcionarlo.

El System Prompt / Bloque de Contexto Con el Que Siempre Empiezo

Ya sea que esté usando una herramienta basada en chat o una integración de IDE, lo primero que hago es establecer un bloque de contexto. Lo configuro como system prompt o lo pego al principio de cada conversación. Así se ve aproximadamente para un proyecto standalone basado en signals. La línea de versión y las restricciones cambian de codebase en codebase:

text
You are an Angular expert assisting a senior developer on an enterprise Angular 17.3+ project.

Constraints:
- Use standalone components only. No NgModules.
- Use Angular Signals for local state. Avoid BehaviorSubject for component-level state.
- RxJS is acceptable for HTTP streams and complex async flows.
- All components must be typed. No use of 'any'.
- Use inject() function for dependency injection, not constructor injection.
- Follow Angular style guide naming: MyFeatureComponent, MyFeatureService, etc.
- Use OnPush change detection strategy on all components.
- Tests use Jest + Angular Testing Library. No TestBed unless strictly necessary.
- CSS is SCSS. No inline styles.
- All observables must be unsubscribed. Prefer async pipe or takeUntilDestroyed().

When generating code, always provide the full file, not snippets.

Este bloque solo cambia el output dramáticamente. Antes de agregarlo, obtenía servicios con inyección por constructor, boilerplate de NgModule y componentes sin OnPush. Después, obtengo algo mucho más cercano a lo que yo realmente escribiría.

Prompting para Componentes

Antes: La Solicitud Vaga

Al principio, escribía algo como esto:

text
Create an Angular component for a user profile card.

Lo que recibía era un componente basado en clases con inyección por constructor, sin tipos en los inputs, sin OnPush, y a veces una declaración de NgModule. Técnicamente correcto para Angular 2018, no útil para mi proyecto.

Después: La Solicitud Estructurada

text
Create a standalone Angular 17.3+ component called UserProfileCardComponent.

Inputs:
- user: input.required<User>() (interface: { id: string; name: string; email: string; avatarUrl: string | null })
- isLoading: input<boolean>(false)

Behavior:
- When isLoading is true, show a skeleton placeholder (use a div with class 'skeleton')
- When user.avatarUrl is null, show initials from user.name in a circle div
- Emit an output called profileClicked: OutputEmitterRef<string> emitting user.id on card click

Style: SCSS. Card has a shadow and rounded corners. Use BEM naming.
Change detection: OnPush.
Do not use @Input() decorator. Use the input() signal API.
Provide the full .ts and .html files.

Esto produce un componente que puedo agregar al proyecto con quizás uno o dos ajustes. Las diferencias clave: especifiqué la versión de Angular, el estilo de API (signal inputs), la forma exacta de la interfaz, la lógica de comportamiento y el formato de output. La versión tiene que ser precisa. Los signal inputs llegaron en 17.1 y output() con OutputEmitterRef en 17.3, así que un modelo al que le dices solo 'Angular 17' tiene derecho a devolverte @Input() y EventEmitter, y estar en lo correcto.

Prompting para Servicios

Los servicios necesitan una plantilla diferente porque las preocupaciones principales son diferentes: scope de inyección, manejo de HTTP, gestión de errores y tipos de retorno.

text
Create an Angular service called UserService.

- Injectable at root level
- Uses inject(HttpClient) for HTTP calls
- Base URL comes from inject(APP_CONFIG) token, property: apiBaseUrl
- Methods:
  1. getUser(id: string): Observable<User>
     - GET /users/:id
     - Pipe through catchError, rethrow as typed AppError
  2. updateUser(id: string, payload: Partial<User>): Observable<User>
     - PATCH /users/:id
     - Same error handling
- AppError interface: { code: string; message: string; statusCode: number }
- No BehaviorSubject or state management inside this service. Pure data access layer.
- Use typed HttpClient (pass response type in generic).

Las cosas críticas que siempre incluyo para servicios: el scope de inyección, de dónde viene la configuración, las firmas exactas de métodos con tipos de retorno, y lo que el servicio NO debe hacer. Ese último punto es sorprendentemente importante: si no dices 'esto es una capa pura de datos', el modelo frecuentemente agregará un BehaviorSubject cache y de repente tu servicio está haciendo gestión de estado.

Prompting para Tests

La generación de tests es donde el prompt engineering rinde más. La brecha entre un test genérico y un test útil es enorme, y es casi completamente controlable a través del prompt.

Prompt de Test Malo

text
Write tests for UserProfileCardComponent.

Obtendrás tests de renderizado superficial que verifican si el componente se renderiza, quizás uno o dos property bindings. Nada que realmente atrape una regresión.

Prompt de Test Bueno

text
Write Jest tests for UserProfileCardComponent using Angular Testing Library (render from @testing-library/angular).

Test these scenarios:
1. When isLoading is true, the skeleton placeholder is visible and the user name is not
2. When isLoading is false and user.avatarUrl is null, initials are derived from user.name and shown
3. When isLoading is false and user.avatarUrl is set, an img tag is rendered with the correct src
4. Clicking the card emits the correct user.id through the profileClicked output
5. Component renders without errors with minimum required input (user with all fields, isLoading false)

Do not use TestBed directly. Use the render() function from Testing Library.
Mock no services. This component has no service dependencies.
Use userEvent from @testing-library/user-event for interaction tests.

El resultado es un archivo de test con cobertura real de comportamiento real. Defino los escenarios explícitamente porque el modelo no sabe qué importa en mi contexto de negocio: solo yo lo sé.

Prompting para Decisiones de Arquitectura

Más allá de la generación de código, también uso IA para preguntas de arquitectura. Esas necesitan un tipo de prompt muy diferente, uno que lleve suficiente contexto para que el modelo razone sobre tradeoffs en lugar de elegir la respuesta obvia. El ejemplo de abajo es una plantilla. El dominio en él es deliberadamente simple, porque lo que hace el trabajo es la lista de restricciones y la última línea, no el tema.

text
I'm building a self-contained feature module in Angular 17.3+ (standalone).

Context:
- The screen renders 6 independent panels, each with its own data source
- Panels can be shown or hidden per user preference (stored through a preferences API)
- Data refreshes every 30 seconds or on user request
- The app uses NgRx for global state but this feature should stay self-contained

Question: Should I use:
a) NgRx feature state for this module
b) A signal-based store (a plain service with signals)
c) NgRx ComponentStore

Explain the tradeoffs for each in this specific context. Consider DevTools debugging, boilerplate cost, testability, and the refresh pattern. Don't just list general pros/cons. Apply them to my situation.

La última oración es crucial. Sin ella, obtienes una lista estilo Wikipedia de tradeoffs que no te ayuda a decidir nada. Pedirle al modelo que aplique el razonamiento a tu situación específica lo fuerza a tomar una posición.

Qué Incluir Siempre en el Contexto

  • Versión de Angular y feature flags principales (standalone, signals, etc.)
  • Qué patrón de gestión de estado estás usando y a qué scope
  • El stack de testing (Jest vs Karma, Testing Library vs TestBed)
  • Tu enfoque de CSS (SCSS, Tailwind, CSS Modules, design system tokens)
  • APIs específicas que están prohibidas o son requeridas (inject() vs constructor, input() vs @Input)
  • Expectativas de formato de output (archivo completo vs fragmento)
  • Cualquier interfaz o tipo al que el código generado necesita conformarse

Iterando Prompts Como Código

Mantengo un archivo personal de plantillas de prompts que he refinado con el tiempo, básicamente un prompts.md en mis notas. Cuando un prompt produce un output excelente, lo guardo. Cuando falla, anoto por qué y ajusto. Es el mismo ciclo de feedback que uso para refinar código. El prompt engineering es un oficio, y mejoras a través de iteración deliberada.

Un patrón que he encontrado consistentemente útil: cuando un output generado está mal, no simplemente reintentes con un prompt ligeramente diferente. En su lugar, analiza exactamente qué estuvo mal: ¿fue la versión de Angular asumida? ¿El patrón de inyección? ¿La interfaz faltante? Luego corrige esa brecha específica en el prompt. Variar el prompt aleatoriamente desperdicia tiempo.

Los Límites del Prompt Engineering

Quiero ser honesto sobre lo que este enfoque no resuelve. Ninguna cantidad de prompt engineering reemplaza entender lo que estás pidiendo. Si no sé qué significa la detección de cambios OnPush, no puedo especificarla correctamente y no notaré cuando el código generado use Default en su lugar. El prompt amplifica tu conocimiento, no lo sustituye.

El otro límite es todo lo que abarca varios archivos a la vez. Un feature hecho de un componente, un servicio, un resolver y un store sigue siendo difícil de generar en un solo intento. Los divido en piezas más pequeñas y bien prompteadas y las ensamblo manualmente. Eso no es un fallo de la herramienta; es simplemente una comprensión precisa de en qué es buena.

La Precisión Le Gana a la Astucia

Los desarrolladores que veo sacando más provecho de las herramientas de IA no son los que tienen los prompts más creativos: son los más precisos. Conocen su stack lo suficientemente bien como para especificarlo con exactitud. Definen interfaces antes de pedir implementaciones. Le dicen al modelo qué no hacer tan claramente como le dicen qué hacer.

Si te llevas una cosa de este post: trata tu bloque de contexto como infraestructura. Mantenlo versionado, actualízalo conforme tu stack evoluciona, y compártelo con tu equipo. Un buen system prompt es un activo de equipo.

Referencias

AIAngularPrompt EngineeringTypeScript