Все началось пару лет назад, когда я был еще совсем зеленым, впервые устроился на работу, разработчиком под Android, сразу после универа, и первые полгода я не верил своему счастью (особенно из-за зарплаты).
В какой-то момент руководство решило, что пора повышать квалификацию, и чтобы работники варились не только в собственном соку, было решено отправить часть команды на какое-нибудь модное мероприятие, выбор пал на Mobius. Денег отправить всех не хватило, так что счастливчикам была поставлена задача вникать во все, о чем будут рассказывать, все запоминать, записывать и подготовиться к пересказам внутри компании.
Неделю спустя мои коллеги вернулись с блестящими глазами, полные энтузиазма и готовые к свершениям. Оказалось, что половина докладов была про реактивное программирование и про то, как с этим жить. На протяжении следующих нескольких недель в офисе только и были разговоры что про реактивщину, архитектуры с ней же, тестирование её же и так далее. Естественно, я не мог не проникнуться и кинулся тоже все это изучать. Я смотрел все замечательные лекции с конференции (без сарказма, правда классные), прочитал все статьи на хабре, выслушал всех моих коллег и их мнение. Поначалу мне казалось, что все идеально, это действительно прекрасное решение, решающее практически все проблемы, с которыми мы только могли столкнуться: ведь столько операторов, так легко потоки переключать, всякие горячие observable, комбинирование потоков и так далее.
Я был полон оптимизма и мы решили, что пора внедрять все это в проект, правда не могли придумать - куда. Суть приложения заключалось в работе с картой на основе SDK, все было было построено на MVVM с биндингами и куда ни посмотри, Rx сувать было некуда. Но так просто отступить было нельзя, и мы решили превратить самые простые и рабочие вещи в реактивщину с целью сделать их еще более рабочими. Забегая вперед скажу, что по большому счету это стало началом конца проекта. Нет, не подумайте, проект не был идеален с самого начала, было много просчетов, которые было очень сложно исправить, однако, он был написан довольно топорно и просто, иными словами, все решения были очевидны, стоило только понять базовый принцип. Но как же так получилось?
Давайте разбираться.
Когда только начинаешь погружаться в реактивный мир, все кажется довольно простым - вот есть Observable, на него подписываешься, запуская цепочку и ожидая какого-то результата. И есть куча операторов, которые превращают сложные вещи в простые, вроде смены потоков (тем самым освобождая от колбек хэлла), фильтров, мапов, мержа потоков и так далее, позволяя обрабатывать данные очень просто. И казалось бы - слепил observable, подписался на него и дело в шляпе, так? Не так ;)
Когда начинаешь разбираться в теме, оказывается, что есть еще всякие Subject, Disposable, Flowable, Maybe, Single, Completable, гочие, холодные observable, и так далее. Операторов оказывается слишком много, разницу между некоторыми понять практически невозможно. Появляется куча проблем, о которых ты даже мог не догадываться до этого, вроде back pressure. Но главное - это идея, что все вокруг - поток данных. Ты теперь не можешь просто сделать объект, хранящий какие-то важные вещи, который можно достать по ссылке, ты должен следовать концепциям реактивного программирование, а именно - для всего делать поток данных, который может быть Single, Maybe, Flowable и так далее, которые потом нужно приводить к нужному типу, постоянно думать о том, а что будет, если Maybe не вернет данных? Но в теории же такого быть не может? Но оно когда-то так и случится, когда невнимательный коллега зафиксит баг, немного переделав этот поток данных.
И именно на этом этапе команды с недостаточным количеством опыта совершают роковую ошибку, полностью отказываясь от императивного стиля программирования, переделывая все на реактивщину. Ошибка заключается в том, что RxJava не нужна там, где без нее можно прекрасно обойтись. К примеру, если у вас есть объект, хранящий какое-то состояние, не нужно из этого делать поток, просто сделайте все как обычно.
Как же это отразилось на нашем проекте? К сожалению, случилось так, что никто не понимал как работает функционал, который написал кто-то другой. К примеру, когда уволился коллега, отвечающий за функционал построения маршрутов, следующий год никто не смог зафиксить ни одного бага, связанного с этим функционалом, так как никто не мог понять как это работает, потому что весь модуль представлял из себя набор observable, которые были как-то связаны друг с другом. Примерно такая же ситуация была во всех остальных частях программы, которая была затронута реактивщиной.
А все дело вот в чем:
1) Порог вхождения в RxJava очень высок. На всю мнимую простоту в начале пути, оказывается, что все ОЧЕНЬ непросто. Без использования общих гайдлайнов и четкой архитектуры, случится то, что коллеги не будут понимать как работает любой функционал, который писали не они.
2) RxJava качественно решает одни проблемы, но взамен создает другие (вроде back pressure, постоянных размышлений о том, что может произойти с потоком, необходимости мержей потоков и так далее)
3) RxJava - это другой стиль программирование. В результате происходит смешение, которое только усложняет разработку, причем, чем менее опытны разработчики, тем опаснее становится микс.
Кроме того, а так ли вообще хороша RxJava? На хабре много статей про то, как она спасает в некоторых кейсах, вроде обработки данных с акселерометра (я убежден, вы тоже натыкались на нее в процессе изучения), но так ли часто встречаются такие задачи? Не знаю как у вас, а в моей практике 95% работы - это показать красивую вьюшку со всякими анимациями, получить данные с сервера и как-то сохранить состояние. И я не вижу никаких причин почему для этого мне необходимо использовать реактивный подход.
Что же я предлагаю?
Во-первых, по большому счету, все сводится к решению проблемы колбек хэлла. Когда я начинал писать на Kotlin, меня все уверяли, что это идеальная связка с Rx, но я категорически не согласен. Если вы пишете на Kotlin, то Rx вам попросту не нужен, coroutines будут вашим спасением. Нет никакой надобности больше писать бесконечные observable, просто стартуйте корутину в условленном архитектурой месте, и выполняйте все действие последовательно. Таким образом, вы полностью избавитесь от колбек хэлла, а недостаток операторов Kotlin компенсирует практически полностью своими встроенными конструкциями. Если в проекте все же используется Java, то просто решите эту проблему, стартуя поток в условленном месте и так же выполняйте действия последовательно, к примеру, в Model, если вы используете MVP. Подумайте дважды, стоят ли остальные преимущества RxJava тех проблем, с которым вы столкнетесь, начав ее использовать?
Во-вторых, если все же встречаются такие задачи, которые сложно решить последовательно, вроде обработки нескольких источников данных параллельно, либо если все же полностью уверены, что вам необходим реактивный подход, незачем его использовать повсюду. Четко определите границы использования Rx и не выпускайте ее за пределы, это опасно.
Итог
Я догадываюсь, что многие скажут, что я просто не разобрался? Но это не так, я сам использовал Rx на протяжении последних двух лет и прошел все стадии: от полного восхищения до понимания того, что это скорее просто модная технология, подход, который создает больше проблем, чем пользы, который, к тому же, излишне сложен. Он делает простые задачи - сложными. И лишь изредка - наоборот.
Кто-то скажет, что да, это сложная технология, но ведь она того стоит? В конце концов, программирование - это само по себе непросто. Я не готов с этим согласиться. Я считаю, что программирование должно быть простым.
Сам по себе очень ленивый человек и программист. Я не из тех гиков, которые пробуют каждую новую технологию, веря, что это изменит их жизнь, тратят на это кучу времени и в итоге делают то, что я сделаю в 3 раза быстрее и потрачу время на что-то другое, к примеру, прохождение God of War, используя простое и топорное решение, которого будет достаточно для этой задачи. Я за эффективность и простоту, а RxJava данным требованиям (или убеждениям) не удовлетворяет.
Подробнее про корутины: https://habr.com/company/alfa/blog/336228/