Архитектурный комитет
Разработка мобильного приложения начинается с проектирования системы. Команда продумывает архитектуру и визуализирует ее в HLD-диаграмме. HLD означает high level design — дизайн высокого уровня, который объясняет архитектуру системы. Диаграмма показывает сервисы и микросервисы, компоненты, интеграции и взаимодействие между ними, а также характер передаваемых данных.
Готовую архитектуру рассматривает архитектурный комитет — митинг, на котором участники оценивают технические решения и предполагают, насколько хорошо они будут выполнять задачи бизнеса. Обсуждение выявляет узкие места, неподходящие способы реализации и мелкие несоответствия. Обычно в него входят тимлиды направлений, ведущие разработчики на проекте и СТО.
Комитет оценивает:
- cможет ли приложение справиться с пиками нагрузки;
- насколько оно отказоустойчивое и не будет ли вылетать при небольшом сбое;
- грамотно ли выбраны способы обработки данных;
- подходят ли базы данных и способы хранения информации задачам клиента;
- каково качество связей между компонентами, интеграторами, фронтендом и бэкендом.
Например, однажды мы проводили архитектурный комитет, на котором обсуждали разработку приложения для сети заведений быстрого питания. Рассматривая механизмы регистрации и авторизации, дошли до процесса подтверждения номера телефона и обнаружили уязвимость. Когда новый пользователь запрашивал код в СМС, приложение присылало ему один из двух вариантов сообщения. Первое с кодом — если пользователь уже был в клиентской базе. Второе — с текстом «Такого пользователя нет». Эта формулировка прямо указывала, что телефонного номера в клиентской базе нет. Злоумышленники могли бы автоматически прогнать через приложение десятки тысяч номеров, вычислить те, что принадлежат клиентам, и продать их конкурентам. На архитектурном комитете мы обнаружили ошибку и скорректировали процедуру регистрации, чтобы из него нельзя было понять, есть пользователь в базе или нет.
Может показаться, что архитектурный комитет — слишком дорогой инструмент, потому что в нем участвуют специалисты с высокой стоимостью рабочего часа. В действительности он экономит деньги. На этом этапе можно легко и быстро отредактировать диаграмму и не допустить перехода ошибки в разработку. Иначе позже ее все равно придется исправлять и тратить намного больше времени.
Дизайн-ревью
Когда дизайнеры спроектировали часть экранов приложения или сценария с учетом переходов и взаимосвязей между ними, собирается дизайн-ревью. Встречу проводят аналитики, бэкенд- и фронтенд-разработчики. Участники рассматривают макеты с технической точки зрения и проверяют, как дизайн отвечает бизнес-логике. Аналитики оценивают возможность реализации дизайна приложения и его соответствие поставленным требованиям и целям бизнеса. Параллельно отмечают несоответствия, пропуски или неясности в требованиях. Бэкенд-разработчики продумывают, к каким обращениям на бэкенд приведут переходы между экранами приложения. А также решают, какая обработка данных нужна, чтобы выдать информацию для отображения экрана. Фронтенд-разработчики понимают, смогут ли реализовать в приложении идеи дизайнеров: например, сложные кастомные UI-компоненты.
Задача участников дизайн-ревью — понять, как система будет работать целиком. Для этого каждый специалист согласовывает с другими работу участка, за который он отвечает.
Например, мы аудировали старое приложение клиента от другого разработчика и сразу после запуска увидели экран с запросом на отправку уведомлений. Если пользователь не успел ни одной задачи решить с помощью приложения, ему трудно понять, зачем соглашаться. Когда он оформит покупку и получит запрос с объяснением, что в уведомлениях будут скидки и акции, он с большей вероятностью даст положительный ответ. Удачный опыт и прозрачная мотивация увеличат шанс согласия — значит, вырастет количество аудитории, до которой приложение сможет дотянуться.
А если бы создатели приложения, которое мы аудировали, обсудили на дизайн-ревью, как запрос с разрешением уведомлений в начале взаимодействия повлияет на бизнес-логику, они бы вряд ли поместили его туда.
Кодогенерация
Принцип contract first и кодогенерация помогают избежать досадных ошибок из-за рассогласований на стыке фронтенда и бэкенда, а также при межсервисном взаимодействии.
Мы описываем контракты для взаимодействия компонентов в формате спецификаций: для REST API, например, OpenAPI; для gRPC и других бинарных протоколов — protobuf. Затем генерируем типы данных, а также клиентские и серверные компоненты. Таким образом мы исключаем ситуации, когда участники взаимодействия по-разному реализовали типы и компоненты для связи. Ведь даже такие незначительные оплошности затрудняют отладку и поиск ошибок.
Также кодогенерация позволяет существенно сократить время разработки — с ней нет необходимости писать, а затем обновлять код вручную.
Линтеры и автоматизированные тесты
До того, как отдать код тестировщикам, разработчики сами проверяют его с помощью автоматизированных тестов и причесывают код с помощью линтеров. Они убирают мелкие ошибки и корректируют стилистику. Например, подсказывают, где достаточно одной строчки вместо трех, находят лишние пробелы и неверно оформленные комментарии. Иногда они видят ошибки вроде бесконечного цикла, которых не замечает компилятор.
Разработчик запускает линтер вручную самостоятельно, пока пишет код. А также встраивает в CI/CD, чтобы проверить перед слиянием веток или развертыванием на стенды.
Помимо линтеров, в процессе разработки мы используем модульные юнит-тесты и интеграционные тесты. На мой взгляд, юнит-тесты должны покрывать только действительно сложные участки. Иногда ты сам не уверен, как будет работать твой кусок кода. Для самопроверки можно прогнать юнит-тесты для этого участка и найти проблему. Или, наоборот, убедиться, что все в порядке.
Даже при очень хорошем покрытии юнит-тестами нет гарантии, что модули системы, которые правильно работали в отдельности, будут корректно работать в сборе. Чтобы проверить это, мы используем интеграционные тесты.
Наш подход — запустить экземпляр тестируемого сервиса и исследовать его поведение. Для этого мы разворачиваем инфраструктурные компоненты, которые имитируют реальную среду эксплуатации сервиса. Затем выполняем миграции для формирования схемы данных в БД и наполнения ее тестовыми данными, а потом обращаемся к методам сервиса по сети. При тестировании мы исследуем как корректность ответов сервиса, так и корректность изменения данных в БД.
Интеграционные тесты могут быть сложными, но обеспечивают тщательное покрытие кода. Как показывает практика, сервисы, которые покрыли интеграционными тестами, редко возвращаются из QA для исправления ошибок.
Стандартизация
Мы в KODE накопили опыт создания сложных приложений и поэтому знаем, какие решения хорошо работают из проекта в проект. Теперь разрабатываем небольшие библиотеки готовых компонентов, которые можно будет использовать в любом проекте.
Готовые решения сокращают затраты на разработку и тестирование. Если мы тщательно проверили компонент в предыдущих проектах, позже он не потребует внимания и освободит время для других задач.
А еще готовое решение помогает улучшить уже существующий проект, например апгрейднуть версию конкретной библиотеки. Обычно в библиотеки мы выносим код для обработки ошибок, разнообразные middleware, паттерны для тестов и компоненты для работы с инфраструктурой.
Подытожим: контроль качества на проекте с ранних этапов помогает вовремя заметить и исправить ошибки. Он предотвращает потерю времени и потенциальное снижение надежности и производительности приложения, бережет нервы команды и бюджет клиента. На контроль качества нужны усилия, но они окупаются: нам ни разу не приходилось переделывать все с самого начала.