Русский
Русский
English
Статистика
Реклама

Онлайн сервисы

В карантин нагрузка выросла в 5 раз, но мы были готовы. Как Lingualeo переехал на PostgreSQL с 23 млн юзеров

17.08.2020 20:19:27 | Автор: admin
image

Проекту Lingualeo уже 10 лет. Более 23 миллионов человек из России, Турции, Испании и стран Латинской Америки учат с помощью нашего сервиса английский.

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

Мы попросили нашего лидера бэкэнд разработки, Олега Правдина, рассказать о том, как они с командой параллельно с поддержкой основного продукта собрали новую модульную структуру сервиса на базе PostgreSQL, перенесли бизнес-логику в базы данных и провели миграцию с миллионами пользователей.

Проблемы зрелого продукта


Я пришёл в Lingualeo в августе 2018 руководить бэкэнд разработкой. Тогда бэком занималась команда из 8 разработчиков и 2 админов, которые обслуживали монолит на 1 миллион строк кода преимущественно на PHP. Чтобы внедрить даже небольшую новую фичу, уходило 2 месяца. А затраты на инфраструктуру на 10 000 активных пользователей превышали 1 000 $ в год.

Как это произошло? Дело в том, что за 10 лет в проекте сменилось несколько команд разработки. Приходили новые люди, как и я, они по-своему добавляли новые модули и фичи. Команды менялись, новички не всегда понимали, как работают старые части системы, в итоге код Lingualeo постепенно превратился в чёрный ящик: непрозрачная логика в бэкенде, перегруженный фронт, обилие костылей, большие пробелы в документации.

Всего у нас в штате было 20 разработчиков, но развивать продукт было невозможно: если что-то добавить, вылезали неожиданные проблемы. У команды уходило 23 недели, чтобы всё починить. Разработчики занимались поддержкой кода из 2013 года, и ресурсов на обновление функциональности не было.

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

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

Шаг 1. Собрали прототип новой архитектуры


Нам предстояло придумать, как обновить техническую составляющую сервиса и заново выстроить Lingualeo на современных технологиях. Я предложил руководству полностью сменить философию бэкенда: перенести бизнес-логику в базу данных, а саму базу данных MySQL заменить на PostgreSQL.

Я начал с прототипа на бумаге: нарисовал новую архитектуру, объяснил, как получится увеличить производительность и сколько ресурсов уйдёт на подготовку. Защищать проект было сложно, потому что под рукой не было однозначных историй успеха: никто не пишет о том, как провести миграцию сервиса с 20 миллионами пользователей без остановки бизнеса. Но в Lingualeo решили пойти на риск и одобрили план изменений.

Схема архитектуры до миграции

image

Запросы с фронта приходят в монолит на PHP, он обрабатывает их, многократно обращается к БД в MySQL и получает оттуда сырые данные. Затем обрабатывает их и формирует ответы в JSON

Схема новой архитектуры

image

Меняем SQL на PostgreSQL, переносим туда расчёты и создание JSON. Убираем монолит и ставим вместо него прокси-сервис, который направляет запросы в нужную базу данных, а затем отдаёт готовый JSON на фронт

Шаг 2. Обновили команду

Когда мы поделились планами с разработчиками, стало понятно, что команда не готова к изменениям. Большинство людей покинули компанию: остались только те, кто пришёл совсем недавно. Чтобы провести миграцию, мы решили заново собрать команду разработки.

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

Некоторых людей нашли случайно, как, например, и меня: я познакомился с CEO Lingualeo Владимиром Сиротинским в самолёте. Будущего лидера фронтэнда Владимир встретил на консультации другого стартапа. Но большинство новых разработчиков мы набрали с рынка. Чтобы закрыть 8 вакансий, мы изучили 1118 откликов и провели 124 собеседования:

image

Воронка кандидатов на вакансии новых разработчиков в Lingualeo. Пример тестового задания

Шаг 3. Упростили оргструктуру


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

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

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

image

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

Шаг 4. Перенесли бизнес-логику в базы данных


Раньше бизнес-логика Lingualeo была на фронте и в приложениях. Функции продукта решались системами, которые не предназначены для обработки данных, например, в коде на JavaScript или PHP. Поэтому мы перенесли бизнес-логику Lingualeo в базы данных на PostgreSQL.

Джунгли


Один из 4 главных разделов сервиса Lingualeo Джунгли. Это набор материалов на иностранном языке текстов, аудио и видео, в которых к любому слову можно узнать перевод. То есть, пользователи изучают реальный контент на английском, а если что-то непонятно, могут кликнуть на слово в тексте или в субтитрах к видео и посмотреть перевод.

Текст в Джунглях

image

Видео в Джунглях

image

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

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

Мы реализовали эту функцию в базе данных. Бэк передаёт на фронт готовый JSON, в котором текст уже разбит на слова и выражения. Каждому слову и выражению в базе присвоен ID, по которому легко найти перевод. Также в JSON учитывается, какие слова есть в словаре у пользователя, а каких пока нет. На фронте остаётся просто отобразить информацию и подсветить слова с определёнными признаками.

Словари


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

Раньше логика словарей была на стороне фронта или в прослойке на PHP, а сейчас в системе появился полноценный API между фронтом и бэкендом. Можно отправить один запрос с большим количеством параметров в базу данных, и оттуда придёт готовый JSON:

image

Фильтры словаря: поиск по словам, выбор слов по видам тренировки, выбор между словами и фразами, фильтр по изученным и новым словам

Курсы


Перенос бизнес-логики в базу данных многократно уменьшил количество кода и ускорил работу сервиса. Например, изменился бэкенд-код страницы Курсов после миграции. Её видят зарегистрированные пользователи, а курсы туда отбираются системой по десятку критериев. Раньше такая страница формировалась 600 мсек и отправляла 12 запросов в базу данных, сейчас всего один:

image
image

Шаг 5. Учли обратную связь пользователей после релиза


На разработку ушло примерно полгода: мы занялись обновлениями в конце 2018, а релиз состоялся в мае 2019 года. Большинство пользователей ощутили, что сервис стал работать гораздо быстрее. Раньше в Lingualeo могли без потери скорости заниматься не более 2 000 человек одновременно, а сейчас система выдерживает пики больше 100 000 пользователей.

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

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

Lingualeo стал быстрее для пользователей и удобнее для разработчиков


В апреле 2020 во время самоизоляции нагрузка на Lingualeo была в пять раз больше по сравнению с аналогичным периодом год назад. Это не вызвало проблем: скорость работы сервиса не просела, пользователи ничего не заметили. Я уверен: если бы мы не обновили систему, сервис бы просто не выдержал.

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

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

Наконец, продукт стал безопаснее. Раньше, когда вся бизнес-логика была в прослойке на PHP, оттуда из разных функций шли запросы в базу данных. Открытая для SQL-запросов база данных это проблема: можно сделать SQL-инъекцию и заставить её выполнить опасный код, например, удаление данных. Сейчас снаружи не приходит ни одного SQL-запроса, потому что мы перенесли всю логику внутрь.

Мы хотим и дальше регулярно писать в блог о том, как устроена разработка в обновлённом Lingualeo. Пишите в комментариях, о чём стоит рассказать в первую очередь: у нас поменялась и команда, и структура управления, и технологии. Будем рады ответить на все вопросы.
Подробнее..

Когда имеет смысл писать кроссплатформенные приложения появление и исчезновение React Native в Lingualeo

15.09.2020 10:12:59 | Автор: admin

В приложениях Lingualeo сложился довольно редкий кейс. Их создали до того, как появились кроссплатформенные технологии, но через несколько лет туда добавили модули на React Native. Кроссплатформенные модули прожили в приложениях примерно четыре года: в ближайшем релизе мы их уберём.

Мы попросили лидера мобильной разработки Артёма Рыжкина (phoenix_rav) рассказать о том, откуда в нативных приложениях Lingualeo появились модули на React Native, какие они вызывали проблемы и когда вообще имеет смысл делать кроссплатформенные приложения.

Как в приложении Lingualeo появились кроссплатформенные элементы

Приложение Lingualeo появилось в русских сторах в 2012 году. Тогда ещё не было кроссплатформенных технологий, приложение для iOS написали на Objective-C, а для Android на Java. Их поддерживали и развивали две отдельные команды.

Со временем приоритеты в компании несколько раз менялись. После 2015 в Lingualeo сделали упор на веб: в мобильной команде осталось всего несколько разработчиков. В основном они поддерживали и актуализировали версии библиотек, и редко добавляли новое.

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

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

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

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

Для работы выбрали React Native. Помимо него рассматривали несколько других кроссплатформенных технологий, например, подключаемые библиотеки, игровые движки и Webview. Все варианты оценили по скорости, удобству и качеству разработки, и в итоге выбрали React Native.

К тому же в тот момент веб переезжал на React. В Lingualeo была сильная веб-команда, и на это сделали ставку: решили, что разработчикам, знакомым с React, будет комфортнее и быстрее разобраться с React Native, чем с другой технологией. Шесть игровых тренировок сначала сделали на вебе, потом портировали в приложения.

В итоге когда я пришёл в команду в 2018 году, то увидел интересную ситуацию. Как минимум я с таким раньше не сталкивался: в компании нативные приложения на iOS и Android, но в них есть шесть тренировок, написанных на стороннем фреймворке, языке React Native. Получается, приложения по большей части нативные, но в чём-то кроссплатформенные.

Почему кроссплатформенные элементы в нативных приложениях это проблема?

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

React Native не позволяет полностью отказаться от нативного кода, заменив его кроссплатформенным. Для некоторых функций, например, проигрывания музыки, обращения к сенсорам устройства или к локальному хранилищу придётся писать так называемые bridge-классы.

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

У нас использовались bridge-классы код на двух нативных языках: Java/Kotlin для Android и Objective-C/Swift для iOS. Мы написали их для обращения к медиаплееру, к аналитическим системам, локальному хранилищу и логике авторизации.

Например, вот как bridge-класс запускает Intent Service в Android:

public class LLMergeBridgeModule extends ReactContextBaseJavaModule {

protected static final String MODULENAME = "LLMergeBridgeModule";

@Override

public String getName() {

return LLMergeBridgeModule.MODULENAME;

}

public LLMergeBridgeModule(ReactApplicationContext reactContext) {

super(reactContext);

}

@ReactMethod

public void merge() {

ReactApplicationContext context = getReactApplicationContext();

SyncService.startServiceForceSync(context);

}

}

У разработчика также могут возникнуть проблемы, если на кроссплатформенном фреймворке необходимо реализовать кастомную логику обработки жизненного цикла экрана Activity в Android или ViewController в iOS.

В нативных платформах со временем меняется логика работы: если в команде только разработчик на React Native, ему придётся следить ещё за двумя платформами. Когда выходят новые версии Android или iOS, нужно переписывать bridge-классы, тратить дополнительное время на поддержку. Разработчику нужно следить не только за развитием кроссплатформенного фреймворка, на котором он работает, но и за обновлениями обеих мобильных платформ.

Сложно искать разработчиков: нужен дорогой универсальный специалист или несколько разработчиков разного профиля. Если вы хотите создать приложение на React Native, можно найти разработчика, который знает одновременно iOS, Android и React Native.

Второй вариант искать 23 разработчиков, каждый из которых разбирается хотя бы в одной платформе. Но тогда усложняется отладка приложения. Например, разработчик, который знает React Native и Android, будет постоянно обращаться за помощью к коллеге, который знает iOS. Это замедляет процесс.

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

Например, на момент внедрения React Native у нас уже было два проекта для локализации строк в системе переводов. Один работал для приложения на Android, другой для iOS. Каждый из проектов учитывал особенности своей платформы.

При подключении React Native нужно было завести третий проект, но тогда бы сложилась ситуация дублирования строк: одна и та же строка была бы заведена в 2 проекта сразу. Это бы потребовало дополнительного труда переводчиков. Поэтому мы решили использовать маппер для преобразования строк из iOS-проекта в формат для React Native.

С августа 2019 все приложения Google Play должны обязательно поддерживать 64-битную архитектуру. Если для нативного приложения такая поддержка включалась парой строчек кода, то для React Native пришлось мигрировать на последнюю версию. Нативный разработчик несколько недель разбирался в коде, чтобы адаптировать его под новую версию.

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

Как мы вернулись к полностью нативным приложениям

Сначала мы наняли для этой задачи аутсорсера. Мы предположили, что сторонняя команда справится быстрее, чем наши разработчики, потому что у нас было много текущих задач. Команда разработки продолжала заниматься приоритетными проектами, а аутсорсеры начали переписывать игровые тренировки с React Native на нативные языки. Первым взялись за версию для iOS.

Работа шла дольше, чем мы предполагали. Аутсорс-команда отдавала нам сборку, которую нужно было проверить, поэтому у нас выросла нагрузка на QA. Мы проверяли сборку, давали комментарии, их брали в работу, но процесс шёл медленно. Миграция одной тренировки в среднем занимала 3 месяца от получения ТЗ до отправки в прод.

Для ускорения процесса мы решили взяться за миграцию тренировок самостоятельно. Версии для iOS закончили аутсорсеры, а Android мы взяли сами. Начали с одной тренировки в экспериментальном режиме: получилось быстрее и дешевле, чем на аутсорсе.

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

Дополнительный плюс от перехода на полностью нативные приложения оказался в том, что размер архива приложения уменьшился на 28 Мб. Также сократилось время сборки: раньше билд релиза Android-приложения на Mac mini занимал до 20 минут на холодном старте, а сейчас 2.

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

В каких случаях выгодно делать кроссплатформенное приложение?

React Native вполне подойдёт для многих проектов. Но, по нашему мнению, его выгоднее всего использовать, если ваша задача одновременно соответствует шести критериям:

1. Это простое приложение. Такое приложение не требует реализации сложной бизнес-логики и сложной UI-логики. Например, витрина интернет-магазина: приложение должно просто отобразить список товаров, дать возможность положить товар в корзину и оплатить покупку.

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

4. Это приложение не работает с медиафайлами, сенсорами и навигацией. React Native удобная технология для мобильных приложений, которым не требуется работать с медиафайлами и множеством сенсоров смартфона.

5. Вам важно быстро запуститься и вносить изменения на лету. Кроссплатформенные технологии отличный вариант для быстрого запуска. React Native будет выгодно использовать, например, стартапам. Скорее всего, кроссплатформенная разработка потребует меньше человеко-часов и предприниматель быстрее получит продукт. Его можно тестировать, показывать инвесторам и при необходимости менять код на лету без пересборки проекта, используя механизм code push.

6. У вас пока нет нативных приложений. Добавление кроссплатформенности в нативные приложения приведёт к дублированию сетевого слоя, логики работы UI, локализации, также появятся проблемы с настройками проекта.

Когда кроссплатформенные решения не выигрывают у нативных?

Есть кейсы, когда React Native точно не будет выгоднее, чем нативное приложение. Вот несколько таких:

В приложении нужны сложные механики с медиа-ресурсами. React Native не справится с проигрыванием медиа без bridge-классов. Если в приложении будут многоступенчатые механики, связанные, например, с воспроизведением музыки, то реализовывать это на React Native может быть сложнее, чем в нативных приложениях.

В Lingualeo такие механики как раз есть в игровых тренировках. Например, в аудиотренировке текст сначала проигрывается до конца, затем разбивается на фрагменты. Пользователь может воспроизводить их и перетаскивать при помощи drag and drop.

Приложение будет много обращаться к сенсорам. Чтобы обратиться, например, к GPS, Bluetooth, акселерометрам, датчику лица или микрофону, коду на React Native также нужны bridge-классы.

В приложении будут встроенные покупки. Внутренние покупки очень важная и чувствительная для пользователей фича в приложении. При её реализации лучше обращаться к нативным библиотекам Google и Apple, чтобы, например, чётко понимать, какие проблемы возникли на этапе покупки и верно обрабатывать состояния. Придётся внедрять в приложение нативный код, который будет обращаться к сторам, и для каждой платформы эти модули будут разными.

Фичеринг приложения и следование гайдлайнам. Если вы хотите активно получать фичеринг от Google Play и Apple Store, то, скорее всего, от команды разработчиков потребуется внедрение новой функциональности, специфичной для каждой из двух платформ. Поддержка подобных фичей в React Native потребует времени со стороны разработчиков, а сторонний node module может появиться с заметным опозданием.

Выводы

React Native мощная, удобная и во многом универсальная технология, но лучше всего она подойдёт для приложений, которые:

  • не имеют нативной кодовой базы: то есть, сразу создаются как кроссплатформенные;

  • основаны на простой логике: им не нужен доступ к медиафайлам и датчикам;

  • не имеют встроенных покупок, например, платного премиум-доступа или игровой валюты;

  • должны запуститься быстро и экономно: например, чтобы показать MVP инвесторам.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru