(Не очень) скрытые издержки общей кодовой базы iOS и Android

в 10:37, , рубрики: android, c++, iOS, java, kotlin, objective-c, swift, разработка мобильных приложений, Разработка под android, разработка под iOS, С++

До недавнего времени у Dropbox была техническая стратегия использовать общий код C++ для мобильных приложений iOS и Android. Идея понятна: написать код один раз на C++ вместо его дублирования отдельно на Java и Objective C. Мы приняли эту стратегию ещё в 2013 году, когда группа инженеров мобильной разработки была относительно небольшой и приходилось быстро развивать продукт. Такое решение позволило выдавать большой объём кода как на Android, так и на iOS силами маленькой команды.

Теперь мы полностью отказались от этой стратегии в пользу родных языков каждой платформы (в первую очередь Swift и Kotlin, которые не существовали, когда мы начинали). Решение связано с (не очень) скрытыми издержками на совместное использование кода.

Все проблемы вытекают из главного: оверхед оказался больше, чем просто написать код два раза.

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

Также стоит отметить, что гораздо более крупные компании, такие как Google и Facebook, уже несколько лет разрабатывают масштабируемые решения для совместного использования кода. Такие решения пока не очень распространены. Хотя сторонние системы вроде React Native или Flutter позволяют избежать части накладных расходов, некоторые издержки всё равно остаются (по крайней мере, до тех пор, пока одна из этих технологий не станет популярной и не созреет в достаточной степени). Например, Airbnb отказалась от использования React Native во многом по тем же причинам, что изложены в этой статье.

Все издержки можно сгруппировать по четырём основным категориям.

Оверхед пользовательских фреймворков и библиотек

Проще всего прогнозировать издержки на создание фреймворков и библиотек. Они примерно разбиваются на две подкатегории:

  • Фреймворки, которые позволяют взаимодействовать с хост-средой для создания полноценного мобильного приложения. Например:
    • Djinni, инструмент для создания межъязыковых объявлений типов и интерфейсов соединения
    • Фреймворк для выполнения задач в фоновом режиме против основного потока (тривиально на родных языках платформы)

  • Библиотеки на замену языковым стандартам или решениям open source, которые можно было использовать в родных языках, например:
    • json11 для (де)сериализации JSON
    • nn, ненулевые указатели для C++

Ничего из этого не нужно, если оставаться на родных языках платформы. И наше участие в проектах open source на родных языках, вероятно, принесло бы больше пользы разработчикам. В сообществе C++ культура open source была (и есть?) не так развита, как в сообществе мобильных разработчиков, тем более что мобильного сообщества C++ практически не существует.

Обратите внимание, что эти издержки особенно высоки для C++ (в отличие от других возможных неродных языков, таких как Python или C#), потому что здесь нет одной полнофункциональной стандартной библиотеки. При этом только у C/C++ есть компилятор, поддерживаемый как Google, так и Apple, поэтому переход на иной язык порождает целый ряд других проблем.

Оверхед нестандартной среды разработки

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

Мне особенно запомнилась одна ошибка, которая вызывала блокировку фоновой потоковой структуры, что приводило к случайным сбоям приложения. Такие ошибки трудно отследить даже с простым, стандартным стеком. Поскольку проблема включала отладку многопоточного кода, работающего между C++ и Java, на её отслеживание ушли недели!

В дополнение к потере стандартного инструментария, пришлось инвестировать время в создание своих инструментов для поддержки общего кода C++. Самое главное, требовалась кастомная система сборки для создания библиотек, которые содержат код C++, а также оболочки Java и Objective-C. Она должна генерировать цели, понятные и Xcodebuild, и Gradle. Создание такой системы отняло у нас много ресурсов, поскольку её приходилось постоянно обновлять для поддержки изменений в двух системах сборки.

Оверхед на устранение различий между платформами

Хотя iOS и Android являются «мобильными приложениями», которые обычно предоставляют одни и те же функции, в самих платформах есть определённые различия, которые влияют на реализацию. Например, как приложение выполняет фоновые задачи. Даже похожие вещи со временем могут начать сильно отличаться (например, взаимодействие с камерой).

В результате нельзя просто так написать код один раз и запустить его на другой платформе. Нужно потратить много времени на интеграцию и кодирование под конкретную платформу, и иногда этот код заканчивается прямо на уровне C++!

Теоретическая экономия от написания кода только один раз не соответствует реальности, что сразу сильно снижает эффективность этого подхода.

Оверхед на найм, обучение и удержание разработчиков

Последнее, но не менее важное, — это стоимость обучения и/или найма разработчиков для работы с нашим очень своеобразным стеком. Когда Dropbox начал использовать эту мобильную стратегию, у нас была основная группа опытных разработчиков на C++. Эта группа запустила проект C++ и обучила других мобильных разработчиков.

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

В результате мы столкнулись с реальной нехваткой критических знаний для поддержания кодовой базы C++. Оставалось только два варианта, и каждый требовал существенных усилий:

  1. Найти и нанять кандидатов с очень специфическим набором навыков (мы безуспешно пытались в течение года).
  2. Обучить собственных мобильных (или C++) разработчиков, что практически невозможно сделать в отсутствие сеньоров с нужным набором навыков для выполнения обучения. Даже когда основная группа ещё не разошлась, мобильные разработчики обычно не интересовались C++, поэтому поиск людей для обучения также представлял большую проблему.

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

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

Вывод

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

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

Автор: m1rko

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js