Найти ошибки в коде java

Вчера всё работало, а сегодня не работает / Код не работает как задумано

или

Debugging (Отладка)


В чем заключается процесс отладки? Что это такое?

Процесс отладки состоит в том, что мы останавливаем выполнения скрипта в любом месте, смотрим, что находится в переменных, в функциях, анализируем и переходим в другие места; ищем те места, где поведение отклоняется от правильного.

Заметка: Отладка производится как правило в IDE (Интегрированная среда разработки). Что это такое можно чуть подробнее ознакомиться в вопросе

Какие есть способы предупреждения ошибок, их нахождения и устранения?

В данном случае будет рассмотрен пример с Intellij IDEA, но отладить код можно и в любой другой IDE.


Подготовка

Достаточно иметь в наличии IDE, например Intellij IDEA

Запуск

Для начала в левой части панели с кодом на любой строке можно кликнуть ЛКМ, тем самым поставив точку останова (breakpoint — брейкпойнт). Это то место, где отладчик автоматически остановит выполнение Java, как только до него дойдёт. Количество breakpoint’ов не ограничено. Можно ставить везде и много.

введите сюда описание изображения

Отладка запускается сочетанием Shift+F9 или выбором в верхнем меню RunDebug или нажатием зеленого «жучка»:

введите сюда описание изображения

В данном случае, т.к. функция вызывается сразу на той же странице, то при нажатии кнопки Debug — отладчик моментально вызовет метод, выполнение «заморозится» на первом же брейкпойнте. В ином случае, для активации требуется исполнить действие, при котором произойдет исполнение нужного участка кода (клик на кнопку в UI, передача POST запроса с данными и прочие другие действия)

введите сюда описание изображения

Цифрами обозначены:

  1. Стэк вызовов, все вложенные вызовы, которые привели к текущему месту кода.
  2. Переменные. На текущий момент строки ниже номера 24 ещё не выполнилась, поэтому определена лишь data и numsStringArr
  3. Показывает текущие значения любых переменных и выражений. В любой момент здесь можно нажать на +, вписать имя любой переменной и посмотреть её значение в реальном времени. Например data или nums[0], а можно и nums[i] и item.test.data.name[5].info[key[1]] и т.д. На текущий момент строки ниже номера 24 ещё не выполнилась, поэтому sum и output во вкладке Watchers обозначены красным цветом с надписью «cannot find local variable».

Процесс

Для самого процесса используются элементы управления (см. изображение выше, выделено зеленым прямоугольником) и немного из дополнительно (см. изображение выше, выделено оранжевым прямоугольником)

введите сюда описание изображения

Show Execution Point (Alt+F10) — переносит в файл и текущую линию отлаживаемого скрипта. Например если файлов много, решили посмотреть что в других вкладках, а потом забыли где у вас отладка :)

Step Over (F8) — делает один шаг не заходя внутрь функции. Т.е. если на текущей линии есть какая-то функция, а не просто переменная со значением, то при клике данной кнопки, отладчик не будет заходить внутрь неё.

Step Into (F7) — делает шаг. Но в отличие от предыдущей, если есть вложенный вызов (например функция), то заходит внутрь неё.

Step Out (Shift+F8) — выполняет команды до завершения текущей функции. Удобна, если случайно вошли во вложенный вызов и нужно быстро из него выйти, не завершая при этом отладку.

Rerun (Ctrl+F5) — Перезапустить отладку

Resume Program(F9) — Продолжает выполнения скрипта с текущего момента. Если больше нет других точек останова, то отладка заканчивается и скрипт продолжает работу. В ином случае работа прерывается на следующей точке останова.

Stop (Ctrl+F2) — Завершить отладку

View Breakpoints (Ctrl+Shift+F8) — Посмотреть все установленные брейкпойнты

Mute Breakpoints — Отключить брейкпойнты.

Итак, в текущем коде видно значение входного параметра:

  • data = "23 24 11 18" — строка с данными через пробел
  • numsStringArr = {"23", "24", "11", "18"} — массив строк, который получился из входной переменной.

введите сюда описание изображения

Если нажмем F8 2 раза, то окажемся на строке 27; во вкладках Watches и Variables и в самой странице с кодом увидим, что переменная sum была инициализирована и значение равно 0, а также nums инициализирована и в ней лежит массив целых чисел {23, 24, 11, 18} .

Если теперь нажмем F8, то попадем внутрь цикла for и нажимая теперь F8 пока не окончится цикл, можно будет наблюдать на каждой итерации, как значение num и sum постоянно изменяются. Тем самым мы можем проследить шаг за шагом весь процесс изменения любых переменных и значений на любом этапе, который интересует.

Дальнейшие нажатия F8 переместит линию кода на строки 31, 32 и, наконец, 36.


Дополнительно

Если нажать на View Breakpoints в левой панели, то можно не только посмотреть все брейкпойнты, но в появившемся окно можно еще более тонко настроить условие, при котором на данной отметке надо остановиться. В методе выше, например, нужно остановиться только когда sum превысит значение 20.

введите сюда описание изображения

Это удобно, если останов нужен только при определённом значении, а не всегда (особенно в случае с циклами).

Больше информации об отладке можно посмотреть в http://learn.javajoy.net/debug-intellij-idea, а также в официальной документации к IDE

How to use the free code checker

Code

Copy and paste your Java code into the editor.

Language

Select your language from the dropdown.

Check

Click the Check code button.

Improve

Use the results to improve your Java code.

Get code security right from your IDE

This free code checker can find critical vulnerabilities and security issues with a click. To take your application security to the next level, we recommend using Snyk Code for free right from your IDE.

This free web based Java code checker is powered by Snyk Code. Sign up now to get access to all the features including vulnerability alerts, real time scan results, and actionable fix advice within your IDE.

Human-in-the-Loop JavaScript Code Checker

Snyk Code is an expert-curated, AI-powered JavaScript code checker that analyzes your code for security issues, providing actionable advice directly from your IDE to help you fix vulnerabilities quickly.

Real-time

Scan and fix source code in minutes.

Actionable

Fix vulns with dev friendly remediation.

Integrated in IDE

Find vulns early to save time & money.

Ecosystems

Integrates into existing workflow.

More than syntax errors

Comprehensive semantic analysis.

AI powered by people

Modern ML directed by security experts.

In-workflow testing

Automatically scan every PR and repo.

CI/CD security gate

Integrate scans into the build process.

Frequently asked questions

Интенсивность разработки ПО с каждым годом становится всё выше. И растёт необходимость в различных помощниках, которые поддерживают качество программного кода на высоком уровне.

К таким помощникам можно отнести статические анализаторы кода. Они могут найти и исправить проблемный код (баги, опечатки, уязвимости) на самых ранних этапах разработки. В данном обзоре мы кратко рассмотрим популярные статические анализаторы для Java-кода.

Чуть подробней о статическом анализе

Старый добрый Code Review — незаменимый помощник, который пользуется большим успехом по сей день. Но от того количества кода, который ревьюверам приходится просматривать и обдумывать, становится страшно. На это уходит очень много времени и сил. Поэтому в большинстве случаев должное внимание уделяют только критичному с точки зрения работы приложения коду.

Необходимо снять с ревьюверов нагрузку и при этом не потерять в качестве кодовой базы. На помощь приходят инструменты статического анализа кода. Они, в паре с Code Review (а также другими методологиями), будут подстраховывать друг друга. И держать качество продукта на достойном уровне. По сути, статический анализ можно рассматривать как автоматизированный Code Review.

Вся суть статических анализаторов в том, что на вход подают исходный код/байт-код. А на выходе получают отчёт с предупреждениями. Чтобы выявить потенциальные проблемы, исходный код преобразуется во внутреннее представление анализатора — как правило, AST с семантической информацией — с дальнейшим применением различных методологий для извлечения всей необходимой информации.

Встроенный анализатор кода в IntelliJ IDEA

Статический анализатор Java-кода, встроенный в IntelliJ IDEA, ничуть не уступает специализированным инструментам статического анализа. Поиском подозрительного, неоптимального или ошибочного кода занимаются инспекции. Они используют различные современные методологии статического анализа: анализ потока данных, сопоставление с шаблоном.

IntelliJ IDEA насчитывает большое количество инспекций. По правде говоря, множество из них сообщают не об ошибке, а скорее, о неаккуратности в коде или о возможности замены на более красивую/идиоматическую альтернативу.

Немного изучив ‘Inspections > Java’, я пришёл к выводу, что инспекции из категорий Probably Bugs, Numeric Issues, Serialization Issues с большей вероятностью обнаруживают реальные баги в коде. Во всяком случае, стоит самим пробежаться по всем инспекциям. И определить те, которые будут полезны именно вам и специфичны для вашего проекта.

При использовании IntelliJ IDEA множество ошибок можно исправлять за секунды после того, как их допустили. Так как статический анализ проводится постоянно в режиме редактирования кода. Код с обнаруженными дефектами подсвечивается в редакторе.

Это очень удобно и круто! А комбинация ‘Alt + Enter’ на подсвеченном фрагменте кода позволяет вызвать контекстное меню. Через него можно мгновенно исправить код, выбрав один из предложенных вариантов (если лень исправлять вручную):

Помимо этого, можно узнать причину срабатывания инспекции, что в некоторых случаях сокращает время на поиски.

Также анализ можно запустить вручную ‘Analyze > Inspect Code’, либо запустить отдельную инспекцию с помощью ‘Analyze > Run Inspection by Name’, указав перед этим область действия анализа (например, на проект, модуль или только на файл). При этом запуске становятся доступны некоторые инспекции, которые из-за трудоёмкости не работают в режиме редактирования.

После анализа результаты будут сгруппированы по категориямдиректориям в отдельном окне. Из него можно перейти к конкретному срабатыванию инспекции:

IntelliJ позволяет сохранить результат анализа только в форматах XML и HTML. Но удобно работать с обнаруженными проблемами можно только в самой IDE (по моему мнению).

Большинство возможностей статического анализатора доступны в IntelliJ IDEA Community Edition (бесплатной версии).

SonarJava

SonarJava — статический анализатор кода для Java от компании SonarSource, которая позиционирует его лучшим инструментом среди конкурентов.

Для построения AST модели, наполненной семантической информацией, SonarJava на вход принимает исходный код файла. Если код использует внешние зависимости (а они, как правило, всегда есть), то для полного построения модели также необходимо передавать байт-код зависимостей. Но за это не стоит переживать, если вы интегрировали анализатор в сборочную систему (например, Gradle, Maven). Она сделает всё за вас.

Далее к уже построенной модели применяются современные методологии статического анализа (анализ потока данных, символьное выполнение, сопоставление с шаблоном). Они обнаруживают ошибки, запахи кода и уязвимости.

Особенности:

  • 150+ правил обнаружения ошибок;
  • 350+ правил обнаружения запахов кода;
  • 40+ правил обнаружения потенциальных уязвимостей;
  • интеграция с Maven, Gradle, Ant, Eclipse, IntelliJ IDEA, VS Сode;
  • возможность расширения пользовательскими диагностическими правилами;
  • SAST-специализированный инструмент: большинство диагностических правил сопоставлены CWE, CERT, OWASP.

Анализ можно запустить как в рамках различных IDE (через плагин SonarLint), так и в рамках SonarQube.

В IntelliJ IDEA SonarLint, аналогично встроенному анализатору кода, обнаруживает подозрительный/ошибочный код в режиме редактирования кода с возможностью ручного запуска на всех файлах. SonarLint работает бок о бок со встроенным анализатором кода IntelliJ IDEA. Поэтому, если навести курсор на подсвеченный фрагмент кода, то частенько можно увидеть предупреждения обоих анализаторов:

Разумеется, просматривать предупреждения можно и в отдельном окошке:

Например, в VS Code результаты работы плагина выглядят следующим образом:


Возможность запускать SonarJava разными способами делает его очень привлекательным. Это развязывает руки разработчикам в выборе инструментария при написании кода.

Большую часть срабатываний анализатора занимают предупреждения категории ‘Code Smells’, которые лишь сигнализируют о признаках проблемного кода. Большинство этих предупреждений вы проигнорируете (так нехорошо делать, но, думаю, так и будет). Поэтому, чтобы не утонуть в этих предупреждениях, перед анализом советую пройтись по списку и оставить только интересующие вас.

В рамках этой статьи платформа SonarQube не будет рассматриваться, но если вы серьёзно зададитесь вопросом качества кода, то обязательно обратите внимание на эту платформу. Она позволяет аккумулировать отчёты сторонних анализаторов в одном месте, предоставляя удобный web-интерфейс для непрерывного контроля качества кода с различными дополнительными возможностями. Что немаловажно, есть бесплатная версия SonarQube Community.

FindBugs/SpotBugs

FindBugs, к большому сожалению, давно ушёл в закат, так как последний стабильный релиз выходил в далёком 2015 году. Но его не стоит забывать по той причине, что это, пожалуй, наиболее известный бесплатный статический анализатор Java. Если спросить у Java-разработчика про статический анализ, то ему, скорее всего, сразу же на ум придёт FindBugs. Его отличительная черта от большинства рассмотренных ранее анализаторов в том, что он анализирует не исходный код, а байт-код.

Вслед за FindBugs эстафету принял его преемник – open-source анализатор SpotBugs. Скорее всего, все преимущества и недостатки FindBugs так же плавно перетекли в преемника, но хорошо этоили плохо — покажет время. А пока анализатор активно развивается своим сообществом. Как доказательство:

  • 400+ правил обнаружения ошибок;
  • интеграция в Ant, Maven, Gradle, Eclipse, IntelliJ IDEA;
  • возможность расширения пользовательскими диагностическими правилами.

Для поиска подозрительного кода используются всё те же самые методологии: сопоставление с шаблоном, анализ потока данных. Анализатор может обнаружить различные типы ошибок, связанные с многопоточностью, производительностью, уязвимостями, запутыванием кода и прочее.

В IntelliJ IDEA окно с предупреждениями выглядит следующим образом:

Предупреждения можно группировать по категориям, классам, директориям и по уровню достоверности. Одновременно с предупреждениями можно просматривать документацию на диагностическое правило.

Анализ запускается вручную, но после анализа все найденные проблемные участки кода подсвечиваются наравне с другими предупреждениями от IntelliJ IDEA и SonarLint. Есть один нюанс. Чтобы все предупреждения (подсветка проблемного кода) актуализировались с учётом всех ваших изменений в файле, нужно перезапустить анализ. Также много рекомендательных срабатываний, поэтому перед активным использованием анализатор необходимо сконфигурировать.

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

PVS-Studio

В основе PVS-Studio лежит open-source библиотека Spoon. Она принимает на вход исходный код и генерирует хорошо спроектированную AST-модель с семантической информацией. На основе полученной модели применяются передовые методологии статического анализа:

  • анализ потока данных (Data-Flow Analysis);
  • символьное выполнение (Symbolic Execution);
  • аннотирование методов (Method Annotations);
  • сопоставление с шаблоном (Pattern-based analysis).

На данный момент анализатор оперирует более чем 105 диагностическими правилами, которые позволяют обнаруживать различные недостатки/ошибки в коде: опечатки, разыменование нулевой ссылки, недостижимый код, выход за границу массива, нарушение контракта использования метода и так далее. О всех возможностях диагностических правил можно посмотреть здесь.

Особенности:

  • ориентирован на поиск реальных ошибок;
  • помимо CLI версии анализатора также есть интеграция в IntelliJ IDEA, Maven, Gradle, Jenkins, SonarQube;
  • возможность запуска анализа в инкрементальном режиме;
  • позволяет выявлять потенциальные проблемы совместимости Java SE API при миграции проекта с Java 8 на более свежие версии;
  • PVS-Studio позволяет конвертировать отчёт в различные удобные для использования форматы: json, xml, html, txt;
  • SAST-специализированный инструмент: большинство диагностических правил сопоставлены CWE, CERT, OWASP.

Как обычно, рассмотрю использование анализатора в IntelliJ IDEA.

К сожалению, запустить анализ кода можно только отдельным шагом. Либо вручную, либо автоматически после сборки проекта. Впрочем, благодаря этому выполняется более глубокий анализ, не ограниченный как временем, так и потребляемой памятью. Это позволяет обнаруживать более сложные паттерны ошибок.

Все предупреждения анализатора сводятся в специальную таблицу, в которой можно удобно с ними работать (сортировать, фильтровать, помечать как False Alarm):

PVS-Studio проприетарное ПО, но его также можно использовать бесплатно. Конкурентное преимущество анализатора — быстрая и качественная поддержка, оказываемая непосредственно разработчиками анализатора.

PMD

PMD – open-source статический анализатор, позволяющий обнаруживать общие ошибки программирования: неиспользуемые переменные, пустые блоки, создание ненужных объектов и так далее.

Анализатор использует в качестве входных данных исходный код. В настоящее время PMD анализирует один исходный файл за раз, что накладывает ограничения на полноту анализа. Поэтому, если взглянуть на дорожную карту, там упоминается о желании получать больше межклассовой информации, что позволит реализовать гораздо больше диагностических правил.

Несмотря на то, что анализируется исходный код, советуют собирать проект. Это позволит также извлекать информацию об используемых типах в анализируемом коде.

Особенности:

  • умеет интегрироваться во многие IDE (IntelliJ IDEA, Eclipse, NetBeans и так далее) и сборочные системы (Maven, Gradle, Ant);
  • большое разнообразие доступных форматов отчёта анализатора: sarif, csv, ideaj, json, text(default), xml, html, textcolor и так далее;
  • 300+ паттернов диагностических правил. Категории: стиль кодирования, лучшие практики, ошибки, многопоточность, производительность и так далее;
  • вместе с PMD поставляется CPD (copy-paste detector), который позволяет находить дубликаты в коде.

Если взвесить все диагностические правила, то в большей степени PMD ориентирован на решение проблем стиля кодирования и поиск простых очевидных ошибок. Диагностические правила могут противоречить друг другу, поэтому перед началом использования нужно уделить время конфигурированию.

Для IntelliJ IDEA также есть плагин, через который можно запускать анализ с различными наборами правил, но выбирать отдельные файлы для анализа нельзя. Окно с предупреждениями выглядит следующим образом:

По моему мнению, работать с предупреждениями не совсем удобно из-за невозможности их группировки по файлам и неочевидным сообщениям (если только наводить курсор на предупреждение).

Подводим итоги

Конечно же, помимо рассмотренных анализаторов есть и другие: как платные (Coverity, Klockwork, JArchitect и так далее), так и бесплатные (Error Prone, Infer, Checkstyle и так далее). Все они ориентированы на одно: предотвратить попадание ошибочного или потенциально ошибочного кода в production.

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

Немаловажную роль в выборе статического анализатора играют:

  • интеграция в различные IDE;
  • интеграция в сборочные системы;
  • удобство запуска анализатора на сервере;
  • возможность обнаружения ошибок в режиме редактирования кода;
  • возможность удобной работы с предупреждениями;
  • SAST-ориентированность;
  • процент ложных срабатываний;
  • сложность конфигурирования;

Совокупность всех ‘за’ и ‘против’ невольно приведут вас к ряду статических анализаторов, которые вы будете считать лучшими.

Приводил примеры интеграции с IntelliJ IDEA, так как часто ею пользуюсь. Поэтому для меня интеграция с этой IDE была приоритетна при обзоре.

Заключение

Мы с вами рассмотрели инструментарий, который поможет быстро обнаружить и исправить проблемный код до того, как он попадёт в ‘production’. На сегодняшний день таких инструментов достаточно много и каждый имеет свои слабые и сильные стороны.

С учётом этого останавливаться на каком-то одном анализаторе — не совсем верное решение. Чтобы создать достойный барьер от багов и уязвимостей, советую вам всё-таки использовать комплексную защиту из нескольких анализаторов, так как они будут прекрасно взаимодополнять друг друга. Так что выбор за вами!

Debugging в переводе с английского означает «Отладка». Она необходима в программировании Java, чтоб отслеживать логические ошибки в коде. И инструменты NetBeans позволяют это сделать.

debugging java

Логические ошибки — это ошибки программиста, когда код работает не так, как вы ожидали. Их может быть трудно отследить. К счастью, в NetBeans есть несколько встроенных инструментов, которые помогут вам найти решение этой проблемы.

Для начала изучите этот код:

package errors;

public class ErrorsHand {

    public static void main(String[] args) {
        int LetterCount = 0;
        String check_word = "Debugging";
        String single_letter = "";
        int i;
        
        for (i = 0; i < check_word.length(); i++) {
            
            single_letter = check_word.substring(1, 1);
            
            if (single_letter.equals("g")) {
                LetterCount++;
            }
        }
        System.out.println("G было найдено " + LetterCount + " раз.");
    }
    
}

Введите этот код либо в проект, который у вас уже есть, либо запустив новый. В этом коде мы пытаемся подсчитать, сколько раз встречается буква «g» в слове «Debugging». Ответ очевиден 3. Однако, когда вы запускаете программу, в окне «Вывод» выведется:

вывод с ошибкой

Итак, мы сделали где-то в нашем коде ошибку. Но где? Java-программа выполняется нормально и не выдает никаких исключений для изучения в окне вывода. Так что делать?

Чтобы отследить проблемы с вашим кодом, NetBeans позволяет вам добавить нечто, называемое точкой остановки (Breakpoint).

Чтоб добавить новую точку остановки, нажмите в поле нумерации строк в NetBeans окна кода напротив той строки, которую хотите проверить:

Точка остановки в Java

В меню NetBeans выберите Debug > Debug Имя Вашего Проекта:

Debug Netbeans

NetBeans перейдет к точке остановки и строка станет зеленого цвета. Сейчас он остановил выполнение кода. Также вы должны увидеть новую панель инструментов:

Панель инструментов Debug Netbeans

Первые три кнопки позволяют остановить сеанс debugging, приостановить и продолжить. Следующие три кнопки позволяют перейти по коду для debugging, дальше четвертая — выйти и последняя — перейти к курсору.

Вы можете нажать клавишу F5 и продолжить. Код должен работать как обычно с точкой остановки. Сеанс debugging завершится.

Когда сеанс debugging завершится, нажмите на точку остановки, чтобы избавиться от нее. Теперь добавьте точку остановки в цикл for:

Breakpoint Netbeans

Теперь нажмите Debug > New Watch. Watch позволяет отслеживать, что находится в переменной. Введите букву i в диалоговом окне Watch и нажмите OK:

Debug Watch

Добавьте еще один Watch и введите single_letter. Нажмите ОК. Добавьте третий Watch и введите LetterCount. Вы должны увидеть эти три переменные внизу экрана:

Debug Watches

Теперь нажмите значок Step Into на панели инструментов:

Панель инструментов Debug Step Into

Или просто нажмите клавишу F7 на клавиатуре. Продолжайте нажимать F7 и наблюдайте, что происходит в окне Watch.

Вы обнаружите, что переменная i каждый раз увеличивается на 1. Но две остальные переменные остаются прежними:

Debug Watches i

Поскольку в переменной single_letter ничего нет, то LetterCount не может выходить за пределы нуля. Итак, мы нашли нашу проблему — использование substring является неправильным, так как оно не захватывает символы, указанные в круглых скобках.

Остановите сеанс debugging и измените строку с substring следующим образом:

single_letter = check_word.substring(i, i + 1);

Теперь снова запустите сеанс debugging. Продолжайте нажимать клавишу F7, чтобы пройти по каждой строке цикла for. На этот раз вы должны увидеть изменения переменных sinle_letter и LetterCount.

Запустите Java-программу, и вы увидите в окне «Вывод»:

Debugging вывод

Теперь у нас правильный ответ.

То есть, если ваш код работает не так, как планировалось, попробуйте установить точку остановки и несколько Watches для своих переменных. И начните сеанс debugging.

В следующем разделе мы рассмотрим совсем другую тему: как открывать текстовые файлы в Java.

Время на прочтение
9 мин

Количество просмотров 12K

Привет, Хабр!

Сегодня я хочу затронуть тему, которая будет полезна как Java-разработчикам, так и начинающим тех- и тимлидам. Я расскажу о том, как добиться высокого качества кода на вашем Java проекте и перестать волноваться о стилях кодирования.

Когда-то (очень давно, на четвёртом-пятом годах карьеры) я носил лычки «проектировщика программного обеспечения» и готовил многостраничные гайды по оформлению кода, правильных инструментах и библиотеках. Это были красивые всеобъемлющие документы, с прочтения которых начинался онбординг любого разработчика на проекте. А затем я тщательно следил за соблюдением этих правил на этапе code review. И знаете, сейчас я понимаю, что это было полное абсолютное стопроцентное дерьмо.

Если вы идёте по пути подготовки развесистых страничек на wiki по стилям кодирования и правилам оформления кода, то это дурно пахнет. Есть другой более надёжный способ, как защитить вашу кодовую базу и добиться полного соблюдения всех принятых стандартов и соглашений. И это, конечно же, статический анализ кода.

Далее я покажу своё видение того, какие инструменты и в какой конфигурации должны применяться на Java проектах (а особенно в микросервисах), но сначала немного вводной информации.

Зачем вообще следить за качеством кода?

Мы занимаемся разработкой программных продуктов. Эти продукты должны быть достаточно качественными, чтобы заказчики продолжали платить нам деньги, а наши пользователи продолжали их использовать.

Качество — комплексная оценка продукта. Это не только количество ошибок в продукте, но также скорость работы, возможность масштабирования под растущую нагрузку, скорость выведения на рынок новых возможностей и т.п.

Я глубоко убеждён, что качество программного продукта неразрывно связано с качеством программного кода: продукт не может быть качественным, если его код — отстой.

Если код плохо написан, если он плохо структурирован или плохо отформатирован, то его будет сложно читать. Сложно — значит долго. Разработчики будут тратить лишнее время на работу с таким кодом, что в свою очередь будет снижать скорость вывода новых возможностей на рынок, а значит и качество итогового продукта. Плохой код демотивирует хороших разработчиков. А ещё на старый технологический стек трудно нанимать людей.

Зачем нужен архитектор или техлид?

На каждом проекте должен быть человек, играющий роль архитектора или технического лидера. Этот человек несёт персональную ответственность за технические решения, принимаемые на проекте, а также следит за качеством кода. Делает он это посредством CI пайплайна, который нужно настроить на максимально тщательную проверку программного кода. Весь код, который не соответствует принятым критериям качества, должен отвергаться и никогда не должен попасть в основную ветку разработки (master/development).

Какие проверки кода могут быть?

Проверяться могут любые аспекты кода. В целом, чем больше проверок, тем лучше.

Все проверки можно разделить на две категории — автоматические и экспертные. Экспертные — это code review. Это самые медленные, дорогие и ненадёжные проверки, которые только могут быть, но, тем не менее, они должны быть.

Автоматические проверки включают в себя:

  • компиляцию кода;

  • выполнение тестов;

  • статический анализ;

  • проверку на уязвимости и т.д.

Зачем нужен Checkstyle и EditorConfig?

Одна из распространенных проблем, встречающаяся особенно среди опытных разработчиков — это вкусовщина. Люди привыкают к определённым стилям написания и оформления кода и начинают переносить их с одного места работы на другое. Очень часто возникают разногласия между разработчиками по принципам форматирования кода.

Checkstyle позволяет зафиксировать и отслеживать во время сборки проекта соответствие кода принятым стандартам оформления. Больше не будет споров по типу: табуляция или пробелы; 2 или 4 пробела для отступа и т.д. За этим будет следить Checkstyle.

Чтобы разработчикам было проще следовать стандартам оформления кода, необходимо в каждом проекте иметь сконфигурированный файл EditorConfig. Современные IDE умеют его подтягивать и брать из него настройки.

Checkstyle работает на основе построения AST и содержит очень много проверок. Одни из моих любимых: RedundantModifier, SingleSpaceSeparator, EmptyLineSeparator и IllegalImport. Пример конфига, который я использую для своих проектов, можно найти на GitHub.

Основная рекомендация по внедрению: начинайте с небольшого количества проверок; подбирайте их под ваш проект и стиль, постепенно добавляя новые проверки, до тех пор, пока не попробуете все. Checkstyle должен обязательно проверять код ваших тестов и ронять сборку, если что-то нашёл.

Пример конфигурации для Gradle:

plugins {
    id 'checkstyle'
}
test {
    useJUnitPlatform()
    dependsOn checkstyleMain, checkstyleTest 
}
checkstyle {
    toolVersion '10.3.1'
    configFile file("config/checkstyle/checkstyle.xml")
    ignoreFailures = false
    maxWarnings = 0
    maxErrors = 0
}
checkstyleMain {
    source ='src/main/java'
}
checkstyleTest {
    source ='src/test/java'
}

Пример конфигурации для Maven:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-checkstyle-plugin</artifactId>
    <dependencies>
        <dependency>
            <groupId>com.puppycrawl.tools</groupId>
            <artifactId>checkstyle</artifactId>
            <version>10.3.1</version>
        </dependency>
        <dependency>
            <groupId>com.thomasjensen.checkstyle.addons</groupId>
            <artifactId>checkstyle-addons</artifactId>
            <version>6.0.1</version>
        </dependency>
    </dependencies>
    <configuration>
        <encoding>UTF-8</encoding>
        <consoleOutput>true</consoleOutput>
        <violationSeverity>warning</violationSeverity>
        <failOnViolation>true</failOnViolation>
        <failsOnError>true</failsOnError>
        <linkXRef>false</linkXRef>
        <includeTestSourceDirectory>true</includeTestSourceDirectory>
        <sourceDirectories>
            <directory>${project.build.sourceDirectory}</directory>
            <directory>${project.build.testSourceDirectory}</directory>
        </sourceDirectories>
        <checkstyleRules>
           ...
           <!-- Правила можно описать прямо в parent pom. Удобно для многомодульных проектов -->
        </checkstyleRules>
    </configuration>
    <executions>
        <execution>
            <id>check</id>
            <phase>validate</phase>
            <goals>
                <goal>check</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Зачем нужен PMD?

PMD — это ещё один статический анализатор, гармонично дополняющий Checkstyle. Он также строит AST, но помимо этого использует байт-код, поэтому его нужно запускать после компиляции проекта. А ещё PMD позволяет достаточно легко добавлять свои собственные проверки. Подробнее об этом можно почитать в статье от Wrike.

Моя любимая проверка — JUnitTestsShouldIncludeAssert. Я о ней недавно рассказывал в статье про AssertJ; почитайте, если ещё нет.

В PMD все проверки объединены в группы, что на мой взгляд удобно, но некоторые вещи приходится сразу убирать. Пример моей конфигурации тут.

К Gradle проекту PMD можно подключить так:

plugins {
    id 'pmd'
}
test {
    useJUnitPlatform()
    dependsOn pmdMain, pmdTest
}
pmd {
    consoleOutput = true
    toolVersion = "6.47.0"
    ruleSetFiles = files("config/pmd/pmd.xml") // Исключения только через внешний файл
    ruleSets = []
}

Maven плагин также не позволяет настраивать исключения прямо в parent pom, поэтому приходится использовать файл с конфигом:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-pmd-plugin</artifactId>
    <configuration>
        <failOnViolation>true</failOnViolation>
        <includeTests>true</includeTests>
        <linkXRef>false</linkXRef>
        <printFailingErrors>true</printFailingErrors>
        <rulesets>
            <ruleset>config/pmd/pmd.xml</ruleset> <!-- Есть нюансы для многомодульных проектов -->
        </rulesets>
    </configuration>
    <executions>
        <execution>
            <id>pmd-check</id>
            <phase>test</phase> <!-- Желательно наличие байт-кода; см. https://maven.apache.org/plugins/maven-pmd-plugin/faq.html -->
            <goals>
                <goal>check</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Зачем нужен SpotBugs?

SpotBugs — тоже статический анализатор, но ориентированный больше на поиск ошибок в байт-коде. Он является преемником FindBugs и имеет свыше 400 эвристик, на основании которых работает. Он может обнаруживать потенциальные NPE или игнорирование возвращаемого методом значения (смотрите статью про AssertJ, которую я упоминал ранее).

Из недостатков стоит отметить, что SpotBugs достаточно параноидален и часто жалуется на вещи, которые мы не хотим исправлять (много false positive по отдельным правилам). Такие эвристики можно проигнорировать, используя excludeFilterFile.

Основной секрет правильного использования SpotBugs на проекте состоит в том, что порог фильтрации ошибок должен быть выставлен в Low: так вы не пропустите действительно важных предупреждений.

Конфигурация для Gradle:

plugins {
    id 'com.github.spotbugs' version '5.0.9'
}
spotbugs {
    showProgress = true
    effort = 'max'
    reportLevel = 'low'
    excludeFilter = file("config/spotbugs/exclude.xml")
}

Maven вариант конфига:

<plugin>
    <groupId>com.github.spotbugs</groupId>
    <artifactId>spotbugs-maven-plugin</artifactId>
    <version>4.5.3</version>
    <configuration>
        <includeTests>true</includeTests>
        <effort>Max</effort>
        <threshold>Low</threshold>
        <xmlOutput>false</xmlOutput>
        <excludeFilterFile>spotbugs-exclude.xml</excludeFilterFile>
    </configuration>
    <executions>
        <execution>
            <id>check</id>
            <phase>test</phase>
            <goals>
                <goal>check</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Почему бы не использовать только SonarQube?

На многих проектах (особенно в «кровавом энтерпрайзе») уже может использоваться SonarQube. И тут возникает резонный вопрос: не достаточно ли только его?

На мой взгляд, нет. Не существует никакой серебряной пули в разработке ПО, которая бы решила все проблемы с качеством кода. Sonar не исключение: он большой и сложный; проверки выполняются долго, а ещё конфигурация правил находится вне репозитория проекта.

Признаюсь честно, я не поклонник Sonar’а. И тем не менее, на всех своих проектах я стараюсь использовать SonarQube, но при этом предпочитаю рассматривать его как последнюю линию обороны.

Зачем JaCoCo в проекте?

Помимо статического анализа кода в каждом проекте должны быть модульные и интеграционные тесты. Они также должны запускаться в CI пайплайне и ронять сборку при обнаружении проблем. Важно понимать, что тестируемый код значительно отличается от «просто хорошего кода, который работает».

Критически важно фиксировать и контролировать минимальное покрытие кода тестами.
 Я для этого предпочитаю использовать библиотеку JaCoCo: она уронит сборку, если покрытие кода станет ниже зафиксированного порога.

Пример настройки правил для Gradle:

plugins {
    id 'jacoco'
}
test {
    useJUnitPlatform()
    finalizedBy jacocoTestReport
    finalizedBy jacocoTestCoverageVerification
}
jacocoTestReport {
    reports {
        html.enabled true
    }
}
jacocoTestCoverageVerification {
    dependsOn test
    violationRules {
        rule {
            limit {
                counter = 'CLASS'
                value = 'MISSEDCOUNT'
                maximum = 0
            }
        }
        rule {
            limit {
                counter = 'METHOD'
                value = 'MISSEDCOUNT'
                maximum = 0
            }
        }
        rule {
            limit {
                counter = 'LINE'
                value = 'MISSEDCOUNT'
                maximum = 0
            }
        }
        rule {
            limit {
                counter = 'INSTRUCTION'
                value = 'COVEREDRATIO'
                minimum = 1.0
            }
        }
    }
}
check.dependsOn jacocoTestReport, jacocoTestCoverageVerification

И аналогичные правила для Maven:

<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.8</version>
    <configuration>
        <excludes>
            <!-- excludes generated code -->
            <exclude>**/*MapperImpl.class</exclude>
        </excludes>
    </configuration>
    <executions>
        <execution>
            <id>prepare-agent</id>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
        </execution>
        <execution>
            <id>report</id>
            <phase>test</phase>
            <goals>
                <goal>report</goal>
            </goals>
        </execution>
        <execution>
            <id>check-minimal</id>
            <phase>package</phase>
            <goals>
                <goal>check</goal>
            </goals>
            <configuration>
                <rules>
                    <rule>
                        <element>BUNDLE</element>
                        <limits>
                            <limit>
                                <counter>INSTRUCTION</counter>
                                <value>COVEREDRATIO</value>
                                <minimum>0.80</minimum>
                            </limit>
                            <limit>
                                <counter>CLASS</counter>
                                <value>MISSEDCOUNT</value>
                                <maximum>0</maximum>
                            </limit>
                            <limit>
                                <counter>METHOD</counter>
                                <value>MISSEDCOUNT</value>
                                <maximum>0</maximum>
                            </limit>
                            <limit>
                                <counter>LINE</counter>
                                <value>MISSEDCOUNT</value>
                                <maximum>0</maximum>
                            </limit>
                        </limits>
                    </rule>
                </rules>
            </configuration>
        </execution>
    </executions>
</plugin>

А какие проверки можно использовать ещё?

CI пайплайн не ограничивается только статическим анализом кода и тестами. Можно сканировать зависимости на предмет уязвимостей. Если вы собираете Docker-образы, то можно проверять их, используя разнообразные линтеры.

Если вы используете БД, то можно (и нужно!) проверять её структуру и код ваших миграций на соответствие лучшим практикам и типовые ошибки. У меня есть такой инструмент — pg-index-health — набор проверок для PostgreSQL. Очень рекомендую.

Ну, и напоследок

При внедрении статического анализа на проекте ни в коем случае не забывайте про элементарную психологию. Какие бы классные ребята с вами не работали, они в первую очередь люди, а большинство людей негативно воспринимает нововведения; особенно те, которые их как-то ограничивают. Дайте вашей команде привыкнуть. Внедряйте проверки постепенно, демонстрируя их полезность. И помните: негативная реакция и естественное сопротивление команды не должны вас ни в коем случае останавливать.

Понравилась статья? Поделить с друзьями:
  • Найти ошибки в коде css онлайн
  • Найти ошибки в именном и глагольном управлении
  • Найти ошибки в документе онлайн
  • Найти ошибки в выполнениям задания
  • Найти ошибки в брачном договоре задание