State Design • Change Detection • Production Incidents • Migrations • Code Review • 2026

Angular Interview Questions for Experienced Candidates (5 Years)

Angular interviews for 5 years of experience rarely ask what a pipe is; they ask why you picked NgRx or plain services, what broke when you turned on SSR or zoneless, how you traced a bug that only happened in production, and when you said no. This page is written for Angular developers with roughly five to seven years behind them. You own a feature area or a whole app, you choose how state, forms and data loading work inside it, you get the call when a release breaks, and you review other people's pull requests. Each answer below is a first-person story or a decision you can defend. Swap in your own project details before you say it.

Search all questions by round, difficulty and level, or save the ones you want to practice.

State and Data 3 questions

Hard System design round Mid-level, Senior Practice question

1. For the Angular app you owned, did you go with NgRx, a signal-based store, or plain services with signals? Why that one, and what did it cost you?

What the interviewer is really testing:
Whether you picked a state approach for concrete reasons (team size, how much state is shared, debugging needs) and can name the price you paid, instead of following a trend.
Answer frame:

Context: how much state was shared across features, how many developers, how hard bugs were to trace.

Choice: what you picked and the one or two reasons that decided it.

Cost: boilerplate, learning curve or lost tooling, and how you kept it under control.

Sample spoken answer:

“On our claims app most state was local to one screen, and only the signed-in user, permissions and a few reference lists were truly shared. So I went with small services per feature holding signals, with computed values for derived data, instead of a full NgRx store. It kept new joiners productive in days and cut a lot of action and reducer files. The cost showed up in debugging: without the store devtools we couldn't replay what happened before a bug. I made up for it by keeping every write behind a named method on the service and logging those calls in dev builds. If we'd had many features editing the same data at once, with undo or offline sync, I'd have picked NgRx and accepted the boilerplate.”

Red flag to avoid:

Saying one approach is always right, or picking NgRx for a small app without being able to say what problem it solved.

They may ask next:
  • What would make you move one feature to a proper store later?
  • How do you stop a feature service from becoming a dumping ground for every screen's state?
Say it in 60 seconds
Hard Technical round Mid-level, Senior Practice question

2. In a grid you owned, edits saved optimistically so the screen felt instant. How did you handle a failed save, and two quick edits to the same row?

What the interviewer is really testing:
Whether you can build optimistic updates that roll back correctly and never let an older request overwrite a newer edit.
Answer frame:

Apply first: write the change into the row state straight away and remember the previous value.

Roll back: on failure restore the old value only if nothing newer touched that row, and tell the user.

Ordering: send saves for one row one at a time, in order, or use version numbers so the server rejects stale writes.

Sample spoken answer:

“Users edited status and owner inline, and waiting for the server felt sluggish, so I wrote each change into the row state immediately and saved in the background. For failures I kept the previous row and a per-row edit counter. If a save failed and no newer edit had happened, I put the old row back and showed a message saying the change didn't save. If a newer edit existed, I left it alone. The nastier bug was two quick edits to one row: both requests raced, and if the first finished last, the server stored the older value. I grouped saves by row and used concatMap inside each group, so one row's saves go out in order while different rows still save in parallel. The trade-off is that a user can briefly see a change that later disappears, so I only did this for low-risk fields, never for anything that needs a firm confirmation.”

Code:
private saves = new Subject<RowEdit>();

constructor() {
  this.saves.pipe(
    groupBy(edit => edit.rowId),
    mergeMap(row$ => row$.pipe(
      concatMap(edit => this.api.saveRow(edit).pipe(
        catchError(() => {
          this.rollBack(edit); // restores only if no newer edit touched the row
          return EMPTY;
        })
      ))
    )),
    takeUntilDestroyed()
  ).subscribe();
}
Red flag to avoid:

Rolling back blindly and wiping a newer edit, or never telling the user that a save failed.

They may ask next:
  • Where would you refuse to use optimistic updates, and why?
  • How would version numbers on the server change this design?
Say it in 60 seconds
Hard Technical round Mid-level, Senior Practice question

3. In a feature that mixed signals and RxJS, where did you draw the line between them, and what bug did the crossing points cause?

What the interviewer is really testing:
Whether you use each tool for what it is good at and understand the behaviour of toSignal, toObservable and effect well enough to explain a real bug.
Answer frame:

Line: RxJS for events over time (HTTP, debouncing, websockets), signals for state the template reads.

Crossing points: toSignal needs an injection context and a starting value; toObservable emits later through an effect, not the moment you set the signal.

Bug and fix: a concrete surprise and how you changed the code.

Sample spoken answer:

“In our search feature I kept typing, debouncing and HTTP calls in RxJS, then turned the result into a signal with toSignal for the template. Filters and selection were plain signals, with computed for derived values. The bug came at the crossing point. A colleague turned two filter signals into observables with toObservable and combined them with combineLatest to drive the search. toObservable emits through an effect, not the moment you set the signal, so when a clear-filters button reset both signals, the search fired twice, the first time with one filter cleared and the other still set. I merged the related filters into one signal holding an object, so a reset was one update and one search. I also replaced an effect that copied one signal into another with computed. Our rule became: effects for side effects only, never for syncing state.”

Red flag to avoid:

Converting everything back and forth without a rule, or using effect to keep one signal in sync with another.

They may ask next:
  • Why does toSignal complain when you call it inside a click handler?
  • When would you still reach for an effect?
Say it in 60 seconds

Change Detection 2 questions

Hard Technical round Mid-level, Senior Practice question

4. You switched an app to zoneless change detection. How did you prepare, and what stopped updating on screen afterwards?

What the interviewer is really testing:
Whether you know what actually tells Angular to refresh a view once zone.js is gone, and whether you found the code that silently relied on it.
Answer frame:

What still triggers a refresh: signals read in templates, template event handlers, the async pipe, markForCheck.

What breaks: plain field changes inside setTimeout, subscribe callbacks or third-party callbacks.

Rollout: move components to OnPush and signals first, then remove zone.js and test the risky screens.

Sample spoken answer:

“Before touching the provider I made every component OnPush and fixed whatever broke, because an app that works with OnPush is most of the way to zoneless. Then I switched on zoneless change detection and removed zone.js from the polyfills. Three kinds of things stopped updating. A toast service changed a plain field inside setTimeout, so nothing told Angular to re-render. A map library fired callbacks that updated component fields directly. And some old code waited on NgZone.onStable, which never fires without zones. I turned those fields into signals, which schedule a render on their own, and removed the onStable waits. Our fakeAsync tests also needed rework, since they depend on zone.js. The payoff was a smaller bundle and far fewer wasted change detection passes.”

Red flag to avoid:

Thinking zoneless means nothing ever refreshes automatically, or flipping it on for a big app with no audit of setTimeout and third-party callbacks.

They may ask next:
  • How would you find every place that changes state outside Angular's knowledge before you flip the switch?
  • Can you mix zoneless with components that still use the default strategy?
Say it in 60 seconds
Hard Behavioral round Mid-level, Senior Practice question

5. Users said their laptop fan spun up whenever a certain page was open, even when they weren't touching it. How did you trace it in Angular, and what was the fix?

What the interviewer is really testing:
Whether you can profile change detection, recognise timers and chatty listeners inside the zone as the cause, and fix it without breaking updates.
Answer frame:

Profile: Angular DevTools profiler and the browser performance panel on an idle page.

Cause: a timer, animation loop or high-frequency listener running inside the zone, so every tick checks the whole app.

Fix: run it outside Angular and come back in only when data actually changes.

Sample spoken answer:

“I opened the page, left it idle and recorded with the Angular DevTools profiler. It showed change detection running dozens of times a second with nobody touching anything. The browser's performance panel pointed at two sources: a polling timer that ran every 200 milliseconds to check a job status, and a charting library that ran a requestAnimationFrame loop for its hover effects. Both ran inside the zone, so every tick made Angular check the entire app. I moved the polling and the chart setup into runOutsideAngular, slowed the poll to once a second, and only set a signal when the job status actually changed. Idle CPU dropped to almost nothing. The trade-off is that anyone editing that code must remember it lives outside the zone, so I left a clear comment.”

Code:
private zone = inject(NgZone);
readonly status = signal<JobStatus>('pending');

startPolling(jobId: string) {
  this.zone.runOutsideAngular(() => {
    this.timer = setInterval(async () => {
      const next = await this.api.jobStatus(jobId);
      if (next !== this.status()) this.status.set(next); // signal schedules the render
    }, 1000);
  });
}
Red flag to avoid:

Blaming the browser or the laptop, or making the whole page OnPush without finding what keeps triggering change detection.

They may ask next:
  • What changes about this problem once the app is zoneless?
  • How would you catch this kind of regression before users do?
Say it in 60 seconds

Upgrades and Migration 2 questions

Hard Technical round Mid-level, Senior Practice question

6. After you turned on server-side rendering with hydration, the console filled with hydration errors and the page flickered on load. What caused it, and how did you fix it?

What the interviewer is really testing:
Whether you understand that hydration needs the server and browser DOM to match, and know the usual culprits: browser-only APIs, direct DOM edits and data fetched twice.
Answer frame:

Mismatch: code that renders differently on server and browser, like dates, random ids or localStorage reads.

DOM edits: libraries or directives that change the DOM directly behind Angular's back.

Fixes: run browser-only code after render, skip hydration for one stubborn component, and reuse the server's data.

Sample spoken answer:

“The errors pointed at a header and a chart. The header read the user's theme from localStorage behind a browser check, so the server always drew the light theme and the browser then drew dark. I moved that into afterNextRender, which only runs in the browser, and put the theme in a cookie so the server could render it right. The chart library built its own DOM inside our component, so the server output never matched. I put ngSkipHydration on that one host element and let it render fresh on the client. The flicker had a second cause: the page fetched the same data again after load. Using HttpClient's transfer cache from the hydration setup meant the browser reused what the server had fetched, so the content stopped jumping.”

Red flag to avoid:

Wrapping everything in a browser check or skipping hydration app-wide, which throws away the benefit of SSR.

They may ask next:
  • Why is ngSkipHydration a last resort rather than the first fix?
  • How do you keep a new developer from adding a window or document reference that breaks the server build?
Say it in 60 seconds
Medium Behavioral round Mid-level, Senior Practice question

7. Tell me about moving an NgModule-based Angular app to standalone components. How did you stage it so feature work didn't stop?

What the interviewer is really testing:
Whether you can run a large mechanical migration safely in steps, use the official schematics, and handle what they can't fix.
Answer frame:

Plan: run the standalone schematic step by step, one area at a time, each step its own pull request.

Manual work: shared modules, providers that lived in modules, and tests that declared modules.

Protect the team: short-lived branches, clear rules for new code, done before the next big feature.

Sample spoken answer:

“Our app had about forty NgModules, including a huge SharedModule every feature imported. I used the Angular standalone schematic, which works in steps: convert declarations to standalone, remove modules that are no longer needed, then switch to standalone bootstrap. I ran the first step one feature folder at a time, each as a small pull request that merged within a day, so nobody was stuck on a long branch. The schematic couldn't untangle SharedModule, so I split it by hand and made each component import only what it used. Providers that lived in modules moved into route providers or the app config, which I checked carefully because it changes where instances are created. From day one the rule was that new code is standalone. It took three sprints alongside normal work.”

Red flag to avoid:

Doing the whole migration in one giant pull request, or trusting the schematic without reviewing where providers ended up.

They may ask next:
  • What went wrong with services when providers moved out of modules?
  • Did you migrate to the new control flow syntax at the same time? Why or why not?
Say it in 60 seconds

Performance 3 questions

Medium Behavioral round Mid-level, Senior Practice question

8. The main screen of a module you owned took several seconds to show anything because of how it loaded its data. How did you restructure the loading, and what did it cost?

What the interviewer is really testing:
Whether you can spot a request waterfall, choose between a resolver and loading in the component, and make a deliberate call about what the user sees first.
Answer frame:

Find it: the network panel showed requests running one after another, each waiting for the last.

Restructure: start independent requests together and chain only the ones that truly need an earlier result.

What the user sees: show the page frame first, and give each section its own loading and error state.

Sample spoken answer:

“The account overview took about four seconds before anything showed. In the network panel the requests formed a staircase: a route resolver fetched the account, then the component fetched the plan, then invoices, then usage, each waiting for the one before, even though only usage needed the plan id. The resolver also kept the screen blank until the account came back. I dropped the resolver, rendered the page frame straight away, and started the account, plan and invoice requests at the same time as separate streams, each feeding its own section. Only usage waited on the plan. Each section got its own skeleton and error state, so a slow invoice call no longer held up the rest. First content came in under a second. The cost was more loading states to design and test, and the layout shifting as sections filled in, so I gave each skeleton a fixed height.”

Red flag to avoid:

Blaming the back end for slowness without looking at the order the front end makes its requests in.

They may ask next:
  • When would you still keep a resolver on a route?
  • How do you stop one failed section from breaking the whole page?
Say it in 60 seconds
Hard Coding round Mid-level, Senior Practice question

9. A dashboard you owned shows live prices over a WebSocket. How did you keep it smooth under heavy updates, and how did it recover when the connection dropped?

What the interviewer is really testing:
Whether you can control the rate of UI updates from a noisy stream and build reconnection that backs off instead of hammering the server.
Answer frame:

Throttle the UI: batch messages and apply them at most a few times a second, not once per message.

Render less: OnPush or signals, tracked lists, and update only changed rows.

Reconnect: retry with growing delays, show a stale-data banner, resubscribe to channels after reconnect.

Sample spoken answer:

“The socket sent hundreds of price ticks a second, and at first each one updated the view, so the page stuttered. I collected ticks with bufferTime into batches every quarter second, merged them into a map by symbol so only the latest price per row survived, and wrote that into a signal. The table used track by symbol, so only changed rows re-rendered. For drops, I used RxJS retry with a delay that doubles each time up to a cap, plus a little random jitter, so a server restart didn't bring every client back at the same instant. While disconnected, a banner said prices might be stale, and after reconnecting the app sent its subscribe messages again. The trade-off is prices on screen can be a quarter second old, which the traders were fine with.”

Code:
readonly prices = signal(new Map<string, number>());

connect() {
  webSocket<Tick>(this.url).pipe(
    bufferTime(250),
    filter(batch => batch.length > 0),
    retry({
      delay: (_err, attempt) =>
        timer(Math.min(30000, 1000 * 2 ** attempt) + Math.random() * 1000),
      resetOnSuccess: true
    }),
    takeUntilDestroyed(this.destroyRef)
  ).subscribe(batch => {
    const next = new Map(this.prices());
    for (const t of batch) next.set(t.symbol, t.price);
    this.prices.set(next);
  });
}
Red flag to avoid:

Updating the view on every message, or reconnecting in a tight loop with no delay.

They may ask next:
  • Why does takeUntilDestroyed go last in the pipe, after the retry?
  • How would you tell the user which rows are stale after a long disconnect?
Say it in 60 seconds
Medium Technical round Mid-level, Senior Practice question

10. Your main landing page had a poor Largest Contentful Paint on mobile. What Angular-specific changes moved the number, and which ones didn't help?

What the interviewer is really testing:
Whether you tie a Core Web Vitals problem to concrete fixes (render path, image loading, deferred code) and are honest about what didn't work.
Answer frame:

Find the element: check which element is the LCP and what delays it.

Fixes: prerender or SSR the page, NgOptimizedImage with priority on the hero, defer heavy widgets below the fold.

Honest part: a change you expected to help that made little difference.

Sample spoken answer:

“The LCP element was the hero image, and on a slow phone it didn't start downloading until the whole app booted, because everything rendered on the client. Prerendering that route at build time made the biggest difference, since the HTML and image tag arrived straight away. Then I switched the hero to NgOptimizedImage with the priority attribute, which gives it high fetch priority and preloads it, and served properly sized versions for phones. The reviews carousel and a chat widget below the fold went into defer blocks so their code didn't compete for the first load. What didn't help much was shaving a little more off the main bundle. Once the page was prerendered, the bundle was no longer on the path to the LCP.”

Red flag to avoid:

Optimising at random without knowing which element is the LCP or what is delaying it.

They may ask next:
  • What does NgOptimizedImage warn you about in development?
  • How do you check LCP for real users rather than only in a lab test?
Say it in 60 seconds

Production Incidents 3 questions

Hard Behavioral round Mid-level, Senior Practice question

11. Support reported that switching quickly between two customers sometimes showed the first customer's orders on the second customer's page. How did you trace and fix it?

What the interviewer is really testing:
Whether you can reproduce a timing bug, recognise out-of-order responses when a routed component is reused, and fix it by cancelling stale requests.
Answer frame:

Reproduce: throttle the network and click fast between two records.

Cause: the component is reused when only the id changes, and each id started its own request with nothing cancelling the old one.

Fix: derive the data from the route param with switchMap, and add a test for the race.

Sample spoken answer:

“I couldn't reproduce it on a fast network, so I throttled the connection and clicked between two customers quickly. That did it. The router reuses the same component when only the id in the URL changes, and our code subscribed to the param map and, inside that, started a fresh HTTP call for each id. Nothing cancelled the earlier call, so if customer one's response was slower, it landed after customer two's and overwrote the page. I rewrote the stream as param map piped into switchMap, which drops the old request when a new id arrives, and fed the result to the template with the async pipe. Then I wrote a test with HttpTestingController that switches between two ids, checks the first request was cancelled, and answers the second.”

Red flag to avoid:

Adding a delay or a loading flag that hides the race instead of cancelling the stale request.

They may ask next:
  • Would a route resolver have prevented this, and what would it cost the user?
  • Where else in the app would you look for the same pattern?
Say it in 60 seconds
Hard Technical round Mid-level, Senior Practice question

12. After every release, some users hit errors about failing to load a module when they navigate, and a refresh fixes it. Why does this happen, and what did you do about it?

What the interviewer is really testing:
Whether you understand how hashed lazy chunks, caching and deploys interact, and can fix it on both the hosting side and in the app.
Answer frame:

Cause: users still running the old app ask for old lazy chunk files that the deploy deleted.

Hosting fix: never cache index.html, and keep the previous release's chunks around for a while.

App fix: catch the failed lazy load and reload once, or prompt for an update if a service worker is in use.

Sample spoken answer:

“Each build gives lazy chunks new hashed names. Someone who opened the app before the release still has the old main bundle, and when they navigate to a lazy route it asks for an old chunk file that the deploy had already removed. On the hosting side I set index.html to no-cache and changed the deploy so the previous release's chunks stay for a few days. In the app I added a global error handler that spots a failed dynamic import and reloads the page once, using a session flag so it can't loop, and I checked the router's navigation errors lead there too. After that the error reports from releases dropped to almost none. The trade-off is keeping old files on the server longer, which was cheap.”

Code:
@Injectable()
export class AppErrorHandler implements ErrorHandler {
  handleError(error: unknown): void {
    const msg = String((error as Error)?.message ?? error);
    const chunkFailed = /Loading chunk|dynamically imported module/.test(msg);
    if (chunkFailed && !sessionStorage.getItem('reloaded-for-chunk')) {
      sessionStorage.setItem('reloaded-for-chunk', 'yes');
      location.reload();
      return;
    }
    console.error(error);
  }
}
// providers: [{ provide: ErrorHandler, useClass: AppErrorHandler }]
Red flag to avoid:

Telling users to clear their cache, or reloading on every error without a guard against loops.

They may ask next:
  • How would a service worker change this problem and your fix?
  • Why not reload the page on every error instead?
Say it in 60 seconds
Hard Behavioral round Mid-level, Senior Practice question

13. A feature worked in ng serve and in every test, but broke only in the production build. How did you track it down?

What the interviewer is really testing:
Whether you know what differs between a development and a production Angular build, such as minified names, dev-only checks and file replacements, and can narrow a bug down instead of guessing.
Answer frame:

Reproduce: build with the production configuration and serve it locally, with source maps on for that local build only.

Know the differences: minified and renamed code, no dev-mode checks, different environment files and build options.

Fix and guard: remove the reliance on build details, and run a smoke test against a production build.

Sample spoken answer:

“Our analytics events started arriving with screen names like e and t, but only from production. Locally everything looked right. First I built with the production configuration and served it on my machine, with source maps turned on for that local build only, so I could step through it. The cause was a helper that used this.constructor.name to label each screen. In development the class name survives, but the production build minifies the code and renames classes, so every screen reported a short meaningless name. I replaced it with an explicit screen name in each route's data, read from the router. Then I added a smoke test in CI that runs against the production build instead of the dev server and checks a few key events. The lesson I shared with the team was simple: nothing may depend on class or function names at runtime.”

Red flag to avoid:

Guessing and redeploying over and over, or turning off optimisation in production to make the bug go away.

They may ask next:
  • What other differences between development and production builds have caught you out?
  • Why is it risky to serve source maps publicly in production?
Say it in 60 seconds

Forms and Quality 3 questions

Hard System design round Mid-level, Senior Practice question

14. You owned a long form built from server config, with sections that show and hide based on answers. How did you design it with typed reactive forms?

What the interviewer is really testing:
Whether you can structure a large dynamic form so it stays typed, validates correctly when sections are hidden, and submits the right value.
Answer frame:

Structure: one FormGroup per section, built from the config, with typed controls or a FormRecord for keys only known at runtime.

Hidden sections: disable them rather than remove, so they don't block validity and drop out of value.

Submit: know when to use value versus getRawValue, and keep display logic out of the template.

Sample spoken answer:

“The insurance quote form came from a config the back end sent, with around fifteen sections. I built one FormGroup per section from that config, using non-nullable controls so reset went back to defaults instead of null. For custom fields whose keys we only knew at runtime I used a FormRecord. The tricky part was hidden sections. At first we removed and re-added controls, which lost what the user typed and caused flicker. I changed it so a hidden section is disabled instead. Disabled controls don't count toward validity and aren't included in value, so the form was valid when it should be and the payload only had visible answers, while their typed text survived if they toggled back. Where we needed everything, like saving a draft, we used getRawValue.”

Red flag to avoid:

Typing the whole form as any, or hiding sections with CSS while their required validators still block the submit.

They may ask next:
  • How do you keep the show and hide rules from spreading across templates?
  • What goes wrong if you use getRawValue for the final submit?
Say it in 60 seconds
Medium Technical round Mid-level, Senior Practice question

15. For an Angular app you owned, what mix of unit, component and end-to-end tests did you settle on, and which tests did you decide to delete?

What the interviewer is really testing:
Whether your testing choices come from where bugs actually happened, and whether you can drop tests that cost more than they catch.
Answer frame:

Unit: services, pipes, validators and pure logic, fast and many.

Component: TestBed with harnesses and a fake HTTP backend for real user flows on one screen.

End-to-end and delete: a few critical journeys; remove tests that only check implementation details.

Sample spoken answer:

“Most of our real bugs were in how a screen behaved with real data, not in single functions. So I kept plain unit tests for services, validators and pipes, and put most effort into component tests with TestBed, using the component harnesses for material controls and HttpTestingController to fake the back end. Those tested what a user does: fill the form, click save, see the error. End-to-end tests covered only five journeys, like login and checkout, because they were slow and flaky. What I deleted were tests that spied on private methods or checked that a component had a certain property. They broke on every refactor and never caught a bug. The trade-off was lower line coverage on paper, which I explained to the team lead with the list of bugs our tests had actually caught.”

Red flag to avoid:

Chasing a coverage number, or testing that private methods get called rather than what the user sees.

They may ask next:
  • Why use component harnesses instead of querying the DOM directly?
  • How did you deal with flaky end-to-end tests?
Say it in 60 seconds
Medium Technical round Mid-level, Senior Practice question

16. An accessibility audit failed the module you own: keyboard users got lost in dialogs and screen readers missed updates. What did you change in the Angular code?

What the interviewer is really testing:
Whether you know the practical Angular tools for accessibility (CDK a11y, focus handling on navigation, live announcements) and fixed the root causes, not just labels.
Answer frame:

Dialogs: trap focus inside, return it to the trigger on close, close on Escape.

Updates: announce async results and errors through a live region.

Navigation: give each route a title and move focus to the main heading after navigation.

Sample spoken answer:

“The audit found three big problems. Our home-made dialogs let Tab wander behind them and never returned focus when they closed. I moved them onto the CDK dialog, which traps focus and restores it to the button that opened it, and closes on Escape. Second, when a search finished or a save failed, nothing told screen reader users. I used the CDK LiveAnnouncer to announce results and errors politely. Third, after route changes focus stayed on the link just clicked, so screen readers didn't know the page had changed. I set a title on every route and moved focus to the page's main heading after each navigation. I also added an automated accessibility check to our component tests so the basics couldn't slip back in.”

Red flag to avoid:

Treating accessibility as adding alt text and aria labels, with no thought for focus and keyboard flow.

They may ask next:
  • How do you test keyboard and screen reader behaviour beyond automated checks?
  • Where would you put the focus after deleting an item from a list?
Say it in 60 seconds

Mentoring and Libraries 2 questions

Medium Behavioral round Mid-level, Senior Practice question

17. Several apps needed the same components, so you built a shared Angular library. How did you package and version it, and what problems came up?

What the interviewer is really testing:
Whether you know how an Angular library is packaged and consumed, and have dealt with the real costs of sharing code: versions, breaking changes and tree-shaking.
Answer frame:

Packaging: generated with the CLI library tooling, Angular as a peer dependency, secondary entry points.

Versioning: semantic versions, a changelog, and deprecations before removals.

Problems: apps on different Angular versions, bloated imports, and who owns fixes.

Sample spoken answer:

“Three apps had copied the same table, date picker and layout components, and they'd drifted apart. I created a library with the CLI, which builds it in the Angular package format, and listed Angular as a peer dependency so apps didn't end up with two copies. At first everything came from one entry point, and one app complained that importing a button pulled in the table's dependencies. I split it into secondary entry points, one per component group. We used semantic versions and a changelog, and anything we planned to remove got marked deprecated for one release first. The hardest problem was people, not code: one app lagged a major Angular version behind. We agreed the library supports the current and previous major, and the lagging app had a date to catch up.”

Red flag to avoid:

Putting Angular in the library's regular dependencies, or shipping breaking changes in a minor version.

They may ask next:
  • How do you test the library against the apps before you publish a new version?
  • When would you choose a monorepo over publishing a package?
Say it in 60 seconds
Medium Behavioral round Mid-level, Senior Practice question

18. A developer joined your team from a React background and struggled with your Angular codebase. How did you help them get productive?

What the interviewer is really testing:
Whether you can mentor with a plan, map what someone already knows onto Angular, and give real work early instead of a reading list.
Answer frame:

Map what they know: components, props and hooks to inputs, outputs, services and signals.

Real work early: a small, well-scoped ticket with pairing on the Angular parts that trip people up.

Feedback: reviews that explain the why, and a check-in after a few weeks.

Sample spoken answer:

“She was strong in React but kept fighting dependency injection and RxJS. Instead of pointing her at docs, I spent an hour mapping what she knew: props to inputs, callbacks to outputs, context to services provided at the right level, and useMemo roughly to computed signals. Then I gave her a small feature on a quiet screen and paired on the first day, mostly on where to provide a service and why our streams used switchMap. In her reviews I explained the reason behind each comment and linked to an example in our own code rather than just saying change this. After three weeks she was shipping on her own. She also pointed out that our RxJS use was more complex than needed in places, and we simplified two services because of it.”

Red flag to avoid:

Handing over the docs and a big ticket, then judging them in review for not knowing Angular conventions.

They may ask next:
  • What did you do differently when the new joiner was a junior rather than experienced?
  • How do you know when to stop pairing and let someone struggle a bit?
Say it in 60 seconds

Reviews and Decisions 4 questions

Medium Situational round Mid-level, Senior Practice question

19. A junior's pull request calls methods like getTotal() and isOverdue(order) all over a template, and the page has become sluggish. How do you review it?

What the interviewer is really testing:
Whether you can explain why template method calls run far more often than people expect, offer the right fix, and review in a way that teaches.
Answer frame:

Explain: template expressions run on every change detection pass for that view, so the method runs again and again.

Suggest: a computed signal, a pure pipe, or a value prepared when data arrives.

Teach: show one example, let them change the rest, and point to how to measure it.

Sample spoken answer:

“I'd start by saying the feature works and the structure is fine. Then I'd explain the one thing that's slowing it: anything called in a template runs on every change detection pass for that view, not once. With a long list and methods that loop over orders, that adds up with every keystroke. I'd rewrite one of them in the review as an example, turning getTotal into a computed signal based on the orders signal, and suggest a pure pipe for isOverdue since it takes an argument per row. I'd ask them to do the rest themselves and show them how to confirm it in the DevTools profiler. Small getters that just return a field are fine, so I wouldn't ask them to change those.”

Code:
readonly orders = input.required<Order[]>();
readonly total = computed(() =>
  this.orders().reduce((sum, o) => sum + o.amount, 0)
);
// template: {{ total() }}  -- reads a cached value, recalculates only when orders change
Red flag to avoid:

Rewriting the whole pull request yourself, or banning every function call in templates without explaining why.

They may ask next:
  • Why is calling a signal in the template fine when calling a method isn't?
  • Would OnPush alone have solved this?
Say it in 60 seconds
Medium Situational round Mid-level, Senior Practice question

20. In code review, a teammate uses bypassSecurityTrustHtml to show rich-text comments that users type. What do you say, and what do you suggest instead?

What the interviewer is really testing:
Whether you recognise a stored XSS risk in Angular code, know that Angular already sanitises innerHTML, and can offer a safe path that still meets the feature need.
Answer frame:

Risk: bypassing turns off Angular's sanitiser, so a comment with a script or event handler runs for every reader.

Default: binding to innerHTML already sanitises and keeps safe tags like bold and links.

If more is needed: sanitise on the server with an allow list, and keep the bypass out of shared code.

Sample spoken answer:

“I'd block the merge, but explain why rather than just flag it. Users write these comments and other users read them, so bypassing the sanitiser means anyone can post HTML with an onerror handler that runs in every reader's session. That's stored XSS. I'd ask why they needed the bypass. Usually it's because a style or a certain tag got stripped. In most cases plain innerHTML binding is enough, because Angular's sanitiser keeps bold, italics and safe links and removes scripts and handlers. If the feature really needs more, the right place is a server-side sanitiser with a strict allow list, so the data is clean before anyone sees it. I'd also suggest a content security policy as a second layer, and a lint rule that flags bypass calls for review.”

Red flag to avoid:

Approving it because only logged-in users can post, or not knowing Angular sanitises innerHTML by default.

They may ask next:
  • When is using a bypass method acceptable?
  • How do Trusted Types help in an Angular app?
Say it in 60 seconds
Hard Situational round Mid-level, Senior Practice question

21. Your lead wants to split the app into micro frontends with module federation so teams can deploy on their own. You own one area. What do you ask before agreeing?

What the interviewer is really testing:
Whether you can challenge a big architectural move with questions about the actual problem and the real costs, and propose a smaller step, without just saying no.
Answer frame:

Problem first: what pain is this solving: slow releases, merge conflicts, team ownership?

Costs: shared Angular versions, duplicated code in bundles, shared login and state, harder local setup and testing.

Smaller step: lazy feature libraries with clear owners and separate pipelines, then split only what truly needs it.

Sample spoken answer:

“I'd start by asking what problem we're solving. When I asked this in a similar situation, the real pain was that one team's bugs blocked everyone's release. Then I'd lay out what micro frontends cost in Angular: every remote has to agree on the Angular version or you ship it twice, shared services like auth have to work across builds, and local setup and end-to-end testing get harder. I'd suggest a smaller step first: keep one app, but split it into lazy feature libraries with clear owners, separate test pipelines, and feature flags so one team's broken work doesn't block a release. If a team still needs its own release cycle after that, we split out just that area. I'd offer to try it with my area first so we learn on real code.”

Red flag to avoid:

Either agreeing because it sounds modern or refusing flatly, without asking what problem it solves.

They may ask next:
  • What would convince you that micro frontends are the right call?
  • How would you handle a shared design system across separately deployed parts?
Say it in 60 seconds
Medium Behavioral round Mid-level, Senior Practice question

22. Tell me about a technical decision you made in an Angular codebase that you'd reverse today. What did it cost, and what did you do about it?

What the interviewer is really testing:
Whether you can own a mistake calmly, measure its cost in real terms, and show what you changed in how you decide.
Answer frame:

Decision: what you chose and why it made sense at the time.

Cost: how it showed up: slow builds, bugs, onboarding pain.

What changed: how you undid it or contained it, and the lesson you apply now.

Sample spoken answer:

“Early on I built a generic base component that every page extended. It held subscriptions, loading flags, error handling and a few helpers, and it felt clever because pages were short. Two years later it was a trap. Every page inherited behaviour it didn't need, changing the base class meant retesting the whole app, and new developers couldn't tell where anything came from. Every subclass also had to pass its services up through super(), which made the constructors messy. I stopped adding to it and replaced it piece by piece with small composable pieces: a loading helper built on signals, takeUntilDestroyed instead of the base class's cleanup, and an error service. It took a few months of small pull requests. Now I prefer composition over inheritance for components, and I'm wary of anything generic before there are three real users.”

Red flag to avoid:

Choosing a trivial mistake, or blaming the team or the framework instead of owning the decision.

They may ask next:
  • How did you convince the team to spend time undoing it?
  • What signs would tell you a shared abstraction is going the same way?
Say it in 60 seconds
Were you asked something else? Share it A person checks every question before it goes on the site. No name is shown.
For the call itself

You practiced these. On the real call, ClapAssist helps with the rest.

ClapAssist is an AI interview assistant for Mac and Windows. It listens to the interview on your computer and shows you what to say, in short lines you can read while you talk. Your live interview audio and screen are never stored. Your resume and notes are saved to your account so the app fills them in on any computer. It stays out of screen share on every plan, including Free; only you can see it.

Download with 10 free minutes
Mac and Windows · Stays out of screen share · No card