Хороший и плохой код… Иногда мы используем эти понятия в разговоре, но не каждый сможет формализовать их. Можно следовать различным принципам проектирования, изменять workflow и т.д, но сегодня я хочу поведать о численной метрике, описывающей то, на сколько хороша архитектура вашего кода.

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

Метрику, о которой пойдет речь, я увидел в книге Роберта Мартина Боба “Чистая архитектура”. Эта книга, по сути длинная подводка к его оригинальной статье, и по совместительству 22ой главе)
Дядюшка Боб вводит 2 понятия - устойчивость и абстрактность.

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

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

Метрика I (неустойчивость): I = fan_out / (fan_in + fan_out), где fan_in — число входящих зависимостей (сколько компонентов зависит от данного), а fan_out — число исходящих зависимостей (от скольких компонентов зависит данный компонент). Метрика I изменяется от 0 до 1, где 0 означает максимальную устойчивость, а 1 — минимальную.

Метрика A (абстрактность): A = Na / Nc, где Nc — общее число классов в компоненте, а Na — число абстрактных классов или интерфейсов. Эта метрика тоже изменяется от 0 до 1, где 0 означает полностью конкретный компонент, а 1 — полностью абстрактный.

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

На этом графике мы можем ответить две особенные области:

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

  2. Зона бесполезности: В этой зоне находятся компоненты с высокой абстрактностью и низким количеством входящих зависимостей. Обычно это устаревший и забытый код, который не используется активно в системе.

Оптимально, компоненты должны находиться на так называемой "главной последовательности" — линии, проходящей от точки (0,1) до точки (1,0) на графике. Это означает, что они имеют сбалансированное соотношение абстрактности и устойчивости, что делает их хорошо спроектированными и адаптируемыми к изменениям.
Мы можем построить диаграмму рассеяния компонентов и увидеть на сколько они удалены от главной последовательности. Ниже на рисунке мы видим что некоторые модули удалены больше чем на 2 стандартных отклонения. Это не хорошо.

И вот мы подошли к основной метрике, предложенной Робертом Мартином. Она позволяет оценить, насколько удалены компоненты от главной последовательности: D = |A+I-1|. Значение 0 указывает, что компонент находится прямо на главной последовательности, а значение 1 сообщает, что компонент находится на максимальном удалении от нее.
Мы также можем построить график изменения метрики D с течением времени и принять определенные действия заранее.

Итак, подводя итог, мы сегодня познакомились с метриками устойчивости (I), абстрактности (A) и расстояния от главной последовательности (D), которые могут помочь оценить качество архитектуры кода и выявить области для улучшения.

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

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