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 ofhttps://in production - Exposed secrets in environment files: Production credentials in
environment.tsfiles
Common Mistakes
Subscriptions & Memory Leaks
- Missing
unsubscribe()inngOnDestroy-> Angular General - RxJS Patterns - Not using
takeUntil,takeUntilDestroyed, orasyncpipe 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
asyncpipe would work
Change Detection
- Missing
OnPushchange 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
trackexpression in@forloops 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 ofasyncpipe in templates - Nested
.subscribe()calls (pyramid of doom) - Not handling errors in observables (missing error callback or
catchError) - Using
SubjectwhenBehaviorSubjectorReplaySubjectappropriate - 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
valueChangesobservables - Storing sensitive form data in component state unencrypted
- Missing loading/disabled states during form submission
Dependency Injection
- Using
anytype 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
OnPushchange 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
Subjectwhen simpler state management would work (use signals) - Missing error handling causing observable streams to die
- Not using
shareReplayfor expensive operations (HTTP calls) - Using
subscribein 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
trackexpression in@forfor dynamic lists - Inline styles for static styling (use CSS/SCSS)
- Not using
ng-containerto avoid extra DOM elements - Control flow misuse (complex nested
@ifwhen@switchis clearer)
TypeScript Issues
- Using
anytype (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
OnPushchange 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
asyncpipe in templates for automatic subscription cleanup -> Angular General - RxJStakeUntilDestroyed()for component subscriptionsshareReplay(1)for HTTP calls that should be cached- RxJS operators (
map,filter,switchMap) instead of nested subscribes - Error handling with
catchErroroperator - Proper observable composition
Signals (Angular 16+)
- Signals for synchronous state management -> Angular State Management - Signals
computed()for derived stateeffect()sparingly (only for side effects)- Signals for simple state, RxJS for async/complex flows
- Read-only signals exposed from services
Change Detection Optimization
OnPushstrategy by default -> Angular Performancetrackexpression on all@forloops- 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
valueChangesfor search/filter - Unsubscribe from
valueChangesinngOnDestroy
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
| Category | What to Check | Severity | Reference |
|---|---|---|---|
| Security | No DomSanitizer bypass without validation | Block | Security |
| Security | No innerHTML with unsanitized user data | Block | Security |
| Security | No API keys in source code | Block | Security |
| Subscriptions | Unsubscribe in ngOnDestroy | Fix | RxJS Patterns |
| Subscriptions | Use async pipe or takeUntilDestroyed | Fix | RxJS Patterns |
| Change Detection | OnPush on presentational components | Fix | Performance |
| Change Detection | track expression in @for loops | Fix | Performance |
| Performance | Large lists use virtual scrolling | Fix | Performance |
| Performance | Feature routes lazy loaded | Review | Performance |
| RxJS | No nested subscriptions | Fix | RxJS Patterns |
| RxJS | Error handling in observables | Fix | RxJS Patterns |
| State | Signals for sync state, RxJS for async | Review | State Management |
| State | No state mutation (immutable updates) | Fix | State Management |
| Forms | Reactive forms for complex validation | Review | Angular General |
| Components | Standalone components (Angular 20+) | Review | Angular General |
| TypeScript | No any types | Fix | TypeScript |
| Testing | Critical flows tested | Review | Testing |
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
runOutsideAngularfor non-Angular event listeners - Zone pollution from setInterval/setTimeout
- Not using
NgZone.runOutsideAngularfor 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
shareReplayfor 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
-
trackexpression on all@forloops - No nested subscriptions (use RxJS operators)
- Error handling on all HTTP requests
- No direct state mutation
- TypeScript strict mode passing (no
anywithout 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
Related Documentation
Angular Guides:
- Angular General Guidelines - Components, RxJS, DI, forms
- Angular State Management - Signals, NgRx, service state
- Angular Performance - Change detection, lazy loading, optimization
- Angular Testing - Unit tests, component tests, E2E
Cross-Cutting:
Process: