/angular-code-style-guide

Primary LanguageJavaScriptApache License 2.0Apache-2.0

My Angular coding style guide

This guide line has as a fallback the Angular coding style guide, if you find something that this document does not cover please check it.


Legend :
πŸ§‘β€πŸ”¬πŸ”Ž - Requires manual verification
πŸ€– - Has an automated process of verification
πŸ‘· - Verification on process of automation
❌ - Invalid, negative or bad practice
βœ… - Valid, positive, checked or good practice

Section 01 - Creating a Standard.

Rule 01-01 - This document has the objective to create and enforce standards. πŸ§‘β€πŸ”¬πŸ”Ž

The code is easier to understand and maintain when every developer on the team writes it on the same manner. This way it easier to identify bug and solutions and provide automation to the code.

Rule 01-02 - The code base should be written using USA English. πŸ§‘β€πŸ”¬πŸ”Ž

The standard English to be used on naming things is the USA English so some words that have slight different spelling don't get typos. Ex : color and colour. ItΒ΄s recommended to use some kind of spell checker on your IDE to help not create typos on the code.

Rule 01-03 - Never ignore a warning or error alert. πŸ§‘β€πŸ”¬πŸ”Ž

We are going to use every kind of technology, like linters and scripts, approved by the team to enforce the rules. It's recommended the usage of those tools on the IDE to help during the development to check code. There are also scripts that check the standards before you push the code to the repository. Every warning or error on those steps should be addressed before code being pushed.

Section 02 - Naming conventions.

Rule 02-01 - File naming convention. πŸ€–

Files are named using kebab-case and dots only. Every file below src/app should have a suffix, limited by dots, that describes the propose of this file. The only exception to this rule are the index.ts files used as barrels. This rule is an extension of Angular's General Naming Guidelines.

Every file that represent the following types like:

  • component
  • directive
  • module
  • pipe
  • service
  • interface
  • type
  • enum
  • utils

Some files may have an additional suffix that give additional information about it. Ex:
feature-a-http.service.ts
feature-a-web-socket.service.ts
feature-a-routing.module.ts

Should be named with this pattern <feature>(-<suffix>).<type>.ts|html|scss|spec.ts .

// Eslint rules status = πŸ€–

/**
 * This rule only applies for .ts files
 * and does not work yet with interface and type.
 * Also plan to add a validation to .html and .scss file.
 */

  extends: [
    "plugin:angular-file-naming/recommended"
    ...
  ],

Rule 02-02 - Names have to be meaningful and give notion of what they represent. πŸ§‘β€πŸ”¬πŸ”Ž

During Pull Requests review you should evaluate if the naming for members and type like give helps to understand the code. Methods and members alike should be verbs or give a notion of action.

Rule 02-03 - Interfaces do NOT starts with the suffix 'I'. πŸ€–

// Example

❌ export interface ITestCode {}

βœ… export interface TestCode {}

// Eslint rules status = βœ…
    rules: {
        ...
        "@typescript-eslint/naming-convention": [
          "error",
          {
            selector: "interface",
            format: ["PascalCase"],
            custom: {
              regex: "^I[A-Z]",
              match: false,
            },
          }
        ],
      },

Rule 02-04 - Enum members or keys have to be CONSTANT_CASE πŸ€–

// Example

❌ export enum TestMember {
    aMember,
    anotherMember = 'another member'
}

βœ… export enum TestMember {
    A_MEMBER,
    ANOTHER_MEMBER = 'another member'
}

// Eslint rules status = βœ…
    rules: {
        ...
        "@typescript-eslint/naming-convention": [
          "error",
          {
            selector: "enumMember",
            format: ["UPPER_CASE"],
          },
        ],
      },

Rule 02-05 - Class, interface and type members name have to be camelCase πŸ€–

// Example

❌ export class TestClass {
  a_private_member = 0;
  AnotherMember = 1;
  LAST_MEMBER = 2;
}

βœ… export class TestClass {
  aPrivateMember = 0;
  anotherMember = 1;
  lastMember = 2;
}

// Eslint rules status = βœ…
    rules: {
        ...
        "@typescript-eslint/naming-convention": [
          "error",
          {
            selector: 'default',
            format: ['camelCase'],
            leadingUnderscore: 'forbid',
            trailingUnderscore: 'forbid',
          },
        ],
      },

Rule 02-06 - Class, interface and type name have PascalCase πŸ€–

// Example

❌ export class _TEST_CLASS_ {}

❌ export class test-class {}

❌ export class TESTClass {} // Although is a valid Pascal case name.

βœ… export class TestClass {
}

// Eslint rules status = βœ…
    rules: {
        ...
        "@typescript-eslint/naming-convention": [
          "error",
          {
            selector: 'typeLike',
            format: ['PascalCase'],
          },
        ],
      },

Rule 02-07 - Members ordering. πŸ€–

According to the visibility, modifiers and type they can be ordered applying first the modifiers, visibility modifiers then members types, that are:

modifiers order

  • signature
  • static
  • decorated
  • instance
  • abstract
  • regular

visibility modifiers order

  • public
  • protected
  • private
  • #private
  • no modifiers

members types order

  • index signatures
  • fields
  • static initialization
  • constructors
  • getters
  • setters
  • methods.

Some very special cases it could differ but it is considered a code smell.
Check this link to learn more about modifiers, visibility and types.

// Example

❌ export class TetsComponent {

  private aMethod = () => {
    console.log(`this should be after constructor`)
  }

  otherMethod(){
    this.aMethod()
  }

  constructor(){
    this.aField = `a string`
  }

  aField : string;
}

βœ… export class TetsComponent {
  aField: string;

  constructor() {
    this.aField = `a string`;
  }

  otherMethod() {
    this.aMethod();
  }

  private aMethod = () => {
    console.log(`this should be after constructor`);
  };
}

// Eslint rules status = βœ…
    rules: {
        ...
        "@typescript-eslint/member-ordering": "error",
      },

Rule 02-08 - Naming Observables. πŸ€–

Observables should be named camelCase just like a regular member but it should contain a $ suffix. Ex:

// Example

❌
export class TestClass {
   public aObservable : Observable<unknown>;
}

βœ… export class TestClass {
  public aObservable$ : Observable<unknown>;
}

// Eslint rules status = βœ…
    plugins: ["rxjs"],
    rules: {
        ...
        "rxjs/finnish": [
          "error",
          {
            types: {
              "^EventEmitter$": false,
              "^Subject$": false,
            },
          },
        ],
    },

Rule 02-09 - Naming Subjects. πŸ€–

Subjects should be named camelCase just like a regular member but it should contain a Subject suffix. Ex:

// Example

❌
export class TestClass {
   public aValue : Subject<unknown>;
}

βœ… export class TestClass {
  public aValueSubject : Subject<unknown>;
}

// Eslint rules status = βœ…
    plugins: ["rxjs"],
    rules: {
        ...
        "rxjs/suffix-subjects": [
          "error",
          {
              "functions": true,
              "methods": true,
              "parameters": true,
              "properties": true,
              "strict": false,
            "suffix": "Subject",
            "types": {
              "^EventEmitter$": false
            },
            "variables": true,
          }
        ]
      },

Section 03 - Structure conventions.

Rule 03-01 - Angular project Source Code Folder Structure. πŸ€–

Every single folder have to be named using kebab-case.

Empty folders are not allowed.

Aside to the app component files there should be only features/ and shared/ folders under src/app. All the logic that is specific created to a feature should be place on a sub-folder under features/. For every feature it should have a folder named as the feature and several sub folders named according to the functionality of the files on this folder. Reusable elements have to be placed under shared/ and inside this one we also have sub folders named according to the functionality of the files on this folder.

So the skeleton of the folder structure should be something like this:

// Folder tree pattern example

πŸ“root/
β”œβ”€πŸ“src/
β”‚  β”œβ”€πŸ“app/
β”‚  β”‚  β”œβ”€πŸ“features/
β”‚  β”‚  β”‚  β”œβ”€πŸ“feature-a/
β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“containers/
β”‚  β”‚  β”‚  β”‚  β”‚   β”œβ”€πŸ“view-a/
β”‚  β”‚  β”‚  β”‚  β”‚   β”‚  β”œβ”€πŸ“„view-a.component.ts
β”‚  β”‚  β”‚  β”‚  β”‚   β”‚  β”œβ”€πŸ“„view-a.component.html
β”‚  β”‚  β”‚  β”‚  β”‚   β”‚  β”œβ”€πŸ“„view-a.component.scss
β”‚  β”‚  β”‚  β”‚  β”‚   β”‚  β”œβ”€πŸ“„view-a.component.spec.ts
β”‚  β”‚  β”‚  β”‚  β”‚   β”œβ”€πŸ“view-b/
β”‚  β”‚  β”‚  β”‚  β”‚   β”‚  β”œβ”€πŸ“„...
β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“models/
β”‚  β”‚  β”‚  β”‚  β”‚   β”œβ”€πŸ“„feature-a.interface.ts
β”‚  β”‚  β”‚  β”‚  β”‚   β”œβ”€πŸ“„feature-a.enum.ts
β”‚  β”‚  β”‚  β”‚  β”‚   β”œβ”€πŸ“„feature-a.type.ts
β”‚  β”‚  β”‚  β”‚  β”‚   β”œβ”€πŸ“„feature-a-routes.enum.ts
β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“utils/
β”‚  β”‚  β”‚  β”‚  β”‚   β”œβ”€πŸ“„feature-a.utils.ts
β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“services/
β”‚  β”‚  β”‚  β”‚  β”‚   β”œβ”€πŸ“„feature-a-http.service.ts
β”‚  β”‚  β”‚  β”‚  β”‚   β”œβ”€πŸ“„feature-a-web-sockets.service.ts
β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“store/
β”‚  β”‚  β”‚  β”‚  β”‚   β”œβ”€πŸ“„feature-a.actions.ts
β”‚  β”‚  β”‚  β”‚  β”‚   β”œβ”€πŸ“„feature-a.reducer.ts
β”‚  β”‚  β”‚  β”‚  β”‚   β”œβ”€πŸ“„feature-a.selectors.ts
β”‚  β”‚  β”‚  β”‚  β”‚   β”œβ”€πŸ“„feature-a.effects.ts
β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“„feature-a.module.ts
β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“„feature-a-routing.module.ts
β”‚  β”‚  β”‚  β”œβ”€πŸ“feature-b/
β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“„.../
β”‚  β”‚  β”‚  β”œβ”€πŸ“„.../
β”‚  β”‚  β”œβ”€πŸ“shared/
β”‚  β”‚  β”‚  β”œβ”€πŸ“components/
β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“shared-a/
β”‚  β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“„shared-a.component.ts
β”‚  β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“„shared-a.component.html
β”‚  β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“„shared-a.component.scss
β”‚  β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“„shared-a.component.spec.ts
β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“shared-b/
β”‚  β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“„...
β”‚  β”‚  β”‚  β”œβ”€πŸ“services/
β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“shared-a/
β”‚  β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“„shared-a-http.service.ts
β”‚  β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“„shared-a-web-socket.service.ts
β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“shared-b/
β”‚  β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“„...
β”‚  β”‚  β”‚  β”œβ”€πŸ“models/
β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“„shared-a.type.ts
β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“„shared-b.interface.ts
β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“„shared-c.enum.ts
β”‚  β”‚  β”‚  β”œβ”€πŸ“utils/
β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“„shared-a.utils.ts
β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“„shared-b.utils.ts
β”‚  β”‚  β”‚  β”‚  β”œβ”€πŸ“„shared-c.utils.ts
β”‚  β”‚  β”œβ”€πŸ“„app.*.ts
β”‚  β”‚  β”œβ”€πŸ“„...

models/ - Should contain only files that define data structures or constants like interfaces, types and enums related to the feature or the whole application when on shared/.

services/ - Should contain only files that manipulate data related to the feature or the whole application when on shared/.

utils/ - Should contain only files that contain helper functions related to the feature or the whole application when on shared/.

components/ - Also can be called ui/. It should contain the presentational component that does not have any logic or access services. It also can be composed by other shared components. Usually it holds heavy stylization and have inputs and outputs boundaries to communicate with other components. (Dumb components)

containers/ - Also can be called views/ or ui/. It should contain the components that interact with services or store. It also can be composed by other shared components. Usually it holds little stylization and 'contains' the other components. (Smart components)