В DWH только Python
Вопреки названию, мы используем далеко не только Python. Но большой проект на любом языке требует к себе вдумчивый подход, особенно в плане учета особенностей языка и технологий.
Пройдя все стадии от отрицания до принятия в программировании на Python, могу сказать, что он нам подошел. Но будет неправдой сказать, что нас обошли стороной трудности и проблемы, связанные с особенностями разработки.
Про жизненные неурядицы и то, как мы их решали и продолжаем решать — об этом и немного об устройстве DWH в inDrive я и расскажу. А еще на примере кейсов разберу, что в проекте может пойти не так.
Языки и технологии в нашем DWH
Наш DWH облачный и расположен в Google Cloud. Значительную часть технологий этой платформы мы используем для решения задач:
BigQuery.
Pub/Sub + Dataflow.
Dataproc.
Google Functions.
Composer.
Помимо этого, есть опенсорсные технологии, которые активно используем — Livy и Presto.
Языки нашей разработки можно представить в виде небольшой таблицы:
Scala | SQL | Python |
General-purpose язык. Быстрый. Имеет небольшую распространенность. | Поддерживается для многих систем обработки/хранения данных. Скорость зависит от системы. Очень распространен (основные конструкции). | General-purpose язык. Не очень быстрый. Очень распространен. |
Подружить такое количество технологий с доставкой данных из API и бэкенда требует много усилий. Это выросло в целый проект, позволяющий автоматизировать выгрузки и сбор витрин данных. В качестве основного языка мы используем Python, в узких местах Scala. Проект, предоставляющий простой интерфейс для работы со сложными инструментами, мы назвали de-core.
Что же могло пойти не так?
Кейс 1. Навигация в проекте
В начале проект писали два человека. Со временем команда подросла и встал вопрос, как ориентироваться в коде.
Проблемы:
Непонятно, что и где лежит.
common.py на 10 тысяч строк.
IDE не помогает найти нужные файлы.
Документации нет.
Эти проблемы могут привести к тому, что в какой-то момент возникнет дублирующий функционал, а новички начнут долго разбираться в проекте. А документацию никто писать не любит, плюс понятная документация — это трудно. А если существует часть проекта и его требуется ретроспективно задокументировать? Это уже задачка со звездочкой. В нашем кейсе проблемы подсветили новые коллеги — оказалось, что порог входа в проект очень высокий.
Что можно сделать:
Закоммититься на определенную структуру проекта.
Явно разделить функционал для общего использования и частного процесса.
Описать базовые классы того, что ожидается от фичи.
Начать вести документацию.
Придумать нейминг.
Первым делом мы выделили «ядро» проекта. Это центральная часть, которая часто переиспользуется и должна быть написана наиболее качественно. Провели глубокий рефакторинг этой части, определили базовые структуры для того, чтобы унифицировать разрабатываемые модули. Ввели понятную структуру в проекте для поддержки ETL-процессов, завязались на нашу архитектуру DWH (raw-, ods-, dds-, cdm-слои), и поиск нужных файлов свелся к логическим единицам — источник и таблица.
Пример структуры для источника
Кейс 2. Помоги себе сам
Больше человек в команде — больше работы с чужим кодом. Python часто используют для маленьких скриптов, где требования к коду ниже, так как сам скрипт легко влезает на экран. В большом проекте нужно писать код так, чтобы другой человек смог легко в нем разобраться.
Спор о необходимости типизации в Python
Проблемы:
IDE не подсказывает методы класса.
Передал аргументы не того типа.
Нейминг функций/методов/переменных не всегда помогает.
Типизация помогает понять, что ожидает функция или метод, IDE может подсказать атрибуты и методы. А линтеры автоматически проверят эти ожидания. Мы также определили базовые классы как стандарт написания ETL. Поддержка интерфейса классов позволяет быстрее ориентироваться в чужом коде.
Что можно сделать:
Прописать типы.
Определить интерфейс ответа функции/метода.
Подготовить базовые классы.
Настроить линтеры и хуки.
Функция с типизацией и базовым классом
Кейс 3. Сложная схема зависимостей
В большом проекте возникает соблазн притянуть импорты из другого модуля и быстро решить проблему. Но подход может привести к тому, что возникнут сложные зависимости, которые будет сложно дебажить.
Проблемы:
Всегда нужно деплоить проект целиком.
Трудно вынести часть проекта в отдельную библиотеку.
Пакеты зависят друг от друга циклически для разных файлов.
Здесь помогает регулярный рефакторинг, когда нужно пересматривать актуальность текущего кода после изменения требований. Как вариант разрыва зависимостей — определение объектов для передачи в функцию/метод/класс.
Что можно сделать:
Переразбить проект на пакеты.
Провести код-ревью и груминг.
Заложить время на рефакторинг.
Можете ли вы позволить себе оформить часть проекта как отдельную библиотеку?
Кейс 4. А у меня локально запускается
Одна из частых проблем — запуск в разных средах и локальное тестирование. Существует риск, что среда будет отличаться от продакшена при тесте. А это может привести к непредсказуемым ошибкам.
Проблемы:
Здесь спасает Docker. Все тесты и проверки работы приложения нужно тестировать в виртуальной сред, соответствующей проду.
Что можно сделать:
Вам тоже выдали новый MacBook с M1?
Я пролистал текст, о чем он был?
Изначально простой и маленький проект вырос в сложный, с которым работает много разработчиков. Стандарты работы с этой кодовой базой постоянно эволюционировали. А про те кейсы, которые задели струны в моей душе, я написал выше.
Главное, что стоит держать в голове, начиная большой проект на Python:
Промышленная разработка на Python — это сложно.
IDE ваш друг, используйте возможности по максимуму.
Есть много рекомендаций по тому, как писать код — лучше их изучить.
Не поскупитесь на автоматизацию, линтеры, хуки, документацию.
В итоге задача разработчика — знать о разных практиках, уметь оценить полезность для вашего конкретного проекта и иметь волю по их внедрению при необходимости. Даже если вы пишете на Python.