Вы решили строить продукт, и первое решение: на чём его строить. Оно кажется главным, потому что фреймворк выглядит как единственное, что трудно поменять, хотя на деле меняется за недели и часто не вашими руками. А вот модель данных, с которой вы стартуете, определяет, чем продукту позволено стать; через два года она всё ещё на месте, когда про спор о стеке никто не вспоминает.

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

1. Модель данных решает больше, чем фреймворк

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

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

2. Второй клиент требует границы, а не новой функции

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

Решите это до первой строки коду, одним предложением, которое новый разработчик прочитает без объяснений, а потом держите одно обещание двумя входами. Два клиента, которые не должны видеть друг друга, открывают один и тот же отчёт, и строки различаются. Это и есть вся проверка, и пройдёте вы её сами. Места, где граница рвётся на практике, разобраны здесь: где именно рвётся граница между двумя клиентами: выгрузка, ночное письмо, поле поиска, список с пунктом «все компании».

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

3. Работа, которая не помещается в веб-запрос, должна быть названа

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

Задайте три вопроса к каждой такой задаче. Что её запускает? Какого клиента она имеет право затронуть? Что делает повтор? Если последний ответ «проверяет», запишите, что именно проверяется: именно это предложение и станет политикой повтора. Если задача сорвалась в три часа ночи, утром вы должны увидеть это без открытого вручную сервера. Порядок действий, когда очередь уже растёт, разобран здесь: проверьте состояния, прежде чем добавлять обработчиков, и те же состояния стоит продумать до первой написанной задачи.

4. Первая тысяча пользователей проверяет эксплуатацию, не архитектуру

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

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

5. Сопровождение на второй год решает, что было мудрым

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

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

6. Фреймворк выбирайте последним и по доказательствам

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

До первой строки коду запишите эти пять вещей на одном листе:

Именно в таком порядке я работаю, когда продукт только начинается: сначала граница и модель данных, потом работа, которая уходит из запроса, и напоследок, коротко, фреймворк. Услуги охватывают первую версию SaaS или CRM, архитектуру вокруг неё и работу, которая держит систему после запуска. Проекты показывают те же решения на системах, которые уже работают: CRM, разделённая по филиалам, отчёты, где клиенты не смешиваются, и фоновая работа, пережившая первую тысячу задач. Если вы на этапе, где стек ещё не выбран, начните с разработки продукта с нуля или напишите мне, какие существительные есть у вашего продукта и чему принадлежит каждая строка. Пароли и клиентские данные в сообщение не кладите.