Connect with us

Разработка

Проект Sensenmann: удаление кода в масштабе

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

Фото аватара

Опубликовано

/

     
     

Код в масштабе

В компании Google десятки тысяч инженеров-программистов вносят свой вклад в монорепозиторий с многими миллиардами строк кода. Этот репозиторий, хранящийся в системе под названием Piper, содержит исходный код общих библиотек, production сервисов, экспериментальных программ, инструментов диагностики и отладки — в общем, всего, что связано с кодом.

Такой открытый подход может быть очень эффективным. Например, если инженер не знает, как использовать ту или иную библиотеку, он может найти примеры, просто воспользовавшись поиском. Он также позволяет добросердечным людям выполнять важные обновления во всем репозитории, будь то переход на новые API или развитие языков, таких как Python 3 или дженерики Go.

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

Но что, если бы в обслуживании было меньше кода? Действительно ли все эти строки кода необходимы?

Удаление в масштабе

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

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

Что если бы мы могли автоматически очищать мертвый код? Именно об этом задумались разработчики несколько лет назад во время ежегодного хакатона команды Zürich Engineering Productivity. Проект Sensenmann, названный в честь немецкого слова, обозначающего воплощение смерти, оказался весьма успешным. Он генерирует более 1000 предложений для удаления в неделю и к настоящему времени удалил около 5% всего кода C++ в Google.

Его цель проста (по крайней мере, в принципе): автоматически выявлять мертвый код и отправлять запросы на пересмотр кода («списки изменений», changelists) для его удаления.

Что удалять?

Система сборки Google, Blaze (внутренняя версия Bazel), помогает нам определить это. Представляя зависимости между бинарными объектами, библиотеками, тестами, исходными файлами и многим другим последовательным и доступным способом, мы можем построить граф зависимостей. Это позволяет нам находить библиотеки, которые не подключены ни к одному бинарному файлу, и предлагать их удаление.

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

Единственный реальный способ узнать, полезны ли программы, — проверить, выполняются ли они, поэтому для внутренних двоичных файлов (программ, выполняемых в центрах обработки данных Google или на рабочих станциях сотрудников) при запуске программы записывается лог, в котором фиксируется время и конкретный двоичный файл. Суммируя эти данные, мы получаем сигнал «живучести» для каждого двоичного файла, используемого в Google. Если программа не используется в течение длительного времени, мы пытаемся отправить ее в список изменений на удаление.

Что не удалять?

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

Разработка в деталях

* На само деле тут прекрасная игра слов — заголовок The Devel’s in the Details прямо намекает на The Devil is in the details

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

Проект Sensenmann: удаление кода в масштабе

Если мы видим, что main1 активно используется, а main2 последний раз использовался более года назад, мы можем распространить сигнал живучести по дереву сборки, пометив main1 как живой вместе со всем, от чего он зависит. То, что осталось, можно удалить; поскольку main2 зависит от lib2, мы хотим удалить эти две цели одним и тем же изменением:

Проект Sensenmann: удаление кода в масштабе

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

Проект Sensenmann: удаление кода в масштабе

Инфраструктура тестирования будет запускать все эти тесты, включая lib2_test, несмотря на то, что lib2 никогда не будет выполняться «по-настоящему». Это означает, что мы не можем использовать прогоны тестов в качестве сигнала “живучести”: если бы мы это сделали, мы бы считали lib2_test живым, что позволило бы lib2 существовать вечно. Мы смогли бы только очищать нетестируемый код, что сильно затруднило бы наши усилия.

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

Проект Sensenmann: удаление кода в масштабе

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

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

Рассмотрим следующие два случая:

Проект Sensenmann: удаление кода в масштабе

Слева мы имеем реализацию алгоритма сжатия LZW в виде отдельных библиотек компрессора и декомпрессора. Тест фактически проверяет их обе, чтобы убедиться, что данные не повреждены после сжатия и последующей декомпрессии. Справа у нас есть тест web_test, который тестирует нашу библиотеку веб-сервера; он использует библиотеку кодировщика URL для поддержки, но на самом деле не тестирует сам кодировщик URL. Слева мы хотим рассматривать тест LZW и обе библиотеки LZW как один связанный компонент, а справа мы хотим исключить кодировщик URL и рассматривать web_test и web_lib как связанный компонент.

Несмотря на то, что эти два случая требуют разного подхода, они имеют идентичную структуру. На практике мы можем поощрять инженеров помечать такие библиотеки, как url_encoder_lib, как «только для тестирования» (т. е. только для поддержки модульного тестирования), что может помочь в случае web-test; в остальном наш текущий подход заключается в использовании расстояния правки между именами теста и библиотеки для выбора наиболее вероятной библиотеки, соответствующей данному тесту. Возможность выявления случаев, подобных примеру с LZW, с одним тестом и двумя библиотеками, вероятно, потребует обработки данных о покрытии тестов, и пока не исследовалась.

Фокус на пользователе…

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

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

В коммуникационной стратегии Sensenmann есть три основные части. Первостепенное значение имеют описания изменений, поскольку они — первое, что увидит рецензент. Они должны быть краткими, но при этом содержать достаточно информации, чтобы все рецензенты могли вынести свое суждение. Этого трудно достичь: слишком короткое описание — и многие люди не смогут найти нужную информацию; слишком длинное — и в итоге получится стена текста, которую никто не потрудится прочитать. Здесь очень помогают хорошо обозначенные ссылки на вспомогательную документацию и часто задаваемые вопросы.

Вторая часть — это сопроводительная документация. Здесь также важны краткие и четкие формулировки, а также хорошая навигационная структура. Разным людям понадобится разная информация: кому-то нужно убедиться, что в системе контроля исходных текстов удаление можно отменить, кому-то — подсказать, как лучше поступить с неудачным изменением, например, исправить неправильное использование системы сборки. Благодаря тщательному продумыванию и итерациям, связанным с обратной связью с пользователями, вспомогательная документация может стать полезным ресурсом.

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

Вперед и вверх

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

Реализация требует решения проблем как технического, так и социального характера. Хотя в обеих этих областях достигнут значительный прогресс, они еще не полностью решены. Однако по мере совершенствования системы скорость принятия удалений возрастает, и автоматическое удаление становится все более эффективным. Подобные инвестиции будут иметь смысл не везде, но если у вас огромное моно-хранилище, то, возможно, они будут иметь смысл и для вас. По крайней мере, в Google снижение нагрузки на поддержку C++ на 5% — это огромная победа.

Источник

Если вы нашли опечатку - выделите ее и нажмите Ctrl + Enter! Для связи с нами вы можете использовать info@apptractor.ru.
Advertisement

Наши партнеры:

LEGALBET

Мобильные приложения для ставок на спорт
Telegram

Популярное

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: