Angular Best Practices/Review Checklist

By | March 28, 2020

Analysis and Design activities

  1. Decide on UI structure, if the new feature creates a module.
  2. Decide on components to be built by category of presentational or container components.
  3. Decide on redux state activities to be performed
  4. Decide on redux actions, reducers, effects. 

Coding rules

  1. Any additional npm package, utility or pattern to be implemented must be discussed in the team for a review.
  2. UI structure adheres to accepted Angular project guidelines. 
  3. UI State management pattern should be implemented as per redux state management guidelines.

Principles

  • LIFT – File and folder structure principle
    • Locate code quickly
    • Identify code at a glance
    • Flattest structure possible
    • Try to be DRY (don’t repeat yourself principle)
  • SOLID
    • Single responsibility principle
    • Open/closed principle
    • Liskov substitution principle
    • Interface segregation principle
    • Dependency inversion principle 
  • 5 seconds rule
    • If you can’t understand code in 5 seconds, it probably needs refactoring.
    • 5 seconds is a figure of speech but it means if you can’t figure it out quickly its a candidate.
  • Organization of Code for readability
    • Code 
      • private variables 
      • public properties 
      • public methods
      • private methods
    • Grouped and sorted
    • Consistent naming and spelling matters
  • One item per file – Component, Directive, Services, Pipes (Doesn’t apply to domain/model objects) 

Components

  • Before starting to code, identify smart vs. dummy components or container vs presentational components.
  • The component should deal with ONLY display logic.
  • Avoid logic in templates, it should talk to a model variable that is updated by logic in typescript file.
  • Prefixing component selectors
    • <app name>-<component selector> for application-wide components available in core and shared modules.
    • <app name>-<feature name>-<component selector> for feature module components.
  • Always use @Input() or @Output(), don’t declare them in @Component tag as it’s not recommended.
  • Maintain Immutability when passing data to child components. This will ensure that with reference bugs that are hard to find in JavaScript will not happen.
  • Safe Navigation Operator example *ngIf = “products.length” will fail if products is null/undefined, use “products?.length

Class – Component/Service/Directives/Pipes/Interceptors etc

  • The class name and file name should be the same. eg product-list.component will have class as ProductListComponent.
  • Use Angular guidelines for suffixes such as Component, Service, etc.
  • The class name is PascalCase.
  • Class variables/attributes/properties are camelCase.
  • Class methods are camelCase.
  • Observable type variable names should suffix $ in its name.
  • Constant names are UPPER_CASE with an underscore between each word.
  • Class member sequence (Note variables/methods are public by default in TypeScript)
    • private variables 
    • public properties 
    • public methods
    • private methods
  • Declare actual type used to keep application TypeSafe. DON’T use any until the real type is unknown.
  • Always mark services as injectable
  • Services are singleton by default
  • Services, if declared as a provider in a lazily loaded feature module 
    • Needs caution as you may end up having multiple instances.
    • Multiple instances may be needed at times but that should be the vision rather than an oversight.
  • Consider caching your request results 
  • Service should contain all business and data manipulation Logic
  • No long methods/functions
    • Methods should be readable
    • It should follow the single responsibility principle 
    • The cyclomatic complexity of the method should be low.
    • Apply 5 seconds rule to refactor
  • Follow DRY
    • If there is a code available, similar or few changed lines of code that duplicates same code needs refactoring
    • There should be only one copy of code if several implementations are needed it should be changed to apply DRY. 
  • Replace magic strings with constants (code reuse)
  • Variable/method names should well define what it is doing or used for.
  • Consider caching your request results. 
  • Use barrels to reduce the number of imports.
  • Use Aliases for imports as we do with @shared, @core to have smaller imports

Tests

  • Unit Tests are written to cover all public methods
  • Variables/Methods should be written with a correct scope such as public/private etc. The scope should not be elevated for unit testing purposes.
  • There should be negative and positive unit tests to cover both scenario’s
  • There should be one unit test per logical path, we should have multiple tests to cover the overall public method.
  • All dependencies of a Service or Component that is being tested should be mocked. 
  • Any utility type dependency in service or component under unit test should NOT be mocked. 
  • There can be more than one assertions in the unit test, There MUST be one assertion else it is not a test. 
  • 90% of code and test coverage is needed but the quality of the unit test is what matters. 
  • Change in code SHOULD break unit tests. 
  • A small set of data can be embedded in the unit test file. JSON injection is required, if we need a large set of data for unit tests. 
  • Setup and BeforeEach block should setup initial data for each test. An override can be done for the respective test. 
  • A unit tests should be small and readable – apply long method/function rules.