Angular Senior Interview Questions

Real-world interview questions for Senior Angular Developers, covering Architecture, Performance, RxJS, and Leadership.

🧠 Angular Core & Architecture

1. How does Angular's Change Detection work, and why would you generally use OnPush?

Angular checks the UI to see what changed and updates the DOM. By default, it checks everything, which is safe but can be slow.

OnPush is a strategy that tells Angular: ā€œOnly check this component if my Inputs change reference, I fire an event, or I manually tell you to.ā€ This makes the app much faster because Angular skips checking parts of the tree that haven’t changed.

🧠 Memory tip: Default = check all; OnPush = check smart.

2. What are Signals, and how do they differ from RxJS Observables?

Signals are for synchronous state—like ā€œthis is the current value of the counter.ā€ They are great for template rendering because Angular knows exactly where the value is used and can update just that tiny piece of text.

Observables (RxJS) are for asynchronous streams—like ā€œa user is typing,ā€ or ā€œa data request is happening.ā€ They are about events over time.

I use Signals for holding state (what to show) and RxJS for handling events (complex logic).

🧠 Memory tip: Signals maintain State (Values); RxJS manages Events (Streams).

3. Explain Dependency Injection (DI) in Angular heavily simply.

The DI system is like a restaurant kitchen. You (the component) don’t cook your own burger (create services). You just ask the waiter (Injector) for a burger, and they bring it to you.

This is good because I can easily swap the burger for a fake one (mock service) when I am testing, without changing the component code. It keeps my code clean and decoupled.

🧠 Memory tip: Don’t cook it yourself; order it from the Injector.

4. What are Standalone Components and why are they the new standard?

Before, we had NgModules, which were like big boxes where we had to declare everything. It was confusing and caused ā€œmodule hell.ā€

Standalone Components don’t need Modules. They import exactly what they need directly (like CommonModule or ButtonComponent). It makes the mental model simpler—a component is self-contained. It also makes lazy loading easier and bundling better.

🧠 Memory tip: Standalone = Self-contained; No more Module wrappers.

5. What is the difference between Constructor and ngOnInit?

The Constructor is a JavaScript thing. It runs when the class is created. I only use it for dependency injection.

ngOnInit is an Angular thing. It runs after Angular has finished setting up the component and passed in the Input values. I put my initialization logic (like fetching data) here because the inputs are ready.

🧠 Memory tip: Constructor for Injection; ngOnInit for Initialization.

6. How does Content Projection work (ng-content)?

It’s like making a picture frame. The frame (Parent Component) defines the border, but the picture inside (Projected Content) is provided by whoever uses the frame.

We use <ng-content> to mark where the content goes. It allows us to build reusable UI wrappers, like Cards or Modals, where the insides can be anything.

🧠 Memory tip: Parent builds the Frame; User provides the Picture.

7. What are Directives, and when would you create a custom one?

Directives are just ā€œbehaviorsā€ attached to elements.

  • Attribute Directives: Change how something looks or acts (like changing color on hover).
  • Structural Directives: Add or remove things from the DOM (like *ngIf).

I create custom directives to reuse logic. For example, a ClickOutside directive that listens for clicks outside an element to close a dropdown. It keeps my component code clean.

🧠 Memory tip: Components = UI; Directives = Behavior without UI.

8. Explain the concept of "ViewEncapsulation".

It’s how Angular prevents my CSS from leaking out and messing up other parts of the app.

  • Emulated (Default): Angular adds weird attributes like _ngcontent-c1 to my CSS so it only applies to this component.
  • None: My CSS goes global. Use with caution!
  • ShadowDom: Uses the browser’s native Shadow DOM. Harder to style from outside.

🧠 Memory tip: Emulated = Scoped CSS (Safe); None = Global CSS (Risky).


āš™ļø Performance Optimization

1. How do you identify performance bottlenecks in an Angular app?

I rely on two things:

  1. Chrome DevTools (Performance Tab): To see if the main thread is blocked by heavy scripting.
  2. Angular DevTools: To see the change detection cycles. If I see a bar that is constantly yellow or red, or change detection running too often, I know that component is the problem.

🧠 Memory tip: Chrome for CPU blocking; Angular DevTools for Change Detection loops.

2. What is Lazy Loading and how do you implement it?

Lazy loading means ā€œdon’t download code until the user actually needs it.ā€ We usually do this at the Route level.

In the router config, instead of component: MyComponent, we use loadComponent: () => import(...). This splits that page into a separate file. The app loads faster initially because the main bundle is smaller.

🧠 Memory tip: Load it only when I click it.

3. What is the `@defer` block in modern Angular templates?

It’s a super easy way to lazy load parts of a template. I can say @defer (on viewport) { <heavy-chart /> }. This tells Angular: ā€œDon’t even load the code for this Chart component until the user scrolls it into view.ā€ It’s declarative and very powerful for Core Web Vitals.

🧠 Memory tip: Defer = Lazy load parts of HTML automatically.

4. Why is `trackBy` (or `@for` tracking) important for lists?

Without tracking, if I change one item in a list of 100, Angular might destroy and recreate all 100 DOM elements because it doesn’t know which one changed.

With tracking (using a unique ID), Angular knows: ā€œOh, only Item #5 changed.ā€ It updates only that one element. It saves huge amounts of browser effort.

🧠 Memory tip: No ID = Rebuild All; With ID = Update One.

5. How do you handle preventing memory leaks in components?

The biggest cause is subscribing to Observables and forgetting to unsubscribe. My rules:

  1. Use the AsyncPipe whenever possible (it handles unsubscribing automatically).
  2. In modern Angular, use takeUntilDestroyed().
  3. If manual, save the subscription and call .unsubscribe() in ngOnDestroy.

🧠 Memory tip: Always Unsubscribe or use Async Pipe.

6. What is "Zone Pollution" and how to avoid it?

Zone.js triggers change detection whenever any async event happens (clicks, timers, HTTP). ā€œPollutionā€ is when we use a library (like a charting lib) that runs requestAnimationFrame or lots of timeouts. Angular tries to re-render 60 times a second!

To fix it, we run that code outside Angular: this.ngZone.runOutsideAngular(() => { ... }).

🧠 Memory tip: Heavy 3rd party lib = runOutsideAngular.

7. Explain Preloading Strategies.

Lazy loading waits until the user clicks. Preloading loads the lazy chunks in the background after the initial page load is done. So, the user gets the initial page fast. Then, while they are reading, we quietly download the ā€œSettingsā€ page code. When they click ā€œSettings,ā€ it opens instantly.

🧠 Memory tip: Lazy = Wait for click; Preload = Download in background while idle.

8. How does Server-Side Rendering (SSR) improve performance?

SSR generates the HTML on the server. The user sees the full page content immediately, instead of a white screen while waiting for JavaScript to download and run. It improves First Contentful Paint (FCP) and is crucial for SEO. Angular’s hydration then ā€œwakes upā€ the static HTML to make it interactive.

🧠 Memory tip: SSR = Instant HTML (good for SEO); CSR = Wait for JS (bad for SEO).


šŸ”„ RxJS & State Management

1. When should I use a Store (like NgRx or Elf) vs just a Service?

Service with BehaviorsSubject: Good for simple apps. If state is just ā€œuser profileā€ and ā€œtheme,ā€ a service is fine. Store (NgRx): Necessary when state is shared by many unrelated components, or the logic to update state is complex. It gives you a single source of truth and predictable changes (Redux pattern).

🧠 Memory tip: Simple shared data = Service; Complex shared logic = Store.

2. Explain the difference between `switchMap`, `mergeMap`, and `concatMap`.

They all flatten observables (Observable of Observables), but they handle new values differently:

  • switchMap: ā€œI only care about the latest.ā€ If a new click happens, cancel the previous request. Good for Search.
  • mergeMap: ā€œDo everything at once.ā€ Fire and forget. Good for parallel data fetching.
  • concatMap: ā€œWait your turn.ā€ Finish the first one before starting the next. Good for saving items in order.

🧠 Memory tip: Switch = Latest; Merge = Parallel; Concat = Serial (Queue).

3. What is a Subject vs a BehaviorSubject?
  • Subject: A simple event emitter. If you subscribe after the event happened, you missed it. Use for Events (like ā€œButton Clickedā€).
  • BehaviorSubject: Has a ā€œcurrent value.ā€ If you subscribe later, you get the last value immediately. Use for State (like ā€œIs Loadingā€ or ā€œUser Dataā€).

🧠 Memory tip: Subject = Event Stream; BehaviorSubject = State Holder (Value).

4. How do you handle error handling in RxJS?

We use the catchError operator. Important rule: If you put catchError inside the main pipe, the stream completes (dies) after the error. If you want the stream to stay alive (like in a search input), you must put catchError inside the inner operator (like inside the switchMap).

🧠 Memory tip: Catch inside inner pipe to keep the stream alive.

5. What is the `async` pipe and why is it preferred?

The async pipe (| async) subscribes to an observable in the template and returns the value. Why I love it:

  1. It handles manual subscription boilerplate.
  2. It automatically unsubscribes when the component is destroyed (no leaks!).
  3. It works perfectly with OnPush change detection.

🧠 Memory tip: Async Pipe = Auto-unsubscribe + OnPush friendly.

6. How do you debug RxJS streams?

I use the tap operator. It lets me ā€œspyā€ on the stream without changing it. stream$.pipe(tap(val => console.log('Value is:', val))). Also, naming streams with a $ suffix (like users$) helps me know it’s a stream and not a value.

🧠 Memory tip: Tap = Side effect / Spy; won’t change data.

7. What is "Hot" vs "Cold" observables?
  • Cold (Netflix): The movie doesn’t start until I hit play (subscribe). Each person gets their own private showing from the start. (e.g., HTTP calls).
  • Hot (Live TV): The show is running regardless of me. If I join late, I see what’s happening now. (e.g., Mouse clicks, WebSockets).

🧠 Memory tip: Cold = Producer created per sub; Hot = Producer shared.

8. How do RxJS operators help with avoiding race conditions?

Using switchMap is the classic example for HTTP calls. If a user clicks ā€œSaveā€ 5 times rapidly, switchMap cancels the first 4 requests and only sends the last one. If we used Promises or simple subscribes, all 5 requests might hit the server, and they might return out of order, messing up the data.

🧠 Memory tip: switchMap cancels stale requests.


🧩 Application Architecture & Patterns

1. What is the Container vs Presentational Component pattern?
  • Smart (Container): Knows how to get data (Services, Store). It has no UI logic, it just passes data down.
  • Dumb (Presentational): Knows how to display data. It has no dependencies. It takes @Input() and emits @Output().

This separation makes the ā€œDumbā€ components highly reusable and easy to test because they don’t depend on complex services.

🧠 Memory tip: Smart = Logic/Data; Dumb = UI/Inputs.

2. Why would you choose a Monorepo strategy (like Nx)?

If my company has multiple Angular apps (e.g., Admin Panel, Customer Portal, Mobile App), we likely share code (UI Kit, Interfaces, Utils). A Monorepo lets us keep all code in one place. We can fix a bug in the ā€œButton Componentā€ and it updates all 3 apps instantly. Nx adds caching so builds stay fast.

🧠 Memory tip: One Repo = Easy Code Sharing across Apps.

3. How do you structure a large Angular application?

I avoid organizing by type (not logical folders like /components, /services). I organize by Feature Module (Domain specific). e.g., /user-profile, /shopping-cart, /auth. Each feature folder contains its own components, services, and routes. It keeps code related to ā€œCartā€ in one place.

🧠 Memory tip: Organize by Feature, not by File Type.

4. What is the Interceptor pattern?

Interceptors sit in the middle of every HTTP request and response. I use them for global ā€œcross-cuttingā€ concerns:

  1. Attaching the Auth Token to every request.
  2. Logging errors globally.
  3. Showing a global ā€œLoadingā€¦ā€ spinner. It keeps this logic out of my individual components.

🧠 Memory tip: Middleman for every HTTP request (Auth, Logging).

5. Explain Module Federation (Micro-frontends) and when to use it.

Module Federation allows us to load a separate Angular app inside another Angular app at runtime. I would only use this for massive teams. e.g., Team A owns the ā€œCheckoutā€ app, Team B owns the ā€œCatalogā€ app. They deploy separately. For most companies, it adds too much complexity. A well-structure Monorepo is usually better.

🧠 Memory tip: Separate Deployments for Separate Teams (Complex).

6. How do you handle configuration management (Environment variables)?

I use the environment.ts files for build-time config (like production: true). For runtime config (like API URLs that change per Docker container), I typically load a config.json file when the app starts using an APP_INITIALIZER. This lets us build the Docker image once and run it anywhere.

🧠 Memory tip: Env.ts = Build time; Config.json = Runtime (Docker).

7. What is the Facade Pattern in Angular?

A Facade is a service that sits between a Component and the State Management (Store). The Component just calls facade.loadUsers(). The Facade handles the ugly complexity of dispatching actions, selecting selectors, etc. It makes the component very clean and easy to refactor the state backend later without breaking the UI.

🧠 Memory tip: Facade hides the Store complexity from the UI.

8. How do you decide between a Route Guard and a Resolver?
  • Guard: ā€œCan I go here?ā€ Use for permissions. e.g., ā€œIs the user logged in?ā€ If not, redirect.
  • Resolver: ā€œGet data before showing.ā€ Use if the page is useless without the data. e.g., ā€œLoad the Product Details.ā€ If the data fails, don’t show the page.

🧠 Memory tip: Guard = Permission; Resolver = Pre-fetching Data.


šŸ” Security & Best Practices

1. How does Angular protect against XSS (Cross-Site Scripting)?

Angular automatically sanitizes value binding. If I try to bind <script>alert('hacked')</script> into the HTML using {{ value }}, Angular treats it as text, not code. The only risk is if I manually use [innerHTML] and bypass the security trust, which I should avoid unless absolutely necessary (and then use DomSanitizer).

🧠 Memory tip: Angular sanitizes {{}} automatically; careful with innerHTML.

2. How do you handle Authentication and Authorization?
  • Auth (Who are you?): I use JWT tokens. I store the token (usually in memory or httpOnly cookie) and use an Interceptor to attach it to requests.
  • Authz (What can you do?): I use Route Guards (CanActivate) to block access to Admin pages. Inside templates, I might use a structural directive *hasRole='admin' to hide buttons.

🧠 Memory tip: Interceptor for Token; Guards for Access Control.

3. What is the difference between specific CSS vs Global styles pollution?

If I write generic CSS like h1 { color: red } in styles.css, it hits everyone. If I do it in a component, Angular encapsulates it. Best practice: Use utility classes (Tailwind) or specific BEM classes for global styles. Keep component styles specific to that component.

🧠 Memory tip: Component styles are safe (encapsulated); Global styles are risky.

4. Why should you avoid calling functions in the Template expressions?

e.g. {{ calculateTotal() }}. In default change detection, this function runs every single time something changes in the app. It could run 100 times a second. If the math is heavy, the app lags. Fix: Calculate the value in Typescript and assign it to a property, or use a pure Pipe (which caches the result).

🧠 Memory tip: Function in template = Runs on every change detection cycle (Bad).


🧪 Testing & Quality

1. What is the difference between TestBed and Isolated Unit Tests?
  • Isolated: I just say new MyService(). No Angular magic. Fast and simple. Good for services with logic.
  • TestBed: Angular configures a fake module. It’s slower but necessary for Components because we need to render the Template to test bindings and clicks.

🧠 Memory tip: Isolated = Fast (New Class); TestBed = Slow (Angular Environment).

2. Why would you use Mocks or Spies in testing?

I don’t want my unit test to actually hit the real API server. That’s slow and flaky. I use a Spy to ā€œfakeā€ the API service. I tell it: ā€œWhen this function is called, just return this fake JSON immediately.ā€ It makes tests 100% reliable and fast.

🧠 Memory tip: Mock the API to keep tests fast and deterministic.

3. Cypress vs Karma/Jasmine?
  • Karma/Jasmine: Unit tests. Fast. checks ā€œDoes this specific function work?ā€
  • Cypress (or Playwright): E2E (End-to-End) tests. It runs a real browser. It checks ā€œCan a user actually log in and buy a product?ā€ I rely more on Unit tests for logic coverage, and E2E for critical user flows (Smoke tests).

🧠 Memory tip: Unit = Function Logic; E2E = User Flow.

4. What is a "Flaky" test and how do you fix it?

A flaky test passes sometimes and fails sometimes. It is the worst enemy of a CI pipeline. Usually cause by timing issues (waiting for an animation or API). Fix: Never use setTimeout in tests. Use fixture.whenStable() or wait for specific element visibility selectors in Cypress.

🧠 Memory tip: Flaky = Timing issues; Fix by waiting for specific events/selectors.

5. How do you test Observables?

I use ā€œMarble Testingā€ if it’s complex, but usually, a simple subscribe works. myService.getData().subscribe(data => { expect(data).toBe('test'); done(); }). In modern testing, using fakeAsync and tick() allows us to control time linearly, which is easier to read.

🧠 Memory tip: subscribe() and done() for simple async tests.


šŸš€ Dev Experience & Leadership

1. How do you handle "Tech Debt" in a team?

I never say ā€œWe’ll fix it laterā€ without a plan. I follow the ā€œBoy Scout Ruleā€: Always leave the code a little cleaner than you found it. If a refactor is big, I create a Ticket for it and argue for its business value (e.g., ā€œThis refactor will make adding new features 50% fasterā€).

🧠 Memory tip: Boy Scout Rule: Leave code cleaner than you found it.

2. What do you look for in a Code Review?

I don’t just look for syntax—Prettier/Lint handles that. I look for:

  1. Readability: Can I understand this in 5 seconds?
  2. Architecture: Does this belong in a Service or the Component?
  3. Performance: Are there any obvious leaks or slow loops?
  4. Tests: Did they write tests for the new edge cases?

🧠 Memory tip: Robots check syntax; Humans check Logic & Architecture.

3. How do you mentor junior developers?

I don’t give answers. I ask questions. Instead of ā€œChange this to an Observable,ā€ I ask ā€œWhat happens if the user clicks this button twice rapidly?ā€ I guide them to the solution so they learn the ā€œWhy.ā€ I also encourage them to review my code, which builds confidence.

🧠 Memory tip: Ask questions, don’t just give answers.

4. How do you enforce code standards?

Automation. If it’s a rule, it should be in the linter (ESLint). I set up Husky (pre-commit hooks) so that bad code literally cannot be committed. This saves social friction. Use the robot to be the ā€œbad guy.ā€

🧠 Memory tip: Automate rules with ESLint/Husky; don’t police manually.

5. How do you keep up with Angular updates (e.g. Signals, v18)?

I read the official Changelog and follow key Angular GDEs. But mostly, I try to build a small ā€œtoy appā€ when a new feature drops. You don’t learn Signals by reading; you learn by crashing the app a few times. For the team, I often do ā€œLunch and Learnā€ sessions to demo new features.

🧠 Memory tip: Learn by doing (Tiny Side Projects).