DEV Community

Cover image for Agent Skills: How to Make AI Write Modern Angular Code
hassantayyab
hassantayyab

Posted on • Originally published at hassantayyab.com

Agent Skills: How to Make AI Write Modern Angular Code

Agent Skills: Teaching Your AI Coding Assistant to Write Better Angular Code

If you've used AI coding assistants like Claude Code, Cursor, or GitHub Copilot, you've probably noticed something frustrating: they often generate outdated code. Ask for an Angular component, and you might get constructor-based dependency injection instead of the modern inject() function, or *ngIf instead of the new @if control flow. This happens because these AI models were trained on code from a specific point in time, and frameworks like Angular evolve rapidly.

Agent Skills solve this problem. They're simple instruction files that teach AI assistants how to write code the way you want it written.

What exactly is an Agent Skill?

Think of an Agent Skill like a recipe card you hand to a chef. The chef (the AI) already knows how to cook, but the recipe card tells them exactly how you want your dish prepared—which ingredients to use, which to avoid, and what the final result should look like.

In technical terms, a skill is just a markdown file named SKILL.md that contains instructions for the AI. When you ask the AI to do something that matches a skill's purpose, it reads those instructions and follows them.

Here's the simplest possible skill:

---
name: my-angular-skill
description: "Use when creating Angular components or services."
---

# Angular Rules

Always use the `inject()` function for dependency injection.
Never use constructor injection.
Enter fullscreen mode Exit fullscreen mode

That's it. When the AI recognizes you're working on Angular code, it reads this file and follows the rules.

Why do Angular developers need skills?

Angular has changed dramatically in recent versions. The patterns that were correct in Angular 14 are now deprecated in Angular 18 and beyond. Without guidance, AI assistants will generate a mix of old and new patterns because their training data contains years of Angular code written in different styles.

Here's what typically happens without a skill:

You ask: "Create a user service that fetches data from an API"

AI generates (outdated pattern):

@Injectable({
  providedIn: 'root'
})
export class UserService {
  constructor(private http: HttpClient) {}  // ❌ Old pattern

  getUsers(): Observable<User[]> {
    return this.http.get<User[]>('/api/users');
  }
}
Enter fullscreen mode Exit fullscreen mode

What you actually want (modern pattern):

@Injectable({
  providedIn: 'root'
})
export class UserService {
  private http = inject(HttpClient);  // ✅ Modern pattern

  getUsers(): Observable<User[]> {
    return this.http.get<User[]>('/api/users');
  }
}
Enter fullscreen mode Exit fullscreen mode

The difference seems small, but multiply this across an entire codebase and you end up with inconsistent code that mixes patterns from different Angular eras.

Creating your first Angular skill

Let's build a practical skill that enforces modern Angular patterns. Create a folder called .claude/skills/angular-modern/ in your project root, then add a SKILL.md file:

---
name: angular-modern-patterns
description: Generate Angular 18+ code using signals, standalone components, 
             and inject(). Use when creating components, services, directives,
             or refactoring existing Angular code.
---

# Modern Angular Development Patterns

When writing Angular code, follow these patterns consistently.

## Dependency Injection

Use the `inject()` function instead of constructor injection:

Enter fullscreen mode Exit fullscreen mode


typescript
// ✅ Correct - use this pattern
export class UserComponent {
private userService = inject(UserService);
private router = inject(Router);
}

// ❌ Wrong - never generate this
export class UserComponent {
constructor(
private userService: UserService,
private router: Router
) {}
}


## Components

All components must be standalone. In Angular v20+, omit `standalone: true` 
since it's the default:

Enter fullscreen mode Exit fullscreen mode


typescript
// ✅ Correct for Angular 20+
@Component({
selector: 'app-user-card',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [CommonModule],
template: ...
})
export class UserCardComponent {
// Use signal inputs instead of @Input decorator
user = input.required();

// Use output function instead of @Output decorator

userSelected = output();
}


## Control Flow

Use the new built-in control flow syntax instead of structural directives:

Enter fullscreen mode Exit fullscreen mode


typescript
// ✅ Correct - new control flow
@if (user()) {

} @else {

No user found


}

@for (item of items(); track item.id) {

}

// ❌ Wrong - old directives



## Signals for State

Prefer signals over plain properties for reactive state:

Enter fullscreen mode Exit fullscreen mode


typescript
export class CounterComponent {
// ✅ Correct - signals for reactive state
count = signal(0);
doubleCount = computed(() => this.count() * 2);

increment() {
this.count.update(c => c + 1);
}
}


Save this file, and now every time you ask the AI to generate Angular code, it will follow these patterns.

## How skills actually work under the hood

When you start a conversation with Claude Code (or another skill-enabled AI), here's what happens:

1. **Metadata loading:** The AI reads just the `name` and `description` from each skill file. This costs very few tokens—roughly 50-100 per skill.
2. **Pattern matching:** When you make a request, the AI checks if any skill descriptions match what you're asking for. "Create an Angular component" matches our skill's description about creating components.
3. **Full loading:** Only when there's a match does the AI read the complete skill instructions. This keeps conversations efficient—you're not paying for unused context.
4. **Code generation:** The AI follows the skill's instructions when generating its response.

This "progressive disclosure" design means you can have dozens of skills installed without slowing anything down. The AI only loads what it needs.

## Organizing skills for a real Angular project

For a production Angular application, you'll likely want several skills working together. Here's a practical folder structure:

Enter fullscreen mode Exit fullscreen mode

your-angular-project/
├── .claude/
│ └── skills/
│ ├── angular-modern/
│ │ └── SKILL.md # Core patterns we created above
│ ├── component-generator/
│ │ ├── SKILL.md # Component scaffolding workflow
│ │ └── templates/
│ │ └── component.ts # Template file for reference
│ ├── ngrx-patterns/
│ │ └── SKILL.md # State management conventions
│ └── testing/
│ └── SKILL.md # Jest and TestBed patterns
├── CLAUDE.md # Project-level context (always loaded)
└── src/
└── ...


The `CLAUDE.md` file at your project root is special—it loads automatically for every conversation and should contain high-level project context:

Enter fullscreen mode Exit fullscreen mode


markdown

Project: Customer Portal

Tech Stack

  • Angular 20
  • NgRx Signals for state management
  • Angular Material with custom theme
  • Jest for testing

Conventions

  • All components use OnPush change detection
  • Feature modules organized by domain (users, orders, products)
  • API calls go through services, never directly in components

Commands

  • npm run test - Run unit tests
  • npm run e2e - Run Playwright tests
  • npm run lint - Check code style

## A complete example: Component generation workflow

Let's create a more advanced skill that handles a common workflow—generating a complete Angular feature with a component, service, and tests.

Create `.claude/skills/feature-generator/SKILL.md`:

Enter fullscreen mode Exit fullscreen mode

markdown

name: angular-feature-generator
description: Generate complete Angular features including component, service,
and tests. Use when asked to create a new feature, page, or

module for the application.

Angular Feature Generation

When asked to create a new feature, generate these files in order:

1. Service (data layer first)

// feature-name.service.ts
import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({ providedIn: 'root' })
export class FeatureNameService {
  private http = inject(HttpClient);

  // Methods return Observables for flexibility
  // Components convert to signals as needed
}
Enter fullscreen mode Exit fullscreen mode

2. Component (presentation layer)

// feature-name.component.ts
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';

@Component({
  selector: 'app-feature-name',
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [],  // Add required imports
  template: ``
})
export class FeatureNameComponent {
  private featureService = inject(FeatureNameService);

  // Convert service observables to signals for template
  // data = toSignal(this.featureService.getData());
}
Enter fullscreen mode Exit fullscreen mode

3. Test file

// feature-name.component.spec.ts
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { provideHttpClientTesting } from '@angular/common/http/testing';

describe('FeatureNameComponent', () => {
  let component: FeatureNameComponent;
  let fixture: ComponentFixture<FeatureNameComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [FeatureNameComponent],
      providers: [provideHttpClientTesting()]
    }).compileComponents();

    fixture = TestBed.createComponent(FeatureNameComponent);
    component = fixture.componentInstance;
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});
Enter fullscreen mode Exit fullscreen mode

File organization

Place generated files in the appropriate feature folder:

src/app/features/feature-name/
├── feature-name.component.ts
├── feature-name.component.spec.ts
├── feature-name.service.ts
└── feature-name.service.spec.ts
Enter fullscreen mode Exit fullscreen mode

Now when you ask "Create a user profile feature," the AI knows exactly what files to generate, in what order, and following which patterns.

Skills versus other AI extension methods

You might wonder how skills compare to other ways of extending AI assistants, like MCP servers or function calling. They serve different purposes and work well together:

Skills teach the AI how to approach tasks. They provide patterns, conventions, and domain knowledge. Skills are static instruction files that get loaded into the conversation.

MCP (Model Context Protocol) connects the AI to external systems. An MCP server might give the AI access to your Figma designs, your GitHub repository, or a live database. MCP provides dynamic, real-time data.

Function calling lets the AI execute specific actions, like running a calculation or calling an API endpoint.

For Angular development, a practical setup might combine all three:

  • Skill: Teaches the AI your component patterns and coding standards
  • MCP server: Connects to Figma so the AI can see your designs while generating components
  • Function: Runs ng generate commands to actually create the files

Getting started today

Here's a practical path to start using skills in your Angular projects:

Step 1: Create the .claude/skills/ folder in your project root.

Step 2: Add a basic Angular patterns skill using the example from this post.

Step 3: Create a CLAUDE.md file documenting your project's tech stack and conventions.

Step 4: Test it by asking the AI to generate a component and verify it follows your patterns.

Step 5: Iterate. When the AI generates something wrong, add a rule to your skill to prevent it next time.

The Angular team has also published official AI configuration that you can generate with ng new in Angular CLI v20.2+. This gives you a solid starting point that you can customize for your team's specific conventions.

Conclusion

Agent Skills transform AI coding assistants from generic code generators into specialized development partners that understand your project's conventions. For Angular developers dealing with rapidly evolving framework patterns, skills ensure the AI generates modern, consistent code instead of a mixture of deprecated approaches.

The investment is minimal—a few markdown files—but the payoff is significant. Instead of constantly correcting AI-generated code, you teach the AI once and it follows your patterns consistently. As your conventions evolve, you update the skill files and every future interaction reflects those changes.

Start small with a single skill covering your most important patterns, and expand from there as you identify more opportunities for guidance.

Top comments (1)

Collapse
 
bhavin-allinonetools profile image
Bhavin Sheth

This resonates a lot. The outdated patterns problem is exactly what makes AI-generated code feel unreliable in fast-moving frameworks like Angular.

I really like the recipe card framing for Agent Skills — it explains why this works without getting abstract. The progressive loading idea (metadata first, full skill only on match) also clears up a lot of confusion around token usage.

Curious how you’ve seen teams adopt this in practice: do most start with one strict modern patterns skill, or do they usually need to break it up early into multiple smaller skills?