Back to Blog
AI & DevelopmentJanuary 25, 20269 min read

Using AI Coding Assistants (Copilot, Claude Code) in an Angular Workflow

DR

Doug Ribeiro

9 min read

The first three months I used Copilot, I accepted too many suggestions. I was a senior developer with about a decade in front-end at the time, working in Angular since 2017, and I still merged subtle bugs because the generated code looked plausible. An RxJS chain that used switchMap where I needed exhaustMap. A signal update that bypassed the immutability contract. A component with a subscription that never cleaned up. The code compiled. The tests passed because they were generated by the same tool and tested the same wrong behavior. I started being more selective after that.

GitHub CopilotInline completions in the edito…Agent mode in VS Code, multi-fi…A coding agent that works in th…Repository custom instructions,…Code review and PR summaries on…Claude CodeRuns in the terminal, not insid…Reads the repository itself ins…A project instructions file it …Multi-step work: edit, run the …Scriptable, so it can run as a …VS

The two do not compete on features so much as on where they sit. Copilot lives in the editor and completes what I am typing. Claude Code lives in the terminal and works on the repository: it reads files I did not open, makes an edit across several of them, runs the test suite, and reacts to what failed. The other difference is where the policy lives. Copilot Business answers to an organization's admin, which is why it was the one I used at work. On my own projects nobody is setting that policy but me.

What AI Tools Are Actually Good For

The gains are real in the right categories. Any task that is structural and repetitive, where the shape of the output is well-defined and I would just be filling in a pattern I already know, AI is faster than I am. Any task that requires judgment about architecture, business rules, or subtle behavioral correctness, AI is a distraction.

  • Scaffolding new components and services that follow patterns already in the codebase
  • Writing Jest test cases for a component once the implementation exists
  • Generating TypeScript interfaces from API response shapes or JSON examples
  • Drafting regex patterns and date formatting functions
  • Refactoring repetitive code blocks into shared utilities
  • Suggesting RxJS operators when you describe the behavior you want in plain language

The scaffolding gain on a typical Angular service is easy to feel. In a current Angular codebase, writing a new service with signals, error handling, and takeUntilDestroyed patterns, following the codebase conventions, is a slow and careful job without AI. With Copilot referencing an existing service as a template, it becomes a short pass with one round of corrections. That is real time saved, and it compounds across every service a team adds.

The Angular Version Problem

Angular has changed more between v14 and v19 than at any other point in its history. Standalone components, signals, the new control flow syntax, inject() replacing constructor injection in most patterns, functional guards, and on the NgRx side, the Signal Store. AI tools trained on pre-v16 code will confidently generate the old patterns. They look correct. They compile. They just are not how you write Angular today.

  • NgModule-based component declarations instead of standalone: true
  • Constructor injection instead of inject()
  • Subject-based ngOnDestroy teardown instead of takeUntilDestroyed
  • ngIf and ngFor instead of @if and @for control flow
  • Old NgRx patterns instead of Signal Store for feature state
  • ActivatedRoute constructor injection instead of the new input binding approach

I review every AI-generated Angular snippet for these before accepting it. It takes about 15 seconds. If the tool generates a constructor with injected dependencies, I convert it. This is not the AI's fault. It is a knowledge cutoff and training data problem. The fix is knowing the current patterns well enough to catch the gaps.

How I Use Claude Code on My Own Projects

On my own projects, which means my SaaS and whatever side build is alive at the time, what I want is code that matches the project, not code that matches the framework docs. So I keep a project instructions file in the repository describing the conventions, and I point at the specific files I want a new piece to look like. The prompt below is the shape that works: name the reference file, name the patterns, name the shared services.

typescript
// Prompt approach that produces consistent results
// (illustrative; paths and names are generic):
// "Generate a JobApplicationService following the same patterns as
// src/app/features/inbox/services/inbox.service.ts
// Use inject() for dependencies, expose state via signals,
// handle errors with the ErrorHandlerService already in core,
// and include takeUntilDestroyed where needed."

// The output follows the project's conventions rather than
// the generic Angular answer, which is what you want
// in a codebase that already has standards.

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

  private _applications = signal<JobApplication[]>([]);
  private _saving = signal(false);

  readonly applications = this._applications.asReadonly();
  readonly saving = this._saving.asReadonly();

  track(postingId: string, stage: ApplicationStage): Observable<JobApplication> {
    this._saving.set(true);

    return this.http
      .post<JobApplication>('/api/applications', { postingId, stage })
      .pipe(
        tap(application => {
          this._applications.update(list => [...list, application]);
          this._saving.set(false);
        }),
        catchError(err => {
          this._saving.set(false);
          return this.errorHandler.handle(err);
        })
      );
  }
}

Using AI at a Bank: What the Rules Look Like

At a bank, the conversation about AI tools is not optional. Banks set policies on what code can be sent to external AI services, and the short version is usually the same: no proprietary business logic, no customer data, no internal API contracts. At BMO, the team's assistant was GitHub Copilot Business, which has enterprise data privacy guarantees. I did not use the chat interface to paste in internal service code or data models.

In practice this meant AI helped me with Angular patterns and TypeScript mechanics, not with the lending domain logic. I did not ask it to help me implement a credit decision algorithm or a regulatory compliance check. Those require human judgment and review, and sending them to a third-party AI is a data governance risk even with enterprise tiers.

  • Use enterprise-grade subscriptions with data processing agreements
  • Never paste credentials, connection strings, JWT secrets, or PII into any AI interface
  • Never paste proprietary business logic or internal API schemas into public AI chat
  • Review generated code the same way you review a junior developer's PR: assume mistakes, read carefully

The Guardrails, and Which One Actually Blocks a Merge

Guardrails come in layers and they are not interchangeable. The instruction layer goes first: a repository instructions file that tells the assistant what the codebase is and what it must never suggest, and on Copilot Business, content exclusion, which keeps whole paths out of the model's context in the first place. That layer shapes what gets generated, and it is the one I have written up in detail in the post on AI guardrails in a banking environment. It does not decide what merges. The pipeline decides that. ESLint with angular-eslint rules, Jest with a coverage threshold, SonarQube quality gates, all of it on every pull request. If the AI generates a deprecated pattern, lint fails and the merge is blocked. Note what does not happen automatically: if it hands you a service method with no error branch, nothing catches that unless somebody wrote the lint rule or the test first. The two layers are complementary, and the ordering is the point. Instructions are a request a model is free to ignore. A failing quality gate is not negotiable. If you can only build one of them this quarter, build the pipeline.

javascript
// eslint.config.js
// angular-eslint ships flat config from v18, and a v19 workspace
// generates this file instead of .eslintrc.json. These are the rules
// that catch what an AI assistant gets wrong most often in Angular.
// @ts-check
const eslint = require('@eslint/js');
const tseslint = require('typescript-eslint');
const angular = require('angular-eslint');

module.exports = tseslint.config({
  files: ['**/*.ts'],
  extends: [
    eslint.configs.recommended,
    ...tseslint.configs.recommended,
    ...angular.configs.tsRecommended,
  ],
  processor: angular.processInlineTemplates,
  rules: {
    '@angular-eslint/prefer-standalone': 'error',
    '@angular-eslint/no-lifecycle-call': 'error',
    '@angular-eslint/use-injectable-provided-in': 'error',
    '@typescript-eslint/no-explicit-any': 'warn',
    '@typescript-eslint/explicit-function-return-type': 'warn',
    'no-console': ['error', { allow: ['warn', 'error'] }],
  },
});

AI tools give leverage to the developer who already knows the framework. They give false confidence to the developer who does not.

My overall assessment after two years of daily use: Copilot is the one I have open while I type, and Claude Code is the one I hand a whole task to when the work spans files and needs the tests run. The speed gains on repetitive structural work are real and they compound across a quarter. The key is treating every suggestion as a starting point, not a solution. The review takes seconds. Skipping it takes minutes to fix later, if you catch it at all.

References

AIAngularClaude CodeCopilotDeveloper Tools