Масштабирование SaaS после запуска без поломки продукта
Запуск был лёгкой частью. Опасный отрезок — следующий год, когда рост тихо ломает простой продукт, который довёл вас до сюда. Это спокойное практическое руководство по масштабированию без замедленного обвала.

Все празднуют запуск. Почти никто не предупреждает вас о том, что наступает после, — о странном, нервном годе, когда продукт, давший вам первую сотню клиентов, начинает прогибаться под весом следующей тысячи. Ничего драматичного не происходит. Просто всё становится медленнее, капризнее, труднее менять. В один из вторников вы понимаете, что функция, на которую раньше уходил день, теперь занимает неделю, и никто толком не может сказать почему. Именно здесь, а не на запуске, большинство SaaS-продуктов тихо выигрывают или проигрывают.
Мы сидели со множеством основателей именно в этой точке. Они не терпят неудачу — вот что сбивает с толку. Выручка растёт, команда расширяется, демо проходят хорошо. Но под поверхностью продукт стонет. Обращения в поддержку растут быстрее числа пользователей. Деплои, которые раньше были скучными, теперь идут с затаённым дыханием. Кодовая база, год назад казавшаяся умной, теперь похожа на минное поле, где каждое изменение рискует задеть что-то ещё. Они не сделали ничего плохого. Они просто переросли то, что построили, и никто не сказал им, что так и должно быть.
Поэтому это руководство, которое мы хотели бы дать большему числу основателей до того, как появились трещины. Речь не о гипермасштабе, Kubernetes или о том, что сделал какой-то единорог на пятидесяти миллионах пользователей. Речь о скучной, решающей середине — переходе от работает для немногих к работает для многих — без переписывания всего, без отпугивания клиентов и без выгорания команды. Цель не идеальная архитектура. Цель — продукт, который продолжает расти, а не тот, что тихо начинает ломаться.
Что на самом деле ломается, когда SaaS растёт
Вот что больше всего удивляет основателей: проблемы масштабирования почти никогда не приходят как драматичный сбой, к которому вы готовитесь. Сервер не вспыхивает. Вместо этого у продукта развивается своего рода вялотекущая лихорадка. Страницы, грузившиеся мгновенно, начинают занимать три секунды. Отчёт, который нормально работал у ранних клиентов, упирается в таймаут у крупного, что только подписался. Один и тот же баг возвращается, потому что две части кода тайно зависят друг от друга так, как никто не задокументировал.
На самом деле ломаются редко ваши серверы — это ваши допущения. В начале вы строили под форму первых пользователей: несколько аккаунтов, мало данных, простые рабочие процессы, все примерно одинаковые. Рост не просто добавляет больше того же. Он добавляет разнообразие. Клиент с данными в десять раз больше. Команда, использующая функцию так, как вы никогда не представляли. Всплеск в 9 утра понедельника, когда все логинятся разом. Каждый из них тихо нарушает допущение, вшитое в ваш код год назад, когда нарушить его было немыслимо.
“Ваш продукт ломается не потому, что у вас стало больше пользователей. Он ломается потому, что эти пользователи отличаются друг от друга сильнее, чем когда-либо отличались первые.”
Четыре места, где это обычно проявляется первым, предсказуемы. База данных почти всегда канарейка — запросы, бывшие мгновенными на малых таблицах, ползут по мере роста данных. Медленный эндпоинт: одна-две страницы, делающие слишком много работы на запрос, нормально, пока трафик не накапливается. Хрупкий деплой, где выкатить что-либо стало страшно, потому что код слишком запутан, чтобы его осмыслить. И нагрузка на поддержку, которая вовсе не инфраструктура, но является самым правдивым ранним сигналом, что продукт больше не соответствует тому, как люди им на самом деле пользуются.

Ловушка решения проблем, которых у вас ещё нет
Прежде чем говорить о починке, предупреждение, спасшее больше продуктов, чем любая оптимизация. Самая большая угроза для растущего SaaS — не игнорировать масштаб, а гнаться за ним слишком рано. В момент, когда основатель чувствует первое замедление, инстинкт тянется к архитектуре, о которой он читал в знаменитом инженерном блоге. Микросервисы. Очередь сообщений. Мультирегиональная конфигурация. Шардирование базы до того, как в ней появился миллион строк.
Так вы тратите шесть месяцев и состояние на инфраструктуру для масштаба, которого ещё не достигли, пока сам продукт перестаёт двигаться. Хуже того, вы теперь усложнили каждое будущее изменение, потому что распределённую систему драматически сложнее строить и отлаживать, чем простую, что у вас была. Вы обменяли проблему, которой ещё не имели, на гарантированную, что есть сегодня: ничего не выкатывается.
Дисциплина здесь та же, что вообще делает продукты хорошими: решайте проблему, что перед вами, а не ту, которую вам льстит воображать. Скучный, хорошо понятный монолит, который вы можете быстро менять, обгонит по масштабу модную распределённую систему, которую вы боитесь трогать. Сложность — это издержка, которую вы платите каждый день, а не разовая покупка.
Измерьте, прежде чем что-либо менять
Почти каждый основатель, которого мы встречаем на этом этапе, убеждён, что знает, где проблема. В половине случаев он ошибается — не по небрежности, а потому что интуиция — ужасный профилировщик. Часть кода, которая кажется медленной, часто в порядке; настоящий виновник — какой-то тихий запрос, выполняемый сорок раз на странице, о которой никто не подумал. Нельзя починить то, что вы не измерили, и угадывание — это то, как команды тратят недели на оптимизацию не того.
Чтобы начать, вам не нужен навороченный стек observability. Вам нужны три скучные цифры перед глазами, всё время. Какие эндпоинты медленнее всего и насколько медленно под реальным трафиком. Какие запросы к базе занимают больше всего суммарного времени — не самый медленный одиночный запрос, а тот, чьё время складывается за тысячи вызовов. И где на самом деле случаются ошибки, с достаточным контекстом, чтобы их воспроизвести. С этими тремя туман обычно рассеивается за день.
- 1Включите базовый мониторингВремя отклика по эндпоинтам, частота ошибок и логирование медленных запросов на базе. Хостинговые инструменты делают это за полдня. Нельзя улучшить цифру, которую вы не видите.
- 2Найдите настоящую тройку лидеровСортируйте по суммарно затраченному времени, а не по чутью. Три виновника почти всегда дают большую часть боли. Запишите их — это ваша настоящая дорожная карта.
- 3Исправьте одно, измерьте сноваИзмените одну вещь, затем перепроверьте цифры. Подтвердите, что помогло, прежде чем двигаться дальше. Два изменения разом — и вы никогда не узнаете, какое было важным.
- 4Остановитесь, когда достаточно хорошоОпределите 'достаточно быстро' до начала — скажем, каждая страница меньше секунды при текущей нагрузке. Дальше оптимизация — пожиратель времени, а не победа.
Последний шаг важнее, чем кажется. Работа над производительностью по-настоящему затягивает; всегда есть ещё одна миллисекунда, которую можно срезать. Но ваши клиенты не чувствуют разницы между 200 мс и 120 мс, а часы, потраченные на погоню за ней, — это часы, не вложенные в функцию, которая действительно вырастила бы бизнес. Измерьте, исправьте тройку лидеров, объявите победу, двигайтесь дальше.
База данных почти всегда первая стена
Если бы нам пришлось ставить деньги на то, где растущий SaaS упрётся в первый настоящий потолок, мы бы каждый раз ставили на базу данных. Это та часть системы, где малые ранние решения накапливаются сильнее всего. Запрос без индекса выполняется в мгновение на тысяче строк и вязнет на миллионе. Код не менялся. Изменились данные — а данные только растут.
Хорошая новость в том, что база данных — это также место, где живут самые дешёвые и самые результативные исправления. Классическое — отсутствующий индекс: единственная строка, превращающая многосекундный запрос в мгновенный, потому что база перестаёт сканировать каждую строку в поисках тех немногих, что ей нужны. Сразу за ним — проблема запросов N+1: страница, которая вместо одного вопроса тихо задаёт базе один и тот же маленький вопрос сотни раз в цикле. Оба распространены, оба невидимы, пока не посмотришь, и оба обычно правятся за день, как только их найдёшь.
Здесь есть последовательность, на которую стоит опереться, и выгоднее следовать ей по порядку, а не прыгать к концу. Сначала исправьте запросы — индексы, N+1, медленный отчёт. Затем добавьте кэширование для данных, которые читаются постоянно, но меняются редко. Только после этого имеет смысл говорить о репликах для чтения, более крупных инстансах или разделении данных. Большинству SaaS-продуктов поздние шаги вообще не нужны. Им нужно было лишь как следует выполнить первые.

Масштабировать продукт значит масштабировать то, как вы его меняете
Вот сдвиг, застающий основателей врасплох: за определённой точкой масштабирование перестаёт быть о том, как продукт справляется с бóльшим числом пользователей, и становится о том, как ваша команда справляется с бóльшим числом изменений. Когда были вы и один разработчик, каждый держал всю систему в голове. Можно было менять что угодно, потому что вы знали, чего это коснётся. На пяти или десяти людях эта мысленная модель рассыпается — и код, предполагавший, что все знают всё, становится обузой.
Это и есть настоящая причина, почему деплои становятся страшными. Дело не в том, что код за ночь стал хуже; дело в том, что никто больше не может полностью предсказать радиус поражения изменения. Решение — не геройство и не заморозка выкаток. Это вложение в неэффектные леса, позволяющие большей команде двигаться, не наступая друг другу на ноги: автоматический набор тестов, ловящий очевидные поломки, деплои, ставшие рутиной, а не церемонией, и способ выключить плохой релиз за секунды, а не метаться.
- Набор тестов, покрывающий ту горстку сценариев, поломка которых была бы катастрофой, — вход, оплата, ключевое действие. Не всё; те критические немногие.
- Деплои, запускаемые кнопкой, а не ритуалом, чтобы выкатывать мелко и часто стало безопасно, а не нервно.
- Быстрый способ откатиться, чтобы плохой релиз был пятиминутным событием, а не ночным инцидентом.
- Фиче-флаги, чтобы можно было выкатить код сначала нескольким клиентам и мгновенно выключить, если он ведёт себя плохо.
- Достаточно документации, чтобы отпуск одного человека не замораживал целую область продукта.
Ничего из этого не появляется в демо. Ничего из этого напрямую не добавляет функцию. И именно эта работа отделяет продукт, продолжающий ускоряться, от того, что мелет медленнее с каждым новым нанятым. Команды, хорошо масштабирующиеся, — это те, что относятся к своей способности безопасно менять продукт как к функции самой по себе — потому что при масштабе это именно она и есть.
Короткая история из точки надлома
Чтобы это стало конкретным, вот собирательный пример из работы, которую мы делали, — детали размыты, форма верна жизни. Небольшой SaaS для управления выездными сервисными бригадами хорошо запустился и вырос до пары сотен платящих компаний. Основатели были в равной мере в восторге и измотаны. Затем подписался крупнейший в их истории клиент: фирма с бóльшим числом пользователей и бóльшим объёмом исторических данных, чем десять предыдущих клиентов вместе взятые.
В течение недели панель, в которой все жили, замедлилась до ползания для этого клиента — и, что странно, для всех остальных тоже. Обращения в поддержку взлетели. Основатели решили, что им нужен гораздо больший сервер, и готовились к болезненной, дорогой переархитектуре. Это был момент, когда позвали нас, и инстинкт был понятным, но неверным.
Мы не трогали архитектуру. Мы включили логирование медленных запросов и понаблюдали полдня. Виновник был почти неловко мал: главная панель загружала список задач каждого пользователя классическим паттерном N+1, выпуская по одному запросу на задачу. Для малого клиента это означало пару десятков безобидных запросов. Для нового гиганта — тысячи на загрузку страницы, что на общей инфраструктуре утягивало всю систему вниз для всех.
Урок, который вынесли основатели, был не техническим. Он был в том, что ужасающая проблема масштабирования, которую они вообразили, — та, что требовала перестройки и раунда инвестиций, — при измерении оказалась двухдневным исправлением, прятавшимся за страшным симптомом. Они уже собирались потратить месяцы на решение не той проблемы. Этот разрыв, между воображаемым кризисом и измеренным, — то место, где тратится большая часть денег на масштабирование.
Когда действительно пора перестроить какой-то элемент
Вся эта осторожность насчёт преждевременного масштабирования может звучать как никогда не рефактори, никогда не перестраивай. Это не так. Иногда часть продукта по-настоящему дошла до конца своей жизни, и латать её снова — дорогой выбор. Хитрость в том, чтобы различить настоящий структурный предел и обычные боли роста, с которыми справилось бы измеренное исправление.
Честный сигнал таков: перестраивайте компонент, когда стоимость его изменения стабильно стала выше стоимости его замены. Не когда он уродлив — уродливый код, который стабилен и редко трогается, в порядке. Вы ищете часть системы, где каждое изменение медленно и рискованно, где одни и те же баги возвращаются, где новые разработчики не могут безопасно работать и где вы уже пробовали более дешёвые исправления и упёрлись в стену. Когда несколько из этих условий верны разом, прицельное переписывание этого одного элемента — правильное решение.
| Сигнал | Вероятно, просто исправление | Вероятно, перестройка |
|---|---|---|
| Симптом | Одна медленная страница или запрос | Любое изменение в области медленно и рискованно |
| Баги | Изредка, исправимы | Одни и те же баги возвращаются |
| Дешёвые исправления | Ещё не пробованы | Уже исчерпаны, всё равно тупик |
| Охват | Ограничен одной функцией | Расползается по всему модулю |
| Правильный ход | Измерить и залатать | Осознанно перестроить этот один элемент |
И когда вы перестраиваете, перестраивайте элемент — не продукт. Полное переписывание с нуля — это песнь сирен масштабирования, вещь, что кажется чистой, а кончается потоплением года, пока конкуренты выкатывают. Замените один прогнивший компонент за чёткой границей, пока остальной продукт продолжает работать и зарабатывать. Хирургически, не героически.

Упёрлись в стену и не уверены, исправление это или перестройка?
Это решение, которое дорого ошибиться и дёшево попасть в точку. Мы измерим, где ваш продукт на самом деле напрягается, и честно скажем, двухдневное это исправление или что-то глубже, — прежде чем кто-либо напишет строку нового кода.
Посмотрите, как мы подходим к масштабированию ПОЧастые вопросы
Как понять, что мой SaaS вот-вот упрётся в стену масштабирования?
Стоит ли переходить на микросервисы ради масштабирования?
Дешевле оптимизировать код или просто купить сервер побольше?
Когда полное переписывание действительно правильное решение?
Сколько стоит вложить в масштабирование до того, как появятся пользователи?

Have a nice day — это софтверная студия, которая помогает малому и среднему бизнесу перейти в цифру: автоматизация, ИИ и софт под заказ, который работает в повседневных процессах, а не только на слайдах.