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

В университете мне дали задание программно определить набор фактов о фрагменте кода C. Сколько у этого функций? Сколько там комментариев и утверждений? Сколько пробелов в файле? Спустя годы я присоединился к Solidware в качестве временного вице-президента по разработке и первого сотрудника, и - в те дни, когда Adobe Flash был крутым - мы подняли его на новый уровень с помощью Splat, в конечном итоге эволюционировав в SWaudit, где мы могли предсказать, как Рискованный фрагмент кода был основан на наборе проанализированных качеств: конечно, код может работать, но насколько безопасно его изменять?

Программы должны быть написаны для того, чтобы люди могли их читать, и только случайно, чтобы машины могли их выполнять.
- Хэл Абельсон

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

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

Длина символа

Вы когда-нибудь ломали голову, задаваясь вопросом, что делает эта странная именованная функция? Наличие переменных в блоке кода с именами i, j, k и p без видимой причины и без объяснения, скорее всего, не вызовет у вас симпатии к вашим преемникам.

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

Длина содержимого (длина по Холстеду)

Вы когда-нибудь смотрели на функцию и очень боялись? Знаете, те, которые состоят из трех или четырех страниц, и вам нужно так много прокручивать, что вам нужно десять минут даже на то, чтобы понять, что происходит.

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

В качестве иллюстрации я однажды запустил что-то вроде find . -name '*.cc' | xargs wc | sort -n на очень успешном коммерческом встраиваемом приложении. Я обнаружил, что один исходный файл, в частности, имел размер 175 КБ и содержал сотни классов! В течение следующих нескольких лет этот файл вырос еще на 50%. Возможно, здесь явно есть некоторые архитектурные проблемы и проблемы с надзором, но, прежде всего, я ненавидел бремя обучения для разработчиков программного обеспечения, которые были новичками в этом проекте.

Цикломатическая сложность

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

Томас МакКейб провел небольшое исследование в 1970-х годах, в котором он измерил количество различных путей (то есть точек принятия решения и условных циклов) через конкретный фрагмент кода.

Цель программной инженерии - контролировать сложность, а не создавать ее.
- Памела Заве

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

Решение? Убедитесь, что у вас есть не более десяти или около того (лично я ненавижу более пяти или около того) точек принятия решения в рамках функции или метода, и не откладывайте рефакторинг и абстрагирование по мере роста этого числа.

Связь

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

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

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

Есть два типа связи, и каждый имеет свои последствия. Я использую здесь слово компонент, но оно может относиться к методу, классу, пакету или даже службе.

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

Эфферентная связь (Ce) измеряет количество компонентов, от которых зависит данный компонент. Так что, если ваш компонент имеет высокую эфферентную связь, то более вероятно, что изменение одного из этих последующих компонентов сломает ваш компонент.

Еще есть индекс нестабильности, который рассчитывается как Ce / (Ce + Ca). Высокая нестабильность возникает, когда компонент имеет множество зависимостей как от восходящего, так и от нисходящего потока, и обычно работает как отличный канал для распространения ошибок.

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

Сплоченность

Вы когда-нибудь видели класс, который, кажется, делает все, с десятками полей и методов? Иногда это нормально, но иногда есть возможность лучше разделиться на несколько классов. Чем более согласованы поля и методы друг с другом, тем легче будет понять класс.

Всегда программируйте так, как будто парень, который в конечном итоге поддерживает ваш код, будет жестоким психопатом, который знает, где вы живете.
- Джон Вудс

Вот конкретный пример. У вас есть класс под названием ProductInventory, который описывает инвентарь продуктов на складе, а вы пишете код. Вы добавили методы для проверки уровня запасов, добавления и удаления продуктов и т. Д.

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

Но на самом деле склад и товарный инвентарь - это разные задачи, и в итоге вы получите один набор методов, который работает с полями, относящимися к запасам, и другой набор методов, который будет работать с деталями склада. Было бы лучше создать отдельный Warehouse класс, содержащий все эти вещи.

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

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

В этом есть еще нюанс, но подумать о том, как вы проектируете классы с данными, с которыми они работают, - это хорошее начало.

Обнаружение копирования-вставки

Итак ... у вас есть этот класс DoSomething, и вам нужно что-то похожее, но с некоторыми незначительными отличиями. Если вы действительно хотите получить результат быстро, то самое быстрое, что вы можете сделать, - это скопировать этот класс, возможно, в DoSomethingPlus, внести необходимые изменения и бац! все сделано. Ваш босс будет поражен тем, как быстро вы внесли изменения.

Теперь пропустим шесть месяцев вперед.

Возможно, один из ваших клиентов обнаружит ошибку, или, возможно, кто-то из ваших товарищей по команде обнаружит дыру в безопасности в DoSomething. Инженер, которому поручена работа, быстро устраняет неисправность, и жизнь продолжается. Но они не участвовали в вашей работе и не знают DoSomethingPlus. Проблема на самом деле не решена, и компания все еще имеет уязвимость.

Разве это не страшно? К счастью, инструменты CPD могут сообщать о том, насколько похожи различные блоки кода, и выделять те, которые кажутся связанными.

IDE - не всегда ваши друзья, но непрерывная интеграция - это

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

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

Таааак… Как все это применимо к DevOps?

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

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

Код сложнее читать, чем писать.
- Джоэл Спольски

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

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

Дальнейшее чтение

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

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

Наконец, если у вас есть время, стоит прочитать книги Роберта К. Мартина Чистый код, Чистая архитектура и Чистый кодер.