Экосистема открытого программного обеспечения постоянно развивается. Ежедневно на GitHub появляются новые библиотеки и решения, призванные ускорить разработку и облегчить жизнь разработчиков.
Стартапы сегодня очень зависят от Open Source библиотек, благодаря которым экономятся ресурсы разработки, это важный фактор успеха. Большинство разработчиков уже применяют такие библиотеки, но немногие умеют создавать их простыми в использовании. Для этого требуется много усилий и ресурсов: проектирование API, документация, стратегия выпуска, исправление багов, сопровождение, поддержка.
Вот примеры созданных на GitHub библиотек с открытым исходным кодом для Android и Kotlin: Pokedex, Balloon, Landscapist, ColorPickerView, Sandwich. Они использовались в миллионах проектов инженерами ПО и компаниями вроде Twitter, PayPal, Wikipedia, MyJio, Azar.
Чтобы создать программное обеспечение с открытым исходным кодом Kotlin/Android, сначала необходимо спланировать надежную стратегию согласно жизненным циклам проекта.
Жизненные циклы Open Source
ПО с открытым исходным кодом — это продукт для разработчиков, а не игрушечный проект или бесплатное программное обеспечение. Большинство Open Source проектов стабильно и систематически управляются в течение длительного времени.
Оптимальный способ создания ПО с открытым исходным кодом — доводить специальные жизненные циклы проекта от идей до выпуска, постоянно предоставляя доработки и новый функционал. Рекомендуем сделать собственную модель жизненного цикла. Если спланировать его сложновато, воспользуйтесь этой:

Все фазы этих жизненных циклов проходятся по стрелке — начиная от проектирования и до выпуска. И так для каждого нового выпуска. Каждой фазе отводится особая роль:
- Проектирование: это первая фаза, здесь идеи перемещаются в макеты, изучается осуществимость проекта. Если корректно проектировать и планировать, на других фазах потребуется намного меньше ресурсов. А если до реализации спецификаций освоить проектирование API-интерфейсов, тяжесть фазы разработки значительно уменьшится. Не умеете проектировать API-интерфейсы и архитектуру? Тогда будет достаточно собрать идеи и убедиться в осуществимости каждого пункта списка. Прежде чем переходить к следующей фазе, поделитесь наработками с сообществом и получите от него обратную связь, это отличный способ проектировать API с разных сторон.
- Разработка: эта фаза разделяется на реализацию и тестирование. При реализации в соответствии со спецификациями проектирования создается основной функционал. Здесь, исходя из новых идей, меняются наработки проектирования API или подходы к разработке. При тестировании проверяется работоспособность основного функционала: пишутся модульные тесты или выполняются интеграционные тесты с предыдущими проектами.
- Подготовка: затем корректно протестированные API вместе с контентом подготавливаются к опубликованию. Open Source не ограничивается выкладыванием кода на GitHub. Пользователи получают доступ к проекту и решают, годится ли он для их проекта. Поэтому нужно общаться с ними, предоставляя максимум информации о проекте, документацию и файл README для API. А также создавать для внешних участников шаблоны проблем и запросов на включение изменений в репозиторий проекта или, чтобы обсудить свои API и получить обратную связь, открывать в репозитории GitHub Discussions.
- Выпуск: после того как API подготовлен к публикации, в общедоступных репозиториях публикуется пакет, который пользователям легко взять и импортировать в свой проект. Это последняя фаза, и, прежде чем отправлять продукт на рынок, здесь убеждаются в отсутствии проблем с обновлениями.
Процесс проектирования API
Проектирование ПО — важная часть программной разработки, особенно библиотек и комплекта разработчика SDK, которой определяется комплексная структура API-интерфейсов.
Этот процесс аналогичен процессам дизайн-систем и системного анализа, которым перед программной разработкой требуются изучение проблемы и проектирование интерфейсов, а масштаб и факторы проектирования обычно определяются платформами.
Вот пошаговый процесс проектирования для лаконичного описания идей и преобразования их в четкие спецификации:
- Определите проблему: это самый первый этап всего жизненного цикла: определить проблему и четко сформулировать, что нужно решить в реальных проектах.
- Изучите общие идеи: если уже имеется собственное решение проблемы, этот этап пропускается. Если нет, нужно посмотреть, как решить проблему, и убедиться в целесообразности подхода. А также приблизительно оценить стоимость идеи, например реализацию API и варианты применения.
- Проверьте осуществимость идей: проверяется осуществимость рассмотренных подходов, для этого также реализуется их упрощенный функционал. Если не уверены в целесообразности реализации здесь, идти дальше будет сложновато.
- Изучите зависимости: проверяя осуществимость, нужно определиться со включаемыми в проект для реализации деталей зависимостями, учитывая импортируемые библиотеки или сложные взаимосвязи модулей собственной библиотеки. И наглядно представить все это в виде графа зависимостей.
- Набросайте схемы интерфейсов приложения: пора преобразовать подходы в схемы. Прежде чем проектировать детали, изображаются общая архитектура библиотеки и структуры API. Важно определить видимость будущих API-интерфейсов, например, какую их часть предоставить конечному пользователю.
- Спроектируйте интерфейсы приложения, то есть подробные спецификации для них: соглашения об именовании, наследование между интерфейсами/классами и абстрактными методами. А главное, определитесь с предоставляемыми пользователям интерфейсами.
Заблаговременным проектированием интерфейсов приложения экономится немало ресурсов, необходимых для продумывания всей структуры. И общие идеи, наработки проектирования не потеряются, даже если немного увязнуть в конкретном функционале при возникновении проблем с реализацией.
Теперь рассмотрим стратегии Open Source разработки для одноименной фазы.
Стратегии разработки
В фазе разработки жизненного цикла Open Source реализуются детали: классы, методы, расширения и все, что нужно для создания библиотеки.
Сфокусируемся на стратегиях для Kotlin и Android, хотя как общие концепции они сгодятся и для других платформ.
Минимизация поверхностей API

Важная часть фазы разработки — минимизация поверхностей API. Ведь Open Source библиотеки используются другими разработчиками, которые помещают в них зависимости своих проектов.
Минимизируя поверхности API, пользователи библиотек уменьшают распределенные зависимости в этих API и получают такие преимущества:
- Предотвращение неожиданных поведений: видимость API-интерфейсов ограничивается, пользователям библиотек их доступно меньше. Так обеспечивается работа API-интерфейсов с ожидаемыми поведениями, доступ к внутренним API предотвращается, благодаря чему на стороне пользователя она становится более предметной.
- Легкая миграция: когда пользователи библиотеки зависят от многочисленных общедоступных API, миграция оборачивается огромными затратами при новом выпуске. Если же библиотекой предоставляется лишь несколько интерфейсов API и большая часть работы выполняется внутри, то для перехода на новый выпуск требуется намного меньше ресурсов, а сложность использования API снижается.
- Простота сопровождения: если библиотекой предоставляется очень ограниченное количество API-интерфейсов, рефакторинг проводится без особого внимания миграциям в новом выпуске. То есть пользовательские проекты не сломаются при переходе на новый выпуск, поэтому библиотеку легко сопровождать: добавлять новый функционал, проводить рефакторинг внутренних структур.
Поверхности API минимизируются со старта на этапах разработки, но можно сфокусироваться сначала на реализации нового функционала, а по завершении постепенно сокращать предоставляемые поверхности API.
Явный режим API в Kotlin
В проектах Kotlin незаданные модификаторы видимости по умолчанию являются public. Поэтому, чтобы ограничить доступ API к внутренним функциям, модификаторы видимости задаются явно.
Модификаторы видимости распознаются и задаются при создании нового API, но в Kotlin разработчикам API предлагается явный режим API, при котором применяются явно задаваемые модификаторы видимости включением вот таких параметров компилятора Kotlin:
// проект Kotlin
kotlin {
// включается явный режим API
explicitApi()
}
// проект Android
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
kotlinOptions.freeCompilerArgs += ["-Xexplicit-api=strict"]
}
Можно перейти на явный режим видимости API постепенно, включив его с предупреждением, получаемым в IDE вместо ошибки компиляции.
Проверка бинарной совместимости в Kotlin
Все общедоступные API управляются вручную, но с увеличением поверхности API управлять всей видимостью уже сложнее. Кроме того, в процессе принятия добавлений извне обнаруживается, что участники/рецензенты забывают о добавлении модификаторов видимости и по ошибке раскрывают внутренние функции.
Чтобы управлять общедоступными API в проекте, используют поддерживаемый командой Kotlin валидатор бинарной совместимости.
С этим плагином отслеживаются изменения в общедоступных API, проверяется их корректность:

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

Из-за наличия в библиотеках Android этих ресурсов он передается в пользовательские проекты, сказываясь на размере APK-файла пакета Android. Поэтому разработчику библиотеки необходимо убедиться, что в ней нет неиспользуемых ресурсов, и минимизировать количество незащищенных.
Другая проблема — возможность применения передаваемых из библиотеки ресурсов в пользовательских проектах. Пользователи библиотеки не поймут, откуда взялись ресурсы, зависимые от ресурсов вашей библиотеки.
Допустим, в текущей версии библиотеки содержится ресурс color. Доступ к нему получается пользовательскими проектами, где он задействуется без каких-либо ограничений и где имеются все его зависимости.
Что, если он больше не нужен и в следующей версии будет удален? Обновив библиотеку до последней версии, пользователи получат ошибку IDE Unresolved reference. Отладка на стороне пользователя сильно усложнится.

Здесь неплохо бы добавить к названию ресурса префикс или постфикс, давая понять пользователям, что источник ресурса — ваша библиотека.
Хотя соглашение об именовании ресурса управляется вручную, в Android Studio имеется удобный функционал добавления в файл Gradle параметра resourcePrefix:
android {
resourcePrefix 'libraryName_'
...
}
Нажав Sync Now в Android Studio, увидим ошибку времени выполнения, если ресурсами не соблюдается соглашение об именовании префиксов:

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

Это называется транзитивной зависимостью, которой объединяются определения ресурсов всех транзитивных модулей. Поэтому, если библиотека зависит от других библиотек вроде Material Components и Appcompat, все их ресурсы передаются пользовательскому проекту, даже если у него нет напрямую зависимостей в этих библиотеках.
В итоге время сборки пользовательского проекта увеличивается. Чтобы отключить параметр транзитивной зависимости, в файл gradle.properties добавляется такое свойство Gradle:
android.nonTransitiveRClass=true
Если нажать Sync Now в Android Studio, параметр transitive R class отключится и ресурсы из зависимостей больше не возьмутся:

Open Source лицензии
Согласно Open Source Initiative, лицензии Open Source согласуются с определением открытого исходного кода. Если вкратце, программное обеспечение свободно применяется, изменяется и совместно используется. Таким образом, благодаря лицензиям конечные пользователи и коммерческие компании используют библиотеки с открытым исходным кодом бесплатно.
Вот популярные библиотечные лицензии, которые обычно применяются в экосистеме с открытым кодом: Apache License 2.0, лицензия MIT, универсальная общедоступная лицензия GNU и библиотека GNU, или «меньшая» универсальная общедоступная лицензия.
Не будем на них подробно останавливаться, важно выбрать оптимальную для Open Source проекта. Для проекта под бизнес-продукт вроде платного SDK сгодятся Apache License 2.0 и MIT — в экосистемах с открытым кодом это самые популярные лицензии, благодаря которым программное обеспечение применяется пользователями для коммерческих продуктов или в любых целях, а распространение и изменение Open Source проекта пользователями сопровождается уведомлениями о лицензиях и авторскими правами.
Документация
С хорошей документацией — хотя писать ее скучно — меньше проблем интеграции и получаемых службой поддержки запросов. Снабдить API-интерфейсы такой документацией иногда важнее, чем создать решения. Если пользователи применяют API-интерфейсы некорректно или не понимают их, пусть даже само решение превосходно, ценность проекта нивелируется.
Структурирование документации — неотъемлемая часть Open Source проекта, который нужно как-то описать и поделиться спецификацией API.
Рассмотрим, как обычно настраивается документация.
README на GitHub
Первое впечатление об Open Source проекте, размещаемом на GitHub, формируется у пользователей по файлу README. Чем он добротнее составлен, тем очевиднее ожидаемые от них решения и привлекательнее сам проект.
Сначала описывается его назначение: даже если это известный в сообществе проект, новичкам пригодится. Отсюда добавляются значки с очень компактной информацией о спецификациях, статусе сборки и т. д.:

Назначение пакета для проекта пользовательского интерфейса нагляднее передается скриншотами:

Затем описывается, как Open Source проект импортировать в пользовательские проекты. Это важная часть, связанная с выпуском решения. Для удобства пользователей раздел импорта размещается вверху README:

Далее максимально подробно описывается применение API, при этом следует избегать противоречий. Разработчиками API обычно упускается из виду, что для начинающих это темный лес. А чтобы было проще понять, добавляются такие примеры кода с описаниями API:

В конце добавляется контекст или информация о проекте: лицензия, технический контент, видео и т. д.
Вот примеры README: Balloon, Landscapist.
Спецификация API
Все общедоступные API проекта включаются не в файл README, а в отдельные спецификации: удобнее находить нужные API на веб-страницах, чем соответствующий код в IDE.
Вообще, спецификации API автоматически генерируются по-разному. Например, в Kotlin движком генерирования документации создается собственная спецификация API. В командной строке для таких API-спецификаций движком создаются HTML-страницы:

Веб-страницы развертываются на GitHub с помощью GitHub Pages. Вот пример.
Выпуск
Выпустим для пользователей первую или новейшую версию Open Source библиотеки. Можно также создать специальный процесс выпуска и в определенный момент предоставлять пакет с открытым исходным кодом.
Обычно пакеты библиотек с открытым исходным кодом легко и быстро загружаются и выпускаются таким средством автоматизации сборки, как Apache Maven: с ним проще управлять версиями сторонних библиотек.
Выпуск пакета во многом определяется платформой, поэтому следует изучить нюансы выпуска Open Source библиотеки в центральном репозитории и поддерживать бесперебойный выпуск.
Изучим эти нюансы в Android, Kotlin и Java.
Sonatype Nexus и Apache Maven
Выпускаемые для пользователей пакеты Android/Kotlin/Java загружаются в общедоступные репозитории вроде Nexus от Sonatype, в котором централизованно и в общем доступе используется все стороннее ПО с открытым исходным кодом.
В этот, а также репозиторий Maven загружаются библиотеки Android.

После того как Jcenter Bintray ушли в закат (Eng), из вариантов загрузки и архивирования Open Source пакетов сейчас остается только архивирование в Apache Maven. Если публиковать библиотеки в репозиториях Nexus с Maven для вас в новинку, рекомендуем это (Eng).
Автоматизирование выпуска с GitHub Actions
Обычно для выпуска нового пакета требуются ресурсы на версионирование, обертывание и загрузку пакета. Автоматизацией сборки выпуска экономятся ресурсы для будущей публикации.
Если библиотека с открытым исходным кодом размещается на GitHub, задачи публикации в Gradle автоматизируются с GitHub Actions (Eng). Рабочий процесс тоже сильно зависит от платформы. Для Android, Kotlin и Java при публикации библиотек Android в MavenCentral применяется непрерывная интеграция (Eng).
Опубликовав библиотеку, научимся долгосрочному управлению продуктами с открытым исходным кодом.
Open Source управление
Спланируем стратегии в отношении долгосрочных аспектов. Весь жизненный цикл Open Source проходится реализацией нового функционала или совершенствованием имеющихся API.
По окончании жизненного цикла обязательно осуществляется Open Source управление: необходимо обеспечить жизнеспособность проекта, привлечь долгосрочных пользователей библиотеки.
Для этого задействуются возможности сообщества. Ведь свободное ПО опирается на сообщество, в рамках которого к Open Source проекту присоединяются новые участники. Они работают над кодом, устраняют баги, добавляют документацию или как-то совершенствуют проект. В этом мощнейшие возможности работы с открытым исходным кодом: получать помощь или учиться у разработчиков со всего мира.
GitHub Issues
Огромное преимущество работы с открытым исходным кодом — решение проблем силами сообщества. Здесь выясняется, что думают о проекте пользователи, в общении с ними он дорабатывается.
Кроме того, пользователи участвуют в Open Source проекте, сообщая о проблемах через GitHub Issues и GitHub Discussions, когда находят баги в приложениях:

При устранении проблем и совершенствовании Open Source проекта проверяются различные сценарии, повышается его стабильность. Внимать клиентскому опыту — значит обеспечить успешность проекта, легко достигаемую при помощи Open Source сообщества.
Внимательно слушая и проявляя готовность принимать предложения, можно привлечь немало пользователей к совершенствованию проекта, повышению его стабильности. Стабильная библиотека с открытым исходным кодом — это проект для всех, а не чьи-то личные ресурсы.
Обмен с сообществами
Чтобы создать успешный Open Source проект, нужно поделиться им с сообществом и постоянно общаться с пользователями через Medium, Twitter, LinkedIn.
Оптимальная стратегия общения — ведение блога об Open Source проекте, предложение решений типичных проблем сообщества, обмен мнениями. Иногда очень ценными и в правильном направлении.

От сообщества также получается много идей со всего мира, это оптимальный способ развить свой проект и себя как разработчика. Создание библиотеки с открытым исходным кодом начинается диалогом с сообществом, принятием его мнения и пониманием его болевых точек.
Заключение
Так создается и поддерживается ПО с открытым исходным кодом, исходя из стратегий. Они не под грифом «совершенно секретно», так что берите и дорабатывайте по своему вкусу, совершенствуя Open Source сообщество.
Open Source проект, если им делиться, становится ценнее. Делясь решениями, мы не только учимся у разработчиков со всего мира, но и получаем признание сообщества, а также стремление участвовать в работе над открытым исходным кодом и развитии сообщества в целом.
Читайте также:
- Девять вопросов на собеседованиях для разработчиков Android
- Кэширование трендовых новостей в приложении TrendNow с помощью OkHttp Cache. Часть 6
- От кода до APK: полный разбор задач Android-сборки
Читайте нас в Telegram, VK и Дзен
Перевод статьи Jaewoong Eum: Becoming A Successful Android Open-Source Librarian





