Поддержка целостности проекта в Python требует усилий

Опубликовано 06 October 2017 в Python

Python позволяет программисту довольно много. Это очень выразительный язык. Его динамическая природа даёт возможность делать элегантные решения, которые довольно сложно представить, к примеру, в Java.

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

Первая проблема — навигация по коду

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

Traceback в Python не самый полезный инструмент: добавьте пару декораторов, парочку лямбд, что-нибудь из itertools и можете распрощаться возможностью проследить вызовы функций просто прочитав трейсбек. Придётся вооружиться грепом, принтами и дебаггером. Что приводит к потерям часов на поиск проблемы.

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

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

Конечно, подобное случается в самом сложном и запутанном месте системы. Конечно, 3 года назад — это был самый элегантный и понятный кусок системы. Конечно, за это время он оброс «жирком», обрюзг. И уже неважно кто придумал так написать. Разбираться с этим всё равно экстремально сложно.

Что делать?

Как с этим бороться? У меня нет хорошего ответа на этот вопрос. Кажется, стоит следовать дзену питона: явное лучше неявного, даже если это означает немного больше кода. Не бойтесь добавить немного "духа Java" в свой проект. Если вернуться к более серьёзному обсуждению, то следуйте двум этим правилам:

Правило №1: На то, чтобы сломать навигацию по коду, должна быть хорошая причина

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

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

Правило №2: Места где ломается навигация по коду должны логгироваться очень тщательно

Хитрые решения нужно логгировать особо тщательно. В логи должны попадать все существенные детали о том, что и в каком месте сломалось. Проверяете валидность запроса? Логгируйте какой валидатор на каком куске какого запроса сломался. Разбираете XML? Логгируйте на каком куске данных и почему сломались. Неплохо добавлять в логи разного рода id: запроса, сессии, процесса или чего-то ещё в этом роде — это поможет потом выстроить историю работы системы.

Типы и проверка типов могут помочь, но на них не стоит сильно рассчитывать

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

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

Инструменты

Не могу посоветовать никаких особых инструментов для проверки связности проекта. Да и нет, наверное, таких метрик. Все это на уровне ощущений. Конечно, mypy для проверки типов незаменим. Но в остальном приходится полагаться на своё чутье.

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

Хороший результат даёт код ревью. Чтобы понять чужой код, приходится походить по коду. А ещё лучше когда приходится поискать что-нибудь по проекту. Вот здесь все хитрые решения проявляются во всей красе. Для меня сигналом к тому, что что-то пошло не так, является сильное желание переключиться на ветку в IDE и продолжить ревью там.

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

---
Возник вопрос? Мне всегда можно написать в Twitter: avkorablev