Хабрахабр

Citymobil — пособие для стартапов по увеличению стабильности на фоне роста. Часть 1

Статья начинается с рассказа про наш бизнес, про задачу, про причину появления самой задачи повышения стабильности и про ограничения. Этой статьей я открываю короткий цикл из двух статей, в которых подробно расскажу, как нам удалось за несколько месяцев в разы увеличить стабильность сервисов Citymobil. За 2018 год он вырос более чем в 15 раз по количеству успешно совершенных поездок. Citymobil — это быстрорастущий агрегатор такси. В некоторые месяцы рост превышал 50 % по сравнению с предыдущим месяцем.

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

1. Формулировка задачи: что конкретно хотим улучшать

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

Но нам одинаково важны все поездки, даже убыточные, потому что они позволяют увеличивать долю рынка (по сути, убыток на поездках — это плата за рост доли рынка). Какие-то поездки приносят прибыль, какие-то — убыток. Все поездки равноправны с точки зрения успешности бизнеса. Поэтому каждая дополнительно полученная поездка — это хорошо, а каждая потерянная — плохо.

Под технической проблемой подразумевается, например, баг в коде, 500-я ошибка, авария на инфраструктуре, сломанная интеграция с сервисом партнеров (например картами Google). Отсюда у нас появился понятный критерий измерения стабильности: количество потерянных поездок — это поездки, которые мы явно потеряли из-за технических проблем.

2. Как считать потерянные поездки?

Потерянные поездки иногда подсчитать просто, иногда сложно. Например, в случае полного отказа в обслуживании, когда вообще ничего не работает (тьфу-тьфу-тьфу), потерянные поездки посчитать очень просто. Мы знаем тренд графика количества поездок до падения, видим тренд этого графика после падения, достраиваем линию между точкой, когда начался простой, и точкой, когда он закончился. Площадь графика количества поездок под этой достроенной линией — это и есть потерянные поездки.

По оси X — время. На нижеследующем графике черной линией показаны поездки в некоторый день и зеленой линией — поездки неделю назад. Виден явный пик вниз в виде остроугольного треугольника. По оси Y — количество поездок в некоторый временной интервал вокруг точки X. Разумеется, это приблизительное количество, т.к. Площадь этого треугольника — это и есть количество потерянных поездок. график флуктуирующий, но мы понимаем, что точности даже 10-20 % нам достаточно, чтобы оценить масштаб аварии для бизнеса.

Например, если есть баг, из-за которого 10 % заказов никогда не распределяются по автомобилям, то мы видим на графике поездок провал, а потом отскок (после исправления бага). Если простой не полный, а частичный (тоже, тьфу-тьфу-тьфу), то подсчет чуть сложнее. В подобной ситуации потерянные поездки — это площадь, ограниченная сверху линией тренда; снизу — фактическим графиком количества поездок, слева — вертикальной линией начала простоя, справа — вертикальной линией окончания простоя.

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

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

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

То есть одна потерянная поездка из-за технических проблем означает, на самом деле, несколько потерянных поездок.

Чтобы не путаться в терминах, назовем поездки, потерянные непосредственно из-за технической проблемы, первичными потерянными поездками, а поездки, потерянные из-за ухода к конкуренту, вторичными потерянными поездками.

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

Пример: если мы в прошлом месяце потеряли 1000 первичных поездок, то вторичных поездок мы потеряли 1000*K, а в сумме потеряли 1000*(1+K). В предположении, что коэффициент K не особо меняется со временем нам для понимания тренда потери поездок достаточно считать первичные потерянные поездки и стремиться уменьшать их число, поскольку отношение первичных потерянных поездок период-к-периоду будет такое же как отношение вторичных потерянных поездок период-к-периоду. При этом вне зависимости от значения коэффициента K мы стали в 1000*(1+K) / (500 * (1+K)) = 2 раза меньше терять поездок. Если, далее, мы в текущем месяце потеряли 500 первичных поездок, то вторичных поездок мы потеряли 500*K, а в сумме потеряли 500*(1+K).

является функцией от времени K(t), то все равно мы заинтересованы в снижении количества первичных потерянных поездок. Даже если коэффициент K со временем изменяется, т.е. урон от потери каждой из них все выше и выше. Потому что, если K(t) растет со временем, то мы тем более обязаны приложить усилия, чтобы меньше терять первичных поездок, т.к. С другой стороны, если K(t) падает со временем, то это означает, что по какой-то причине пользователи нам все более и более лояльны, несмотря на плохой сервис, а значит мы просто обязаны оправдать их ожидания!

Итого: мы боремся за постоянное снижение первичных потерянных поездок.

3. Ок, потерянные поездки считаем. Что дальше?

Вооружившись понятным инструментом измерения потерянных поездок, переходим к самому интересному — как сделать так, чтобы уменьшить потери? И при этом не замедлить текущий рост! Поскольку субъективно нам казалось, что львиная доля технических проблем, из-за которых теряются поездки, связана с бэкендом, то мы решили в первую очередь обратить внимание на процесс разработки бэкенда. Забегая вперед скажу, что так и оказалось — бэкенд стал основным полем боя за потерянные поездки.

4. Как устроен процесс

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

Когда я работал в РБК в далеком 2006 году (боже, как я стар!), то на одном из почтовых сервисов компании РБК был гейтвей, через который проходил весь трафик, и который проверял IP-адреса на вхождение в черные списки. Самым прикольным таким исключением на моем опыте был следующий сервис. Но в один прекрасный день работать перестал. Сервис работал на FreeBSD, и работал исправно. На этой машине рассыпался диск (bad blocks копились-копились и накопились). Угадайте почему? И все жило с рассыпавшимся диском. Рассыпался за 3 года до отказа сервиса в обслуживании (!). Уверен, что Linux бы не стал так делать. А далее «фряха» по никому неизвестным ее фряховским мотивам решила, вдруг, обратиться к рассыпавшемуся диску и в итоге зависла. Но это уже отдельный холивар.

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

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

Процесс был полностью ориентирован на быстрое развитие и был устроен следующим образом:

  • 20-30 релизов в сутки;
  • разработчики выкатывают сами;
  • быстрое тестирование в тестовой среде силами разработчика;
  • минимум автоматизированных/модульных тестов, минимум ревью.

Разработчики в сложнейших условиях, по сути, без прикрытых тылов в лице QA, с огромным потоком важных для бизнеса продуктовых задач и экспериментов, работали максимально сосредоточенно и слаженно, решали сложные проблемы простыми путями, не давали коду «зарастать», хорошо понимали бизнес-проблематику, очень ответственно относились к изменениям, быстро откатывали неработающее. Citymobil тут не уникален. 8 лет назад в Почте Mail.ru, когда я пришел туда работать, была похожая ситуация. И Облако Mail.ru мы тоже стартовали быстро и просто, без реверансов. И уже впоследствии меняли процессы, чтобы добиться большей стабильности.

У меня у самого был такой опыт (даже еще более хардкорный). Наверняка, вы замечали это по себе: когда вашу спину никто не прикрывает, когда только вы наедине с production, когда давит огромный груз ответственности — вы творите чудеса. Когда-то давно, еще в прошлом веке (прикиньте, в прошлом веке был уже Интернет, я сам удивляюсь, когда вспоминаю), я работал почти единственным разработчиком почтового сервиса newmail.ru, развёртывал в эксплуатацию сам, и тестировал в production тоже сам на себе через if (!strcmp(username, “danikin”)) 🙂 Поэтому мне эта ситуация близка.

Я не удивился, если бы узнал, что с такого простого подхода «на коленке» начинали многие стартапы, как впоследствии успешные, так и неуспешные, но движимые одной страстью — ориентацией на быстрый рост бизнеса и захват рынка.

Разработчиков было изначально мало. Почему конкретно в Citymobil процесс был именно такой? Выкатки были несколько раз в сутки. Они работали в компании давно и хорошо понимали код и бизнес. Процесс работал идеально для тех условий. Ошибки были крайне редки.

5. Почему с хорошим процессом появилась угроза стабильности?

С ростом инвестиций в проект мы сделали наши продуктовые планы более агрессивными и начали нанимать много разработчиков. Количество выкаток на production выросло, но при этом ожидаемо упало их качество, потому что новые ребята вникали в систему и в суть бизнеса прямо в боевых условиях. С увеличением штата разработчиков стабильность начала падать даже не линейно, а квадратично (количество выкаток росло линейно, и качество средней выкатки падало тоже линейно; «линейно» * «линейно» == «квадратично»).

Он просто не был заточен под новые условия. Очевидно было, что оставлять процесс в таком виде нельзя. Ведь именно в большом количестве релизов и был весь смысл. Но менять его нужно было без ущерба для time-to-market, то есть с сохранением 20-30 релизов в сутки (и с ростом их количества пропорционально размеру команды). Быстро проверяли продуктовые и бизнес-гипотезы, учились на них и выдвигали новые гипотезы, которые опять быстро проверяли, и т. Мы быстро росли, ставили много экспериментов, быстро оценивали их результаты и ставили новые эксперименты. Мы не хотели ни в коем случае снижать этот темп. д. Т.е. Более того, хотели его наращивать, и наращивать скорость найма разработчиков. наши действия, направленные на рост бизнеса, создавали угрозу стабильности, но и корректировать эти действия мы ни в коем случае не хотели.

6. Ok, задача ясна, процесс понятен. Что дальше?

Имея опыт работы в Почте Mail.ru и Облаке Mail.ru, где стабильность с какого-то момента была поставлена во главу угла, где выкатки раз в неделю, описанная в деталях функциональность и тест-кейсы, всё покрыто авто- и модульными тестами, код ревьюится минимум один раз, а иногда и по три раза, я столкнулся с абсолютно новой для себя ситуацией.

Но, как в том самом похабном анекдоте, есть нюансы: а) выкатки в Почте/Облаке проводятся один раз в неделю, а не 30 раз в день, и в Citymobil мы не хотели жертвовать частотой релизов, б) в Почте/Облаке весь код покрыт авто/модульными тестами, а в Citymobil у нас не было на это времени и ресурсов, все силы бэкенд-разработки были брошены на проверку гипотез и продуктовых улучшений. Казалось бы, всё просто: повторить в Citymobil процесс как в Почте или Облаке и повысить стабильность сервиса. Думаю, что про наш HR-процесс будет отдельная статья), то есть не было никакой возможности плотно заниматься тестами и ревью без замедления темпа. При этом бэкенд-разработчиков физически не хватало, даже при высокой скорости найма (отдельное спасибо HR Citymobil — это лучшие HR в мире!

7. Когда не знаешь, что делать, то учись на ошибках

Итак, что же мы сделали волшебного в Citymobil? Мы решили учиться на ошибках. Метод улучшения сервиса через обучение на ошибках стар как мир. Если система работает хорошо, то это хорошо. Если система работает с ошибками, то это тоже хорошо, потому что на этих ошибках можно учиться. Звучит просто. Сделать… тоже просто. Главное — задаться целью.

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

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

  • дата, время, продолжительность;
  • корневая причина;
  • что сделали, чтобы решить проблему;
  • влияние на бизнес (количество потерянных поездок, другие эффекты);
  • выводы.

По крупным авариям мы создавали отдельные большие файлы с детальным поминутным описанием, начиная с момента начала аварии и до момента завершения: что мы делали, какие решения принимали (обычно такие описания называют post-mortem analysis). А в общую таблицу мы добавляли ссылки на такие пост-мортемы.

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

8. Пример ошибки, на которой научились

Корневая причина — это то, устранение чего предотвратит подобные аварии в будущем. А выводы — это как устранить корневую причину (или снизить вероятность её возникновения).

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

Что есть корневая причина? Приведу пример: выкатили код — всё упало, откатили — всё заработало. Если бы её не было, то не было бы аварии. Выкатка, скажете вы. Плохой вывод (вредный для бизнеса, если быть точнее). Значит какой вывод: не делаем выкатки? Выкатка с багом. То есть это, скорее всего, не корневая причина, нужно копать глубже. Допустим. Корневая причина? Тестами, скажете вы. Как её устранить? Например, полным регрессом всей функциональности. Какими тестами? Но стабильность надо повысить здесь и сейчас, пока нет полного регресса. Это хороший вывод, запомним его. Выкатка с багом, который случился из-за того, что мы сделали отладочную печать в таблицу в базе, нагрузили её сверх меры и база сломалась под нагрузкой. Нужно копать еще глубже. Сразу становится понятно, что даже полный регрессионный тест не спасет от этой проблемы. Вот это уже интересней. Ведь на тестовой базе не будет такой же нагрузки, как на production.

Чтобы ее узнать, мы пообщались с разработчиком. В чём же корневая причина этой проблемы, если копнуть еще глубже? Но в условиях стремительного роста проекта вчера база справлялась, а сегодня — уже нет. Оказалось, он привык к тому, что база справляется с нагрузками. Например, для меня это первый такой проект. Мало кто из нас работал в проектах, которые растут на 50 % от месяца к месяцу. Пока первый раз не столкнешься с чем-то, то никогда не узнаешь, что бывает и такое. Погрузившись в такой проект, ты начинаешь осознавать новые реалии.

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

9. Чему еще можно научиться на этой ошибке, или do’s & dont’s

Итак, продолжаем разбирать эту аварию. Компания быстро растет, приходят новые сотрудники. Как они научатся на этой ошибке? Рассказать каждому новому сотруднику? Очевидно, что ошибки будут еще и еще — как сделать так, чтобы все на них учились? Ответ почти очевиден: завести файл do’s & don’ts (читается как «дус энд донтс»)! В вольном переводе на русский идиома do’s & don’ts означает «что такое хорошо и что такое плохо». В этот файл мы записываем все выводы на тему разработки. Файл показываем всем новым сотрудникам, а также показываем его в общем чате разработчиков каждый раз, когда файл дополняется, и убедительно просим всех прочесть его еще раз (чтобы и старое знание освежить, и новое увидеть).

Вы скажете, что многие сразу забудут после прочтения. Вы скажете, что не все будут читать внимательно. Но вы не станете отрицать, что у кого-то что-то останется в голове. И будете оба раза правы. По опыту Citymobil, разработчики очень серьезно относятся к этому файлу, и случаи, когда какие-то уроки забывались, происходили крайне редко. И это уже хорошо. Очень часто такое копание приводит к более точным и четким формулировкам в do’s & don’ts. Кстати, сам факт того, что урок забылся, можно считать проблемой и сделать из нее вывод, то есть разобраться в деталях и понять, как на будущее изменить процесс.

Вывод из вышеописанной аварии: завести файл do’s & don’ts, записать в него то, чему научились, показать файл всей команде и попросить изучать его всех новичков.

Как только вы это произнесли, сразу все это понимают так, что ничего делать не надо, выводы не нужны, люди же ошибались, ошибаются и будут ошибаться. Из общих советов, что мы поняли в разборе аварий: не надо использовать словосочетание «человеческих фактор». Вывод — это хотя бы маленький, но шажок в изменении процесса, улучшении мониторинга, улучшении автоматических тулзов. Поэтому вместо произнесения этого словосочетания надо сделать конкретный вывод. Из таких маленьких шажков шьется ткань стабильного сервиса!

10. Вместо эпилога

Во второй части я расскажу про виды аварий по опыту Citymobil и погружусь в детали каждого из видов аварий, а также расскажу, какие выводы мы делали из аварий, как меняли процесс, какую вводили автоматику. Самое интересное во второй части! Stay tuned!

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть