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
.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.
You can have a separate
.prettierignore
file if that is more convenient for your case.LintingBenefits of lintingLinting Tools: EslintInstallNPM ScriptsDisable unnecessary rulesFormattingBenefits of formattingFormatting Tools: PrettierInstallConfigurationNPM ScriptsType CheckingBenefits of type checkingType-checking Tools: TypescriptInstallConfigurationNPM ScriptsAutomationAutomation Tools: HuskyInstallSetupCreate a hookNPM ScriptsAutomation Tools: Lint-StagedInstallationConfigurationAutomation Tools: CommitLintResumeThe full package.jsonConclusion