Skip to main content

Angular Code Review Checklist

Rapid-scan checklist for Angular application code reviews.


Security Red Flags - Block PR Immediately

  • Bypassing DomSanitizer: Using bypassSecurityTrust* methods without proper validation -> Angular Security
  • innerHTML without sanitization: Direct HTML binding with user content ([innerHTML] with unsanitized data)
  • eval() or new Function(): Code execution vulnerabilities
  • Unsafe template binding: Using $any() to bypass type checking on user input
  • API keys in source code: Credentials hardcoded in services, components, or environment files committed to repo
  • Sensitive data in localStorage: Tokens, passwords stored unencrypted (use httpOnly cookies)
  • Missing CSRF tokens: HTTP state-changing requests without CSRF protection
  • Insecure HTTP: Making requests to http:// instead of https:// in production
  • Exposed secrets in environment files: Production credentials in environment.ts files

Common Mistakes

Subscriptions & Memory Leaks

  • Missing unsubscribe() in ngOnDestroy -> Angular General - RxJS Patterns
  • Not using takeUntil, takeUntilDestroyed, or async pipe for subscription cleanup
  • Multiple subscriptions to same observable without sharing (use shareReplay)
  • Nested subscriptions (callback hell) instead of RxJS operators (switchMap, mergeMap)
  • Manual subscription when async pipe would work

Change Detection

  • Missing OnPush change detection strategy on presentational components -> Angular Performance - Change Detection
  • Mutating objects/arrays directly (breaks OnPush detection)
  • Heavy computation in component getters (called on every change detection)
  • Missing track expression in @for loops with large lists
  • Using function calls in templates (re-executed on every CD cycle)
  • Zone pollution (running unnecessary code inside Angular zone)

Component Design

  • Components larger than 300-400 lines (extract child components or services)
  • Business logic in components instead of services
  • Direct DOM manipulation (use Renderer2 or Angular directives)
  • Not using standalone components (Angular 20+)
  • Using NgModules when standalone would work
  • Circular dependencies between services/components

RxJS Misuse

  • Not cancelling HTTP requests on component destroy
  • Using .subscribe() instead of async pipe in templates
  • Nested .subscribe() calls (pyramid of doom)
  • Not handling errors in observables (missing error callback or catchError)
  • Using Subject when BehaviorSubject or ReplaySubject appropriate
  • Hot observables without proper sharing operators

Forms

  • Using template-driven forms for complex validation (use reactive forms) -> Angular General - Reactive Forms
  • Missing form validation before submission
  • Not unsubscribing from valueChanges observables
  • Storing sensitive form data in component state unencrypted
  • Missing loading/disabled states during form submission

Dependency Injection

  • Using any type for injected services
  • Providing services in component instead of root (creates multiple instances)
  • Circular dependencies between services
  • Missing @Injectable({ providedIn: 'root' }) on singleton services
  • Not using injection tokens for configuration

Watch For - Performance & Correctness Issues

Performance Issues

  • Large lists without virtual scrolling (cdk-virtual-scroll) -> Angular Performance
  • Missing lazy loading for feature routes
  • Large bundle sizes (> 500KB initial load)
  • Not using OnPush change detection
  • Expensive pipes without pure: true
  • Images not lazy loaded
  • Missing code splitting

Change Detection Problems

  • Mutable objects passed to OnPush components (won't trigger updates)
  • Zone.js running for non-Angular events (setTimeout, DOM events)
  • ChangeDetectorRef.detectChanges() called excessively
  • Expressions in templates causing unnecessary recalculation
  • Component tree too deep (> 10 levels)

RxJS Anti-Patterns

  • Race conditions from multiple async operations
  • Memory leaks from long-lived observables
  • Overusing Subject when simpler state management would work (use signals)
  • Missing error handling causing observable streams to die
  • Not using shareReplay for expensive operations (HTTP calls)
  • Using subscribe in services (prefer returning observables)

State Management

  • Overusing global state (NgRx/Signals) for local UI state -> Angular State Management
  • Mutating state directly in stores
  • Missing error states and loading states
  • Storing derived data instead of computing from source
  • Not using signals for synchronous state (Angular 16+)

Template Issues

  • Complex logic in templates (extract to component methods)
  • Missing track expression in @for for dynamic lists
  • Inline styles for static styling (use CSS/SCSS)
  • Not using ng-container to avoid extra DOM elements
  • Control flow misuse (complex nested @if when @switch is clearer)

TypeScript Issues

  • Using any type (disable type checking)
  • Missing strict null checks
  • Not using discriminated unions for state
  • Type assertions without runtime validation
  • Missing interface definitions for complex objects

Best Practices - Code Quality

Angular Architecture

  • Standalone components (Angular 20+) -> Angular General - Standalone Components
  • OnPush change detection for all presentational components
  • Smart/dumb component pattern (container/presentation)
  • Feature-based folder structure
  • Services for business logic, components for presentation
  • TypeScript strict mode enabled
  • New control flow syntax (@if, @for, @switch) over structural directives (*ngIf, *ngFor)

RxJS Patterns

  • async pipe in templates for automatic subscription cleanup -> Angular General - RxJS
  • takeUntilDestroyed() for component subscriptions
  • shareReplay(1) for HTTP calls that should be cached
  • RxJS operators (map, filter, switchMap) instead of nested subscribes
  • Error handling with catchError operator
  • Proper observable composition

Signals (Angular 16+)

  • Signals for synchronous state management -> Angular State Management - Signals
  • computed() for derived state
  • effect() sparingly (only for side effects)
  • Signals for simple state, RxJS for async/complex flows
  • Read-only signals exposed from services

Change Detection Optimization

  • OnPush strategy by default -> Angular Performance
  • track expression on all @for loops
  • Pure pipes for transformations
  • Immutable data patterns
  • ChangeDetectorRef.detach() for manual control when needed

Forms

  • Reactive forms with typed form controls -> Angular General - Forms
  • Custom validators for business rules
  • Form state management (dirty, touched, valid)
  • Debounce on valueChanges for search/filter
  • Unsubscribe from valueChanges in ngOnDestroy

Testing

  • Unit tests for services and pure functions -> Angular Testing
  • Component tests with Angular Testing Library
  • TestBed configuration for components
  • Mock HTTP requests with HttpTestingController
  • Test user interactions, not implementation details

Dependency Injection

  • providedIn: 'root' for singleton services
  • Injection tokens for configuration
  • Constructor injection for services
  • inject() function in modern Angular (simpler than constructor DI)

Accessibility

  • Semantic HTML in templates
  • ARIA labels for dynamic content
  • Keyboard navigation (tabindex, focus management)
  • Screen reader testing
  • Form labels and error announcements

Quick Scan Table

CategoryWhat to CheckSeverityReference
SecurityNo DomSanitizer bypass without validationBlockSecurity
SecurityNo innerHTML with unsanitized user dataBlockSecurity
SecurityNo API keys in source codeBlockSecurity
SubscriptionsUnsubscribe in ngOnDestroyFixRxJS Patterns
SubscriptionsUse async pipe or takeUntilDestroyedFixRxJS Patterns
Change DetectionOnPush on presentational componentsFixPerformance
Change Detectiontrack expression in @for loopsFixPerformance
PerformanceLarge lists use virtual scrollingFixPerformance
PerformanceFeature routes lazy loadedReviewPerformance
RxJSNo nested subscriptionsFixRxJS Patterns
RxJSError handling in observablesFixRxJS Patterns
StateSignals for sync state, RxJS for asyncReviewState Management
StateNo state mutation (immutable updates)FixState Management
FormsReactive forms for complex validationReviewAngular General
ComponentsStandalone components (Angular 20+)ReviewAngular General
TypeScriptNo any typesFixTypeScript
TestingCritical flows testedReviewTesting

Legend:

  • Block PR - Security vulnerability or critical bug
  • Fix Before Merge - Memory leak, performance issue, or likely bug
  • Review & Discuss - Code quality improvement

Additional Considerations

Zone.js Issues

  • Long-running operations blocking change detection
  • Third-party libraries triggering unnecessary change detection
  • Missing runOutsideAngular for non-Angular event listeners
  • Zone pollution from setInterval/setTimeout
  • Not using NgZone.runOutsideAngular for performance-critical code

HTTP & API

  • Missing error handling on HTTP requests
  • Not using interceptors for auth tokens/headers
  • HTTP requests not cancelled on navigation
  • Missing retry logic for transient failures
  • Not using shareReplay for cacheable GET requests
  • Missing timeout configuration

Routing

  • Missing route guards for protected routes
  • Not preloading lazy modules strategically
  • Deep linking not configured
  • Missing route resolver for data fetching
  • Query params not cleaned up on navigation

Build & Bundle

  • Bundle size over 500KB for initial load
  • Missing tree shaking (unused imports)
  • Development code in production build
  • Missing source maps for debugging
  • AOT compilation not enabled

Styling

  • Inline styles instead of component styles
  • Global styles when component styles appropriate
  • Missing responsive design (mobile, tablet, desktop)
  • No CSS encapsulation (ViewEncapsulation.None without reason)
  • Hardcoded colors instead of CSS variables/theme

Block Checklist - Must Fix Before Merge

  • No DomSanitizer bypass without proper validation
  • No innerHTML with unsanitized user content
  • No API keys or secrets in source code
  • Subscriptions cleaned up in ngOnDestroy
  • OnPush change detection on pure components
  • track expression on all @for loops
  • No nested subscriptions (use RxJS operators)
  • Error handling on all HTTP requests
  • No direct state mutation
  • TypeScript strict mode passing (no any without justification)

Angular-Specific Testing Checklist

Before approving Angular PRs:

  • Services unit tested with proper DI setup
  • Components tested with TestBed or standalone setup
  • HTTP calls mocked with HttpTestingController
  • RxJS observables tested (marble testing if complex)
  • Forms validation tested
  • Change detection strategy tested (OnPush)
  • Accessibility tested (keyboard navigation, screen readers)
  • Lazy-loaded modules loadable
  • No console errors in development mode
  • AOT compilation succeeds

Angular Guides:

Cross-Cutting:

Process: