Доставьте удовольствие своим коллегам-разработчикам, сделав ваше программное обеспечение простым в использовании с помощью среды и автоматизации сборки. С примерами кода на Python и Hatch.

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

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

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

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

Плохой рабочий день

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

Вы получаете ссылку на репозиторий в личном кабинете GitLab компании и готовы ее использовать.

За исключением того, что вы не можете.

В репозитории нет ни README, ни папки с именем docs или чем-то подобным. Похоже, вам есть над чем поработать самостоятельно.

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

Вы также найдете Dockerfile, который, вероятно, используется для производства, но он нигде явно не написан, и requirements.txt, конечно, это проект Python, поскольку он посвящен машинному обучению.

Сейчас 11.30, и вы чувствуете, что готовы попробовать. Вы знаете, где находится новый набор данных, поэтому теперь вы хотите начать с конвейера подготовки данных. Таким образом, вы хотите установить пакет, но замечаете отсутствие setup.py и pyproject.toml. Вы спрашиваете своих коллег, может быть, посылка находится на частном pypi сервере компании, но получаете отрицательный ответ. В конце концов, если нет инструкций по сборке, как его можно было запаковать?

Видимо, придется идти грязным путем. Вы скачиваете репо, находите точку входа с подготовкой данных, которой не было в файле __main__.py, добавляете корневую папку проекта в свою переменную окружения PYTHONPATH. Затем вы создаете новую среду Python с помощью venv, запускаете pip install -r requirements.txt для установки всех необходимых зависимостей, запускаете скрипт и… вуаля!

ImportError: module xxx not found

Вы думали, что в файле requirements.txt будут указаны зависимости разработчиков, но, по-видимому, это не так. Но вот вы смотрите на часы и замечаете, что уже час, и решаете пойти пообедать с коллегами. Вы поймете это позже.

Когда вы вернулись с обеденного перерыва, вы снова полны энергии и готовы снова заняться проблемой.

Ваша работа сейчас состоит в том, чтобы установить недостающие зависимости одну за другой, каждый раз, когда вы устанавливаете одну библиотеку, вы запускаете ее снова, немного ждете, и выходит следующая ошибка ImportError. Вы заканчиваете установку всего этого к 3 часам и все еще задаетесь вопросом, для чего на самом деле нужен файл requirements.txt.

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

В параллельном мире ты просто бежал

pip install petognizer

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

pip install petognizer[train]

Затем нашел команду для подготовки данных, описанную в README, и вы были готовы к работе примерно через 10 минут, задолго до обеденного перерыва.

Наличие pyproject.toml упростило бы как установку пакета (даже проще, если бы он находился на сервере pypi), так и подготовку среды сборки. Кроме того, некоторая правильная документация облегчила бы понимание того, как на самом деле использовать код для различных задач.

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

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

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



Люк

Hatch — это инструмент автоматизации, поддерживаемый Управлением упаковки Python (PyPA), который позволяет создавать среды и создавать программное обеспечение с высокой простотой.

Он использует файл с именем pyproject.toml, который является новым объединяющим файлом настроек проекта Python. Унифицирующий, потому что он используется Hatch и другими подобными инструментами, такими как Поэзия, и может задавать конфигурацию других инструментов, таких как, например, черный или flake8.

Мы можем использовать Hatch для создания нового проекта с

hatch new "My Project"

и он создаст стандартную структуру проекта, например

my-project
├── src
│   └── my_project
│       ├── __about__.py
│       └── __init__.py
├── tests
│   └── __init__.py
├── LICENSE.txt
├── README.md
└── pyproject.toml

Он содержит два отдельных пакета для исходного кода и тестов: README.md, заполненный некоторым текстом по умолчанию, LICENSE.txt, файл с именем __about__.txt, содержащий версию программного обеспечения, и pyproject.toml.

Hatch также можно использовать в существующем проекте, просто запустив

hatch new --init

в его корневой папке. Он сгенерирует значение по умолчанию pyproject.toml, которое необходимо изменить.

Hatch можно установить с помощью pipx install hatch, чтобы сделать его доступным в качестве команды для пользователя, не «загрязняя» существующую среду Python по умолчанию.



Быстрый взгляд на pyproject.toml

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

pyproject.toml поставляется с начальной частью, которая довольно описательная. Здесь мы можем найти название проекта, описание, авторов, поддерживаемые версии Python и версию.

Возьмем в качестве примера этот игрушечный репозиторий, который я сделал для иллюстрации https://github.com/mattiadg/demo_it-analyze/blob/main/pyproject.toml (спасибо моему другу Mario за помощь с интерфейсом этого маленького приложения)

[project]
name = "demo-it-analyze"
description = ''
readme = "README.md"
requires-python = ">=3.7"
license = "MIT"
keywords = []
authors = [
  { name = "Mattia Di Gangi", email = "[email protected]" },
]
classifiers = [
  "Development Status :: 4 - Beta",
  "Programming Language :: Python",
  "Programming Language :: Python :: 3.7",
  "Programming Language :: Python :: 3.8",
  "Programming Language :: Python :: 3.9",
  "Programming Language :: Python :: 3.10",
  "Programming Language :: Python :: 3.11",
  "Programming Language :: Python :: Implementation :: CPython",
  "Programming Language :: Python :: Implementation :: PyPy",
]
dependencies = [
  "flask",
  "pydantic",
  "spacy",
]
dynamic = ["version"]

[project.urls]
Documentation = "https://github.com/unknown/demo-it-analyze#readme"
Issues = "https://github.com/unknown/demo-it-analyze/issues"
Source = "https://github.com/unknown/demo-it-analyze"

Оно должно быть достаточно четким, за исключением двух полей. dynamic = ["version"] означает, что версия программного обеспечения должна вычисляться динамически, а позже в файле говорится, что она должна быть найдена в файле __about.py__. С другой стороны, в поле dependencies перечислены зависимости пакета. Это пакеты, которые будут установлены вместе с этим программным обеспечением при упаковке. Здесь я только перечислил имена, но также применимы некоторые ограничения версии, такие как ==, <= и т. д. Они такие же, как и в pip.

Обратите внимание, что эти зависимости указаны под тегом [project], но есть и другие зависимости, указанные только для некоторых сред. Однако они являются общими для пакета и всех сред. Кодовая база просто не работает без них.

Конфигурация инструментов

Как упоминалось ранее, pyproject.toml можно использовать для указания конфигурации инструментов, используемых в проекте, и Hatch является одним из тех инструментов, которые можно настроить здесь.

[tool.hatch.version]
path = "src/__about__.py"

[tool.hatch.envs.default.env-vars]
  FLASK_APP = "demo_it_analyze/app.py"

[tool.hatch.envs.default]
extra-dependencies = [
  "pytest",
  "pytest-cov",
  "mypy",
]

[tool.hatch.envs.default.scripts]
cov = "pytest --cov-report=term-missing --cov-config=pyproject.toml --cov=demo_it_analyze --cov=tests {args}"
no-cov = "cov --no-cov {args}"
download_ita = "python -m spacy download it_core_news_sm"
serve = "flask --app src.app run"

[tool.hatch.envs.train]
template = "default"
extra-dependencies = [
  "spacy[cuda-autodetect,transformers,lookups]"
]

[tool.hatch.envs.test]
template = "default"

[[tool.hatch.envs.test.matrix]]
python = ["37", "38", "39", "310", "311"]

Обратите внимание, что поля здесь начинаются с tool.hatch, а не с project.

Мы начинаем с того, что сообщаем Hatch, где найти версию программного обеспечения. Затем мы указываем FLASK_APP для среды Python по умолчанию. Hatch позволяет нам создавать множество сред, и среда по умолчанию — это среда, которая всегда определена и из которой происходят все остальные. Мы можем определить зависимости для наших сред, и это зависимости dev, поскольку среды не существуют в коде пакета.

С помощью Hatch мы можем создать среду по умолчанию, просто выполнив

hatch env create 

и он создаст среду по умолчанию со всеми ее зависимостями. Мы также можем создать другую среду, указанную в pyproject.toml (например, test также определено в этом примере), запустив

hatch env create test

Тем не менее, одна хорошая особенность Hatch заключается в том, что нам не нужно явно создавать среды. Команда hatch run позволяет нам запускать любую команду или сценарий внутри среды. В файле, скопированном выше, у нас есть это определение скрипта:

[tool.hatch.envs.default.scripts]
cov = "pytest --cov-report=term-missing --cov-config=pyproject.toml --cov=demo_it_analyze --cov=tests {args}"
no-cov = "cov --no-cov {args}"
download_ita = "python -m spacy download it_core_news_sm"
serve = "flask --app src.app run"

Так что мы можем запустить, например

hatch run serve

и хэтч создаст окружение по умолчанию, если оно не существует, синхронизирует его зависимости с теми, которые указаны в pyproject.toml в момент выполнения, и запустит flask --app src.app run внутри окружения, запустив наш сервер flask. Мы также можем указать для запуска команды в определенной среде, например test с

hatch run test:serve

И обратите внимание, что нам не нужно ничего устанавливать. Hatch читает pyproject.toml и распознает сценарии, которые он может запускать.

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



Здание

Hatch также является достойным инструментом сборки. У него есть собственный бэкэнд сборки, который называется hatchling, а pyproject.toml поставляется с некоторыми конфигурациями для сборки. Из примера:

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.build.targets.sdist]
exclude = [
  "/.github",
  "/docs",
]

[tool.hatch.build.targets.wheel]
packages = ["src"]

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

Затем мы можем просто запустить hatch build, и он создаст обе цели. Или мы можем указать, например, с hatch build -t wheel.

Когда у вас также есть учетные данные для сервера pypi, будь то официальный, тестовый сервер или частный сервер, вы можете опубликовать там свой пакет из своей сборки, используя просто hatch publish.

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

А если у вас включен конвейер CI/CD, например Github Actions, вы также можете указать эти команды там для еще большей автоматизации!

Что мы не освещали

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

О документации мы уже упоминали во вступительном рассказе и больше о ней говорить не будем.

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

Другой важной отсутствующей частью является конвейер непрерывной интеграции/непрерывной доставки (CI/CD), который выполняет некоторые проверки каждый раз, когда какой-либо код добавляется в удаленный репозиторий, а также может создавать и развертывать программное обеспечение. Конвейер CI/CD показывает ожидаемое качество кода из базы кода, поскольку он может выполнять проверки, такие как линтеры или средства проверки статических типов (для динамических языков, таких как Python), а также тесты. Шаг в конвейере для создания и развертывания программного обеспечения также гарантирует, что эти действия легко выполнять и имеют достаточно высокий уровень автоматизации.

Выводы

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

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

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

Библиография

Некоторые книги определенно повлияли на эту статью. Наиболее заметным из них является The Unicorn Project, написанный известным DevOps-автором Gene Kim и отредактированный IT Revolution. Старший разработчик сослан в неблагополучный отдел, где разработчики не могут запускать код локально. Во время романа она и группа просвещенных коллег делают все возможное, чтобы освободить своего коллегу от хлопот тысяч блокировщиков и бюрократии, чтобы позволить им делать то, за что им на самом деле платят.

Grokking Continuous Delivery Кристи Уилсон под редакцией Мэннинга — это руководство о том, как запустить конвейер CI/CD с нуля и в процессе улучшить кодовую базу.

Re-Engineering Legacy Криса Бирчалла, а также под редакцией Мэннинга, всесторонне рассматривает устаревшее программное обеспечение, как с ним работать, когда оно назначено, как его улучшить и как убедиться, что мы не пишем устаревшее программное обеспечение.

Среднее членство

Вам нравится то, что я пишу, и вы подумываете о подписке на Medium Membership, чтобы иметь неограниченный доступ к статьям?

Если вы подпишитесь по этой ссылке, вы поддержите меня своей подпиской без каких-либо дополнительных затрат для вас https://medium.com/@mattiadigangi/membership