Static Code Analysis

Static analysis is the process of analysing code without actually executing it. This is in contrast to dynamic analysis, which involves running the code and observing its behaviour.
Static analysis tools are designed to help developers identify potential issues in their code before it is deployed or executed. These tools typically operate by parsing the source code and using a set of rules or heuristics to identify potential issues. These tools can analyse code for a wide range of issues, including syntax errors, security vulnerabilities, performance bottlenecks, and code quality issues.

Linting

Linting is the process of running a static analysis tool to identify potential issues in source code.
Linting tools typically analyze source code for a variety of issues, including syntax errors, style violations, security vulnerabilities, etc.

Benefits of linting

There are several benefits to linting code, to mention a few:
  • Improved code quality: Linting tools can developers identify and fix issues in their code that may not be immediately apparent (security, performance, etc).
  • Increased consistency: Linting tools typically enforce coding standards and best practices, helping developers to be more consistent across a codebase.
  • Enhanced collaboration: By enforcing a common set of coding standards within a team, they can improve communication and collaboration among team members.

Linting Tools: Eslint

The static code analysis and linting tool ESLint is the de-facto standard for linting JavaScript projects. ESLint lets you put guidelines over coding standards and helps you minimise those errors.

Install

You can install and configure ESLint using this command:
npm init @eslint/config
Follow the instruction and you should end up with a .eslintrc.js in your root folder like this (depending on your options):
module.exports = { "env": { "browser": true, "es2021": true, "node": true }, "extends": [ "eslint:recommended", "plugin:cypress/recommended", "plugin:react/recommended", "plugin:@typescript-eslint/recommended" ], "parser": "@typescript-eslint/parser", "parserOptions": { "ecmaVersion": "latest", "sourceType": "module" }, "plugins": [ "cypress", "react", "@typescript-eslint" ], "rules": {} }

NPM Scripts

Let’s add an npm script called lint so we can run ESLint whenever we want with the proper command line flags.
{ "scripts": { "lint": "eslint --ignore-path .gitignore .", "lint:check": "yarn lint", "lint:fix": "yarn lint --fix", }, }
We run ESlint with --ignore-path pointing to the .gitignore file, so it won't throw errors in those files due to files that do not interest us to lint, like the /dist or /node_modules directories.
💡
You can have a separate .eslintignore file if that is more convenient for your case.

Disable unnecessary rules

This step is only recommended if you’ll be using Prettier.
Because Prettier can automatically fix many stylistic issues in our codebase, it’s not necessary to have ESlint check for those. We'll use eslint-config-prettier to disable all rules that are made irrelevant thanks to prettier.
Install it as a devDependency:
yarn add -D eslint-config-prettier
Then, add "prettier" to the "extends" array in your .eslintrc.* file. Make sure to put it last, so it gets the chance to override other configs.
"extends": [ ..., "prettier"],
 
🎉 ….That’s it! Now, we can lint our code as part of the validation process. In case ESLint detects any issue with our code, the validation will fail.

Formatting

Code formatting is the process of rearranging the source code in a consistent way.
Code formatting typically involves adjusting the layout of the code, including the use of indentation, whitespace, line breaks, etc.

Benefits of formatting

There are several benefits to formatting code, to mention a few:
  • Improved readability: Properly formatted code is easier to read and understand, which can make it easier for developers to work with and maintain.
  • Increased consistency: Formatting tools typically enforce coding standards and best practices, helping developers to be more consistent across a codebase.
  • Enhanced collaboration: Code formatting can help ensure that all members of a team or organisation are following a common set of standards, which can improve communication and collaboration.

Formatting Tools: Prettier

Prettier is an opinionated code formatter. It enforces a consistent style by parsing your code and re-printing it based on a set of rules.

Install

To get Prettier installed in your project run:
yarn add -D prettier

Configuration

Go to the root of the project and create a .prettierrc file. The configuration for Prettier depends on the agreed convention for each project.
💡
You can use the prettier playground to get a config file.
In my case, this is a config I commonly use:
{ "arrowParens": "avoid", "bracketSpacing": true, "htmlWhitespaceSensitivity": "css", "insertPragma": false, "jsxBracketSameLine": false, "jsxSingleQuote": true, "printWidth": 80, "proseWrap": "always", "quoteProps": "as-needed", "requirePragma": false, "semi": false, "singleQuote": true, "tabWidth": 2, "trailingComma": "none", "useTabs": false }

NPM Scripts

Let’s add some npm script in our package.json that help us to use Prettier in our project.
{ "scripts": { "format": "prettier --ignore-path .gitignore .", "format:check": "yarn format --list-different", "format:fix": "yarn format --write", }, }
We have declared a prettier script where we'll have all the shared config like the glob for the file extensions that we want to target, as well as ignoring (--ignore-path) anything that matches the files mentioned in our .gitignore.
💡
You can have a separate .prettierignore file if that is more convenient for your case.
Then we add a script called format to run prettier across all files in our codebase (that match the glob). We use the flag --write to write those changes to the disk.
Lastly, we also want to check files in the project are properly formatted. For this, we're going to do --list different that will let us know whether there are files in the system that need to be formatted.
 
Now, we can check the format of our code as part of the validation process. In case Prettier detects any issue with our code, the validation will fail.

Type Checking

Type checking is a process in which a programming language's compiler or interpreter checks the data type of a value or expression to ensure that it is valid and conforms to the expected data type.
In a typed programming language, variables and expressions are typically associated with a specific data type, such as a string, integer, or boolean. Type checking helps ensure that the values assigned to variables and used in expressions are of the correct data type, and can help catch errors or bugs that might occur if the wrong data type is used.

Benefits of type checking

Type checking is a process in which a programming language's compiler or interpreter checks the data type of a value or expression to ensure that it is valid and conforms to the expected data type. There are several benefits to using type checking in your code:
  • Catch errors and bugs early in the development process: By ensuring that values are of the correct data type, type checking can help catch errors and bugs that might not be detectable by other means.
  • Improve the reliability and maintainability of your code: By catching errors and bugs early, type checking can help prevent issues from occurring in the future and make it easier to maintain and update your code.
  • Improve the readability and understandability of your code: By explicitly specifying the data type of variables and expressions, type checking can make it easier for other developers to understand the purpose and intent of your code.

Type-checking Tools: Typescript

ESLint can check for a lot of things, but it’s not a great tool for checking the types of variables that flow through your application. For this you’ll need a type-checking tool like TypeScript.

Install

To get Typescript installed in your project run:
yarn add -D typescript

Configuration

Go to the root of the project and create a tsconfig.json file.
{ "compilerOptions": { "noEmit": true, "baseUrl": "./src" } }
We're going to say noEmit is true, as we do not want to emit any files and use it just as a type checker in this case.

NPM Scripts

{ "scripts": { "types": "tsc", "types:check": "yarn types --noEmit", }, }

Automation

So far we’ve seen that by utilising static analysis tools developers can significantly improve the overall quality standards of their applications. However, simply having these tools in place is not enough. To truly enforce quality standards, it is important to automate their use as much as possible.
By automating the use of these quality-enhancing tools, developers can ensure that code changes are held to a high standard and that the overall quality of the application is consistently maintained.
There are several tools out there that may help us with that, but we’ll focusing on the following:
  • Husky
  • Lint Staged
  • CommitLint

Automation Tools: Husky

Husky is a tool that is used to automate Git hooks in JavaScript projects.
Git hooks are scripts that are run automatically by Git at certain points in the Git workflow, such as when code is pushed to a repository or when a pull request is opened. Husky allows developers to easily configure these hooks and automate tasks such as linting, testing, and code formatting. Husky supports all Git hooks.

Install

To install Husky, just run:
yarn add -D husky

Setup

To automatically have Git hooks enabled after install, run the following script:
npx husky-init && yarn
It will setup husky, modify package.json and create a sample pre-commit hook in the .husky folder that you can edit. By default, it will add npm test.

Create a hook

To add a command to a hook or create a new one, use husky add <file> [cmd] (don't forget to run husky install before).
npx husky add .husky/pre-commit "yarn test" # or whatever script git add .husky/pre-commit
We are currently just setting the pre-commit hook to run the test script in out package.json, but ideally you may want to add all the validation/checks script that you might consider in here.

NPM Scripts

Add the following script to install Husky:
"prepare": "husky install"
The major benefit of this is it enforces your coding standards without someone needing to configure and install certain extensions in their IDE to enforce them on save or remembering to do anything. Your code gets fixed before it ever leaves your machine, so you don’t have to wait for your CI to inform you that you forgot to run the formatter or linter.

Automation Tools: Lint-Staged

Lint-Staged allows us to run code against our staged git files to automate some parts of our workflows.
By running our static analysis tools only on the staged files, rather than the entire codebase, eslint-staged can help developers to quickly catch and fix issues before committing their code changes.

Installation

To install Lint-Staged, just run:
yarn add -D lint-staged

Configuration

Create a configuration file at the root of the project called .lintstagedrc, it will contain all the specified glob pattern for the files we want to target.
{ "**/*.+(ts|tsx|json|css)": [ "yarn static:fix" ] }
We now need to add the script to the git hook pre-commit that we configured earlier.
#!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" yarn lint-staged

Automation Tools: CommitLint

Resume

The complete workflow, before you commit your code, should consists of the following steps:
  • Husky will run its pre-commit hook
  • Lint-Staged will make sure that only staged files will be checked
  • Prettier will check the format.
  • ESLint will check our code standards.
  • Typescript will check the types
If any of the checks are unsuccessfully completed, that will mean that there is some error with our code that we need to review, otherwise we will be allowed to commit without any problem.

The full package.json

"scripts": { "lint": "eslint --ignore-path .gitignore .", "lint:check": "yarn lint", "lint:fix": "yarn lint --fix", "format": "prettier --ignore-path .gitignore .", "format:check": "yarn format --list-different", "format:fix": "yarn format --write", "types": "tsc", "types:check": "yarn types --noEmit", "static:validate": "yarn lint:check && yarn format:check && yarn types:check", "static:fix": "yarn lint:fix && yarn format:fix", "prepare": "husky install" }

Conclusion

In conclusion, static code analysis is an essential tool for ensuring the quality and maintainability of JavaScript projects. It allows developers to catch potential bugs and security vulnerabilities early in the development process, which can save time and resources in the long run.
By using static code analysis tools such as ESLint, Typescript, Prettier, etc., developers can improve the readability and consistency of their code, enforce best practices, and adhere to industry standards.
While static code analysis should not replace manual code reviews or testing, it is a valuable addition to any development workflow. By incorporating static code analysis into your project, you can reduce the risk of errors and ensure the long-term success and sustainability of your codebase.