Оглавление
Снижение качества кода со временем
При выпуске программных продуктов и оказании услуг естественным является желание измерить их качество. Понимание того, насколько качественным является продукт, помогает при его продаже и доработках, поскольку жизненно необходимо знать насколько мы соответствуем тому, что можно назвать «хорошо».
В статьях и книгах о качестве кода можно увидеть следующий график зависимости производительности разработки от времени жизни проекта:
© Источник: книга «Чистый код», Роберт Мартин
Падение производительности разработки вызвано тем, что в некачественном коде разработчики «вязнут», как в грязи. Такой код сложен для понимания и требует неоднократного редактирования. Чаще всего нет тестов, поэтому приходится только догадываться, на какие части программы могли повлиять внесенные изменения. В большинстве случаев это приводит к упущенным при тестировании ошибкам. При работе с прикладным кодом 1С мы точно так же можем столкнуться с вышеописанными проблематиками.
Отрезок с производительностью 0 на графике — это «бунт», когда команда говорит: «Мы не можем больше вносить изменения». В этот момент начинается рефакторинг: обновление версии БСП и связанных библиотек, переписывание функциональных блоков, изменение архитектуры. Такой рефакторинг откладывает развитие продукта.
Кроме проблемы производительности, в продуктах с низким качеством кодовой базы «фактор автобуса» равен или стремится к 1.
Фактор автобуса (bus-factor) — это число человек, которое нужно сбить автобусом, чтобы проект перестал развиваться. Хорошим значением является количество участников команды (звучит жестоко, но описывает ситуацию на проекте).
В некоторых командах практикуется назначение ответственного за изменения части программы сотрудника. Никто, кроме него, не способен быстро и правильно вносить изменения в этот функционал. Недоступность такого человека приводит к существенным проблемам.
На первый взгляд кажется, что хорошо быть таким незаменимым человеком. Но по факту такой разработчик становится «заложником» блока. Есть желание развиваться и двигаться дальше, а приходится все время возвращаться назад.
Воспринимаемая сложность кода
Обратимся к картинке ниже, она состоит из большого количества мазков. Наш мозг не может работать с таким количеством объектов одновременно. Он прекрасно справляется с крупными объектами: озеро, цветы, деревья, так как их немного. Если мы сосредоточимся на деталях отдельно взятого цветка, мы сможем разглядеть мазки, из которых он состоит. Однако остальные объекты более не в фокусе нашего внимания.
То же самое и при работе с кодом. Когда пишем код, мы работаем с небольшим количеством объектов одновременно и их состав постоянно меняется. Процесс написания кода выглядит приблизительно следующим образом:
- Нашли фрагмент кода, в котором нужно внести изменение.
- Написали строчку в этой функции.
- Перешли в другую, добавили десять строчек.
- Перешли в третью, удалили пару строк.
- Вернулись в первую, написали еще строчку.
- Проверили, что работает.
- Поместили.
В нашем представлении мы реализовали «красивую картинку».
А в хранилище помещение на две с половиной тысячи строки. И на деле для другого человека это выглядит, как на следующем «шедевре»: высокое искусство, но ничего не понятно. Первый возникающий вопрос: «Что это такое? С какого места начинать разбираться?». Непонятно, сложно.
Еще сложнее, если предложенное решение отличается от привычного нам. Используются неизвестные нам алгоритмы и методики.
По мере профессионального развития мы воспринимаем код по-разному. «Новички» в программировании обычно способны в основном оперировать мелкими сущностями: идентификаторы, операторы и вызовы методов. По мере развития наш мозг тренируется. Насмотревшись на различные варианты реализации одних и тех же алгоритмов, становится проще отстраниться от деталей и перейти на более высокий уровень. Мы перестаем видеть код, а видим объекты и связи между ними.
В процессе обучения разработчик учится не только решать технические проблемы, но и перераспределять сложность кода, закрывая его за функциями обертками, вынося в отдельные модули или комментируя. Есть огромное множество способов перераспределять сложность. Но пока мы им учимся, успеваем написать какое-то количество сложного кода.
Критерии качественного кода
Если спросить 10 начинающих разработчиков: «Что такое качественный код?», они могут перечислить такие характеристики, как
- простой,
- понятный,
- поддерживаемый,
- работающий.
К сожалению, это не больше, чем общие слова. На деле же они не могут дать точный ответ. Когда мы сталкиваемся с хорошим кодом, мы с ним работаем, а он не мешается и не запоминается. Но при этом каждый разработчик запомнит код, который доставил ему боль, и вспомнит его не раз «добрым» словом. Более опытные разработчики говорят приблизительно те же слова, могут еще вспомнить несколько методик разработки, самые популярные на данный момент SOLID и YAGNI.
SOLID набор принципов в объектно-ориентированном программировании, призванный помочь в создании программных систем, которые будет легко поддерживать и расширять в течение долгого времени.
S | Принцип единственной ответственности (single responsibility principle). Для каждой сущности должна существовать единственная причина для изменения. |
O | Принцип открытости/закрытости (open–closed principle). Программные сущности должны быть открыты для расширения, но закрыты для модификации. |
L | Принцип подстановки Лисков (Liskov substitution principle). Объекты в программе должны быть заменяемыми на экземпляры их подтипов без изменения правильности выполнения программы |
I | Принцип разделения интерфейса (interface segregation principle). Много интерфейсов, специально предназначенных для клиентов, лучше, чем один интерфейс общего назначения |
D | Принцип инверсии зависимостей (dependency inversion principle). Зависеть нужно от абстракций, а не реализаций. |
Прямые отголоски на SOLID в стандартах 1С найти сложно, но их можно найти в типовых конфигурациях и библиотеках.
Обратим внимание на объект Отчет. Для его построения в платформе используются объекты КомпоновщикМакета, СхемаКомпоновкиДанных, КомпоновщикНастроек, МакетКомпоновкиДанных, ПроцессорКомпоновкиДанных, ПроцессорВывода. Такое разнообразие объектов позволяет легко расширить функционал. Например, процессоров вывода реализовано два: в коллекцию и табличный документ, а замена одного на другой не нарушает всю цепочку. Такое разделение указывает на принципы S и L.
Вся библиотека БСП с большим количеством подсистем и программных интерфейсов для каждой из них, указывает на использование принципов S, I, D.
Хорошим примером принципа D будет подсистема Организации. В каждой конфигурации свой объект Организации и структура его хранения. Получалось, что подсистемы БСП зависели от реализации в типовых конфигурациях. Это усложняет внедрение и обновление функционала. Подсистема Организации реализовывает интерфейс и структуру описывающие абстрактную организацию с предопределенными полями. Что позволяет развернуть зависимость на обратную. БСП зависит от интерфейса и конфигурация зависит от него же, так как его реализует.
YAGNI («You aren't gonna need it»; с англ. — «Вам это не понадобится») — процесс и принцип проектирования ПО, при котором в качестве основной цели и/или ценности декларируется отказ от избыточной функциональности, — то есть отказ добавления функциональности, в которой нет непосредственной надобности.
SOLID говорит нам, что мы должны быть готовы к предстоящим изменениям, а YAGNI утверждает, что не следует писать лишнего, нужно делать рефакторинг в момент необходимости. Данные методики немного противоречат друг другу. Получается, что за все время развития программирования мы так до конца и не сформулировали, что такое качественный код. Мировая практика показывает, что даже если не известно, как «хорошо», то все равно очевидно, когда «плохо». И отсюда вывод: не будем делать «плохо» и постепенно приблизимся к границе «хорошо».
Практика плохих и хороших решений собрана в системе стандартов разработки на «1С:Предприятие». Стандарты регулярно обновляются и расширяются, так как и сама платформа, и мир вокруг нее развиваются.
Для более детального анализа качества кода можно выделить ряд показателей:
- работоспособность,
- покрытие тестами,
- наименования,
- оформление,
- компактность,
- комментирование,
- дублирование.
Давайте поговорим чуть подробнее о каждом показателе качества кода.
Работоспособность
Статические анализаторы позволяют нам анализировать код, не запуская его. Очень удобно, когда возможности статического анализа встроены в IDE. Тогда подобные ошибки вылавливаются автоматически прямо в момент написания кода.
Покрытие тестами
Покрытие кода тестами позволяет программистам перестать бояться вносить изменения в работающий код. Если будет допущена ошибка, мы сможем сразу ее обнаружить. Боязнь прикоснуться к работающему коду помогает множиться некачественному коду. Программист, вместо того чтобы переписать прежний код, пытается найти «безопасное» место для новых правок. И общая картина начинает превращаться в мозаику.
Абсолютно не важно какой вид тестирования вы используете. Разные тесты подходят в разных ситуациях. У себя мы большинство задач решаем через сценарные тесты. Об этом мы подробнее писали в статье «Подходы к сценарному тестированию на примере 1С:Общепит и 1С:Сценарное тестирование».
Наименования
Один из важнейших аспектов, так как наименования несут самую большую долю информации о коде. Хорошие наименования могут направлять и подсказывать путь в коде, а плохие только забивают слоты для объектов в нашем фокусе внимания. Кажется, очень очевидная вещь: наименования должны быть понятными. В качестве примера можно рассмотреть функцию, выбирающую остатки номенклатуры с названием ПолучитьТаблицуНоменклатуры вместо ОстаткиНоменклатуры.
Плохие наименования ведут к лишним вопросам, на которые приходится тратить время, как на рисунке ниже.
Данный метод выполняет рекурсивный обход цепочки документов и формирует перечень актуальных заказов по определенным правилам, но ничто нам об этом не говорит.
Оформление
Опытные программисты с лёгкостью улавливают закономерности в коде. И если сущности одинаково оформлены, их проще вычленять из текста. Хорошее оформление увеличивает скорость чтения кода в разы.
На приведенных ниже рисунках отформатированный код с рисунка, показанного выше. Мы видим разницу и она несущественна. Но если не вчитываться, код кажется разным. Поэтому, если решили контролировать оформление, очень важно добиваться того, чтобы оно было одинаковым на всем проекте, иначе весь эффект от оформления не раскрывается в полной мере.
Компактность
Компактность говорит нам о том, что по возможности следует сокращать количество строк, не увеличивая сложность кода.
Рассмотрим следующий код:
Такой код допустим и скорее всего получился таким по историческим причинам. Скорее всего, между Иначе и ИначеЕсли было еще несколько блоков ИначеЕсли, которые потом удалили в процессе развития продукта. Этот код можно сократить, не потеряв его функциональности.
Но не следует злоупотреблять. Пустая строка, между разными смысловыми блоками, поможет быстрее прочитать и понять код. А несколько простых конструкций обычно лучше одной сложной.
Операторы ветвления сами по себе сложные, а их вложенность особенно мешает восприятию.
Комментирование
Комментарии перетягивают сложность с кода на себя, облегчая его понимание. Плохие комментарии засоряют код, не неся новой информации. Со временем большинство из нас развивает выборочную слепоту и просто не обращает на них внимание, но для новичков эта проблема стоит довольно остро. Хороший комментарий объясняет не что делает код, а зачем он это делает.
Рассмотрим пример:
Комментарий «Настроим блокировку элементов формы» уже полностью содержится в имени процедуры «Блокировать элементы формы» и его можно безболезненно удалить.
«Дальнейшие операции выполняются только для новых объектов» является констатацией факта — следующий код завершит выполнение обработчика для ранее записанных объектов. Даже неопытный разработчик на платформе сможет сделать такой вывод сам, без нашей подсказки. Но гораздо интереснее ему будет понять, почему здесь прерывается выполнение.
Дублирование
Возникает на проекте по нескольким причинам:
- Работа нескольких программистов над похожими задачами в разных частях проекта.
- Копирование уже существующего участка с внесением минимальных исправлений.
- Лень.
Также встречается дублирование, когда конкретные участки кода отличаются внешне, хотя и выполняют одну и ту же задачу. Такое дублирование бывает довольно сложно обнаружить и исправить.
Это самый спорный из всех пунктов. Кажется, так просто — не должно быть кода, который дублируется. Но сколько ошибок было привнесено из-за того, что один код применялся в местах, изменения в которых выполняются для разных заказчиков по разным причинам и разными людьми. Или строились безумные конструкции огромной вложенности, чтобы устранить дублирование в две строчки кода.
Бороться нужно с явным дублированием. При таком дублировании у кода совпадают назначение, реализация и потребитель.
Качество кода и производительность
Распространенный вопрос: как быть с производительностью программы? Ведь в процессе разработки, для облегчения восприятия код часто пишется не оптимально, что может приводить к замедлению программы. Это важный вопрос, но мы не будем его рассматривать в рамках данной статьи.
Производительность важна для комфортной работы пользователей и нельзя пренебрегать ей, предпочитая качество и чистоту кода в проекте, надеясь на вычислительные мощности современных серверов. Но производительность гораздо проще настраивать на проекте, где заботятся о качестве кода:
- В структурированном коде проще проводить анализ производительности.
- В работе с производительностью важным аспектом является эксперимент. А в качественный код проще и быстрее вносить изменения. Вследствие снижается время затрачиваемое на проверку гипотезы.
Качественный код помогает писать программы быстрее. На первом этапе они медленнее работают, но мы можем быстро привести их к оптимальному состоянию.
Делаем и сохраняем код качественным
Можно долго перечислять инструменты и практики, которые помогут вам работать с качеством кода. Но наиболее эффективно заняться формированием культуры написания качественного кода. Команда должна понимать преимущества достижения результата не только в краткосрочной перспективе, но и в будущем. Такая команда сама добьется качественного кода и усовершенствования процессов разработки на проекте.
Неплохо показала себя практика code review, которая позволяет повысить качество и выполняет образовательную функцию. Бытует мнение, что code review должны выполнять более опытные разработчики по отношению к менее опытным. Но это не совсем так: ревью новичков более опытных разработчиков позволяет под новым углом взглянуть на код, который мы пишем. Понять, где мы усложняем код или используем устаревшие методики и шаблоны разработки.
Для упрощения работы с качеством нам понадобится инструмент для измерения показателей качества, обнаружения ошибок, отслеживания динамики изменений на проекте. В сообществе прижилось два инструмента для этих задач «Автоматизированная проверка качества» и SonarQube.
1С:Автоматизированная проверка конфигураций
Данный продукт предназначен для проверки конфигураций на платформе «1С:Предприятие 8» на соответствие принятым стандартам разработки и иных технических требований.
Отлично подходит для сценария, когда периодически собираются замечания и исправляются с последующей проверкой результата.
Продукт разрабатывался под 1С и в нем предусмотрены ряд моментов свойственным только этой группе продуктов:
- Фильтрация проверяемых объектов по подсистемам.
- Сбор замечаний в разрезе метаданных.
- Назначение ответственных за качество в блоках сотрудников. Такой ответственный не является единоличным владельцем блока, а только следит за поддержанием качества в блок.
- Фильтрация замечаний из библиотечных конфигураций.
Поддерживает работу как с хранилищем конфигураций, dt и cf файлами, так и с выгруженной в файлы конфигурацией.
Выгрузка в файлы поддерживается только в формате конфигуратора.
При выполнении анализа потребляет небольшое количество ресурсов процессора и оперативной памяти, занимает значительное место на жестком диске. Время анализа сильно зависит от размеров конфигурации и может составлять от 30 минут до 16 часов при полной проверке конфигурации. Для ускорения процесса анализа существует возможность проверять только измененные с предыдущей проверки объекты, но это не работает с рядом ошибок и периодически придется делать полный анализ.
Вход в инструмент происходит легко. Эта конфигурация 1С, которая говорит с пользователем на понятных ему терминах. Разобраться, что к чему достаточно просто. Для выполнения первой проверки достаточно заполнить карточку с описанием конфигурации.
После создания карточки нужно создать новую проверку и подождать. После чего можно будет приступить к анализу отчета с ошибками.
Как можно увидеть, в описании ошибки указано описание проблемы, ссылка на стандарт и место обнаружения, но нет контекста. Поэтому для работы с системой нужно постоянно держать открытым список ошибок и переключаться между ним и конфигуратором.
SonarQube
Этот инструмент изначально разрабатывался под другие языки программирования, а поддержка 1С в нем появилась позднее и сейчас активно развивается.
Плагинов для sonarqube несколько. У себя мы используем версию развиваемую сообществом 1c-syntax (https://github.com/1c-syntax/
sonar-bsl-plugin-community). За что хочется сказать отдельное спасибо.
SonarQube позволяет решать задачи по:
- поиску ошибок в конфигурации на основе стандартов и общих принципов разработки;
- расчету показателей качества;
- отслеживанию динамики изменений, связанных с качеством кода на проекте;
- отображению покрытия кода тестами.
SonarQube как и АПК можно использовать для периодического поиска ошибок и их исправления. Но вся мощь инструмента раскрывается при использовании технологии разветвленной разработки.
Технология разветвленной разработки — организация разработки подразумевающая, разработку крупных блоков отдельно от места хранения стабильной версии исходного кода. После разработки и тестирования изменения переносятся в стабильную версию. Если взять за основу разработку на хранилищах, то мы все доработки ведем в отдельных базах/хранилищах, а когда закончим сравнением/объединением переносим в хранилище. Такой вариант разработки призван повысить качество разработки и тестирования, обеспечить непрерывное развитие конфигурации. Но появляются накладные расходы связанные организацией процесса разработки, налаживанием инфраструктуры. Если использовать отличные от хранилищ технологии потребуется в них погружаться.
Нам кажется, что лучше всего подходит система контроля версий git. Можно использовать технологию разветвленной разработки на хранилищах, предлагаемую 1С (https://its.1c.ru/db/v8std/
content/709/hdoc). Но для анализа придется все равно выгружать данные из хранилищ и раскладывать по веткам в git. Так как только при использовании git возможно вычислить автоматически, кто допустил ошибку.
При своей работе инструмент потребляет существенные ресурсы процессора и оперативной памяти. Но скорость его работы высокая, анализ даже самых крупных конфигураций на ограниченных мощностях не превышает 1 часа. Например: анализ ERP на 12 ядрах и 16 Гб оперативной памяти занимает около 20 минут в сонар сканере.
За разнообразие возможностей приходится платить высоким порогом входа. Сначала потребуется разобраться с компонентами самого SonarQube, организовать выгрузку хранилищ в git, организовать запуск анализа выгруженных исходников. Общая схема может например выглядеть так:
Как настроить каждый из этих компонентов в рамках данной статьи описывать не будем. Только рассмотрим их функции.
GitConverter выполняет одностороннюю синхронизацию хранилища конфигурации с репозиторием git. Для хранения центрального git репозитория мы выбрали продукт gitlab.
SonarQube состоит из трех компонентов:
- База данных, мы используем postgresql.
- Sonar Scanner. Выполняет анализ исходного кода. Формирует список ошибок и показателей для ядра. Располагаться должен на одном сервере с анализируемыми данными.
- Сервер SonarQube. Занимается обработкой собранных ошибок и показателей. Записью результатов в базу данных. Взаимодействие с пользователем посредством веб-интерфейса.
Jenkins управляет доставкой исходного кода с gitlab и запуск сканера. Запуск может происходить по расписанию или триггеру (например, изменению исходного кода).
Как работает Jenkins, его настройка для работы с SonarQube и другие задачи, для которых его можно использовать мы уже описывали в статье «Jenkins в разработке конфигураций 1С».
С помощью всех этих инструментов мы выполнили анализ и перешли в SonarQube. На главном экране мы можем почерпнуть информацию о проекте в целом. Количестве ошибок, уязвимостей, дефектов, дублирующихся участках и времени на исправление.
Перейдя к списку проблем можно настроить отображение за счет всевозможных фильтров. Каждой проблеме присваивается пользователь внесший данную проблему в код. Для этого используется история изменений в git. На проблему назначается последний менявший строку кода пользователь.
Из карточки проблемы возможно перейти к ее подробному описанию или посмотреть место расположения ошибки в коде для более детальной оценки.
Все замечания делятся на три основных вида.
К ошибкам относятся проблемы способные привести к выбросу исключения во время работы конфигурации или радикальной потери производительности.
Уязвимости указывают на возможные проблемы с безопасностью.
Дефекты — это самая многочисленная группа замечаний. К ней относится все, что может мешать восприятию кода. Яркий представитель этой группы превышение показателя когнитивной сложности.
С главного экрана проекта можно перейти к отчетам о динамике изменений. Их использование поможет понять в каком состоянии проект, и куда он движется.
Организация процесса работы
На проектах разработки мы попробовали разные подходы к работе с качеством кода.
Сначала использовали АПК. За блоками в конфигурации были закреплены ответственные. Раз в неделю проверяли конфигурацию и все найденное исправляли или отмечали как особенность. Занимало это около трех-четырех часов на разработчика в неделю.
Сложности использования АПК
- Первая сложность связана с тем, что поиск виновника некачественного кода требует времени, поэтому ответственные за блоки разработчики тратили больше времени не на исправление ошибок, а на выявление сотрудника внесшего эти изменения. И порой ответственный исправлял замечание сам, вместо того, чтобы тратить усилия на поиски виноватого. Такой подход приводил к нарастанию напряженности в команде.
- Вторая сложность связана с тем, что процесс перехода к контексту некачественного кода требует времени. К тому же при работе с замечаниями не хватало контекста для определения их актуальности.
- Третья сложность связана с тем, что организовать непрерывный процесс мониторинга некачественного кода практически невозможно. Приходилось «замораживать» состояние конфигурации после анализа, исправлять и далее объединять с рабочей конфигурацией, которая могла за это время значительно уйти вперед. Это связано с тем, что, замечания отмеченные как особенности постоянно возвращались, стоило внести малейшие изменения в модуль, где они находятся. А после внесения исправлений требовалось перепроверять блоки.
Переход на SonarQube
Для обхода накопившихся проблем перешли на использование SonarQube. Стали проверять проект каждую ночь, а исправлять по утрам. Время немного сократилось за счет удобства. А напряженность спала, так как каждый из разработчиков исправляет свой код. Но перепроверки избежать не получилось, так как правятся изменения уже попавшие в основное хранилище.
Очередь за 1C:EDT + SonarQube
Сейчас мы перешли на разветвленную модель разработки. На одном из проектов используем разветвленную модель на хранилищах, а второй полностью перевели на разработку в 1C:EDT.
1C:EDT — это современная расширяемая среда разработки прикладных решений. Она создана на основе свободной интегрированной среды разработки модульных кроссплатформенных приложений Eclipse, широко используемой разработчиками во всем мире.
Проверять теперь мы стали не только главное хранилище/ветку, но и каждую «ветку» отдельно. Такая проверка позволяет исправить все замечания до попадания кода в главное хранилище/ветку.
Следить за качеством кода сложно и нет времени
Следить за качеством кода на проекте сложно, тем более, если на вашем проекте нет носителей культуры написания качественного кода. Но как только качественный код станет виден на фоне прочего кода, останавливаться уже не захочется.
Может показаться, что порог вхождения для построения культуры слишком сложен и «дорог», но если его не преодолеть, то раз за разом мы будем оказываться в зоне «бунта», показанной на графике в начале этой статьи.
Если вы один пытаетесь создавать культуру кода в вашей команде, всегда можете вспомнить, что когда Игнац Земмельвейс в 1847 году впервые рекомендовал врачам мыть руки перед осмотром пациентов, его советы были отвергнуты на том основании, что у врачей слишком много работы и на мытье рук у них нет времени. У него ушло много времени и сил, чтоб доказать, что это оправдано. И ваши усилия не останутся напрасными.
Всегда есть срочные задачи и каждый раз кажется, что сейчас неподходящий момент, чтобы начать следить за кодом. Но если уделять внимание только настоящему, то потеряем возможность получить преимущество в будущем. Вот что по этому поводу пишет Роберт Мартин в своей книге «Чистый код»:
«Программисты сталкиваются с основным парадоксом базовых ценностей. Каждый разработчик, имеющий сколько-нибудь значительный опыт работы, знает, что предыдущий беспорядок замедляет его работу. Но при этом все разработчики под давлением творят беспорядок в своем коде для соблюдения графика. Короче, у них нет времени, чтобы работать быстро!
Настоящие профессионалы знают, что вторая половина этого парадокса неверна. Невозможно выдержать график, устроив беспорядок. На самом деле этот беспорядок сразу же замедлит вашу работу, и график будет сорван.
Единственный способ выдержать график — и единственный способ работать быстро — заключается в том, чтобы постоянно поддерживать чистоту в коде».
Статьи по теме качества кода, упомянутые выше:
От экспертов «1С-Рарус»