[Программирование, Разработка под iOS, Objective C, Swift] Модуляризация iOS-приложения: зачем и как мы разбиваем Badoo на модули
Автор
Сообщение
news_bot ®
Стаж: 6 лет 4 месяца
Сообщений: 27286
![Клик для увеличения](https://linkme.ufanet.ru/box/200x100/604749963c0619c925e03b9273d216fa.png)
В iOS-разработке Badoo мы уже несколько лет занимаемся созданием модулей, и большая часть нового кода разрабатывается вне кодовой базы приложений. Сейчас у нас более 100 модулей для Badoo и Bumble. В этой статье я расскажу о нашем опыте и отвечу на самые популярные вопросы о модуляризации:
- по какому принципу выделять модули;
- как организовать связи между ними;
- достаточно ли для фичи одного фреймворка;
- как сократить время запуска многомодульного приложения;
- зачем в этом процессе мониторинг;
- можно ли автоматизировать создание новых модулей;
- как не допустить ошибок линковки;
- и другие.
Мой коллега, лид команды iOS-разработки и Core-команды Артем Лоенко, в начале года говорил об этом на митапе FunCorp в докладе «Катастрофически полезные последствия модуляризации». Я приведу более подробный разбор процесса модуляризации и некоторых опущенных в его рассказе деталей.Модуляризация и её преимуществаМодуляризация — не только про то, что ты берёшь кусочек кода, заворачиваешь его в отдельный фреймворк и наслаждаешься. Это процесс разбиения кодовой базы на небольшие специализированные, готовые к повторному использованию модули. Здесь особенно важны два момента:
- Это процесс. Нужно понимать, что модуляризация — отдельный процесс в работе вашего отдела, который будет требовать регулярной поддержки. Одноразовой акцией обойтись не получится.
- Модули специализированные. При выделении модулей мы решили отдавать предпочтение именно понятному интерфейсу, который отражает решаемую модулем задачу, нежели какой-то технической составляющей и кодовой начинке. Это позволило обходить стереотипы — например, о том, что модули должны быть примерно одинаковыми по объёму кода или иметь одинаковую архитектуру.
Конечно, дополнительные правила приносят в проекты новую головную боль. Для чего же тогда этим заниматься?
- Масштабирование разработки. Подход позволяет горизонтально расширять отдел разработки без особых трудностей: новые сотрудники занимаются новыми модулями в изоляции.
- Экономия времени и ресурсов. Когда вам понадобится переиспользовать код в другом продукте, вы сразу оцените скорость, с которой можно это сделать.
- Синергия между приложениями. Продуктовые запросы обогащают модули функциональностью, которая может быть использована во всех продуктах компании.
- Качество кода. Как уже говорилось выше, когда модули специализированные, а их интерфейс прост, связность кода становится существенно ниже, как и порог вхождения в проект новых программистов. Также упрощаются поддержка и тестирование кода.
Пишите в комментариях, если вам недостаточно этих доводов, чтобы начать заниматься модуляризацией, или если я упустил ещё какие-то важные плюсы.Наш опытКак мы пришли к решению делить приложения на модули? На самом деле, к тому моменту, когда мы задумались об изменении процессов, у нас уже имелось некоторое количество модулей: сетевой слой, аналитика, обработка ошибок и другие. Их мы называем платформой. Встал вопрос: что дальше? Мы решили создать два типа промежуточных модулей между таргетом приложения и платформой:
- Функциональные модули — готовые и зарекомендовавшие себя модули с понятным интерфейсом и функциональностью: «Регистрация», «Платежи», «Чат». Их структура будет описана ниже.
- Экспериментальные (внутренние) модули стремятся стать полноценными функциональными модулями, но в силу каких-то ограничений (ресурсы, маркетинговые дедлайны) пока что не могут.
Скажу оговорюсь, что в нашем подходе запрещены горизонтальные связи между функциональными модулями. Их связыванием занимается таргет приложения (и в идеальном мире сборка модулей и обеспечение переходов между ними должны быть практически единственными его задачами). Экспериментальные модули могут брать на себя эту ответственность, если нужно быстро собрать какую-то функциональность на основе имеющихся фреймворков с “подмешиванием” дополнительной бизнес-логики.Яркий пример экспериментального модуля — интеграция приложения с какой-нибудь новой фичей в iOS, например с авторизацией в приложении с помощью Apple ID. Предположим, менеджеры очень хотят выпустить поддержку этой функциональности в день релиза новой версии iOS, чтобы Apple добавила нас в списки самых классных приложений с Apple ID. Но они пока не уверены в дальнейшей судьбе этой фичи. Что мы делаем?
- Создаём новый экспериментальный модуль.
- Подключаем к нему функциональный модуль авторизации.
- Реализуем возможность входа по Apple ID на основе имеющегося кода.
- …
- Профит!
Если в дальнейшем функциональность получит развитие, мы потратим некоторое время на реорганизацию зависимостей и перенос функциональности в отдельный новый или уже существующий модуль. В противном случае мы просто удалим экспериментальный модуль и забудем о нём.Какой он, идеальный функциональный модуль?![Клик для увеличения](https://linkme.ufanet.ru/box/200x100/9d1d996e96635c6ffeb0c2312f412fc9.png)
Для себя мы решили, что в идеале каждый фичемодуль должен состоять из трёх подмодулей:
- Логика. Здесь можно найти исключительно бизнес-логику фичи, базирующуюся на платформе (сеть, кеш, аналитика и т. д.), которая вдоль и поперёк покрывается юнит-тестами.
- Пользовательский интерфейс. Это отдельный подмодуль для всех view-компонентов разрабатываемой функциональности. В зависимостях у него только платформенные UIKit и дизайн-система.
- Верхнеуровневый подмодуль, занимающийся сборкой логики воедино и подстановкой её в UI. Также он предоставляет удобный публичный интерфейс для пользователей модуля. Для простоты назовём этот подмодуль интерфейсным.
Мы старались оставить место гибкости в том, как мы подходим к организации взаимодействия модулей (как вы уже могли заметить по описанию экспериментальных модулей). Поэтому для простых функциональных модулей разработчик может использовать упрощённую схему подмодулей:![Клик для увеличения](https://linkme.ufanet.ru/box/200x100/c1b47300c5982149bc640eeae9876089.png)
Иногда весь модуль помещается на одном экране (например, модуль с просьбой обновить приложение), его логика — это всего пара запросов. В таких случаях, основываясь на здравом смысле, разработчик может объединить интерфейсный подмодуль с подмодулем логики, позволив логике импортировать UI для сборки компонента. Как видите, даже для простых модулей мы решили делать UI-подмодуль отдельным. Резонный вопрос: почему?![Клик для увеличения](https://linkme.ufanet.ru/box/200x100/26d2fdd7dc21ebb31daa16393f5dd0e1.png)
Потому что это позволило нам создать приложение UIGallery, в которое импортированы все UI-подмодули. В них нет бизнес-логики и зависимостей от платформы — плюсов масса:
- возможность работать над UI в изоляции: можно разработать пользовательский интерфейс нового модуля целиком, подставляя фейковые данные или обработчики действий пользователя;
- отображение на одном экране компонента, сконфигурированного для разных приложений (мы делаем это с помощью стилей, подробнее об этом можно прочитать в статье моего коллеги Андрея Символокова);
- автоматическое добавление теста визуального снимка (Visual Regression Test, или VRT) каждого компонента. Думаю, это главное преимущество нашего инструмента: все компоненты приложения становится невозможно случайно изменить. Мы уже несколько раз обновляли нашу дизайн-систему, и VRT помогали на очень ранней стадии идентифицировать проблемы с новыми элементами: неправильные изображения, слабоконтрастирующие цвета текста и фона и т. д.
Прежде чем перейти к описанию реализации, поделюсь фактами о процессе модуляризации в приложениях Badoo и Bumble:
- всего у нас 110 модулей на два приложения;
- мы работаем в кросс-функциональных командах, каждая из которых отвечает за свой набор модулей;
- код для одной и той же функциональности не дублируется;
- мы быстро интегрируем в наши приложения готовые функциональные модули;
- у нас все мыслят модулями: от менеджеров по продуктам до сотрудников отдела непрерывной интеграции.
Подготовка к новым процессамМы знали, что будем запускать всё больше новой функциональности, которая необходима в нескольких приложениях. Но уверенности в том, что модуляризация спасёт нас от всех проблем, не было. Поэтому мы начали с эксперимента.На тот момент у нас было три приложения с чатами, причём в каждом была своя реализация. Мы решили взять готовую функциональность чата из Badoo, чтобы на её основе создать единый переиспользуемый модуль.![Клик для увеличения](https://linkme.ufanet.ru/box/200x100/f4264b6047ebe36867aacf8df8145241.png)
Определившись с областью эксперимента, мы собрали команду, в которую вошли не только разработчики, но и менеджер по продукту, курировавший проект на всех стадиях. На следующем этапе произвели оценку и планирование.![Клик для увеличения](https://linkme.ufanet.ru/box/200x100/f9f1a9d7f11ca34bec4682a7295772fb.png)
На изображении выше — один из этапов проекта: планирование помогло нам управлять ожиданиями практически с нулевой погрешностью. Описания отдельных задач скрыты (их было порядка 100), но поверьте, что они были детальными, с обозначением сроков и зависимостей. Важно оценивать, сколько времени займёт решение той или иной задачи т. к. на ретроспективе это поможет понять, где скрываются подводные камни.![Клик для увеличения](https://linkme.ufanet.ru/box/200x100/8f8a2b8f379a61b3814aa45ea577829b.png)
Например, задачи, помеченные цифрой 1, относились к разделению и выносу отдельных и довольно независимых компонентов, таких как пузырь вокруг сообщения. Даже нам, людям, знакомым с кодом, показалось, что это сложно. В итоге мы переоценили сроки решения этих зачад примерно в пять раз. Задачи из следующей группы, напротив, казались нам пустяковым делом. Они были связаны с компонентами, которые тесно взаимодействуют с клавиатурой. В результате мы «промахнулись» почти на две недели, зато узнали много нового о том, как клавиатура на самом деле взаимодействует с окружением.Главный вывод, который вы должны сделать из этой истории, — нужно документировать свои шаги во время любых экспериментов. Это поможет вам и, главное, вашим коллегам правильно оценивать ситуацию и не наступать на старые грабли.Когда планирование было завершено, мы взялись за реализацию проекта. Разработка новой функциональности в чате в это время не останавливалась, то есть мы выносили чат в модуль параллельно с его развитием. В какой-то степени это усложняло процесс, но, повторюсь, детальный план действий по выносу модуля позволял разработчикам новой функциональности ориентироваться в том, что можно менять, что — не стоит, как менять и т. д. Если мы видели, что пересекаемся в какой-то области работ, вопрос либо решался локально, либо передавался для решения продуктовым менеджерам.Что было дальше?
- Чат Badoo мы разбили на фреймворки (в структуре функционального модуля были выделены Chat, ChatUI и ChatService).
- Интегрировали модуль чата в Bumble. Это была самая болезненная часть проекта, так как публичный интерфейс модуля, который подходил Badoo, не совсем подходил Bumble. Пришлось многое менять, причём не только в Bumble, но и в Badoo. К счастью, мы справились.
- Чтобы интегрировать чат в третье приложение, мы пригласили его разработчиков. Мы не занимались этим сами — нашей задачей было лишь контролировать процесс. Хотелось понять, какие ещё проблемы возникают, и сгладить все шероховатости.
- Интеграция чата в ещё одно наше экспериментальное приложение заняла около трёх дней, а один из разработчиков на внутреннем хакатоне добавил его в прототип приложения меньше чем за сутки.
Результаты эксперимента
- Готовая документация. По факту у нас был технический план по выделению нового модуля, мы примерно представляли возможные проблемы и варианты их решения.
- Работающий пример в репозитории. Можно было увидеть правильную конфигурацию, посмотреть в истории коммитов, как создавать зависимости и решать проблемы.
- Многие разработчики поучаствовали в процессе и примерно понимали, как всё работает. Изначально сформировалось два лагеря: те, кто выделяет общее и старается не сильно раздувать этот компонент, и те, кто разрабатывает новую функциональность и кому поместить что-то внутрь удобнее, чем заворачивать всё в интерфейсы, внедрять зависимости и т. д. Благодаря этому увеличивалась вовлечённость разработчиков, а в споре рождалась истина.
- Все понимали, чего ожидать в будущем. Вовлечённые проектный и продуктовый менеджеры, команда автоматизации QA, разработчики — все видели, как разработка в общих модулях затрагивает их сферу ответственности. Это позволило определить некоторые проблемы заранее.
Найденные проблемыПеред тем как раскатывать процесс модуляризации на весь департамент iOS-разработки, мы выявили и решили несколько проблем. Унификация конфигураций сборки всех модулейПервая и, я бы сказал, фундаментальная проблема — управление конфигурациями модулей. Когда у вас появится, например, 50 модулей и вы захотите изменить какой-либо флаг компилятора Swift для всего проекта, вам станет грустно. Придётся для всех модулей вручную проходить по Build Settings и расставлять флажки. Это неприятно, долго, к тому же есть велика вероятность допустить ошибку и потом ещё столько же времени разбираться, почему проект не компилируется.Большим минусом является и то, что вы не сможете быстро ответить на вопросы: а как мы всё собираем? везде ли у нас одна и та же версия Swift? везде ли включён/выключен Bitcode? и т. д. Без этой информации, а также без возможности быстро менять конфигурации мы сильно ограничены в экспериментах. А мы этого не любим.Когда мы осознали проблему, у нас уже был зоопарк настроек: отключённые предупреждения компилятора, обрезанные debug-символы, Bitcode в debug-конфиге и т. д. Что со всем этим делать?![Клик для увеличения](https://linkme.ufanet.ru/box/200x100/068013e7d095a08e0abee78558354837.png)
Маленькая оговорка. Многие (если не все) знают про замечательный инструмент CocoaPods. Он позволяет линковать между собой Development-поды, которыми могут быть представлены ваши функциональные модули, и с помощью post_install-хуков прописывать одинаковые конфигурации сборки. Но мы решили не идти по этому пути, потому что любой шаг в сторону от того, как задуман CocoaPods, заставил бы нас отказаться либо от инструмента, либо от своей идеи. Я, например, с интересом наблюдаю, как по-разному решается из версии в версию вопрос статической линковки. Если вы считаете, что CocoaPods — зрелый Enterprise-инструмент для управления внутренними зависимостями, напишите об этом в комментариях. Мне интересны ваши аргументы.Но вернёмся к вопросу унификации конфигураций модулей. Для нас решение лежало на поверхности — это xcconfig’и. Мы уже пользовались ими в платформенных модулях и теперь решили расширить этот подход на функциональные модули. Кратко об xcconfig:
- Текстовый файл с настройками, хранящийся вне файла проекта Xcode (xcodeproj/project.pbxproj).
- Поддерживает вложенность. Написанные один раз настройки можно переиспользовать с помощью директивы #include.
- Поддержка настроек как на уровне проекта, так и на уровне отдельных таргетов.
Мы создали в репозитории отдельный проект, в котором лежат все обобщённые конфигурации с версиями приложений, базовыми настройками компилятора, дополнительными настройками для разных build-конфигураций (Debug, Release, Production и других). Здесь же мы храним верхнеуровневые конфигурации для функциональных модулей и самих приложений.![Клик для увеличения](https://linkme.ufanet.ru/box/200x100/c58b5b186bdc31aed90e7c35804aad1d.png)
В самих модулях содержится минимальное количество настроек: относительный путь к репозиторию для корректного импорта глобальных настроек и базовые Bundle ID, Info.plist, modulemap:![Клик для увеличения](https://linkme.ufanet.ru/box/200x100/27743ea12f971f0c567828e4ba757c41.png)
Если вы будете переводить на общие xcconfig давно существующий проект, это может оказаться сложной и трудоёмкой задачей, но вы однозначно получите ряд преимуществ:
- Унифицированные настройки дают возможность экспериментировать и поддерживают репозиторий в консистентном состоянии.
- Минимальный риск совершить ошибку при обновлении конфигураций.
- Возможность передать ответственность за поддержку и контроль параметров проекта одной команде или конкретному разработчику.
- Любые изменения хорошо видны в системах контроля версий.
В конечном счёте мы пришли к тому, что полностью запретили на уровне Git pre-commit hooks внесение изменений в Build Settings проекта.Явные зависимостиЕщё одна проблема, с которой мы столкнулись на этапе эксперимента, — неявные зависимости по умолчанию. Когда ваше дерево зависимостей начинает расти, знание о том, что от чего зависит, становится более ценным. Из коробки Xcode предпочитает использовать неявные зависимости.![Клик для увеличения](https://linkme.ufanet.ru/box/200x100/fea1b8a78cce5e844943665e2f4c6cc5.png)
Это означает, что система сборки анализирует все модули, входящие в воркспейс, из них выстраивает build-план и на основе своих эвристик собирает ваше приложение. Яркий пример использования неявных зависимостей — то, как работает CocoaPods, когда после установки подов он радостно сообщает, что Xcodeproj «больше не работает, пожалуйста, используйте xcworkspace».![Клик для увеличения](https://linkme.ufanet.ru/box/200x100/2f16b4da3794c9b4f5c7a05ca8c55dae.png)
После перехода на явные зависимости система сборки больше не будет принимать эвристические решения относительно того, что собирать, в каком порядке и т. д. Граф зависимостей модулей чётко определяет приоритеты сборки и план по сборке основного файла приложения.![Клик для увеличения](https://linkme.ufanet.ru/box/200x100/1eb7dfb2b6dc77cb90eaf4292acd5db9.png)
Если что-то «и так работает», любые дополнительные действия выглядят как лишние телодвижения: зачем усложнять жизнь себе и разработчикам? В ответ на этот вопрос я могу привести сразу несколько доводов.Во-первых, с увеличением количества модулей вам будет становиться важно, что от чего зависит. Вносить изменения проще, когда вы понимаете, что они могут затронуть. Во-вторых, без явных зависимостей вероятность ошибки довольно велика. Например, у Xcode есть такая интересная особенность: при запуске приложения в симуляторе он автоматически добавляет во Framework Search Paths папку Derived Data.![Клик для увеличения](https://linkme.ufanet.ru/box/200x100/5129e352574aaf644b81671d6f5537c2.png)
В таком случае с неявными зависимостями ошибки связывания модулей легко пропустить (конечно, если вы не тестируете приложение на реальных устройствах перед мёржем изменений в основную ветку).В-третьих, явные зависимости — единственная возможность построить граф зависимостей прямо из файлов проектов. Эта тема будет освещена в следующей статье, а пока просто обозначим, что это полезная опция.Наконец, при удалении промежуточных зависимостей подход заставляет явно указывать модули, которые хочется использовать. Например, в текущей схеме приложение использует и чат, и платформенный модуль. В случае неявных зависимостей удаление модуля чата позволит неявно продолжать использовать платформу. В случае же явных зависимостей компилятор заставит указать платформу как зависимость.Автоматизация создания новых модулейНа этапе проверки гипотезы мы столкнулись с ещё одним интересным вызовом — автоматизацией создания модулей. Мы не подумали об этом сразу, но быстро осознали, что это станет проблемой. Причин уйма:
- Xcode практически не предоставляет инструменты для автоматизации. Создание и поддержка кастомных шаблонов не выглядят простыми задачами.
- Настройка нового модуля — повтор однотипных шагов. В целом всё сводится к тому, что вы пытаетесь получить максимально похожее на эталон состояние вашего проекта.
- По пути к эталону можно что-то забыть, где-то промахнуться и т. д. Вероятность ошибок велика.
- Недовольство разработчиков. На самом деле, среднестатистический разработчик — творческая личность. Когда для создания нового модуля нужно сидеть и несколько часов кликать мышкой, повторяя инструкцию, или, что ещё хуже, просто смотреть на соседний модуль и пытаться сделать так же, опускаются руки.
Что мы предприняли? Решили проблему радикально — написали свой Swift-скрипт, который знает обо всех наших внутренних соглашениях и генерирует новые модули, принимая на вход только название и относительный путь. Сначала это было Standalone-решение на основе XcodeGen. Но в процессе развития скрипт стал частью нашего инструмента Deps, о котором мы тоже поговорим в следующей статье. Сейчас создание нового модуля выглядит так:![Клик для увеличения](https://linkme.ufanet.ru/box/200x100/ced7277f59941833bf9f0e08c2a1b0dd.png)
![Клик для увеличения](https://linkme.ufanet.ru/box/200x100/0ea11a5b7584db703ea2864a484f7f86.png)
На выходе мы имеем правильный путь, структуру и конфигурацию проекта, основанную на xcconfig. С точки зрения перехода на модуляризацию мы также получили ряд преимуществ:
- Создание нового модуля стало занимать минуты, а не часы.
- Все модули имеют унифицированную структуру.
- Все настройки сборки унаследованы от общих xcconfig’ов.
- Низкий порог входа для новых разработчиков.
- За утилиту отвечают конкретные люди.
Вместо выводовКак вы поняли, проверка концепции модуляризации с помощью эксперимента на реальной функциональности приложения удалась. Мы получили документированное решение для дальнейшего масштабирования процесса и перевели проекты на xcconfig, чтобы воплощать в жизнь дальнейшие планы было легче. Включение явных зависимостей позволило нам получить прозрачную структуру проектов, а Swift позволил автоматизировать процесс создания новых модулей, сократив сроки до минут. Казалось, мы готовы масштабировать наше решение на весь департамент, но некоторые вещи мы всё же не учли…Здесь я бы хотел остановить наш рассказ и продолжить его во второй части. Увидимся!
===========
Источник:
habr.com
===========
Похожие новости:
- [Программирование, C++] Небольшие, но важные функции (перевод)
- [Программирование, Управление разработкой, Управление персоналом, Карьера в IT-индустрии] О плюсах парного программирования
- [Программирование, CRM-системы] Интегрируем web-телефон в свою систему
- [Python, Программирование] Как работает память в Python
- [Разработка под iOS, Разработка мобильных приложений, Разработка под Android] Как ВТБ помогает снизить комиссию за приём платежей до 0,4% с помощью QR-кода
- [Разработка под iOS, ООП] Зачем нужно понимать ООП
- [C++, Компиляторы, C, Разработка под MacOS, Процессоры] Компиляция C/C++ на Apple M1 (перевод)
- [Разработка под Linux, Настольные компьютеры, Ноутбуки, IT-компании] Разработчик запустил на краудфандинговой платформе Patreon сбор денег на адаптацию Linux для Maс на M1
- [Компьютерное железо, Настольные компьютеры, IT-компании] Apple объявила первые модели iMac с дисплеем 5K винтажными (перевод)
- [Высокая производительность, JavaScript, Программирование, TypeScript] Производительность TypeScript (перевод)
Теги для поиска: #_programmirovanie (Программирование), #_razrabotka_pod_ios (Разработка под iOS), #_objective_c, #_swift, #_ios, #_cocoapods, #_modules, #_modularity, #_apple, #_opyt (опыт), #_swift, #_objectivec, #_blog_kompanii_badoo (
Блог компании Badoo
), #_programmirovanie (
Программирование
), #_razrabotka_pod_ios (
Разработка под iOS
), #_objective_c, #_swift
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 07-Июл 07:20
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 4 месяца |
|
![]() В iOS-разработке Badoo мы уже несколько лет занимаемся созданием модулей, и большая часть нового кода разрабатывается вне кодовой базы приложений. Сейчас у нас более 100 модулей для Badoo и Bumble. В этой статье я расскажу о нашем опыте и отвечу на самые популярные вопросы о модуляризации:
![]()
![]() Для себя мы решили, что в идеале каждый фичемодуль должен состоять из трёх подмодулей:
![]() Иногда весь модуль помещается на одном экране (например, модуль с просьбой обновить приложение), его логика — это всего пара запросов. В таких случаях, основываясь на здравом смысле, разработчик может объединить интерфейсный подмодуль с подмодулем логики, позволив логике импортировать UI для сборки компонента. Как видите, даже для простых модулей мы решили делать UI-подмодуль отдельным. Резонный вопрос: почему? ![]() Потому что это позволило нам создать приложение UIGallery, в которое импортированы все UI-подмодули. В них нет бизнес-логики и зависимостей от платформы — плюсов масса:
![]() Определившись с областью эксперимента, мы собрали команду, в которую вошли не только разработчики, но и менеджер по продукту, курировавший проект на всех стадиях. На следующем этапе произвели оценку и планирование. ![]() На изображении выше — один из этапов проекта: планирование помогло нам управлять ожиданиями практически с нулевой погрешностью. Описания отдельных задач скрыты (их было порядка 100), но поверьте, что они были детальными, с обозначением сроков и зависимостей. Важно оценивать, сколько времени займёт решение той или иной задачи т. к. на ретроспективе это поможет понять, где скрываются подводные камни. ![]() Например, задачи, помеченные цифрой 1, относились к разделению и выносу отдельных и довольно независимых компонентов, таких как пузырь вокруг сообщения. Даже нам, людям, знакомым с кодом, показалось, что это сложно. В итоге мы переоценили сроки решения этих зачад примерно в пять раз. Задачи из следующей группы, напротив, казались нам пустяковым делом. Они были связаны с компонентами, которые тесно взаимодействуют с клавиатурой. В результате мы «промахнулись» почти на две недели, зато узнали много нового о том, как клавиатура на самом деле взаимодействует с окружением.Главный вывод, который вы должны сделать из этой истории, — нужно документировать свои шаги во время любых экспериментов. Это поможет вам и, главное, вашим коллегам правильно оценивать ситуацию и не наступать на старые грабли.Когда планирование было завершено, мы взялись за реализацию проекта. Разработка новой функциональности в чате в это время не останавливалась, то есть мы выносили чат в модуль параллельно с его развитием. В какой-то степени это усложняло процесс, но, повторюсь, детальный план действий по выносу модуля позволял разработчикам новой функциональности ориентироваться в том, что можно менять, что — не стоит, как менять и т. д. Если мы видели, что пересекаемся в какой-то области работ, вопрос либо решался локально, либо передавался для решения продуктовым менеджерам.Что было дальше?
![]() Маленькая оговорка. Многие (если не все) знают про замечательный инструмент CocoaPods. Он позволяет линковать между собой Development-поды, которыми могут быть представлены ваши функциональные модули, и с помощью post_install-хуков прописывать одинаковые конфигурации сборки. Но мы решили не идти по этому пути, потому что любой шаг в сторону от того, как задуман CocoaPods, заставил бы нас отказаться либо от инструмента, либо от своей идеи. Я, например, с интересом наблюдаю, как по-разному решается из версии в версию вопрос статической линковки. Если вы считаете, что CocoaPods — зрелый Enterprise-инструмент для управления внутренними зависимостями, напишите об этом в комментариях. Мне интересны ваши аргументы.Но вернёмся к вопросу унификации конфигураций модулей. Для нас решение лежало на поверхности — это xcconfig’и. Мы уже пользовались ими в платформенных модулях и теперь решили расширить этот подход на функциональные модули. Кратко об xcconfig: ![]()
![]() В самих модулях содержится минимальное количество настроек: относительный путь к репозиторию для корректного импорта глобальных настроек и базовые Bundle ID, Info.plist, modulemap: ![]() Если вы будете переводить на общие xcconfig давно существующий проект, это может оказаться сложной и трудоёмкой задачей, но вы однозначно получите ряд преимуществ:
![]() Это означает, что система сборки анализирует все модули, входящие в воркспейс, из них выстраивает build-план и на основе своих эвристик собирает ваше приложение. Яркий пример использования неявных зависимостей — то, как работает CocoaPods, когда после установки подов он радостно сообщает, что Xcodeproj «больше не работает, пожалуйста, используйте xcworkspace». ![]() После перехода на явные зависимости система сборки больше не будет принимать эвристические решения относительно того, что собирать, в каком порядке и т. д. Граф зависимостей модулей чётко определяет приоритеты сборки и план по сборке основного файла приложения. ![]() Если что-то «и так работает», любые дополнительные действия выглядят как лишние телодвижения: зачем усложнять жизнь себе и разработчикам? В ответ на этот вопрос я могу привести сразу несколько доводов.Во-первых, с увеличением количества модулей вам будет становиться важно, что от чего зависит. Вносить изменения проще, когда вы понимаете, что они могут затронуть. Во-вторых, без явных зависимостей вероятность ошибки довольно велика. Например, у Xcode есть такая интересная особенность: при запуске приложения в симуляторе он автоматически добавляет во Framework Search Paths папку Derived Data. ![]() В таком случае с неявными зависимостями ошибки связывания модулей легко пропустить (конечно, если вы не тестируете приложение на реальных устройствах перед мёржем изменений в основную ветку).В-третьих, явные зависимости — единственная возможность построить граф зависимостей прямо из файлов проектов. Эта тема будет освещена в следующей статье, а пока просто обозначим, что это полезная опция.Наконец, при удалении промежуточных зависимостей подход заставляет явно указывать модули, которые хочется использовать. Например, в текущей схеме приложение использует и чат, и платформенный модуль. В случае неявных зависимостей удаление модуля чата позволит неявно продолжать использовать платформу. В случае же явных зависимостей компилятор заставит указать платформу как зависимость.Автоматизация создания новых модулейНа этапе проверки гипотезы мы столкнулись с ещё одним интересным вызовом — автоматизацией создания модулей. Мы не подумали об этом сразу, но быстро осознали, что это станет проблемой. Причин уйма:
![]() ![]() На выходе мы имеем правильный путь, структуру и конфигурацию проекта, основанную на xcconfig. С точки зрения перехода на модуляризацию мы также получили ряд преимуществ:
=========== Источник: habr.com =========== Похожие новости:
Блог компании Badoo ), #_programmirovanie ( Программирование ), #_razrabotka_pod_ios ( Разработка под iOS ), #_objective_c, #_swift |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 07-Июл 07:20
Часовой пояс: UTC + 5