JavaScriptT2 Dark_1200x303

Static analysis in JavaScript can drastically improve your code quality. Take a look at how so, some available tools and tips for implementing this practice.

It has become an accepted best practice to use linting in JavaScript projects, but linting isn’t the only way to reduce errors and increase code consistency. Static analysis is a deep topic, and even in dynamic programming languages like JavaScript, there are ways to ensure high code quality beyond linting.

In this piece, we’ll take a look at what static analysis is, how it helps improve code quality as well as some static analysis tools available to JavaScript developers.

What Is Static Analysis?

Static analysis is the practice of analyzing source code before it is running. In compiled programming languages, static analysis might be built into the compiler, but in dynamically interpreted languages like JavaScript, static analysis tools must be configured to run on the code sometime before it is deployed.

The place of static analysis in the deployment pipeline

Most often, when a team decides to introduce static analysis to their workflow, they will install and configure the requisite packages and then automatically run the analysis on their continuous integration server before code is deployed to the production environment.

Alternatively, if the whole team hasn’t committed to adopting static analysis, you can install these tools on your local environment and run them locally before you submit your code for review.

How Static Analysis Helps Improve Your Code

As software developers, we have many tools at our disposal to help with code quality: unit and integration tests, code reviews, manual QA and static analysis among them. All of these tools are great, but most of them require manual input from a developer or tester. Static analysis is the exception.

Because static analysis can be fully automated, it’s one of the best ways to improve the quality of your JavaScript code without investing developer time. But how exactly can static analysis help, and what tools are available to JavaScript developers?

1. Formatting and Styling Code

The most important thing when working on a team is communication … As developers, we communicate primarily through code. We communicate with other parts of the software through code and we communicate with other developers through code.

Nicholas C. Zakas

The most common tools for static analysis in the JavaScript ecosystem—ESLint, JSHint, Prettier, Standard—are primarily used to ensure consistency in a team’s codebase.

While some people question the need to enforce styling conventions, code is the way developers communicate. We have rules about team communication because they help everyone stay on the same page, and we have rules about code communication because they make code more readable and easier to work with, and reduce disagreement on stylistic preferences.

2. Detecting Bugs and Errors

While no substitute for testing, several static analysis tools can be used to catch likely errors before code is ever run. For example, ESLint provides a rule to “disallow duplicate arguments in function definitions.” This means that if you wrote code like this:

function foo(bar, baz, bar) {
    return bar;
}

The tool would warn you that bar was used twice in foo()’s argument list, and you wouldn’t ship this bug to production. That could save you hours of trouble, and you don’t even have to write a test!

ESLint, PMD, Prettier and Standard are all good free tools that have rules for this kind of error detection. Several paid products also exist, including SonarCloud, which is free for open-source projects. You can also find tools built for specific kinds of errors like Daniel St. Jules’ jsinspect project, which detects similar and duplicated blocks of JavaScript code.

3. Enforcing Best Practices

Over its lifetime, a program will be handled by many pairs of hands and eyes. If a program can clearly communicate its structure and characteristics, it is less likely that it will break when modified in the never-too-distant future. Code conventions can help in reducing the brittleness of programs.

Douglas Crockford

How many classes should you include per JavaScript file? Should you use the eval() function in your code? What about magic numbers?

Experienced JavaScript developers will have their answers, but, strictly speaking, JavaScript does not enforce many rules about how you write it. This optionality leads new JavaScript developers to accidentally do things that might be frowned upon, deprecated or even dangerous. Fortunately, you can enforce best practices automatically using static analysis.

Prettier and Standard include opinionated rules about best practices, while ESLint and PMD are a bit more configurable. It may be tempting to build your own custom set of rules, but this will take a lot of time. Adopting an accepted set of rules is probably a better idea at the start.

4. Measuring Complexity

I’ve never seen a large piece of software that didn’t have some messy parts, but limiting complexity is an important facet of writing good code. Complexity in software can be measured and reported—the most popular method being cyclomatic complexity.

Image demonstrating cyclomatic complexity in a simple JavaScript program

Cyclomatic complexity in a piece of code counts the number of unique paths that the program can take between its start and finish. It ends up being a function of the number of control structures (conditionals, loops, etc.) used in any piece of code. As complexity increases, it generally becomes a good idea to break the code up into smaller classes or functions as appropriate.

There is debate as to whether lowering cyclomatic complexity reduces bugs, but in my opinion, having code with lower complexity makes for software that is easier to read, reason about and work with. If you want to catch increasing complexity or limit the cyclomatic complexity of your source code, Plato, ESLint or complexity-report are good static analysis tools for you.

5. Analyzing Security Risks

While you can’t depend on static analysis alone to prevent security vulnerabilities, it’s certainly worth automating what you can. While some of the tools above indirectly improve security by decreasing likely bugs, LGTM is a security-focused tool that takes a novel approach:

The same bugs often reappear over and over again throughout a project’s lifetime, and in multiple places in a codebase … That’s why it’s important to fix not only the original bug, but also to investigate how often the mistake is repeated in a codebase, or across multiple projects, and fix any other similar vulnerabilities.

- LGTM Documentation

Using the knowledge that common bugs are repeated across projects, LGTM scans thousands of large open-source projects to help you spot similar bugs in your codebase. This may lead to a lot of false alerts, but it might be worth trying in your project.

6. Auditing Third-Party Dependencies

JavaScript’s package management ecosystem has made big strides forward in the past few years, but third-party dependencies remain one of the greatest areas of vulnerability as well.

JavaScript applications tend to rely on many third-party libraries and frameworks. This makes development faster, but it puts a huge strain on teams that have to keep these third-party dependencies up to date. Fortunately, static analysis tools can help remind you when updates are required and even automatically manage this upgrade process.

If you use GitHub for version control, you have probably seen the automated pull requests from Dependabot on your projects. Since acquiring Dependabot, GitHub has integrated it with its core feature set, so if you have a JavaScript project, it might be a good idea to enable this.

Alternatively, you can manually check for security vulnerabilities and out-of-date dependencies using the npm audit command. My team performs these updates at predetermined times throughout the year so that we don’t interfere with our product development cycle. Whether you update your dependencies as they come in or in bulk every month, you need to keep them up to date, so it’s a good idea to use static analysis tools to help.

7. Checking Types

Finally, JavaScript applications can use type checking using tools like Flow or TypeScript to ensure consistent use of variable types. While JavaScript is not natively strictly typed, these tools both allow for progressive or strict typing that can decrease errors and improve code readability.

To demonstrate type hinting in JavaScript, consider the following function:

function getEvents(options) {
  // Options processed and events retrieved here
  return events;
}

If a new developer sees code like this, they might wonder how the options parameter should be formatted: Is it an array? An object? Similarly, what do the events returned from this function look like? Are they a collection? An object? An array?

While you can follow the source code to figure this out, type hinting makes it much clearer and helps you find violations using static analysis rather than having to run the code. Rewriting the function above in TypeScript would give us something like this:

function getEvents(options: Array<Option>): Collection<EventModel> {
  // Options processed and events retrieved here
  return events;
}

Now without knowing any of the internals of this function, we can see that it requires an array of Option objects as its input parameter and returns a Collection of EventModel objects.

Even better, you can implement TypeScript and Flow incrementally—you don’t need strict type hints everywhere just to get started. Just relax the rules to perform only certain type checks on the files you specify.

Tips for Implementing Static Analysis in JavaScript

All these different methods and tools can be overwhelming, but don’t get caught up in getting it perfect right away. Even adding a few linting rules to a large codebase can lead to a vast number of warnings and may even break existing functionality.

If you’re implementing static analysis into a large existing project, keep these tips in mind:

  • Start simple: Use a tool that allows you to relax the rules as needed for your codebase.
  • Fix one file at a time or configure the rules to allow any exceptions your codebase requires.
  • Start with your local development environment by running these tools in your command line. You don’t have to make them part of your continuous integration process right off the bat.
  • Adjust the rules and tools you use over time. The goal should be continuous improvement, not perfection.

Finally, if you have your own tools or suggestions for static analysis in JavaScript, I’d love to hear about them. Leave your thoughts in the comments below.


Karl Hughes
About the Author

Karl Hughes

Karl Hughes is a software engineer, startup CTO, and writer. He's the founder of Draft.dev.

Related Posts

Comments

Comments are disabled in preview mode.