Skip to content

Static Code Analysis: Tools and Best Practices

DodaTech Updated 2026-06-19 6 min read

In this tutorial, you'll learn about Static Code Analysis: Tools and Best Practices. We cover key concepts, practical examples, and best practices.

Static code analysis examines source code without executing it, automatically detecting potential bugs, security vulnerabilities, code smells, and style violations before the code ever runs.

What You'll Learn

  • The difference between linters, type checkers, and security scanners
  • How to configure and use ESLint, Pylint, and SonarQube
  • Integrating static analysis into your CI/CD pipeline
  • Handling false positives and writing custom rules

Why Static Analysis Matters

Static analysis catches issues at the cheapest possible moment — during development, not after deployment. A bug caught by a linter costs seconds to fix. The same bug caught in production costs hours or days. Google runs static analysis on every code change and prevents 5,000+ bugs per year from reaching production.

Doda Browser uses static analysis in its CI pipeline to enforce code quality standards across its open-source codebase.

Learning Path

flowchart LR
  A[Code Review] --> B[Code Smells]
  B --> C[Static Analysis
You are here] C --> D[CI/CD Integration] D --> E[Production Quality Gates] style C fill:#f90,color:#fff

Linters vs Type Checkers vs Security Scanners

Tool Type What It Checks Examples Speed
Linter Style, code smells, potential bugs ESLint, Pylint, RuboCop Very fast
Type Checker Type consistency, null safety TypeScript, mypy, Pyre Fast
Security Scanner Vulnerabilities, secrets, injections Bandit, Semgrep, Snyk Slow
Comprehensive All of the above SonarQube, CodeClimate Slowest

ESLint (JavaScript/TypeScript)

npm init @eslint/config

Configure in eslint.config.js:

import js from '@eslint/js';
import tseslint from 'typescript-eslint';
import reactPlugin from 'eslint-plugin-react';

export default [
  js.configs.recommended,
  ...tseslint.configs.recommended,
  {
    plugins: { react: reactPlugin },
    rules: {
      'no-unused-vars': 'error',
      'no-console': 'warn',
      'react/hooks-rules-of-hooks': 'error',
      '"@typescript"-eslint/no-explicit-any': 'warn',
    },
  },
];

Expected output:

1:15  error  'unusedVar' is defined but never used    no-unused-vars
4:10  error  'any' is not allowed                     @typescript-eslint/no-explicit-any
7:5   warn   Unexpected console statement              no-console

Pylint (Python)

pip install pylint
pylint mymodule.py

Configuration written to .pylintrc:

[MASTER]
load-plugins=pylint_django

[MESSAGES CONTROL]
disable=C0111,  # missing docstring
        R0903,  # too few public methods
        W0611   # unused import (let mypy handle it)

[DESIGN]
max-args=5
max-locals=15
max-returns=3
max-statements=50

Expected output:

************* Module mymodule
src/mymodule.py:12:4: W0621: Redefining name 'data' from outer scope (redefined-outer-name)
src/mymodule.py:45:0: R0914: Too many local variables (18/15) (too-many-locals)

SonarQube (Multi-Language)

SonarQube is a comprehensive platform that tracks quality metrics across your entire codebase over time.

# sonar-project.properties
sonar.projectKey=myapp
sonar.sources=src
sonar.tests=tests
sonar.python.version=3.11
sonar.python.coveragePlugin=jacoco
sonar.qualitygate.wait=true

Run in CI:

sonar-scanner -Dsonar.login=$SONAR_TOKEN

Expected output:

ANALYSIS SUMMARY:
- Bugs: 3 (new: 2)
- Vulnerabilities: 1 (new: 0)
- Code Smells: 42 (new: 5)
- Coverage: 87.3%
- Duplications: 2.1%
- Quality Gate: PASSED

CI Integration

GitHub Actions

name: Static Analysis
on: [pull_request]
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20' }
      - run: npm ci
      - run: npm run lint          # ESLint
      - run: npx tsc --noEmit      # TypeScript
      - run: npm run audit         # Security audit

Pre-commit Hooks

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/pre-commit/mirrors-eslint
    rev: v9.0.0
    hooks:
      - id: eslint
  - repo: https://github.com/pycqa/flake8
    rev: 7.0.0
    hooks:
      - id: flake8

Handling False Positives

Static analysis tools sometimes flag code that's actually correct. Handle false positives properly:

# Pylint false positive — flag with inline comment
def get_user(user_id):  # pylint: disable=redefined-builtin
    return database.query(f"SELECT * FROM users WHERE id = {user_id}")
// ESLint false positive — disable for this line
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const data: any = JSON.parse(response);

Best practice: Explain why the rule is disabled. Future developers need to know the reasoning.

Custom Rules

When standard rules don't cover your team's conventions, write custom rules.

ESLint Custom Rule

// eslint-rules/no-console-error-only.js
export default {
  meta: {
    type: 'suggestion',
    docs: { description: 'Allow console.error only' },
    messages: {
      noConsole: 'Use console.error instead of console.{{ method }}',
    },
  },
  create(context) {
    return {
      CallExpression(node) {
        if (node.callee.object?.name === 'console' &&
            node.callee.property?.name !== 'error') {
          context.report({ node, messageId: 'noConsole' });
        }
      },
    };
  },
};

Common Errors

1. Too Many Rules

Enabling every available rule creates noise that drowns out real issues. Start with recommended presets and add rules gradually.

2. Ignoring Warnings

Treat warnings as errors in CI, or they'll be ignored. Use --max-warnings 0 to fail the build on any warning.

3. Running Analysis Too Late

Waiting until the CI pipeline means developers see issues only after pushing. Run linters in the editor and pre-commit hooks.

4. Not Configuring for the Project

Default configs don't know your project's conventions. Customize rules for your framework, language version, and team preferences.

5. Skipping Security Scanners

Linters catch style issues but miss SQL injection and XSS. Always pair a linter with a security-focused tool.

6. Not Updating Rule Sets

Outdated rule sets miss new vulnerability patterns. Update your analysis tools monthly.

Practice Questions

  1. What is the difference between a linter and a type checker? A linter checks code style and potential bugs. A type checker validates type consistency and null safety.

  2. How do you handle a false positive in ESLint? Use // eslint-disable-next-line rule-name with a comment explaining why.

  3. What quality gate does SonarQube measure? A combined pass/fail check based on bugs, vulnerabilities, code smells, coverage, and duplication.

  4. When should static analysis run in the development workflow? At three levels: in the editor (real-time), in pre-commit hooks, and in CI on every PR.

  5. Why should warnings be treated as errors in CI? Because warnings are otherwise ignored. --max-warnings 0 ensures all issues are addressed.

Challenge: Set up ESLint and SonarQube (or CodeClimate) on a personal project. Configure custom rules for your team's conventions. Document your configuration and the first 10 issues found.

FAQ

Can static analysis find all bugs?

No. Static analysis finds certain classes of bugs (type errors, unused variables, potential null references) but cannot find logic errors or business rule violations.

Does static analysis make code reviews obsolete?

No. Tools catch mechanical issues; humans catch design and architectural problems. Use both.

How often should I update my linter configs?

Every 1-3 months. New rules are added, old ones deprecated, and security patterns evolve.

What is the best static analysis tool for a new project?

Start with the language's standard linter (ESLint for JS, Pylint for Python). Add SonarQube when the project grows beyond 10,000 lines.

Does static analysis slow down the build?

Linters add seconds. Security scanners and comprehensive analysis add minutes. Run fast linters on every commit, slower analysis on PRs or nightly.

What's Next

Tutorial What You'll Learn
Code Review Best Practices Human review to complement automated analysis
CI/CD Pipeline Setup Integrating analysis into automated pipelines
SonarQube Setup and Configuration Deploying your own SonarQube instance

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. Updated 2026-06-19.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro