🎛 Многомодульность
Когда я только начал работать в качестве Android разработчика, моей первой компанией был аутсорс. Это была очень маленькая компания и в основном были мелкие заказы на проекты длительностью 2-3 месяца. Довольно часто я был единственным разработчиком на проекте. Я ничего не слышал про многомодульность, что это и зачем.
В этот же период был расцвет докладов про архитектуры. Тогда только начинали говорить про многомодульность. Я помню как смотрел доклады от разработчиков Яндекс Карт, Lift, Uber идея многомодульности казалась невероятно крутой. Разумеется я подумал, раз крутые ребята так делают на своих проектах то и я так должен делать. На основных проектах времени на это особо не выделяли и поэтому многомодульность можно было попробовать только на своих pet проектах.
После просмотра всех этих докладов казалось что многомодульность это вообще серебряная пуля от всех проблем и все проекты сразу должны быть многомодульными по дефолту. Это так называемый джуниорский перфекционизм. Когда у тебя в руках оказывается молоток ты повсюду начинаешь видеть гвозди. Многомодульность и правда очень крутой инструмент, правда решает он определенные задачи. Порой когда речь идет о многомодульности путают причину и следствие. Сейчас я попробую вам рассказать когда нужна многомодульность, а когда это просто разрабы решили поиграться.
Есть такая штука как Закон Конвея. Суть такая, что архитектура ПО повторяет архитектуру организации в которой это ПО создается/используется. Другими словами, архитектура в приложении повторяет архитектуру бизнеса.
В банковских проектах это очень хорошо прям прослеживается. Например, возьмем обычное банковское приложение. В нем есть функционал для кредитов, функционал для перевода, функционал для формирования каких-то документов. Это все отдельные бизнесы подразделения, которые работают отдельно друг от друга и фичи они делают независимо друг от друга.
Чаще всего это будут и разные команды разработки. Если над Android приложением работают 40 разработчиков, это не значит, что они прям вместе работают. Они разбиваются на отдельные команды по 2-3 человека и каждый пилит свою часть. Разумеется в этом случае в нашем приложении нужно делать отдельные модули. Потому как это разные экраны и развиваться они должны независимо. Грубо говоря это разные приложения, которые работают в рамках одного процесса и имеют одинаковые UI элементы (если дизайнер адекватный). В одном модуле мы бы просто сошли с ума, постоянно исправляя конфликты, и задевая код другой команды.
Когда ребята из крутых компаний говорят про многомоудльноть, они ее делают не потому что это круто с инженерной точки зрения. Это просто единственный способ убрать сложность возникающую когда огромная куча людей работает над одной кодовой базой. Она нужна только тогда, когда бизнес достигает того размера, что начинает разделяться на отделы, работающие независимо друг от друга.
И поэтому довольно забавно слушать истории, когда ребята из аутсорса, при заказе приложения на 3-4 месяца, без дальнейшей поддержки затаскивают многомодульность. Она там не нужна, многомодульность это про очень долгую поддержку большой командой, что чаще всего есть только в продуктовой разработке. В небольшом проекте использовать многомодульность это оверхед.
Про плюсы которые несет многомодульность, я думаю вы уже слышали. Я лишь напомню вам про минусы:
👉 Первый и самый основной это DI. Какую бы либу вы не использовали, придется как-то изворачиваться, чтобы заставить ее работать с многомодульностью.
👉 Второй это навигация. Из-за того, что ваш модуль ничего не знает об остальных придется делать абстракции поверх навигации, а это куча кода которого можно не делать когда все в одном модуле.
Подводя некоторый итог, если у вас небольшая команда или стартап не занимайтесь ерундой, вы больше времени потратите на болерплейт. Многомодульность нужна только в двух случаях. Первый я уже описал выше. Второй это когда вам нужно шарить часть функционала куда-то еще. В этом случае без выделения в отдельные модули никак.
Когда я только начал работать в качестве Android разработчика, моей первой компанией был аутсорс. Это была очень маленькая компания и в основном были мелкие заказы на проекты длительностью 2-3 месяца. Довольно часто я был единственным разработчиком на проекте. Я ничего не слышал про многомодульность, что это и зачем.
В этот же период был расцвет докладов про архитектуры. Тогда только начинали говорить про многомодульность. Я помню как смотрел доклады от разработчиков Яндекс Карт, Lift, Uber идея многомодульности казалась невероятно крутой. Разумеется я подумал, раз крутые ребята так делают на своих проектах то и я так должен делать. На основных проектах времени на это особо не выделяли и поэтому многомодульность можно было попробовать только на своих pet проектах.
После просмотра всех этих докладов казалось что многомодульность это вообще серебряная пуля от всех проблем и все проекты сразу должны быть многомодульными по дефолту. Это так называемый джуниорский перфекционизм. Когда у тебя в руках оказывается молоток ты повсюду начинаешь видеть гвозди. Многомодульность и правда очень крутой инструмент, правда решает он определенные задачи. Порой когда речь идет о многомодульности путают причину и следствие. Сейчас я попробую вам рассказать когда нужна многомодульность, а когда это просто разрабы решили поиграться.
Есть такая штука как Закон Конвея. Суть такая, что архитектура ПО повторяет архитектуру организации в которой это ПО создается/используется. Другими словами, архитектура в приложении повторяет архитектуру бизнеса.
В банковских проектах это очень хорошо прям прослеживается. Например, возьмем обычное банковское приложение. В нем есть функционал для кредитов, функционал для перевода, функционал для формирования каких-то документов. Это все отдельные бизнесы подразделения, которые работают отдельно друг от друга и фичи они делают независимо друг от друга.
Чаще всего это будут и разные команды разработки. Если над Android приложением работают 40 разработчиков, это не значит, что они прям вместе работают. Они разбиваются на отдельные команды по 2-3 человека и каждый пилит свою часть. Разумеется в этом случае в нашем приложении нужно делать отдельные модули. Потому как это разные экраны и развиваться они должны независимо. Грубо говоря это разные приложения, которые работают в рамках одного процесса и имеют одинаковые UI элементы (если дизайнер адекватный). В одном модуле мы бы просто сошли с ума, постоянно исправляя конфликты, и задевая код другой команды.
Когда ребята из крутых компаний говорят про многомоудльноть, они ее делают не потому что это круто с инженерной точки зрения. Это просто единственный способ убрать сложность возникающую когда огромная куча людей работает над одной кодовой базой. Она нужна только тогда, когда бизнес достигает того размера, что начинает разделяться на отделы, работающие независимо друг от друга.
И поэтому довольно забавно слушать истории, когда ребята из аутсорса, при заказе приложения на 3-4 месяца, без дальнейшей поддержки затаскивают многомодульность. Она там не нужна, многомодульность это про очень долгую поддержку большой командой, что чаще всего есть только в продуктовой разработке. В небольшом проекте использовать многомодульность это оверхед.
Про плюсы которые несет многомодульность, я думаю вы уже слышали. Я лишь напомню вам про минусы:
👉 Первый и самый основной это DI. Какую бы либу вы не использовали, придется как-то изворачиваться, чтобы заставить ее работать с многомодульностью.
👉 Второй это навигация. Из-за того, что ваш модуль ничего не знает об остальных придется делать абстракции поверх навигации, а это куча кода которого можно не делать когда все в одном модуле.
Подводя некоторый итог, если у вас небольшая команда или стартап не занимайтесь ерундой, вы больше времени потратите на болерплейт. Многомодульность нужна только в двух случаях. Первый я уже описал выше. Второй это когда вам нужно шарить часть функционала куда-то еще. В этом случае без выделения в отдельные модули никак.
👍38🥰2
{1/2} Это мой первый пост, скорее про управление, нежели про технологию. Ну мы же все хотим когда-то вырасти в крутых лидов, поэтому иногда стоит и в этой теме что-то поизучать. Я дилетант в вопросах управления, поэтому воспринимайте все со здравым скептицизмом.
Недавно у меня с одним из разработчиком возник спор. Его очень сильно удивило, что у нас к компании практикуется создание кроссфункциональных команд. Самый основной вопрос был, зачем вообще объединять в одну команду Backend, Android, iOS, Frontend, QA и т.д?
Есть же старое доброе разделение, команда Android отдельно, команда Backend отдельно и т.д. Даже если так подумать, объединение в одну команду Frontend и Backend имеет смысл. Однако в чем прикол объединять мобильные команды с фронтом, ведь у них принципиально разное поведение, дизайн и т.д.
Меня этот вопрос поставил в ступор. Для меня это была настолько очевидная вещь, что я не сразу нашел аргументы в ее защиту. Однако аргументы есть, и сейчас я расскажу почему в большом продукте отдельные команды это боль и единственная рабочая модель это команды по фичам.
Представьте, мы начинаем какой-то продукт. Когда мы его начинаем у нас совсем небольшая команда, допустим 10 человек. У нас все хорошо и никаких проблем нет. Тут не идет речи ни о каком разделении на фича команды. Вы и так все друг друга знаете, проект маленький и при возникновении проблемы, она быстро решается.
Однако что делать, если вас только в Android команде 40 человек. А если сложить всех то на проекте человек 300. Возникают дико страшные издержки на коммуникацию. В таком случае, если команды разработки Android, iOS, Backend и т.д будут работать отдельно, вы не сможете координировать команды. Грубо говоря, если нет разделения по фича командам, значит все разработчики занимаются всем проектом в целом. Следовательно менеджеры будут кидать задачи в зависимости от загрузки разработчиков.
Давайте на примере, допустим нам нужно выпустить фичу получения списка задач в приложении. Эту фичу нужно выпустить на всех платформах. Вот тут начинается веселье, в любой задаче разные платформы будут двигаться с разной скоростью. Всегда миллион факторов: на одной платформе легаси, у других кто-то в отпуск ушел, у третьих рефакторинг…
Допустим Backend сделал эту задачу быстрее всех. Напомню, разрабы работают над всем проектом. Далее Android и iOS сделали задачу примерно одновременно. Однако при работе с фронтом, оказывается, что из-за специфики работы, нужно переделать запрос на стороне Backend. Только вот незадача, Backend разрабы которые работали над этой задаче, сейчас уже делают другие бизнес задачи, ну ооочень срочные. Ведь нужно загрузить разрабов, а то чего они ничего не делают.
Frontend, не может продолжить работу над задачей, потому как нет свободных разрабов на стороне Backend. Android и iOS не могут релизиться, так как мы не можем выкатить фичу без Frontend. Поэтому Frontend начинает делать другие задачи. Когда Backend освобождается спустя кучу времени, у нас уже Frontend заняты другой более важной задачей. А когда начинается тестирование, ух…
Смекаете к чему я клоню? Из-за такого хаоса скорость разработки казалось бы просто фичи может улететь в космос. Пример конечно утрированный, однако я сам был участником похожей тусовки. Фичу которую засчитывали делать 3 недели, делали 4 месяца.
Недавно у меня с одним из разработчиком возник спор. Его очень сильно удивило, что у нас к компании практикуется создание кроссфункциональных команд. Самый основной вопрос был, зачем вообще объединять в одну команду Backend, Android, iOS, Frontend, QA и т.д?
Есть же старое доброе разделение, команда Android отдельно, команда Backend отдельно и т.д. Даже если так подумать, объединение в одну команду Frontend и Backend имеет смысл. Однако в чем прикол объединять мобильные команды с фронтом, ведь у них принципиально разное поведение, дизайн и т.д.
Меня этот вопрос поставил в ступор. Для меня это была настолько очевидная вещь, что я не сразу нашел аргументы в ее защиту. Однако аргументы есть, и сейчас я расскажу почему в большом продукте отдельные команды это боль и единственная рабочая модель это команды по фичам.
Представьте, мы начинаем какой-то продукт. Когда мы его начинаем у нас совсем небольшая команда, допустим 10 человек. У нас все хорошо и никаких проблем нет. Тут не идет речи ни о каком разделении на фича команды. Вы и так все друг друга знаете, проект маленький и при возникновении проблемы, она быстро решается.
Однако что делать, если вас только в Android команде 40 человек. А если сложить всех то на проекте человек 300. Возникают дико страшные издержки на коммуникацию. В таком случае, если команды разработки Android, iOS, Backend и т.д будут работать отдельно, вы не сможете координировать команды. Грубо говоря, если нет разделения по фича командам, значит все разработчики занимаются всем проектом в целом. Следовательно менеджеры будут кидать задачи в зависимости от загрузки разработчиков.
Давайте на примере, допустим нам нужно выпустить фичу получения списка задач в приложении. Эту фичу нужно выпустить на всех платформах. Вот тут начинается веселье, в любой задаче разные платформы будут двигаться с разной скоростью. Всегда миллион факторов: на одной платформе легаси, у других кто-то в отпуск ушел, у третьих рефакторинг…
Допустим Backend сделал эту задачу быстрее всех. Напомню, разрабы работают над всем проектом. Далее Android и iOS сделали задачу примерно одновременно. Однако при работе с фронтом, оказывается, что из-за специфики работы, нужно переделать запрос на стороне Backend. Только вот незадача, Backend разрабы которые работали над этой задаче, сейчас уже делают другие бизнес задачи, ну ооочень срочные. Ведь нужно загрузить разрабов, а то чего они ничего не делают.
Frontend, не может продолжить работу над задачей, потому как нет свободных разрабов на стороне Backend. Android и iOS не могут релизиться, так как мы не можем выкатить фичу без Frontend. Поэтому Frontend начинает делать другие задачи. Когда Backend освобождается спустя кучу времени, у нас уже Frontend заняты другой более важной задачей. А когда начинается тестирование, ух…
Смекаете к чему я клоню? Из-за такого хаоса скорость разработки казалось бы просто фичи может улететь в космос. Пример конечно утрированный, однако я сам был участником похожей тусовки. Фичу которую засчитывали делать 3 недели, делали 4 месяца.
👍26
{2/2} Один из способов решить проблему координации большой команды, разделять ее на мелкие и объединять на фичам. В итоге мы собираем в команду всех участников, Android, iOS, QA, Frontend. Фича команда работает над частью приложения. Только над этой частью и берет задачи связанные только с ней, никуда в другое место не лезет.
И вот допустим у нас опять ситуация когда мы ждем Frontend. Что в это время делать остальным платформам? А ничего! Сходить погулять, попить кофе, заняться рефакторигом вон того класса, а то чет неудобно. Еще хорошо бы тестами вот этот функционал покрыть.
Другими словами, мы занимаемся менее важными вещами, от которых можно быстро вернуться к важной и единственной задаче. Мы не берем новую бизнес задачу, пока Frontend не закончит. Удивительно, но в таком случае скорость поставки возрастает в разы. Именно в этом идея объединения в одну команду фронтов и мобильщиков, даже несмотря на то, что у них фундаментально разные подходы.
Про этот феномен очень круто и понятным языком написано в книге “Цель, процесс непрерывного улучшения”. Везде ее рекомендуют как must have литература, кто в будущем хочет стать тимлидом. На самом деле ее можно прочитать даже если и не планируете. Она написана как история директора завода которому нужно спастисвою жопу завод от разорения.
Очень интересная история. И для меня там было две основных идеи. Первая – 100% загруженность разработчиков не значит максимальная продуктивность, а скорее наоборот двигаться будете в итоге медленнее. Страшная идея для тех, кто на аутсорсе. Вторая – компания двигается со скоростью самого медленного звена. Это значит от того, что вы сегодня не доработали один час изменится примерно нихрена.
Поэтому когда устраиваетесь в продуктовую компанию, спрашивайте как происходит разделение на команды. Чаще всего если менеджеры действительно знают свое дело, они будут объединять людей по фича командам. Для меня это индикатор, что в компании ориентируются на результат, нежели на загруженность разработчиков.
Опять-таки, это если мы говорим про большую компанию. Если в мелких нет такого разделения, то это ок)
И вот допустим у нас опять ситуация когда мы ждем Frontend. Что в это время делать остальным платформам? А ничего! Сходить погулять, попить кофе, заняться рефакторигом вон того класса, а то чет неудобно. Еще хорошо бы тестами вот этот функционал покрыть.
Другими словами, мы занимаемся менее важными вещами, от которых можно быстро вернуться к важной и единственной задаче. Мы не берем новую бизнес задачу, пока Frontend не закончит. Удивительно, но в таком случае скорость поставки возрастает в разы. Именно в этом идея объединения в одну команду фронтов и мобильщиков, даже несмотря на то, что у них фундаментально разные подходы.
Про этот феномен очень круто и понятным языком написано в книге “Цель, процесс непрерывного улучшения”. Везде ее рекомендуют как must have литература, кто в будущем хочет стать тимлидом. На самом деле ее можно прочитать даже если и не планируете. Она написана как история директора завода которому нужно спасти
Очень интересная история. И для меня там было две основных идеи. Первая – 100% загруженность разработчиков не значит максимальная продуктивность, а скорее наоборот двигаться будете в итоге медленнее. Страшная идея для тех, кто на аутсорсе. Вторая – компания двигается со скоростью самого медленного звена. Это значит от того, что вы сегодня не доработали один час изменится примерно нихрена.
Поэтому когда устраиваетесь в продуктовую компанию, спрашивайте как происходит разделение на команды. Чаще всего если менеджеры действительно знают свое дело, они будут объединять людей по фича командам. Для меня это индикатор, что в компании ориентируются на результат, нежели на загруженность разработчиков.
Опять-таки, это если мы говорим про большую компанию. Если в мелких нет такого разделения, то это ок)
👍25
📖 Скорочтение
Однажды один мой друг сказал: "Жалко что я медленно читаю, вот если бы я обладал навыком скорочтения, я бы мог быстро прочитать все доки и гораздо быстрее осваивал новые технологии и быстрее бы делал задачи". Мне тогда на уровне интуиции эта идея показалась странной, но я не мог понять почему. Признаться честно я и сам увлекался этой темой в начале студенчества, но у меня как-то не получалось и я забил на это дело. Как оказалась правильно сделал.
Речь тут даже несколько про само скорочтение, а больше о том, что у нас огромный пласт информации. Каждый день кто-то пишет интересную статью, выходят release notes новых библиотек, выходят новые фреймворки, библиотеки, подходы. Кажется если ты не будешь сделать за новостями неделю, но ты сразу превратишься в деда который только про Cobol может говорить, ощущаешь себя как Мэтт Дэймон в Cпасти Рядового Райена.
И вот вроде бы есть решение в виде скорочтения. Представь, ты можешь читать ну очень быстро, прочитывать всю страницу при одном только взгляде. Можешь освободить кучу времени на другие дела. Вот только задавали ли вы себе вопрос: а действительно ли прочитанные статьи делают разработчика, крутым разработчиком? Если взять начинающего разработчика, который очень быстро сможет прочитать всю доку по Android сможет ли он написать действительно крутой код? Ну скорее всего нет)
У Максима Дорофеева есть очень крутая мысль и в книге и в видосах, что мы в современном мире уделяем время только потреблению информации и очень мало уделяем обдумыванию и практике. В IT это проявляется в виде того, что мы много читаем всяких статей (ну или как я просто добавляем в список на потом) но мало пробуем на практике то, что написано. Можно много читать про Compose и про то какой он классный, но пока не попробуешь руками сделать проект не поймешь, где в статье приукрасили, а где он реально хорош.
Мысль о том, что в IT все очень быстро меняется это миф. Ну все конечно меняется, но не так быстро как может показаться. Если посмотреть на историю Android за последние 10 лет, что кардинально поменялось в разработке? Единственное действительно сильное изменение происходит только сейчас с выходом Compose, да и то это касается только UI слоя. Ну и разве что мы перестали все делать в Activity и придумали логику выносить в отдельное место.
Поэтому если вам кажется что если вы не будете постоянно следить за новинками, что-то пропустите, расслабьтесь на самом деле все меняется довольно медленно. Скорочтение не нужно, реальный опыт получаешь только решая проблемы. На себе я это точно хорошо прочувствовал, что действительно я разбираюсь в теме, только если пытаюсь решить какую-то проблему на проекте.
Поэтому если будет выбор между разработчиком который быстро читает и который уже решил хренову тучу проблем выбор очевидный)
Однажды один мой друг сказал: "Жалко что я медленно читаю, вот если бы я обладал навыком скорочтения, я бы мог быстро прочитать все доки и гораздо быстрее осваивал новые технологии и быстрее бы делал задачи". Мне тогда на уровне интуиции эта идея показалась странной, но я не мог понять почему. Признаться честно я и сам увлекался этой темой в начале студенчества, но у меня как-то не получалось и я забил на это дело. Как оказалась правильно сделал.
Речь тут даже несколько про само скорочтение, а больше о том, что у нас огромный пласт информации. Каждый день кто-то пишет интересную статью, выходят release notes новых библиотек, выходят новые фреймворки, библиотеки, подходы. Кажется если ты не будешь сделать за новостями неделю, но ты сразу превратишься в деда который только про Cobol может говорить, ощущаешь себя как Мэтт Дэймон в Cпасти Рядового Райена.
И вот вроде бы есть решение в виде скорочтения. Представь, ты можешь читать ну очень быстро, прочитывать всю страницу при одном только взгляде. Можешь освободить кучу времени на другие дела. Вот только задавали ли вы себе вопрос: а действительно ли прочитанные статьи делают разработчика, крутым разработчиком? Если взять начинающего разработчика, который очень быстро сможет прочитать всю доку по Android сможет ли он написать действительно крутой код? Ну скорее всего нет)
У Максима Дорофеева есть очень крутая мысль и в книге и в видосах, что мы в современном мире уделяем время только потреблению информации и очень мало уделяем обдумыванию и практике. В IT это проявляется в виде того, что мы много читаем всяких статей (ну или как я просто добавляем в список на потом) но мало пробуем на практике то, что написано. Можно много читать про Compose и про то какой он классный, но пока не попробуешь руками сделать проект не поймешь, где в статье приукрасили, а где он реально хорош.
Мысль о том, что в IT все очень быстро меняется это миф. Ну все конечно меняется, но не так быстро как может показаться. Если посмотреть на историю Android за последние 10 лет, что кардинально поменялось в разработке? Единственное действительно сильное изменение происходит только сейчас с выходом Compose, да и то это касается только UI слоя. Ну и разве что мы перестали все делать в Activity и придумали логику выносить в отдельное место.
Поэтому если вам кажется что если вы не будете постоянно следить за новинками, что-то пропустите, расслабьтесь на самом деле все меняется довольно медленно. Скорочтение не нужно, реальный опыт получаешь только решая проблемы. На себе я это точно хорошо прочувствовал, что действительно я разбираюсь в теме, только если пытаюсь решить какую-то проблему на проекте.
Поэтому если будет выбор между разработчиком который быстро читает и который уже решил хренову тучу проблем выбор очевидный)
👍41🔥2👏2👎1
Мой коллега недавно написал серию статей про фрагменты. И вот меня довольно сильно заинтересовала часть про Result API. В статье показаны базовые кейсы как это использовать. Однако в статье не указаны недостатки Result API для фрагментов, не порядок я считаю. И так как я очень люблю похейтить либы гугла, поговорим о минусах этого подхода.
Значит, если вдруг пропустили, Result API это новый подход к тому, как мы можем получать результат с других Activity или Fragments. Вот раньше допустим был геморрой с обработкой разрешения. Нужно разрешение сперва запросить, потом переопределить метод onActivityResult, для это нужно придумывать специальные Id для этого. Мягко говоря это неудобно.
Сейчас все намного проще, вызываем специальный метод, устанавливаем в него лямбду. Затем запрашиваем разрешение и система уже дернет нашу лямбду с нужными уже распарщеными данными. Уже намного удобнее.
Result API крутая штука и по факту сейчас это единственный путь как обрабатывать разрешения или например получать фото с галереи. Метод onActivityResult сейчас помечен как Deprecated. Другими словами, Result API круто использовать когда нужно получить данные извне, однако часто это используется для передачи данных м/у своими же Activity. А когда речь заходит за использование Result API для передачи данных м/у фрагментами начинается вообще мрак.
Основная идея, которой я уже давно придерживаюсь, и которая не раз меня спасала, сводится к следующему: “Передавайте данные м/у своими экранами через Domain слой”. View очень сильно ограничивает в плане передачи данных по трем простым причинам:
1️⃣ Формат передачи данных всегда Bundle. Если конечно не дергаются методы других фрагментов напрямую, где нужно быть ну очень аккуратным.
2️⃣ View очень неустойчивая штука. Этот слой часто меняется, он может умереть в любой момент.
3️⃣ Сложность с навигацией. Даже у библиотеки от самого гугла нет адекватной интеграции с Result API. Мб она конечно есть, но я так и не нашел как нормально это сделать.
Подход с передачей View через Domain слой куда гибче, тут 100 способов как передавать данные удобным тебе способом, в удобном тебе формате и ты целиком и полностью все контролируешь.
Еще основная проблема, что Result API для фрагментов заставляет тебя создавать ебучие строковые константы. Это вообще проблема у всех библиотек гугла, они видимо прям в восторге от строковых констант. Иначе как объяснить это и это и вот это.
В Result API для Activity сделано круто, потому как там есть специальный класс контракт, благодаря которому, ты сразу задаешь правила как преобразовать данные из Bundle в нужный объект. Для фрагментов такого нет, в итоге тебе передают Bundle, а ты иди сам ищи в коде, что вообще в него упаковали!
Подводя итог, хотите облегчить тебе жизнь, передавайте данные м/у фрагментами через Domain слой, этот подход реально спасает когда навигация неожиданно меняется, а она будет меняться. Не хотите лишних сложностей, не тащите Result API во фрагменты!
Значит, если вдруг пропустили, Result API это новый подход к тому, как мы можем получать результат с других Activity или Fragments. Вот раньше допустим был геморрой с обработкой разрешения. Нужно разрешение сперва запросить, потом переопределить метод onActivityResult, для это нужно придумывать специальные Id для этого. Мягко говоря это неудобно.
Сейчас все намного проще, вызываем специальный метод, устанавливаем в него лямбду. Затем запрашиваем разрешение и система уже дернет нашу лямбду с нужными уже распарщеными данными. Уже намного удобнее.
Result API крутая штука и по факту сейчас это единственный путь как обрабатывать разрешения или например получать фото с галереи. Метод onActivityResult сейчас помечен как Deprecated. Другими словами, Result API круто использовать когда нужно получить данные извне, однако часто это используется для передачи данных м/у своими же Activity. А когда речь заходит за использование Result API для передачи данных м/у фрагментами начинается вообще мрак.
Основная идея, которой я уже давно придерживаюсь, и которая не раз меня спасала, сводится к следующему: “Передавайте данные м/у своими экранами через Domain слой”. View очень сильно ограничивает в плане передачи данных по трем простым причинам:
1️⃣ Формат передачи данных всегда Bundle. Если конечно не дергаются методы других фрагментов напрямую, где нужно быть ну очень аккуратным.
2️⃣ View очень неустойчивая штука. Этот слой часто меняется, он может умереть в любой момент.
3️⃣ Сложность с навигацией. Даже у библиотеки от самого гугла нет адекватной интеграции с Result API. Мб она конечно есть, но я так и не нашел как нормально это сделать.
Подход с передачей View через Domain слой куда гибче, тут 100 способов как передавать данные удобным тебе способом, в удобном тебе формате и ты целиком и полностью все контролируешь.
Еще основная проблема, что Result API для фрагментов заставляет тебя создавать ебучие строковые константы. Это вообще проблема у всех библиотек гугла, они видимо прям в восторге от строковых констант. Иначе как объяснить это и это и вот это.
В Result API для Activity сделано круто, потому как там есть специальный класс контракт, благодаря которому, ты сразу задаешь правила как преобразовать данные из Bundle в нужный объект. Для фрагментов такого нет, в итоге тебе передают Bundle, а ты иди сам ищи в коде, что вообще в него упаковали!
Подводя итог, хотите облегчить тебе жизнь, передавайте данные м/у фрагментами через Domain слой, этот подход реально спасает когда навигация неожиданно меняется, а она будет меняться. Не хотите лишних сложностей, не тащите Result API во фрагменты!
👍24🤔2
{1/2} Хочу рассказать про интересную тему с многопоточностью. И начну с вопроса: что такое синхронизация, если мы говорим про многопоточность?
Синхронизация – способ сделать так, чтобы например метод, который может дёрнуться из нескольких потоков гарантировано вызвался только одним. Остальные потоки в этот момент, должны дождаться пока не закончит первый и т.д по очереди.
Все это можно сделать при помощи такой штуки как Mutex. В Java Mutex реализован на уровне языка и представлен в виде блока synchronized. Все довольно просто, помечаем метод специальным модификатором synchronized и все начинает работать как нужно, потоки будут вызывать метод по очереди. А что если я скажу вам, что есть способ сделать синхронизацию, без блокирования потоков?
Есть такая штука как Atomic переменные. В большинстве случаев про них вспоминают в тот момент, когда нужно быстренько сделать какой-нибудь счетчик, который будет корректно работать в многопоточной системе. Однако это не все кейсы использования Atomic переменных. Диапазон задач, которые можно решить при помощи Atomic гораздо шире, чем просто создание счетчиков.
У всех Atomic переменных есть метод который называется compareAndSet. Что он делает: как очевидно из названия, сравнивает и потом устанавливает значение. Если удалось установить новое значение он возвращает true если не удалось false. С чем он сравнивает? Сравнивает он первый аргумент со значением которое установлено в самом Atomic. Да, на словах звучит стремно, поэтому давайте на примере кода:
Если текущее значение Atomic переменной, совпадает со значением первого аргумента функции
И казалось бы, ну и что такого крутого, можно и на syncronized блоках сделать точно такой же метод. Не совсем так, под капотом compareAndSet не использует привычные нам инструменты синхронизации, у этого метода нативная реализация.
У каждого современного процессора есть встроенная команда
Как это использовать? Вот самый простой и базовый пример. Представьте, что у вас есть метод, который вызывается из нескольких потоков. И вам нужно сделать так, чтобы поток, который вызывал этот метод первым, сделал один блок кода, а все остальные потоки прошли мимо.
Как это будет сделано с использованием synchronized блока:
Все отработает как нужно, однако пока один поток будет выполнять метод, все остальные просто будут ждать, как-то не круто. А теперь используем магию Atomic.
Для данной задачи, даже кода получилось меньше. Что произойдет тут. Метод
Вот и все, пример представленный тут очень простой, однако даже такой пример я один раз использовал в продакшен коде. Если заглянуть в исходники Rx, можно обнаружить, что весь Rx построен на базе неблокирующей синхронизации.
Возникает вопрос, зачем тогда вообще нужен Mutex, ведь гораздо быстрее делать все на неблокирующей реализации. В нашем примере и правда неблокирующая реализация, намного эффективнее. Однако это не всегда.
Синхронизация – способ сделать так, чтобы например метод, который может дёрнуться из нескольких потоков гарантировано вызвался только одним. Остальные потоки в этот момент, должны дождаться пока не закончит первый и т.д по очереди.
Все это можно сделать при помощи такой штуки как Mutex. В Java Mutex реализован на уровне языка и представлен в виде блока synchronized. Все довольно просто, помечаем метод специальным модификатором synchronized и все начинает работать как нужно, потоки будут вызывать метод по очереди. А что если я скажу вам, что есть способ сделать синхронизацию, без блокирования потоков?
Есть такая штука как Atomic переменные. В большинстве случаев про них вспоминают в тот момент, когда нужно быстренько сделать какой-нибудь счетчик, который будет корректно работать в многопоточной системе. Однако это не все кейсы использования Atomic переменных. Диапазон задач, которые можно решить при помощи Atomic гораздо шире, чем просто создание счетчиков.
У всех Atomic переменных есть метод который называется compareAndSet. Что он делает: как очевидно из названия, сравнивает и потом устанавливает значение. Если удалось установить новое значение он возвращает true если не удалось false. С чем он сравнивает? Сравнивает он первый аргумент со значением которое установлено в самом Atomic. Да, на словах звучит стремно, поэтому давайте на примере кода:
val atomic = AtomicBoolean(false)
val newValue = true
val result = atomic.compareAndSet(false, newValue)
Если текущее значение Atomic переменной, совпадает со значением первого аргумента функции
compareAndSet
, только в этом случае происходит замена на новое значение (newValue).И казалось бы, ну и что такого крутого, можно и на syncronized блоках сделать точно такой же метод. Не совсем так, под капотом compareAndSet не использует привычные нам инструменты синхронизации, у этого метода нативная реализация.
У каждого современного процессора есть встроенная команда
compareAndSet
, в литературе часто сокращают и называют CAS. Так вот, команда CAS реализована аж на уровне процессора, означает что этот метод во много раз быстрее чем synchronized блоки.Как это использовать? Вот самый простой и базовый пример. Представьте, что у вас есть метод, который вызывается из нескольких потоков. И вам нужно сделать так, чтобы поток, который вызывал этот метод первым, сделал один блок кода, а все остальные потоки прошли мимо.
Как это будет сделано с использованием synchronized блока:
var flag = true
fun toDo() = synchronized{
if(flag){
// block
flag = false
}
}
Все отработает как нужно, однако пока один поток будет выполнять метод, все остальные просто будут ждать, как-то не круто. А теперь используем магию Atomic.
var flag = AtomicBoolean(false)
fun toDo() {
if(flag.compareAndSet(false, true)){
// block
}
}
Для данной задачи, даже кода получилось меньше. Что произойдет тут. Метод
compareAndSet
как вы уже знаете, гарантированно синхронизирован, причем на уровне процессора. Первый поток, который выполняет этот код, успешно произведет замену переменных и метод compareAndSet
вернет ему true. Последующие потоки, будут получать результат false, потому как текущее значение уже не совпадает с первым аргументом. Ни один поток не будет заблокирован, все просто пройдут мимо блока if. Вот и все, пример представленный тут очень простой, однако даже такой пример я один раз использовал в продакшен коде. Если заглянуть в исходники Rx, можно обнаружить, что весь Rx построен на базе неблокирующей синхронизации.
Возникает вопрос, зачем тогда вообще нужен Mutex, ведь гораздо быстрее делать все на неблокирующей реализации. В нашем примере и правда неблокирующая реализация, намного эффективнее. Однако это не всегда.
👍39
{2/2} Неблокирующая реализация хорошо работает когда у вас небольшое количество потоков, если же потоков становится много, она сильно проигрывает обычной блокирующей синхронизации. Связано это с тем, что происходит с потоками при использовании Mutex.
В системе с использованием Mutex (блокирующая синхронизация), потоки, пока ждут своей очереди, переходят в особое состояние. В этом состоянии они не тратят CPU. В неблокирующей синхронизации же, потоки никогда не спят и постоянно тратят CPU.
На самом деле, обор тонкостей когда использовать блокирующую синхронизацию, а когда обычную тянет на отдельную статью. Однако точно стоит знать, что такая модель есть.
В системе с использованием Mutex (блокирующая синхронизация), потоки, пока ждут своей очереди, переходят в особое состояние. В этом состоянии они не тратят CPU. В неблокирующей синхронизации же, потоки никогда не спят и постоянно тратят CPU.
На самом деле, обор тонкостей когда использовать блокирующую синхронизацию, а когда обычную тянет на отдельную статью. Однако точно стоит знать, что такая модель есть.
👍42
⚙️ Gradle
Сейчас по работе довольно часто приходится сталкиваться с gradle. До этого я лишь иногда делал мелкие фиксы скриптов, в основном по схеме: загуглил -> скопировал код со StackOverflow -> вроде работает. Сейчас же копая тему глубже, я понял почему его так хейтят.
На самом деле Gradle довольно неплохая билд система, однако в ней куча странных и очевидно неоптимальных решений которые со временем вылились в то, что имеем.
Поэтому давайте пройдемся по конкретным недостатком которые так бесят:
👉 Память
👉 Инкрементальность конфигурации
👉 Язык скриптов
👉 Нет ограничений
Сейчас по работе довольно часто приходится сталкиваться с gradle. До этого я лишь иногда делал мелкие фиксы скриптов, в основном по схеме: загуглил -> скопировал код со StackOverflow -> вроде работает. Сейчас же копая тему глубже, я понял почему его так хейтят.
На самом деле Gradle довольно неплохая билд система, однако в ней куча странных и очевидно неоптимальных решений которые со временем вылились в то, что имеем.
Поэтому давайте пройдемся по конкретным недостатком которые так бесят:
👉 Память
👉 Инкрементальность конфигурации
👉 Язык скриптов
👉 Нет ограничений
👍15
Когда я учился в школе, это был примерно 2009 или 2010 год, у меня был совсем слабенький ноут. Что-то вроде старого pentium, 2гб оперативы и совсем слабенькая карта. И вот я скачиваю первый Crysis, которая в свое время произвела просто фурор в индустрии видео игр. Каково было мое удивление, что это чудо запустилось без каких-либо явных проблем с производительностью.
Да настройки были не максимальные, но тем не менее, того железа хватало чтобы для комфортной игры. 3d игра это куча расчетов в секунду, загрузка огромного количества ассетов и все это за те самые 16ms. Затем смотрим на Gradle, которая, я напомню, является всего лишь билд системой.
Суть билд системы это преобразовать один файл в другой. Это вот та ироничная проблема индустрии, когда рокет сайнс игре достаточно меньше 1Гб оперативы, а билд системе, суть которой сводится к правильному вызову компилятора нужно больше 10гб. Это вообще что такое?
Это настолько распространенная фигня, что мы уже даже не замечаем эту проблему. Сейчас проходя в компанию мобильным разрабам выдают ноуты с 32Гб оперативы. По той причине, что если будет меньше, все будет страшно зависать. И при этом проекты то продолжают расти, почти все большие компании приходят к супер аппам. Все выглядит так, что скорого и 32гб будет мало.
Единого ответа почему Gradle столько ест у меня нет. Да скорее всего уже и сама команда Gradle затрудняется на это ответить. Очевидно что у Gradle куча легаси, куча утечек памяти и куча не самых оптимальных решений. Этому также способствует что Gradle работает на базе JVM, которая сама по себе черная дыра для памяти.
Остается надеятся на то, что мы когда-нибудь слезем с иглы Gradle и нам не нужно будет покупать компы с большим запасом памяти.
Да настройки были не максимальные, но тем не менее, того железа хватало чтобы для комфортной игры. 3d игра это куча расчетов в секунду, загрузка огромного количества ассетов и все это за те самые 16ms. Затем смотрим на Gradle, которая, я напомню, является всего лишь билд системой.
Суть билд системы это преобразовать один файл в другой. Это вот та ироничная проблема индустрии, когда рокет сайнс игре достаточно меньше 1Гб оперативы, а билд системе, суть которой сводится к правильному вызову компилятора нужно больше 10гб. Это вообще что такое?
Это настолько распространенная фигня, что мы уже даже не замечаем эту проблему. Сейчас проходя в компанию мобильным разрабам выдают ноуты с 32Гб оперативы. По той причине, что если будет меньше, все будет страшно зависать. И при этом проекты то продолжают расти, почти все большие компании приходят к супер аппам. Все выглядит так, что скорого и 32гб будет мало.
Единого ответа почему Gradle столько ест у меня нет. Да скорее всего уже и сама команда Gradle затрудняется на это ответить. Очевидно что у Gradle куча легаси, куча утечек памяти и куча не самых оптимальных решений. Этому также способствует что Gradle работает на базе JVM, которая сама по себе черная дыра для памяти.
Остается надеятся на то, что мы когда-нибудь слезем с иглы Gradle и нам не нужно будет покупать компы с большим запасом памяти.
👍10🤯10🤔5🔥2👎1
Вторая самая бесячая проблема Gradle, это то, что он не умеет кешировать конфигурации. Напомню, Gradle работает в три этапа:
1️⃣ Инициализация. Когда Gradle бегает по файлам и собирает инфу о том, сколько у нас вообще есть модулей и создает объекты Project.
2️⃣ Конфигурация. Запускаются все скрипты build.gradle, подтягиваются все таски и строится граф этих самых тасок.
3️⃣ Выполнение тасок. Тут все просто, Gradle просто запускает нужные таски в нужном порядке
Ну так и вот, на нашем проекте расчет конфигурации занимает (если верить метрикам) в среднем около 40 сек. Почти минута! Минута пенальти, просто чтобы он вообще начал билдить.
Конечно тут много причин почему это происходит. Одна из них это потому что мы криво написали скрипты, и сейчас решаем эту проблему. Однако от действительно хорошей билд системы ожидаешь, что она будет бить по рукам если ты делаешь что-то не то. Gradle же позволяет направо и налево делать side эффекты в скриптах, из-за чего системе приходится прогонять эту самую конфигурацию каждый раз.
Проблема в том, что даже если не делать ничего лишнего и писать скрипты продумано по всем правилам, Gradle не умеет инкрементально запускать конфигурацию. Что удивительно, ведь он умеет в инкрементальность файлов, но при этом не умеет в инкрементальность скриптов, что вроде как не сильно сложнее. Было бы конечно круто, если бы Gradle умел просчитывать изменения в конфигурации и запускать только измененные части.
Помимо отсутствия кеширования и инкрементальности, этап конфигурации не умеет параллелится. Что довольно грустно, у нас сейчас куча ядер в компе, но во время конфигурации используется только одно.
В итоге все эти проблемы сводятся к одной и той же причине. Gradle слишком многое позволяет делать разработчикам, что приводит к тому что он не может применить оптимизации. При этом не думаю что у разработчиков был такой запрос, чтобы можно было делать что угодно. Возможно Gradle попал в ловушку стартапа, решили выдуманную задачу и плохо в итоге стало всем.
Вообщем ситуация такая, каждый раз запуская билд, я сижу в котле ярости, ведь я ничего не менял, а тем не менее опять должен ждать пока рассчитается конфигурация.
1️⃣ Инициализация. Когда Gradle бегает по файлам и собирает инфу о том, сколько у нас вообще есть модулей и создает объекты Project.
2️⃣ Конфигурация. Запускаются все скрипты build.gradle, подтягиваются все таски и строится граф этих самых тасок.
3️⃣ Выполнение тасок. Тут все просто, Gradle просто запускает нужные таски в нужном порядке
Ну так и вот, на нашем проекте расчет конфигурации занимает (если верить метрикам) в среднем около 40 сек. Почти минута! Минута пенальти, просто чтобы он вообще начал билдить.
Конечно тут много причин почему это происходит. Одна из них это потому что мы криво написали скрипты, и сейчас решаем эту проблему. Однако от действительно хорошей билд системы ожидаешь, что она будет бить по рукам если ты делаешь что-то не то. Gradle же позволяет направо и налево делать side эффекты в скриптах, из-за чего системе приходится прогонять эту самую конфигурацию каждый раз.
Проблема в том, что даже если не делать ничего лишнего и писать скрипты продумано по всем правилам, Gradle не умеет инкрементально запускать конфигурацию. Что удивительно, ведь он умеет в инкрементальность файлов, но при этом не умеет в инкрементальность скриптов, что вроде как не сильно сложнее. Было бы конечно круто, если бы Gradle умел просчитывать изменения в конфигурации и запускать только измененные части.
Помимо отсутствия кеширования и инкрементальности, этап конфигурации не умеет параллелится. Что довольно грустно, у нас сейчас куча ядер в компе, но во время конфигурации используется только одно.
В итоге все эти проблемы сводятся к одной и той же причине. Gradle слишком многое позволяет делать разработчикам, что приводит к тому что он не может применить оптимизации. При этом не думаю что у разработчиков был такой запрос, чтобы можно было делать что угодно. Возможно Gradle попал в ловушку стартапа, решили выдуманную задачу и плохо в итоге стало всем.
Вообщем ситуация такая, каждый раз запуская билд, я сижу в котле ярости, ведь я ничего не менял, а тем не менее опять должен ждать пока рассчитается конфигурация.
👍18👏2🤔1
Итак. Я немного проебался и не уточнил всю инфу, благо в коментах меня поправили. Более того, слона то я и не заменил, оказывается у нас на проекте оно давно используется.
Значится, Gradle за последние несколько релизов сделал серьезный шаг вперед и научился кешировать конфигурацию.
Причем для кеширования конфигурации, даже есть две опции. Обе кстати до сих пор экспериментальные, но вроде как работают нормально:
1️⃣ org.gradle.unsafe.configuration-cache. Самая рекомендуемая опция, все довольно очевидно, если input для конфигурации не меняется, т.е не меняются скрипты или другие входные данные все будет закешированно и повторно запускаться не будет. Работает хорошо, но для настройки есть потешные параметры вроде того, сколько ошибок мы можем выдержать прежде чем кэш упадает.
2️⃣ org.gradle.configureondemand. Не совсем кеширование, а скорее мы говорим Gradle, давайка ты не будешь конфигурацию запускать на всем проекте, а только вот на нужных мне модулях. Это будет работать только в том случае, если у вас разработка ведется в небольших sample проектах, т.е когда вы не собираете большой проект со всеми модулями, а только необходимые вам фичи.
Касательно параллелизации конфигурации, Gradle и это умеет поддерживать. Если верить доке, для этого нужно включить этот самый кэш. Вот только что-то на практике не особо ощущается эта параллельность, по ощущениям все также запускается в одном потоке, наверное нужно выполнить еще какие-то требования для этого. Если есть знатоки то плиз подскажите в коментах.
Однако несмотря на то, что Gradle таки научился сохранять кэш проблема с инкрементацией остается актуальной. В примере с кешированием конфигурации, стоит поменять один скрипт, у вас перезапустится вся конфигурация. Это естестенно приводит к тому, что стоит вам переключится на другую ветку, с большой долей вероятности вы опять будете ждать конфигурацию
Значится, Gradle за последние несколько релизов сделал серьезный шаг вперед и научился кешировать конфигурацию.
Причем для кеширования конфигурации, даже есть две опции. Обе кстати до сих пор экспериментальные, но вроде как работают нормально:
1️⃣ org.gradle.unsafe.configuration-cache. Самая рекомендуемая опция, все довольно очевидно, если input для конфигурации не меняется, т.е не меняются скрипты или другие входные данные все будет закешированно и повторно запускаться не будет. Работает хорошо, но для настройки есть потешные параметры вроде того, сколько ошибок мы можем выдержать прежде чем кэш упадает.
2️⃣ org.gradle.configureondemand. Не совсем кеширование, а скорее мы говорим Gradle, давайка ты не будешь конфигурацию запускать на всем проекте, а только вот на нужных мне модулях. Это будет работать только в том случае, если у вас разработка ведется в небольших sample проектах, т.е когда вы не собираете большой проект со всеми модулями, а только необходимые вам фичи.
Касательно параллелизации конфигурации, Gradle и это умеет поддерживать. Если верить доке, для этого нужно включить этот самый кэш. Вот только что-то на практике не особо ощущается эта параллельность, по ощущениям все также запускается в одном потоке, наверное нужно выполнить еще какие-то требования для этого. Если есть знатоки то плиз подскажите в коментах.
Однако несмотря на то, что Gradle таки научился сохранять кэш проблема с инкрементацией остается актуальной. В примере с кешированием конфигурации, стоит поменять один скрипт, у вас перезапустится вся конфигурация. Это естестенно приводит к тому, что стоит вам переключится на другую ветку, с большой долей вероятности вы опять будете ждать конфигурацию
👍21
Про язык я планировал сделать один пост, но он чет разросся, поэтому я сделаю два поста, один про Groovy и уже второй про Kts.
Бывало у вас такое пытаешься ты написать скрипт для Gradle, и во время его написания, будто магия какая-то происходит. В одном месте так нужно писать, в другом по другому.
Вся это магия происходит из-за неудачного выбора Gradle. Смысл в чем, Gradle работает на базе JVM, естественно разработчики хотели для написания скриптов язык который максимально близок к JVM.
Эта близость нужна из-за Maven. В индустрии тогда было принято писать плагины на Java, естественно нужно было сделать мягкий переход для новых пользователей, чтобы откушать часть рынка у Maven.
Помимо этого, язык должен быть скриптовым, не охото компилировать скрипты перед компиляцией проекта.
В момент разработки Gradle таких языков было всего 2: Groovy и Clojure. В расчет не берем всяких JPython и подобных монстров. Clojure не стали использовать так как, он вышел буквально за год до выхода Gradle и его никто не воспринимал всерьез. Плюс, писать скрипты в стиле Lisp не каждый захочет. Остался только один единственный вариант – Groovy.
Groovy отлично подходил под требования. Одновременно и динамический и типизированный, работает на базе JVM и имеет кучу синтаксического сахара. Вот только этот самый сахар и создает ощущение магии для всех, что пытался погрузиться в тему с Gradle. Вот смотрите, на примере, базовая вещь, как можно указать Gradle где находятся сорцы:
1️⃣
2️⃣
3️⃣
Три способа сделать одно и тоже. Приверженцы дзена Python на этом момент должны были в космос улететь от ярости. И ведь это только один мелкий пример, добавить сюда еще то, что ты каждый раз гадаешь типы входного аргумента и что в Gradle все мутабельное и можно поменять почти все что хочешь в любом месте.
Мы с вами творческие ребята, разумеется иногда это круто, когда у тебя есть несколько способов решить задачу. По себе знаю, что иногда хочется выебнутся и решить типичную задачу новым способом. Это круто, когда это все в меру то почему нет? Однако это относится только к основному проекту. В билд системе напротив, такого точно не должно быть, в ней должен быть только один единственно правильный способ сделать что-то. Это важно по двум причинам:
☝️У разработчиков самой билд системы должна быть возможность проводить оптимизации исходя из того, что есть только один способ что-то сделать. Когда ты точно уверен, что у клиента нет возможности сделать что-то иначе, это развязывает тебе руки в оптимизации.
✌️У разработчиков, которые используют билд систему, не должно быть сложностей в изменении скриптов. С Gradle же ты начинаешь в угадайку играть, перебирая различные варианты скопированные с инета.
Ну что тут можно сказать. Скорее всего в ближайшие годы мы не слезем с иглы Gradle. Поэтому если хотите уменьшить уровень магии и упростить себе жизнь, посвятите часок другой изучению Groovy.
Бывало у вас такое пытаешься ты написать скрипт для Gradle, и во время его написания, будто магия какая-то происходит. В одном месте так нужно писать, в другом по другому.
Вся это магия происходит из-за неудачного выбора Gradle. Смысл в чем, Gradle работает на базе JVM, естественно разработчики хотели для написания скриптов язык который максимально близок к JVM.
Эта близость нужна из-за Maven. В индустрии тогда было принято писать плагины на Java, естественно нужно было сделать мягкий переход для новых пользователей, чтобы откушать часть рынка у Maven.
Помимо этого, язык должен быть скриптовым, не охото компилировать скрипты перед компиляцией проекта.
В момент разработки Gradle таких языков было всего 2: Groovy и Clojure. В расчет не берем всяких JPython и подобных монстров. Clojure не стали использовать так как, он вышел буквально за год до выхода Gradle и его никто не воспринимал всерьез. Плюс, писать скрипты в стиле Lisp не каждый захочет. Остался только один единственный вариант – Groovy.
Groovy отлично подходил под требования. Одновременно и динамический и типизированный, работает на базе JVM и имеет кучу синтаксического сахара. Вот только этот самый сахар и создает ощущение магии для всех, что пытался погрузиться в тему с Gradle. Вот смотрите, на примере, базовая вещь, как можно указать Gradle где находятся сорцы:
1️⃣
sourceSets {
main {
java {
srcDirs('src')
}
}
}
2️⃣
sourceSets.main.java.srcDirs = [‘src’]
3️⃣
sourceSets.main.java.srcDirs('src')
Три способа сделать одно и тоже. Приверженцы дзена Python на этом момент должны были в космос улететь от ярости. И ведь это только один мелкий пример, добавить сюда еще то, что ты каждый раз гадаешь типы входного аргумента и что в Gradle все мутабельное и можно поменять почти все что хочешь в любом месте.
Мы с вами творческие ребята, разумеется иногда это круто, когда у тебя есть несколько способов решить задачу. По себе знаю, что иногда хочется выебнутся и решить типичную задачу новым способом. Это круто, когда это все в меру то почему нет? Однако это относится только к основному проекту. В билд системе напротив, такого точно не должно быть, в ней должен быть только один единственно правильный способ сделать что-то. Это важно по двум причинам:
☝️У разработчиков самой билд системы должна быть возможность проводить оптимизации исходя из того, что есть только один способ что-то сделать. Когда ты точно уверен, что у клиента нет возможности сделать что-то иначе, это развязывает тебе руки в оптимизации.
✌️У разработчиков, которые используют билд систему, не должно быть сложностей в изменении скриптов. С Gradle же ты начинаешь в угадайку играть, перебирая различные варианты скопированные с инета.
Ну что тут можно сказать. Скорее всего в ближайшие годы мы не слезем с иглы Gradle. Поэтому если хотите уменьшить уровень магии и упростить себе жизнь, посвятите часок другой изучению Groovy.
🔥22👍6❤4