loading...
Cover image for Static Analysis: What Is It and Why Is It Important?

Static Analysis: What Is It and Why Is It Important?

mccataldo profile image Mike Cataldo Updated on ・4 min read

You're likely familiar with the concept of a code review: scrutinizing code by visual inspection, without the assistance of automated tools. You know manual processes are prone to human error and you'd jump at the opportunity to automate some of that process if you could, right? Enter static analysis:

In short, static analysis can:

  • provide an understanding of the code structure
  • help to ensure that the code adheres to a set of standards
  • and reveal errors that don't manifest themselves until disaster strikes
    • which could be weeks, months or years post-release

Types of Static Analysis & Tooling

Automated tools can assist developers in carrying out different types of static analysis, loosely defined as linting, formatting and type-checking.

Linting - ESLint

ESLint is a tool for identifying and reporting on patterns found in ECMAScript/JavaScript code, with the goal of making code more consistent and avoiding bugs

High-level Overview

  • ESLint statically analyzes your code to quickly find problems
    • it's built into most editors like VSCode
    • it can be run as part of a continuous integration pipeline
  • Some problems are fixed automatically
    • fixes are syntax-aware
      • you won't experience errors introduced by traditional find-and-replace algorithms
  • And it's customizable to work exactly the way you need it for your project
    • you can write your own rules that work alongside ESLint's built-in rules
    • there are plenty of valuable plugins (rulesets written by others) to save you time

Example

Given this single line of code:

var foo = bar;

ESLint would output the following messages:

1:5 - 'foo' is assigned a value but never used. (no-unused-vars)
1:11 - 'bar' is not defined. (no-undef)

That output is a product of the recommended configuration which enforces rules no-unused-vars and no-undef (along with many others) that can be reconfigured to show a warning instead of an error or even be turned off completely.

Beyond initial setup and configuration, linting your entire codebase is easy:
$ npx eslint .

To lint React jsx files:
$ npx eslint --ext .jsx .

ESLint can be used to enforce standards around formatting with very fine control - down to the character. Even the whitespace around your code can be scrutinized! Other more opinionated formatting tools are designed to avoid this sort of bike-shedding.

Code Formatting - Prettier

Prettier reformats your code in a way that's more readable and consistent. While being a stickler for code style can seem unfruitful, there are some hidden code-quality gems to be found. Here's an example:

Example

const a = false
const b = false
const c = true
const d = a && b || c

If you don’t remember the order of operations of && and || - or you don’t trust that all developers on the team do - in order to get the value of d, then a bug could get introduced when this get refactored.

Formatting the code with Prettier results in this:

const a = false
const b = false
const c = true
const d = (a && b) || c

The extra parentheses are helpful even if you do know the order of operations. If by chance you realize that wasn’t your intention, you can add the parentheses yourself and Prettier will leave it that way:

const a = false
const b = false
const c = true
const d = a && (b || c)

Code formatting can make the intent of your code more obvious, reducing cognitive load on the developer reviewing or refactoring the code.

By design, Prettier configuration is limited. This delegation is at the cost of finer control over formatting which can be achieved by using ESLint instead to format your code.

Type-checking - TypeScript

Trying to invoke an object that’s not a function results (unsurprisingly) in an error like this:

x is not a function

However this happens at execution time.

A static type checker allows you to specify what data type a variable is to ensure - at compile time - that it’s being used properly by adding syntax to JavaScript to follow that variable through the code.

You might be saying to yourself, but JavaScript is not a compiled language! - and you'd be right! But TypeScript's compiler can be used to type-check your code - whether it's TypeScript or JavaScript.

Example

This code contains a bug:

function getFullName(customer) {
  const {
    name: {first, middle, last},
  } = customer
  return [first, middle, last].filter(Boolean).join(' ')
}
getFullName({first: 'John', middle: 'A', last: 'Smith'})

Adding type-checking helps us find the bug automatically:

type Customer = {
  name: {
    first: string,
    middle: string,
    last: string,
  },
}
function getFullName(user: Customer): string {
  const {
    name: {first, middle, last},
  } = customer
  return [first, middle, last].filter(Boolean).join(' ')
}

Here’s the output:

Argument of type '{ first: string; middle: string; last: string; }' is not assignable to parameter of type 'Customer'.2 Object literal may only specify known properties, and 'first' does not exist in type 'Customer'.(0123)

Type-checking can be adopted incrementally:

Conclusion

Static analysis should be an arrow in every JavaScript developer's quiver. In addition to manual and automated testing, it will give you more confidence that you're shipping quality code.

Discussion

markdown guide