Анализ7 мин чтения2026-03-06

Анализ кода: от статического сканирования до автоматизации в CI/CD

Статический анализ кода — не просто проверка синтаксиса. Это систематическое изучение исходного кода без его запуска, которое ловит баги на этапе разработки, ко

Статический анализ кода — не просто проверка синтаксиса. Это систематическое изучение исходного кода без его запуска, которое ловит баги на этапе разработки, когда они стоят дёшево. Динамический анализ смотрит на поведение программы во время выполнения. Вместе они дают полную картину.

Я видел, как одна неловкая переменная или забытый обработчик исключения разваливают production. И видел, как правильно настроенный анализ кода это предотвращает. Вот о чём будем говорить.

Что такое статический анализ кода и зачем он нужен

Статический анализ — это проверка текста программы без её запуска. Инструмент парсит код, строит граф вызовов, анализирует потоки данных и ищет паттерны, которые потенциально опасны или неэффективны.

По-хорошему, статический анализ должен ловить:

Когда я работал в стартапе, мы не использовали статический анализ первый год. Потом подключили SonarQube. Результат? На первом проходе нашли 47 потенциальных багов в коде, который уже месяц работает в боевых условиях. Семь из них были реальные уязвимости.

Главное преимущество — скорость и масштабируемость. Один проход статического анализатора проверит 100 тысяч строк кода за минуту. Человек — за несколько дней code review.

Синтаксический анализ кода vs глубокий анализ потоков данных

Тут нужно понимать разницу. Синтаксический анализ — это первый уровень. Парсер проверяет, валидна ли грамматика языка. Скобки сбалансированы? Переменные объявлены правильно? Это базовое.

# Синтаксическая ошибка — парсер сразу упадёт
def foo(x
    return x + 1

Глубокий анализ потоков данных — совсем другое. Инструмент отслеживает, откуда берутся данные, как они трансформируются, куда попадают. Вот пример:

def process_user_input(user_data):
    query = "SELECT * FROM users WHERE id = " + user_data  # Уязвимость!
    return db.execute(query)

Синтаксис здесь идеален. Но анализ потоков данных увидит: пользовательский ввод напрямую конкатенируется в SQL-запрос. Это SQL-injection.

На практике хороший статический анализатор работает в несколько фаз:

  1. Парсинг — построение абстрактного синтаксического дерева (AST)
  2. Символический анализ — построение таблицы символов, отслеживание типов
  3. Анализ потоков управления — какие пути выполнения возможны
  4. Анализ потоков данных — где живут данные, как они изменяются
  5. Применение правил — сопоставление с известными паттернами уязвимостей и ошибок

Первые три уровня — относительно дешёвые. Последние два — дорогие по времени, но дают глубокие результаты.

Динамический анализ кода: когда нужны инструменты для запущенной программы

Динамический анализ — это наблюдение за программой во время выполнения. Инструмент подключается к процессу (обычно через инструментирование кода или отладчик) и смотрит:

Динамический анализ ловит то, что статический может пропустить. Например, race condition или проблему, которая проявляется только при конкретном сочетании входных данных.

Инструменты динамического анализа кода:

Сравним на примере. Статический анализ скажет: "Здесь потенциально может быть null pointer". Динамический скажет: "На 47-й итерации цикла произошёл null pointer, вот стек вызовов".

Но динамический анализ требует:

На практике большинство команд используют оба подхода. Статический анализ в CI/CD на каждый коммит — быстро и дёшево. Динамический анализ на ночных прогонах и перед релизом — глубоко, но дорого по времени.

Статический анализ кода на примере конкретных инструментов

Давайте по конкретике. Вот самые используемые инструменты:

SonarQube — король в enterprise. Анализирует 30+ языков, ловит баги, уязвимости, техдолг. Есть cloud версия, есть self-hosted. Цена соответствующая.

# Конфиг SonarQube в CI/CD (GitHub Actions)
name: SonarQube Analysis
on: [push, pull_request]
jobs:
  sonarqube:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run SonarQube Scanner
        uses: SonarSource/sonarcloud-github-action@master
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

ESLint (JavaScript/TypeScript) — быстрый, простой, настраивается как угодно. Нашёл ошибку в переменной на этой неделе в проекте, где использовали только eslint.

// .eslintrc.json
{
  "extends": ["eslint:recommended"],
  "rules": {
    "no-unused-vars": "error",
    "no-undef": "error",
    "eqeqeq": "error",
    "no-console": "warn"
  }
}

Pylint (Python) — настраивается в /dev/null, но работает. Или используй Ruff — быстрее в 100 раз, меньше false positives.

# Установка и запуск Ruff
pip install ruff
ruff check --select=E,W,F .

Clippy (Rust) — встроен в cargo, предупреждает о неидиоматичном коде. Предотвратил не одну ошибку.

Bandit (Python) — специализируется на безопасности. Ловит hardcoded пароли, опасные функции (eval, pickle), уязвимости в криптографии.

bandit -r src/ -f json -o report.json

На проекте с Python я обычно использую pipeline: Ruff (быстрая проверка стиля) → Pylint (глубокий анализ) → Bandit (безопасность).

Как внедрить анализ кода в CI/CD: практический гайд

Теория — дело хорошее. Но как это работает в реальном пайплайне?

Вот типичная схема:

# .github/workflows/code-analysis.yml
name: Code Analysis Pipeline
on: [pull_request, push]

jobs:
  analyze:
    runs-on: ubuntu-latest
    steps:
      # 1. Статический анализ Python
      - uses: actions/checkout@v3
      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'
      
      - name: Install dependencies
        run: |
          pip install ruff pylint bandit safety
      
      - name: Run Ruff
        run: ruff check .
      
      - name: Run Pylint
        run: pylint src/ --exit-zero --output-format=json > pylint.json
      
      - name: Run Bandit (Security)
        run: bandit -r src/ -f json -o bandit.json
      
      - name: Check dependencies (Safety)
        run: safety check --json
      
      # 2. Статический анализ JavaScript (если есть)
      - name: Run ESLint
        run: npm run lint

      # 3. Отправить результаты в систему отчётности
      - name: Upload analysis results
        if: always()
        uses: actions/upload-artifact@v3
        with:
          name: analysis-reports
          path: |
            pylint.json
            bandit.json

Ключевой момент: анализ должен быть быстрым на каждом коммите. Если пайплайн будет ждать 15 минут, разработчики начнут его пропускать.

Поэтому стратегия такая:

Я видел команды, которые внедрили анализ кода и в первый месяц обнаружили сотни проблем. Потом настроили правила, и количество новых issues упало на 80%.

ГОСТ и стандарты: статический анализ кода по регламентам

Если ты пишешь для государственных структур, банков или критичной инфраструктуры, нужно соответствовать ГОСТ или другим стандартам.

ГОСТ 19.701-90 регулирует схемы программ. ГОСТ 34.973-90 — требования к качеству ПО. Для анализа кода это означает:

# Плохо: цикломатическая сложность = 8
def check_user(user):
    if user.age < 18:
        return False
    if user.status == 'banned':
        return False
    if user.balance < 0:
        return False
    if not user.email_verified:
        return False
    if user.country not in ALLOWED_COUNTRIES:
        return False
    if user.device not in TRUSTED_DEVICES:
        return False
    if user.last_login < CUTOFF_DATE:
        return False
    return True

# Хорошо: цикломатическая сложность = 2
def check_user(user):
    checks = [
        user.age >= 18,
        user.status != 'banned',
        user.balance >= 0,
        user.email_verified,
        user.country in ALLOWED_COUNTRIES,
        user.device in TRUSTED_DEVICES,
        user.last_login >= CUTOFF_DATE,
    ]
    return all(checks)

Инструменты для проверки ГОСТ-соответствия:

На практике большинство российских команд используют SonarQube с кастомными правилами под ГОСТ.

Сравнение подходов: статический vs динамический анализ

Вот честное сравнение:

Критерий Статический Динамический
Скорость Минуты Часы
Coverage 100% кода Зависит от тестов
False positives Могут быть Редко
Нужны тесты Нет Да
Ловит race conditions Сложно Хорошо
Ловит XSS Да Зависит
Ловит утечки памяти Теоретически Да
Стоимость инструмента От бесплатно до 50k$/год Обычно встроено

Правильный подход: используй оба. Статический как первый барьер на каждый PR, динамический как страховка перед релизом.

Как инструменты анализа работают в реальной работе

За три года в Яндексе я видел, как анализ кода становится частью культуры. Вот что работает:

  1. Настрой strict rules с самого начала. Если разрешить все warnings, потом от них не избавишься.

  2. Не игнорируй issues навалом. Если есть 500 warnings, разработчики их просто не будут читать.

  3. Покажи результаты в PR-интерфейсе

Попробуйте Distiq для автоматического code review

AI-бот анализирует каждый MR/PR и оставляет комментарии с замечаниями. Интеграция за 2 минуты.

Попробовать бесплатно

Похожие статьи