Адаптация IDE к контексту программной системы

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

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

Обзор

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

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

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

В двух словах о GT Releaser

GT Releaser - это движок для выпуска новых версий вложенных проектов Pharo, используемых Glamorous Toolkit. Мы представили это в предыдущей статье. Короче говоря, GT Releaser запускается из конфигурации, загружающей последний код проекта Pharo, и создает новую фиксированную семантическую версию, загружающую этот код.

Интересным аспектом GT Releaser является то, что его разработка следует практике разработки форм:

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

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

Изучение конфигурации загрузки

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

Изучение конфигурации загрузки - общий способ

Чтобы рассуждать о зависимостях, связанных с базовой линией в GT Releaser, мы создаем объект, моделирующий эту базовую линию. Это экземпляр класса Project и содержит список пакетов и зависимостей, определенных в базовой линии. Чтобы исследовать объект, мы можем начать с создания фрагмента кода и просмотреть его в инспекторе объектов - инструменте, который должен помочь нам понять этот объект. В качестве конкретного проекта рассмотрим Brick, библиотеку виджетов от Glamorous Toolkit.

По умолчанию общее представление Raw показывает нам список атрибутов этого объекта вместе с их значениями. Атрибут childProjects содержит список зависимостей. Мы выбираем его и детализируем этот объект, чтобы увидеть, что он содержит. В данном случае это коллекция. Затем мы выбираем зависимость Bloc, исследуем ее дальше, а также углубляемся в ее зависимости.

Путем навигации по атрибутам объекта несколько раз мы можем лучше понять зависимости. Выше мы видим, что Brick напрямую зависит от Bloc и Beacon, и что Bloc зависит от пяти других проектов.

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

Изучение конфигурации загрузки - формуемый способ

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

В этом представлении мы можем легко получить представление обо всех зависимостях загрузки для Brick. Поскольку это оказалось полезным, мы можем потратить еще несколько минут и просто сделать это представление первоклассной сущностью в среде IDE, чтобы любой пользователь или программист мог получить к нему доступ при проверке объекта Project. Для этого мы добавляем новый метод в класс Project, который определяет расширение инспектора, создающее это представление.

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

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

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

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

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

На скриншоте ниже показан такой сценарий навигации. Начнем с рассмотрения дочерних зависимостей Brick, а затем Bloc. В случае Sparta мы переключаемся на граф зависимостей. Затем мы углубляемся в Beacon и смотрим на исходный код базовой версии. Исходный код отображается в другом настраиваемом представлении.

Погружение в релиз

До сих пор мы рассматривали только зависимости между базовыми показателями. Однако цель GT Releaser - создавать новые версии для системы. Давайте теперь исследуем объект выпуска.

Объект выпуска - это экземпляр класса ProjectRelease. Он указывает на объект Project и добавляет информацию о семантической версии этого выпуска и ветви, в которой этот выпуск выполняется. Релизы выполняются для всех зависимостей проекта, если есть новые изменения.

Мы можем попытаться определить, какие версии проекта будут созданы в рамках выпуска, просто посмотрев на атрибуты объекта. Например, на скриншоте ниже мы видим, что следующий выпуск v0.5.38 для Brick будет выполнен в ветке #release, а для объекта RepositoryRelease для репозитория Brick атрибут isPassiveRelease установлен на false, что означает, что в этом репозитории есть новые изменения. .

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

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

Мы можем заметить, что три базовых уровня, Bloc, BlocLayout и BlocCore, имеют одинаковую версию выпуска, v0.5.20. Это указывает на то, что они, скорее всего, находятся в одном репозитории. Чтобы подтвердить это, мы также можем аннотировать представление, показывающее проекты, сгруппированные на основе репозиториев, с информацией о выпуске.

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

Погружение в действия по выпуску

Хотя взгляд на выпуск с точки зрения высокого уровня полезен, полезно также более подробно понять, какие действия будут выполнены для фактического создания этого выпуска. Например, какие действия выполняются для создания v0.5.38 выпуска в github://feenkcom/Brick репозитории?

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

Например, ниже мы проверяем объект, моделирующий репозиторий github://feenkcom/Brick в выпуске v0.5.38. Мы можем заметить, что для выполнения этого выпуска основная ветвь merged в ветвь release, базовый код обновляется, некоторые метаданные экспортируются, создается новая фиксация с тегом и, наконец, отправляются локальные изменения.

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

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

Подробно о действиях по выпуску

Просмотр списка действий дает нам больше информации, но все же не всегда помогает нам понять, что будет делать действие. Например, что будет делать действие по компиляции базовой линии Brick для выпуска v0.5.38?

Чтобы получить более подробную информацию, мы можем проверить действия. Если мы проверим действие для компиляции базовой линии Brick, мы сразу же увидим новый код базовой линии и сможем увидеть разницу между текущей версией базовой версии и новой. Таким образом, мы видим, что создание версии v0.5.38 для Brick заключается в добавлении ссылок на исправления версий для Bloc и Beacon.

Также может быть полезно увидеть, чем базовый план в версии v0.5.38 отличается от базового плана в предыдущей версии Brick. Представление Отличить предыдущую версию показывает нам это. Теперь мы обнаруживаем, что обе версии используют одну и ту же версию исправления для Beacon. Обновлена ​​только версия Bloc.

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

Действие для фиксации показывает сообщение, которое будет использоваться для фиксации.

Помимо просмотра данных о команде, пользователь также может выполнять команды прямо в инспекторе. Оба представления для отображения отдельного действия или списка действий позволяют пользователям выполнять действия. Ниже пользователь выполнил первые три действия; эти действия выделены серым.

Инспектор как пользовательский интерфейс

До сих пор мы не видели специального пользовательского интерфейса для GT Releaser. Мы взаимодействовали с инструментом только через инспектор объектов. Инспектор объектов позволяет нам видеть как детали реализации, так и высокоуровневые представления наших объектов. Таким образом, инспектор действует как пользовательский интерфейс для конечных пользователей и как инструмент разработки для программистов, работающих над инструментом.

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

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

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