img

Разработка современного программного обеспечения

21 ноября
20:00
Бесплатный вебинар
Введение в Docker
Ведущий — Филипп Игнатенко.
Руководитель центра разработки
Записаться
img
img

 

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

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

Здесь я затрону такие темы, как управление зависимостями (и что такое зависимости на самом деле!), инструменты сборки, и вишенка на торте - системы непрерывной интеграции. Изучив основные понятия, вы сможете лучше понять то, что я буду рассказывать дальше.

Что такое библиотеки?

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

Библиотеки – это наборы предварительно написанного кода, который разработчики используют для оптимизации задач. Они повышают нашу продуктивность, избавляя от написания одних и тех же вещей, которые только тратят наше время. Numpy, Matplotlib, Lodash, jQuery и React – это все примеры популярных библиотек с открытым исходным кодом. 

Вы, возможно, уже обратили внимание, что у каждой из этих библиотек (как и у любых других) есть свой номер версии. Как правило, это несколько числовых полей, которые разделены точками: v1.0.0 или 1.0.0. И эти числа не являются случайными! Есть немало различных методов, как можно определить версию программного продукта.

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

Самая известная схема управления версиями – SemVer (Semantic Versioning – семантическое версионирование). Оно используется большей частью библиотек кода (если не всеми).

Что такое семантическое версионирование?

Семантическое версионирование – это схема управления версиями. У него есть три поля, которые разделены точкой. Первое поле (слева) назовем Major, среднее – Minor, а последнее – Patch. Это выглядит примерно вот так (с некоторыми отклонениями): Major.Minor.Patch

По стандарту SemVer значения всех этих полей могут только увеличиваться. Вы не имеете права уменьшать ни одно из них. Когда увеличивается номер родительской версии, все номера дочерних версий сбрасываются. То есть, если поле Major увеличивается, то поля Minor и Patch сбрасываются до 0.

Номер исправления (Patch)

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

Изменения, внесенные в проект, всегда являются совместимыми в двух направлениях при условии, что родительские версии одинаковы. То есть код, который был написан для версии v1.0.1, будет работать и для версий v1.0.0 и v1.0.2.

Дополнительный номер версии (Minor)

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

Изменения, внесенные в дополнительную версию, всегда являются совместимыми с предыдущими версиями при условии, что основная версия остается неизменной.

То есть код, который был написан для версии v1.1.0, будет также работать и для версии v1.2.0. Однако он может не работать для версии v1.0.0, так как в ней могут использоваться функции, которые были добавлены в более поздней версии.

Основной номер версии (Major)

Основная версия обладает наивысшем приоритетом и является самым «опасным» полем из всех трех. Увеличение значения этого поля говорит о том, то в проект были внесены какие-то критические изменения. Как правило, эти изменения связаны с API/интерфейсом и/или с переименованием или удалением объектов. 

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

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

Явным примером внесения критических изменений является пара Python 2 и Python 3. Операторы печати Python 2 не работают с интерпретатором Python 3 и наоборот. Конечно, некоторые операторы, например, циклы for и какие-то другие базовые структуры, могут работать, но далеко не все. 

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

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

Итак… начнем с того, как же мы можем установить и использовать внешние библиотеки, которые были написаны другими людьми?

Как управлять зависимостями вашего проекта

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

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

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

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

Что такое диспетчер зависимостей?

Зависимость – это библиотека или утилита, которые ваш проект использует для работы. Говоря простым языком, если программа А нуждается в программе Б, чтобы скомпилироваться и/или запуститься, то программа А зависит от программы Б. Программа может зависеть от нескольких программ.

Диспетчер зависимостей – это инструмент, который автоматически отслеживает зависимости проекта. С его помощью вы можете запускать в терминале простые команды для того, чтобы устанавливать, обновлять или удалять зависимости. Вот несколько примеров диспетчеров зависимостей: NPM, Yarn, Composer, Gradle и Bundler.

Только не путайте их с диспетчерами пакетов, так как это инструменты, которые управляют системными пакетами. Вот некоторые диспетчеры пакетов: apt-get, yum, Homebrew и Chocolatey. 

Некоторые диспетчеры выполняют сразу две функции – управляют системными пакетами и зависимостями проекта. Вот несколько примеров: NPM и Yarn.

Как работает диспетчер зависимостей?

Диспетчер зависимостей использует два основных файла: манифест и файл блокировки.

Манифест – это список прямых зависимостей вашего проекта. Здесь находятся зависимости, которые вы указали непосредственно при установке чего-либо. Так что, когда вы запускаете команду npm install jsdom, в списке зависимостей манифеста проекта появляется пакет jsdom.

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

Получается, что когда вы запускаете команду npm install jsdom, в манифесте появляется только пакет jsdom, несмотря на то, что у jsdom могут быть свои другие зависимости. Итак, как же диспетчеры зависимостей умудряются отслеживать весь граф зависимостей?

Что такое файл блокировки?

Файл блокировки – это журнал, в котором хранится список всех зависимостей проекта. Здесь находятся как прямые зависимости (которые перечислены в манифесте), так и весь граф зависимостей. Для всех зависимостей указаны их версии, репозитории, из которых они были извлечены, и прочая информация. 

Изображение, представленное ниже, демонстрирует разницу между графом зависимостей (перечисленным в файле блокировки) и списком прямых зависимостей (перечисленным в манифесте) – реализацией многих веб-стандартов для тестирований.

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

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

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

В общем, мы выяснили, что такое библиотеки, семантическое версионирование и диспетчеры зависимостей. А теперь пришла пора собрать наш проект. 

Что такое система сборки?

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

В общем случае система сборки состоит из трех компонентов:

  • Целевые объекты
  • Зависимости 
  • Правила

Целевой объект – это то, что вы хотите получить. Если вы хотите получить двоичный файл с названием «test.exe», то это и есть ваш целевой объект. Зависимости – это зависимости проекта, в том числе утилиты среды, например, установленный компилятор С++, имеющийся npm и т.д. Правила задают то, как вы из исходного объекта должны получить целевой. Это могут быть используемые вами команды.

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

Итак… а что, если вы хотите, чтобы несколько разработчиков могли постепенно выпускать версии вашего приложения? Здесь вам пригодиться CI/CD!

Системы непрерывной интеграции

Если коротко, то непрерывная интеграция (CI – Continuous Integration) – это парадигма, при которой вы регулярно проверяете наличие изменений в программном продукте. CI-система автоматически собирает и тестирует каждое изменение, чтобы избежать различных проблем, которые могут возникнуть при их выпуске. 

Непрерывная доставка (CD – Continuous Delivery) – это метод автоматизации процесса выпуска. Выпуски основных версий автоматически развертываются для подготовки и производства, обеспечивая, таким образом, автоматизированный процесс выпуска.

Непрерывное развертывание (CD – Continuous Deployment) идет на шаг впереди относительно непрерывной доставки. Этот метод подразумевает что каждое изменение, если оно прошло все этапы производственного процесса, будет развернуто автоматически, не дожидаясь прямого одобрения.

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

Система непрерывной интеграции (для краткости CI) – это система сборки в облаке, которая активирует систему сборки проекта автоматически или по запросу. Это основа успеха любой agile-команды.

CI состоит из трех основных компонентов:

  • Триггеры
  • Действия
  • Наборы правил

Триггеры – это события, прослушиваемые CI, которые запускают системы сборки. Это может быть коммит в ветке main, запрос на включение изменений для предварительного просмотра функций и т.д. У каждой платформы может быть несколько событий такого рода.

Действия – это команды и сценарии, которые запускаются, когда срабатывает триггер. На языке системы это может звучать так: «Сборка проекта должна быть произведена после того, как в ветке main будет сделан коммит».

Наборы правил – это конфигурации, которые устанавливают триггеры и действия, установку среды, переменные среды, системы сборки и зависимости системы. Это язык системы.

Стоит отметить, что в одной CI может быть несколько систем сборки, у каждой из которых есть свои целевые объекты и правила. 

Вот примеры CI, с которыми мы встречаемся ежедневно, - TravisCI, Jenkins, CircleCI, GitHub Actions и GitLab CI/CD. Ниже приведен пример набора правил GitHub Actions, который отвечает за выпуск новых версий программы и их отправку в GitHub Releases:

on:
  push:
    branches:
      - main // will start the CI when a push to branch main is made

jobs:
  release_linux:
    runs-on: ubuntu-latest // must be run on ubuntu@latest

    steps:
      - name: check out git repository
        uses: actions/checkout@v1

      - name: install Node.js, npm and yarn // required env tools
        uses: actions/setup-node@v1

      - name: install deb packages // required env dependencies
        run: sudo apt-get install fakeroot dpkg rpm

      - name: build and release app
        uses: kl13nt/action-electron-forge@master
        with:
          // release to github releases after successful build
          release: ${{ startsWith(github.ref, 'refs/tags/v') }}

Пример набора правил

Я опустил довольно много вещей, связанных с конфигурацией, но идею вы поняли. В качестве триггера я установил коммит в ветке main, а в качестве действия – клонирование репозитория проекта, установку NodeJS, npm, yarn и других зависимостей среды.

Этап сборки запустит систему сборки npm-scripts, которая проанализирует и протестирует код прежде, чем он будет собран. После чего CI отправит получившиеся двоичные файлы на страницу проекта в GitHub Releases.

При отправке проекта в CI в игру также вступает файл блокировки! Если CI установит версии зависимостей, которые отличаются от тех, которые установлены у вас локально, то это может привести к сбою. Именно поэтому для CI (и для разработчиков) необходим файл блокировки. В таком случае вы сможете быть уверены в том, что код, который работал на вашем локальном компьютере, будет работать и в CI.

Заключение

Если вы проделали весь это путь, то я надеюсь, что он был для вас захватывающим (и не таким уж сложным!).

Ссылка
скопирована
Получите бесплатные уроки на наших курсах
Все курсы
Программирование
Скидка 25%
Python-программист с нуля
Стань разработчиком на одном из самых популярных языков программирования.
Получи бесплатный
вводный урок!
Пожалуйста, укажите корректный e-mail
отправили вводный урок на твой e-mail!
Получи все материалы в telegram и ускорь обучение!
img
Еще по теме:
img
Гипервизор - это программное обеспечение для виртуализации, используемое для создания и запуска виртуальных машин (ВМ). Гипервиз
img
Виртуализация серверов позволяет запускать несколько виртуальных машин на одном физическом сервере. Запуск виртуальных машин (ВМ
img
Сегодня мы рассмотрим, как настроить и использовать PHP в проекте. Но прежде чем начать, нужно понять, что такое PHP. Что такое
img
Как разработчик, вы знаете, что HTML расшифровывается как HyperText Markup Language (язык разметки гипертекста). HTML — это язык
img
Бесконечные споры вокруг искусственного интеллекта приводят к путанице. Существует много терминов, которые кажутся похожими, но
img
SVG расшифровывается как масштабируемая векторная графика. Это веб-дружелюбный векторный формат файлов, используемый для отображ
21 ноября
20:00
Бесплатный вебинар
Введение в Docker