Après une longue période à itérer sur ma façon d'utiliser les assistants IA dans mon workflow Angular, j'en suis arrivé à une conclusion : la qualité du code que vous récupérez est presque entièrement déterminée par la qualité du prompt que vous écrivez. Ça semble évident, mais la plupart des développeurs traitent l'IA comme un collègue senior qui connaît déjà leur codebase, leurs conventions et leurs contraintes. Ce n'est pas le cas. Vous devez lui apprendre à chaque fois, et la façon dont vous lui apprenez, c'est par le prompt.
Rien de ce qui suit n'est le conseil générique du type 'soyez spécifique, donnez du contexte'. Ce sont les prompts que j'utilise vraiment quand je génère des composants, des services, des tests et des décisions d'architecture en Angular, montrés avant et après, parce que la différence entre un prompt vague et un prompt précis est plus facile à voir qu'à décrire.
Pourquoi Angular nécessite des prompts différents
Angular est opinioné. Il a une façon spécifique de faire l'injection de dépendances, un système de modules (ou des composants standalone, sortis en developer preview en 14.0 puis stables en v15), des conventions RxJS, des attentes TypeScript fortes, et une structure de fichiers très particulière. Les modèles IA génériques connaissent Angular, mais ils ne savent pas lequel. Ils pourraient générer du code avec des NgModules quand vous êtes sur un projet standalone, ou utiliser des composants basés sur des classes avec des hooks de cycle de vie quand vous avez adopté les signals. Vous devez leur dire.
En plus de ça, les projets Angular d'entreprise ont leurs propres couches de conventions : bibliothèques partagées, composants de design system, patterns de gestion d'état spécifiques, conventions de nommage pour les services vs facades vs resolvers. Rien de tout ça n'est dans les données d'entraînement du modèle. Donc la charge est sur vous pour le fournir.
Le prompt système / bloc de contexte avec lequel je commence toujours
Que j'utilise un outil de clavardage ou une intégration IDE, la première chose que je fais est d'établir un bloc de contexte. Je le définis soit comme prompt système soit je le colle en haut de chaque conversation. Voici à quoi ça ressemble approximativement pour un projet standalone basé sur les signals. La ligne de version et les contraintes changent d'un codebase à l'autre :
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.Ce bloc seul change radicalement la sortie. Avant de l'ajouter, j'obtenais des services avec injection par constructeur, du boilerplate NgModule, et des composants sans OnPush. Après, j'obtiens quelque chose de beaucoup plus proche de ce que j'écrirais moi-même.
Prompter pour des composants
Avant : la demande vague
Au début, j'écrivais quelque chose comme :
Create an Angular component for a user profile card.Ce que j'obtenais était un composant basé sur une classe avec injection par constructeur, pas de types sur les inputs, pas de OnPush, et parfois une déclaration NgModule. Techniquement correct pour Angular 2018, pas utile pour mon projet.
Après : la demande structurée
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.Ça produit un composant que je peux intégrer au projet avec peut-être un ou deux ajustements. Les différences clés : j'ai spécifié la version Angular, le style d'API (signal inputs), la forme exacte de l'interface, la logique de comportement, et le format de sortie. La version doit être précise. Les signal inputs sont arrivés en 17.1 et output() avec OutputEmitterRef en 17.3, donc un modèle à qui l'on dit simplement 'Angular 17' a le droit de vous rendre du @Input() et de l'EventEmitter, et d'avoir raison.
Prompter pour des services
Les services nécessitent un template différent parce que les préoccupations principales sont différentes : scope d'injection, gestion HTTP, gestion d'erreurs et types de retour.
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).Les choses critiques que j'inclus toujours pour les services : le scope d'injection, d'où vient la config, les signatures exactes de méthodes avec types de retour, et ce que le service ne devrait PAS faire. Ce dernier point est étonnamment important : si vous ne dites pas 'c'est une couche de données pure', le modèle ajoutera souvent un cache BehaviorSubject et soudain votre service fait de la gestion d'état.
Prompter pour des tests
La génération de tests est là où le prompt engineering rapporte le plus. L'écart entre un test générique et un test utile est énorme, et il est presque entièrement contrôlable via le prompt.
Mauvais prompt de test
Write tests for UserProfileCardComponent.Vous obtiendrez des tests de rendu superficiel qui vérifient si le composant se rend, peut-être un ou deux bindings de propriétés. Rien qui attraperait réellement une régression.
Bon prompt de test
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.Le résultat est un fichier de test avec une couverture réelle de vrais comportements. Je définis les scénarios explicitement parce que le modèle ne sait pas ce qui compte dans mon contexte métier : seul moi le sais.
Prompter pour des décisions d'architecture
Au-delà de la génération de code, j'utilise aussi l'IA pour des questions d'architecture. Celles-ci nécessitent un type de prompt très différent, un qui porte assez de contexte pour que le modèle raisonne sur les compromis au lieu de choisir la réponse évidente. L'exemple ci-dessous est un gabarit. Le domaine y est volontairement banal, parce que ce qui fait le travail, c'est la liste de contraintes et la dernière ligne, pas le sujet.
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 dernière phrase est cruciale. Sans elle, vous obtenez une liste de compromis style Wikipédia qui ne vous aide pas à décider quoi que ce soit. Demander au modèle d'appliquer le raisonnement à votre situation spécifique le force à prendre position.
Ce qu'il faut toujours inclure dans le contexte
- Version Angular et flags de fonctionnalités majeurs (standalone, signals, etc.)
- Quel pattern de gestion d'état vous utilisez et à quel scope
- Le stack de test (Jest vs Karma, Testing Library vs TestBed)
- Votre approche CSS (SCSS, Tailwind, CSS Modules, design tokens)
- APIs spécifiques interdites ou requises (inject() vs constructeur, input() vs @Input)
- Attentes de format de sortie de fichier (fichier complet vs extrait)
- Toute interface ou type auxquels le code généré doit se conformer
Itérer sur les prompts comme sur le code
Je maintiens un fichier personnel de templates de prompts que j'ai affinés au fil du temps : essentiellement un prompts.md dans mes notes. Quand un prompt produit un excellent résultat, je le sauvegarde. Quand il échoue, je note pourquoi et j'ajuste. C'est la même boucle de rétroaction que j'utilise pour affiner le code. Le prompt engineering est un métier, et on s'améliore par l'itération délibérée.
Un pattern que j'ai trouvé systématiquement utile : quand une sortie générée est fausse, ne retentez pas juste avec un prompt légèrement différent. Au lieu de ça, analysez exactement ce qui était faux : c'était la version Angular supposée ? Le pattern d'injection ? L'interface manquante ? Puis corrigez cette lacune spécifique dans le prompt. Tâtonner le prompt au hasard fait perdre du temps.
Les limites du prompt engineering
Je veux être honnête sur ce que cette approche ne résout pas. Aucune quantité de prompt engineering ne remplace la compréhension de ce que vous demandez. Si je ne sais pas ce que signifie la détection de changement OnPush, je ne peux pas la spécifier correctement et je ne remarquerai pas quand le code généré utilise Default à la place. Le prompt amplifie votre connaissance : il ne la remplace pas.
L'autre limite, c'est tout ce qui couvre plusieurs fichiers à la fois. Une fonctionnalité faite d'un composant, d'un service, d'un resolver et d'un store est encore difficile à générer en un coup. Je les découpe en morceaux plus petits, bien promptés, et je les assemble manuellement. Ce n'est pas un échec de l'outil ; c'est juste une compréhension précise de ce en quoi il est bon.
La précision bat l'astuce
Les développeurs que je vois tirer le plus de valeur des outils IA ne sont pas ceux avec les prompts les plus créatifs : ce sont ceux qui sont les plus précis. Ils connaissent leur stack assez bien pour le spécifier avec exactitude. Ils définissent les interfaces avant de demander les implémentations. Ils disent au modèle ce qu'il ne doit pas faire aussi clairement qu'ils lui disent ce qu'il doit faire.
Si vous ne retenez qu'une chose de cet article : traitez votre bloc de contexte comme de l'infrastructure. Gardez-le versionné, gardez-le à jour à mesure que votre stack évolue, et partagez-le avec votre équipe. Un bon prompt système est un atout d'équipe.
Références
- Angular Signals. https://angular.dev/guide/signals
- Angular dependency injection. https://angular.dev/guide/di/dependency-injection
- About GitHub Copilot. https://docs.github.com/en/copilot/get-started/about-github-copilot