В данной статье я коснусь вопроса порога входа в проект с устоявшейся архитектурой и дам несколько вариантов ответа на очень часто возникающий вопрос: почему так сложно?
Такой вопрос частенько возникает у новичков, которые приходят на проект. Или же бывают ситуации, когда проект уходит на поддержку и развитие в новые руки, и у нового разработчика также появляется этот вопрос. И редко кто из них пытается разобраться в реальных причинах, почему здесь так, а не иначе. Это может привести к печальным последствиям для бизнеса, например, новый разработчик может настоять на том, чтобы переписать все приложение с нуля.
Чтобы минимизировать такие риски, следует вместо вопроса почему так сложно? задаться такими вопросами:
- Какие требования к процессу разработки закладывал архитектор?
- Какой результат процесса разработки требуется на выходе?
Требования к процессу разработки
Сначала разработчик должен вникнуть в систему процесса разработки, он должен задать такой вопрос:
- По какой системе выстроен процесс?
Часто в заказной разработке на вход приходит проект с однозначными требованиями и фиксированным набором функционала. Насколько проработанными они могут быть — это уже тема другой статьи. И процесс разработки в таких проектах чаще всего выстроен по водопадной системе, потому как предполагает непосредственную обратную связь от пользователей продукта — после разработки всего функционала продукта, тогда как при итерационной модели обратную связь можно получить уже после первой итерации. У архитектора для таких проектов обычно уже припасена устоявшаяся архитектура, которая отвечает определенным требованиям к этому процессу. Какие же требования к такому процессу разработки закладывает архитектор?
1) Pipeline процесса разработки должен быть максимально сложным для разработчика. И отбраковывание кода, поступающего в репозиторий проекта, должно по максимуму происходить автоматически и, по возможности, без участия самого архитектора.
Т.е. в процессе должен быть настроен определенный pipeline. Код, который прошел весь этот pipeline — считается удовлетворяющим требованиям. Это очень важно, т.к. хорошему архитектору необходимо избавить его разработчиков от головной боли и ответственности за не рабочую сборку после попадания кода в репозиторий. Если такого pipeline нет — ваши разработчики будут страдать от постоянного стресса. Если код попал в репозиторий и pipeline его принял, и этот код сломал сборку или повредил уже работающий функционал — это уже проблема самого pipeline.
Поэтому в таком pipeline необходимо использовать:
- Множество статических анализаторов кода
- Автоматические тесты и соблюдение пирамиды тестирования
- Автоматический подсчет покрытия кода тестами
- Ворота качества кода(Quality gates). По всевозможным метрикам: процент покрытия кода тестами, процент дублирования кода, code smells, security, bugs, т.д.
- перекрестное Code Review
- etc
Все эти пункты в совокупности и приводят к появлению у разработчика вопроса: почему так сложно?
Для примера попробуйте написать тесты для вот такого кода:
class SomeService(
private val restApi: SomeApi // SomeApi - не абстракция, а конкретный класс, который умеет ходить в сеть
) {
fun doSomething(): Single<SomeResult> = restApi.loadSomething()
.map { /*какая-то бизнес логика здесь*/ }
}
Вам придется запускать такие тесты на реальном android устройстве, либо на эмуляторе. И это сразу приведет к существенной просадке во втором требовании к процессу разработки:
2) Автоматизированные элементы Pipeline'а процесса разработки должны выполняться как можно с большей скоростью
Если вашим разработчикам приходится ждать выполнения тестов очень долго — это проблема вашего pipeline и если архитектура не позволяет ускорить этот процесс — это проблема вашей архитектуры
Давайте перепишем пример:
interface SomeApi {
fun loadSomething(): Single<NetworkResult>
}
class NetworkSomeApi : SomeApi {
override fun loadSomething(): Single<NetworkResult> { /*реализация, которая ходит в сеть*/ }
}
class SomeService(
private val restApi: SomeApi // SomeApi - уже абстракция, которую легко можно замокать в тестах
) {
/*CODE*/
}
Мы видим, что сложность кода увеличилась, но с точки зрения процесса разработки — мы сделали архитектуру более гибкой, теперь нам не обязательно запускать тесты бизнес логики для блока map
в эмуляторе или на устройстве, достаточно запустить их в быстрых тестах. Это уменьшит количество интеграционных тестов, которые нужно запускать в медленном окружении.
Уменьшить количество дорогих интеграционных тестов может правильно выбранный архитектором паттерн проектирования. Не поленитесь и разберитесь с популярными на сегодня паттернами: MVP, MVVM, MVI.
Давайте еще немного затронем тему навигации в приложении. Мы тоже хотим уметь тестировать ее, и тестировать в быстрых тестах. Опять получаем "усложнение" архитектуры, потому что нам придется прятать систему навигации в приложении за абстракцией.
А еще мы хотим уметь связывать компоненты нашей системы с помощью DI, выстраивать графы зависимостей и проверять их корректность на этапе компиляции проекта, а не в рантайме. Тут на сцене появляется Dagger 2 и его монструозные компоненты и сабкомпоненты с модулями, которые уже в конец запутывают бедного новичка.
И таких неочевидных моментов "усложнения" архитектуры для новичков накапливается очень много. Естественно, без понимания процессов разработки и требований к этим процессам, у них появляется тот самый вопрос — почему так сложно?
Результат процесса разработки
Чтобы оценить успешность выстроенного процесса разработки и, опосредованно, архитектуру проекта, необходимо проанализировать его результат. Как правило, результатом является продукт(приложение если мы говорим про мобильную разработку). И метрики успешности продукта у всех разные: у заказчика — одни метрики, у разработчика — свои метрики, у пользователя продукта — свои.
Как минимум, вам, как архитектору, создающему pipeline процесса разработки, следует учитывать при оценке эффективности процесса разработки метрики своей компании и метрики бизнеса.
Это непрерывный процесс: разработка -> сбор метрик -> анализ результата -> внесение необходимых модификаций в процесс разработки.
Таким образом результат влияет на формирование процесса разработки и уже процесс разработки влияет на изменение архитектуры проекта. Т.е. важная мысль, которую я хочу донести — архитектура вторична, первичны результат и процесс разработки.
Заключение
В заключении еще раз проговорим:
- без понимания процессов разработки и требований к этим процессам у разработчика создается ощущение об усложненной архитектуре проекта;
- архитектура вторична, первичны результат и процесс разработки;
Без понимания этих вещей у разработчика может возникнуть желание взять и все переписать с нуля. На мой взгляд, это оправдано только когда процесс и результат разработки абсолютно не удовлетворяют заказчика этой разработки.
Для новичков я советую попасть на проект с отточенным pipeline разработки. Порог входа в такие проекты высок, но перешагнув его, вы серьезно приблизитесь к пониманию того, как выстраиваются процессы разработки.