RxJS

Angular interview questions for catching senior talent

Companies ask me to take care of technical interviews from time to time. Having done quite a lot of these interviews, I learned a lot from them. By helping out a lot of different clients, I also have been on the other side of the table more than a few times too. This means I have learned some patter

Angular interview questions for catching senior talent — RxJS article by brechtbilliet on Angular In Depth
Angular interview questions for catching senior talent — RxJS article by brechtbilliet on Angular In Depth
On this page · 21 sections

From time to time, companies ask me to oversee their technical interviews. After conducting many of them, I've gathered a great deal of insight. Being on the other side of the table for various clients has also given me a sense of what makes a candidate stand out. I've learned how to leave a positive impression as an interviewee, and I also have a good read on estimating a candidate's level. I steered away from requesting coding assessments because I respect the candidate's time—I view them as people, not code-producing units. That said, I realize that some teams find assessments useful; it’s a matter of personal preference and fit. This article kicks off a series focused on interview questions. Each installment will present 20 questions for interviewers, along with the kind of responses I consider strong. Keep in mind that there are no perfect answers, but I’d like to walk you through my reasoning during these interviews. This one centers on Angular; future ones might tackle architecture, JavaScript/TypeScript, testing, or RxJS. Also, keep in mind that many of these questions will be tough for junior developers—candidates don’t need to get everything right.

I’m not keen on asking questions like:

List all the lifecycle hooks available in Angular components and tell me the precise order of their execution.

Such questions make candidates uncomfortable and stressed. Neither do I like questions such as:

What differentiates `markForCheck()` from `detectChanges()`?

If candidates mention that in response to another question, great—but we shouldn’t dwell on API specifics. Occasionally, I might dig into specifics, but that’s only after the candidate has answered most other things well. Nobody should be judged on rote memorization of a framework's API.

Instead of quizzing people on memorized APIs, why not aim for understanding how they reason through problems and solutions? That means focusing on open-ended questions with many potential responses. The broader the question, the more insight we gain into the person we’re interviewing. We only have a limited time, right?

Candidates often surprise us by bringing up best practices or warning against anti-patterns. I enjoy hearing extended explanations where the candidate’s enthusiasm for Angular and its ecosystem becomes clear.

For every question, I’ll share how I would answer personally and why—essentially, what would impress me as an interviewer. I won’t provide exact answers here, as that would be doing the work for you.

1: List every method you can think of for enabling communication between Angular components

This appears simple but can really showcase a candidate’s depth. I’d start by mentioning that the most straightforward path is through @Input() and @Output() properties. Data flows from a parent component down to a child using @Input() and is bound in the template with square brackets [foo]="bar". The child notifies the parent via @Output(), using parentheses like (change)="onChange($event)". So it’s a bit API-specific, but without knowing that, they likely haven’t used Angular at all. Some candidates have missed this one, and I’ve seen my fair share of them.

Another point I’d make: avoid naming outputs like onChange or onClick; prefer change or click instead. That aligns with Angular’s existing APIs and is a widely recognized convention.

One could also inject a parent component into a child component via the constructor and dependency injection. I’d like to hear when that makes sense and whether it’s usually a good approach. In many cases, I see injecting parents as poor practice since it goes against unidirectional data flow and adds complexity. Still, I can think of an exception: injecting a custom form component into a wrapper component to check if a form is submitted.

Another option: use ViewChildren to get references to child components from a parent. That’s also a way to communicate. And, I’d mention the difference between ViewChildren and ContentChildren.

Finally, services can enable communication between components. In principle, I don’t use them for parent–child communication. Sibling components, though, can benefit from sharing a service, though often the parent can mediate. When there is a parent component, a child component, and a router-outlet in between, that’s a good moment to use a service.

Bonus question: Tell me one ineffective way for components to communicate and explain why it’s a problem.

A candidate might say using the window object, which is problematic because it breaks encapsulation and creates unwanted dependencies—you’re communicating outside the framework. Another poor practice: using a global store like @ngrx/store for simple component-to-component messaging. That adds a heavy framework where a simple solution would do, and it inserts a hard dependency into every layer. That’s my personal stance; I welcome other perspectives.

2: How have you structured components in Angular?

I’d like to hear about smart and dumb components: their responsibilities and how a “dumb” component can still be complex—like a month view in a calendar—without knowing anything about the rest of the app. I’d say that splitting into smaller, focused components helps with:

  • Managing change detection with ChangeDetection.OnPush
  • Testability
  • Fewer observable subscriptions
  • Applying the single responsibility principle
  • Separation of concerns
  • Reusability

I keep smart and dumb components in separate folders to prevent mixing them.

Also, I’d discuss when to extract code from a component. I could weigh inline templates against separate files—each has pros and cons. I want to show that I’m not biased and that both approaches can be valid.

Bonus question: Distinguish between pipes and components

Here, there’s not much discussion—this is straightforward.

3: Dependency injection, services, service lifecycles, and practical use cases. Go ahead.

This subject lets a candidate demonstrate a deep understanding of Angular's dependency injection system. I’d mention making services injectable, perhaps through modules, which results in an app-wide singleton. Then, using providedIn:'any' in lazy-loaded modules gives a singleton at that module level. Additionally, we can provide services at the component level, and their lifecycle will be tied to those components’ lifecycles. Also, if the service is provided in a component, we can use the ngOnDestroy() hook there as well. A good example of service usage would be eliminating duplication between two “smart” parent components that share 80% of their logic. This is the place to mention I choose composition over inheritance.

We could add details about the providedIn: 'root' syntax on the Injectable() decorator, which yields an app-wide singleton instance. This syntax also enables tree shaking.

It’s also a great time to reference Angular 14's standalone components and share our thoughts on Angular modules. A senior can show experience by noting that Angular modules appeared in angular2@rc5.

I mention that not to show off, but to illustrate how deep a candidate can go.

4: Explain the distinction between guards and interceptors

I like this one because it tests two topics at once. They’re unrelated tools but both important. After explaining guards, a candidate can bring up examples for using canActivate, but also the less common canDeactivate. A classic example: blocking navigation away from a page when the user has unsaved form changes.

Then, for interceptors, it’s not just about attaching JWT tokens. Candidates might bring up interceptors for handling loading states or centralized error handling.

5: What does the async pipe do?

It doesn’t just subscribe to observables; it also unsubscribes when the component is destroyed and triggers a markForCheck(). That shows an understanding of RxJS and its place in Angular (alongside zone.js).

I would label the async pipe a best practice because it does necessary management behind the scenes—subscribing, unsubscribing, and marking for check. A candidate might go further, into *ngIf="foo$|async as foo" syntax to share the observable’s value within a template. There’s a caution though: this can be an issue if foo holds a falsy value.

6: Walk me through RxJS’s integration with Angular. Which reactive APIs exist?

This is a gem—there’s a lot one might say. It’s possible to start off with a personal passion for RxJS. After that, the router itself is reactive; subscribing to its events is possible. The ActivatedRoute exposes params, queryParams, etc., as observables. Then there are Reactive Forms—where valueChanges is an observable—and also ViewChildren and ContentChildren as observables.

A candidate can mention that HttpClient returns observables—not promises—and that aborting a subscription during an XHR request triggers an xhr.abort() under the hood.

To go one level deeper, you can note that @Output() EventEmitters are observables behind the scenes. That means any kind of observable can replace them.

7: Show me an easy way to create a memory leak in Angular, and tell me how to fix it. For instance, using setInterval()

If the candidate seems lost, a hint: add a setInterval(console.log) somewhere in code that gets destroyed. Then we should still see numbers logged.

Usually components get destroyed and instantiated again. Putting that statement inside a constructor or ngOnInit() of a page component, then navigating away, would show logs continuing after component destruction. The fix: clear the interval in ngOnDestroy().

Memory leaks with RxJS are easy to create, so the importance of unsubscribing is something good to bring up. This might be a hard question, but it often reveals the inner logic of the candidate.

8: What’s your approach to testing Angular applications?

This has many valid answers, so there’s no right or wrong. But it reveals the person’s testing philosophy, priorities, and actual experience testing Angular.

My take would start with the testing pyramid vs. snowcone and then move toward the honeycomb style: a small amount of unit tests (just the tricky logic), many integration tests for components, and a lighter dose of E2E to cover the happy flows. I’d also say that I unit test complex logic in services without TestBed, using jest alone, since services are often plain TypeScript classes. My reasons: complexity, performance, and not being tied to a specific framework.

For component integration tests, I’d use cypress plus storybook, creating a storybook for the use case and testing it via cypress. Then for E2E, cypress works too, but these tests are slower and harder to maintain so I don’t write many of them.

I’d also question the value of having many unit tests because they tend to be brittle—a single dependency added in a constructor can cause every test to fail. But a failing cypress-storybook test usually signals that something actually broke.

9: Walk me through Angular’s change detection

This is where I might note that zone.js monkey-patches native events and tells Angular when something occurs. Angular then runs top to bottom over the component tree by default. Since that’s not super efficient, the ChangeDetection.OnPush strategy helps. Then, talk about markForCheck() vs. detectChanges(). And I’d add that this pattern needs immutable data flows to function properly and doesn’t make sense for components without @Input() properties.

There’s also a benefit to sidetracking: immutable data isn't just about performance; it’s about predictability.

And yes, the async pipe calls markForCheck, but that's already been mentioned!

Bonus question: What would happen if you applied the ChangeDetection.OnPush strategy on the app component?

In short: break your change detection. You’d need to run markForCheck() manually. For instance, when we do httpClient.get('url').subscribe(result => this.result = result), the UI won’t actually update. Also, the deeper parts of the component tree could break. Since the app component has no @Input(), OnPush offers no benefit anyway.

10: What does “Expression has changed after it was checked” mean?

I’d probably laugh and admit that I’ve run into this bug more than once. It comes from Angular running change detection twice in development and comparing both results. When the values are different, it indicates something changed between runs—so our dataflow isn’t clean.

I’d add that we don’t perform that double-run in production. While it’s not harmful, it does reveal problems worth investigating.

11: In Angular, how do you deal with redundancy?

This is broad for a senior to talk about perhaps at length. Now a shorter version:

  • Extract logic into reusable components
  • Extract logic into reusable directives
  • Extract logic into reusable pipes
  • Extract logic into reusable functions within JavaScript modules (better tree shaking)
  • Extract logic into services

12: What is trackBy, and can you explain how it works?

You can gauge a candidate’s Angular seniority with this question. It applies to every *ngFor and aids in rendering performance. For very large lists, it matters considerably.

13: Discuss the Angular CLI.

A candidate can cover code generation, Webpack usage, maybe reflect on why Webpack and not rollup, or why vite would potentially be a poor fit. You could go into schematics, nx (which extends the CLI), how to update between versions, using ng-packagr to publish an Angular library, and that the CLI setups include Webpack config, testing configuration, and linting/prettier settings.

14: How do you get params from the ActivatedRoute, and what complexities do you notice?

You’d get observables of params and also snapshots. Note that it only sees its own params, not those of parent router outlets—unless the router config’s paramsInheritanceStrategy is set to always.

That is a bit of pain: child routes don’t easily see parent params even if those are in the URL. They probably implemented it like this to handle auxiliary routes.

From here, we can talk about nesting router-outlets to display dialogs—using Angular to manage their lifecycle. Also, note that when a param changes, the component doesn’t get destroyed/reinstantiated.

15: Name 3 things you like most about Angular

Simple, yet it shows passion, experience, and the ability to challenge decisions just by leaning back and listening.

My picks are the RxJS integration, the "opinionatedness," and suitability for big projects.

16: Name your 3 biggest Angular frustrations.

Again, this shows experience, reasoning, and could lead to challenging technical decisions.

Mine would be bundle size, build speed, and ReactiveForms. Still, Angular 14’s typed forms are nice—but they can use more improvement.

17: How do you handle content projection in Angular?

We have <ng-content> as a slot. Whatever goes between our component tags gets rendered there. Also, you can have several slots, and I can name each of them. These slots can be referenced through @ContentChildren(). The classic example is dialogs with a title slot and a separate body area.

It’s also worth bringing up ngTemplateOutlet—which I’d rather look up than recall by heart. Still, it's important to know there is such a thing.

In advanced cases, we can even pass template references via a service to render content from a different view. I know this, but I’d perhaps say it only to sound sharp (even though it works).”

18: What do you know about selector prefixes?

Here, you check actual experience and reasoning.

The points:

  • Every component selector has a prefix, but selectors aren’t necessary, so prefixes aren’t either.
  • We set prefixes to keep things unique and make code location obvious
  • Linters can enforce using prefixes correctly
  • Components and directives both can have prefixes.
  • Skipping prefixes is not advisable.
  • Prefixes are set in both project.json (for Nx) and angular.json (for Angular CLI) to generate components/directives correctly, and they're also in linting configs.

Unlike other questions, here a candidate can simply enumerate everything they know about that subject.

19: Which Angular version updates excited you, and why?

An easy but powerful way to tell if someone is current with Angular and has enough experience.

Personally, at the time of writing, I’d talk about standalone components and typed forms in Angular 14. It’s also interesting to mention that Angular 3 never happened and why. Also, I might mention frustration around the fact that angular2@rc5 suddenly had modules when angular2@rc4 did not, forcing me to rewrite my workshop at the last minute.

Post that, upgrades have gotten easier; for me, updating to Angular 6 was painful. I might also be negative re: Ivy, because at ngVikings, we heard about a hello world of just a few kilobytes, but that hasn’t been realized in practice so far.

As you see, it’s not about versions in this case; it’s about triggering a conversation about someone’s experiences with Angular and key changes over time.

20: Who do you follow? Which blogs do you read? What’s your method for keeping up with Angular?

I appreciate this question. There are plenty of possible answers—like following courses on Pluralsight or egghead, team members on the core team, interesting pull requests, attending conferences or viewing their talks, reading books, discussing with other colleagues, working on side projects, and doing experiments in free time.

This isn’t about testing their level. It’s about seeing dedication and interest in the framework.

Wrapping Up

Limit the number of questions, keep each one concise, and be ready for lengthy responses. You may be pleasantly surprised by the depth of reasoning a strong candidate brings. Anyone can pick up a framework, but not everyone develops the ability to think critically. Syntax slips away quickly when it hasn’t been used for weeks or months, so don’t over-index on memorization. I hope this discussion proves useful to you.

A heartfelt thank you to the insightful reviewers:

Angular forms course
B
brechtbilliet

Writes about RxJS, Components, State. Active 2016–2022.

All 22 articles →