Static analysis in JavaScript: 11 tools to help you catch errors before users do – LogRocket Blog

I write, speak, and help build startups. I’m the Chief Technology Officer at The Graide Network, where I manage the engineering team, write code, and oversee application architecture. I am also the founder of Draft.dev, a technical content marketing agency that helps create in-depth blog content for companies trying to reach software engineers.

You may know that linting can improve your code quality, but linting isn’t the only way static analysis can ensure your team is writing high-quality code consistently. Static analysis tools can play an integral role in your development cycle, even in a dynamically typed language such as JavaScript.

In this guide, we’ll look at some of the most prominent static analysis tools available in the JavaScript ecosystem and discuss why and when you might use them.

Introduction to static analysis

First, let’s review the definition of static analysis and its place in the software development lifecycle.

Static analysis is the process of verifying that your code meets certain expectations without actually running it. Unlike unit and integration testing, static analysis can be performed on raw source code without the need for a web server or build process.

Static analyzers typically parse your code and turn it into what is known as an abstract syntax tree. This tree is then traversed and the pieces are checked based on the rules dictated by the static analyzer. Most static analyzers also include a way for developers to write their own custom rules, but that varies from tool to tool.

Static analysis is most commonly used to:

  • Ensure consistent style and formatting
  • Check for common mistakes and possible bugs
  • Limit the complexity of code
  • Verify type consistency
  • Minimize security risks
  • Keep third-party dependencies up to date

When does static analysis happen?

In a dynamically interpreted language such as JavaScript, developers must decide when and how they want to run static analysis on their code. I’ve most commonly seen static analysis run on each developer’s machine before they push changes (as a Git pre-commit hook) as part of a continuous integration server’s workflow or as part of every code review.

Static Analysis Software Development LifecycleStatic Analysis Software Development Lifecycle

No matter when or how static analysis happens, the goal remains the same: to help make code more consistent, maintainable, and correct. It won’t replace automated or manual testing, but it may catch errors that other quality assurance tools miss.

Because JavaScript is dynamically typed and it has historically been hard to collect error logs from client-side applications, static analysis tools can be even more beneficial than in statically typed server-side languages.

If you’re new to static analysis, it can be overwhelming to sift through the many tools that are available. I’ll introduce you to some of the most common tools for static analysis in JavaScript. We’ll explore some of their use-cases and how to implement them in your development workflow.

1. ESLint

ESLint is probably the most widely used static analysis tool for JavaScript today. Both Prettier and Standard JS use ESLint to power their formatting engines, so even if you don’t include it explicitly, you might be using it under the hood.

ESLint’s primary use case is as a linter — a specific kind of static analysis tool that catches inconsistent formatting, styling, and possible errors. ESLint does this by using predetermined rules that you can configure or customize based on your needs.

A good example of how this can be used is to prevent developers from accidentally using console statements in production. If you’re writing a JavaScript function to sort numbers and you want to validate whether you did it correctly, you might use console.log() to check yourself.

function sortNumbers(numbers) {
    console.log(numbers);
    const result = numbers.sort((a, b) => (a - b));
    console.log(result);

    return result;
}
sortNumbers([30, 12, 22, 19]);

If the rule is enabled, ESLint will warn you about this likely mistake before you ship it to users.

2. Prettier

Using ESLint can be overwhelming. There are dozens of rules and you can write custom ones on top of that. Some rules may require you to change the behavior of your application, so if you want to start by simply making your code’s formatting more consistent, Prettier might be right for you.

Prettier is not a full-featured linter; it only addresses style and formatting rules. It helps by limiting choices and automatically fixing code that does not conform to its style guide.

For example, say you wrote a function like this:

function createUser() { return {
    id: 1, name: "Karl", birthdate: "6/10/86",
       hometown: "Lansing, MI" };
}

If you were to run it through Prettier, it would rewrite the function to conform to its consistent style.

function createUser() {
  return {
    id: 1,
    name: "Karl",
    birthdate: "6/10/86",
    hometown: "Lansing, MI",
  };
}

While Prettier gives you fewer options for customizing styling rules, it is a great way to make sure everyone on your development team is using the same formatting and style in their code.

3. Standard JS

Somewhere between ESLint and Prettier is Standard. Like Prettier, Standard is opinionated — you don’t need to decide which ESLint rules to implement and configure — but it goes further than just fixing stylistic issues. It also includes rules that may reduce errors and change the behavior of your code.

For example, Standard includes the ESLint rule to always use === instead of ==. Because JavaScript coerces types, blindly following Standard’s suggestion could change the behavior of your code in unexpected ways.

If you were to run the following code, all three log statements would be true.

function isTrue (x) {
  return x == true
}
console.log(isTrue('1'))   // true
console.log(isTrue(1))     // true
console.log(isTrue(true))  // true

But if you follow Standard’s suggestion and make x === true, the results change:

function isTrue (x) {
  return x === true
}
console.log(isTrue('1'))   // false
console.log(isTrue(1))     // false
console.log(isTrue(true))  // true

While Standard may not be as easy to implement on a large existing codebase, it’s still an excellent tool for linting your code. Removing petty disputes over coding style can boost developer productivity and speed up onboarding time.

4. JSHint

Similar to ESLint, JSHint is a linting tool that enables you to set up and configure rules for catching common coding errors and formatting inconsistencies. In general, ESLint has more rules, and it’s a little easier to write custom rules for. The differences mostly come down to preference.

One special case that favors JSHint over ESLint is when you’re developing an application with features specific to Mozilla’s Firefox browser. JSHint has a rule to allow calls to Mozilla-specific JavaScript extensions while ESLint does not.

Like ESLint, going through the rules and deciding which ones are appropriate for your codebase is the part that will take the most time upfront. Because JSHint has fewer rules and configuration options, it might be a little faster to set up if you’re not trying to do something extremely specific.

5. PMD

If you’re building a Java or Saleforce Apex application, you might already be familiar with PMD. PMD — which doesn’t stand for anything, by the way — is a linter with support for several programming languages, including JavaScript.

Its ruleset for JavaScript applications is fairly limited, but unlike the above linting tools, PMD comes bundled with a copy-paste detector. This can help find duplicate or similar code across an extensive application and encourages DRY code.

6. LGTM

Linting based on predetermined rules alone is a great way to increase your code’s quality, but it’s not the only way to check a codebase for common errors. The most significant disadvantage to linting is that it only knows about the rules you can tell it about.

LGTM — which stands for “looks good to me” — uses the fact that bugs often reoccur to check your codebase for common vulnerabilities and exploits that it learns about from analyzing other codebases. In other words, it’s looking not for rules programmers specifically tell it about but changes that may indicate the introduction of a new bug or security vulnerability.

While it’s free for open source projects, LGTM has paid offerings for private codebases.

7. SonarCloud

SonarCloud provides a comprehensive suite of static analysis tools to assess your codebase’s quality across a wide range of measures. While private projects have to pay for access, it’s free for open-source projects and integrates into GitHub so you can ensure that every commit maintains your code quality.

If you want to dig into the checks that SonarCloud does, it provides a sampling of results from open-source projects on its website. Each is assessed based on reliability, security, maintainability, code coverage, and duplicate code.

SonarCloud AssessmentSonarCloud Assessment

You can also drill down into each file and see all the suggestions and errors that SonarCloud found, giving you granular access and the ability to tweak certain quality thresholds as it makes sense.

8. Dependabot

If you host your source code on GitHub, you’ve probably already seen Dependabot in action. GitHub acquired Dependabot in May 2019 and has since integrated it as a feature that is available to all repositories to help address security vulnerabilities from out-of-date dependencies.

Automated Pull Request From DependabotAutomated Pull Request From Dependabot

Given the increasing dependency on third-party libraries in JavaScript, this can save teams time and close security gaps faster.

You do have to be a bit careful merging in Dependabot pull requests, though. If you don’t have a good set of automated tests, it’s possible that even minor version upgrades could cause breaking changes.

9. npm-audit

Since version 6, npm has had the audit command, which offers similar checks to Dependabot. If you’re using npm as your package manager but prefer not to get automated pull requests on your code, running the npm audit command is a good way to ensure that your third-party dependencies are up-to-date and secure.

Since it’s probably not wise to blindly update all your dependencies, npm audit comes with a few options to limit its scope.

  • npm audit fix automatically updates packages but only performs minor semver updates
  • npm audit fix --dry-run outputs a list of changes that will be made so you can double-check them before they take effect
  • npm audit fix --force updates major and minor versions of all packages with security vulnerabilities

As with Dependabot, npm audit should be combined with automated and manual tests to avoid breaking changes.

10. Flow

Facebook’s Flow can use either explicit annotations or implicit inferences to ensure type consistency in your JavaScript code.

Type-checking minimizes errors by ensuring that variables are used in a way that your program expects.

For example, say you have code like this:

function isTrue (x: bool) {
  return x === true;
}
isTrue(1);

Flow would throw an error because it expects x to be a boolean, not an integer. This warning is especially helpful when you want to ensure that objects contain specific properties or that numbers are not mistakenly coerced into strings.

11. TypeScript

Developed by Microsoft, TypeScript is used in Google’s Angular framework. TypeScript extends JavaScript and, like Flow, provides type annotations and type checking. But, unlike Flow, TypeScript is supported by most of the major JavaScript frameworks and IDEs, so it’s probably a better choice at this point.

While you don’t have to use all of TypeScript’s features to get some of the advantages, such as implicit type safety, you can dive deep to get a lot out it. With features such as interfaces, generics, template literals, and enums, there’s a lot for a JavaScript developer to learn.

Conclusion

This list is far from comprehensive, but I hope it helps you get started exploring and using static analysis to improve your codebase.

If you have favorite tools for static analysis in JavaScript, I’d love to hear about them. Leave me a comment below to continue the conversation.

LogRocket: Debug JavaScript errors more easily by understanding the context

Debugging code is always a tedious task. But the more you understand your errors the easier it is to fix them.

LogRocket allows you to understand these errors in new and unique ways. Our frontend monitoring solution tracks user engagement with your JavaScript frontends to give you the ability to find out exactly what the user did that led to an error.

LogRocket Dashboard Free Trial BannerLogRocket Dashboard Free Trial Banner

LogRocket records console logs, page load times, stacktraces, slow network requests/responses with headers + bodies, browser metadata, and custom logs. Understanding the impact of your JavaScript code will never be easier!

Try it for free.

Share this: