{2/2} По началу все с презрением относились к этой технологии, потому как тогда было модно хейтить Гугл за их неудобные и багующие либы. В целом за эти 5 лет ничего не поменялось. Однако за это время все признали ViewModel и по сути сейчас это основной способ хранить данные м/у убийствами View и весьма удобный.
ViewModel удобен тем, что можно делать хоть по 100 мелких на Activity или фрагмент. Плюс к этому есть метод для очистки, который точно дернется системой когда ViewModel умрет. Однако есть пара минус в том, что для проверки работы ViewModel нужно прям крутить экран, потому как Moxy можно было проверять при помощи настройки “Don’t keep Activities”. А ViewModel ломаются с этой настройкой, потому как считают что в этом случае ты сам ушел с экрана.
У ViewModel явное преимущество над другими решениями в том, что код по работе с ViewModel встроен в Activity. Встроен не в обычную Activity, а в ту которая идет с Jetpack. На самом деле встроен не сильно, там лишь добавили пару методов для удобства, при желании можно сделать свою реализацию ViewModel.
ViewModel работает несколько хитрее нежели Moxy. Они сохраняются не в статике, а в ActivityRecord. В предыдущих постах я писал про ActivityThread, которая умеет кучу всего. Так вот, в этом самом ActivityThread хранится список ActivityRecord, это некоторые данные по Activity, которые будут жить пока мы с Activity не уйдем, т.е даже между поворотами.
Как я уже сказал выше, мы можем сделать нашу собственную реализацию ViewModel. Для этого нужно использовать метод переопределить метод
Однако поиграться все равно можно, через метод
Ровно такой механизм и используют ViewModel.
Метод
Значит теперь пара советов по использованию ViewModel. Не страдайте фигней и сразу подключайте ktx и используйте делегаты. Дальше за вас все сделает система, главное правильно интегрируйтесь с DI. Большинство DI сами умеют работать с ViewModel. Что почитать:
👉 Официальная дока по ViewModel это must have и в целом там очень доступно все написано
👉 Тоже с официальной доки, туториал который должен помочь
Ну и совет, не тащите LiveData, предпочитайте Kotlin Flow или Rx.
Вот теперь вопрос к публике: вспомните дурацкое поведение фрагментов, и задумайтесь, а очистится ли ViewModel в таком фрагменте?
ViewModel удобен тем, что можно делать хоть по 100 мелких на Activity или фрагмент. Плюс к этому есть метод для очистки, который точно дернется системой когда ViewModel умрет. Однако есть пара минус в том, что для проверки работы ViewModel нужно прям крутить экран, потому как Moxy можно было проверять при помощи настройки “Don’t keep Activities”. А ViewModel ломаются с этой настройкой, потому как считают что в этом случае ты сам ушел с экрана.
У ViewModel явное преимущество над другими решениями в том, что код по работе с ViewModel встроен в Activity. Встроен не в обычную Activity, а в ту которая идет с Jetpack. На самом деле встроен не сильно, там лишь добавили пару методов для удобства, при желании можно сделать свою реализацию ViewModel.
ViewModel работает несколько хитрее нежели Moxy. Они сохраняются не в статике, а в ActivityRecord. В предыдущих постах я писал про ActivityThread, которая умеет кучу всего. Так вот, в этом самом ActivityThread хранится список ActivityRecord, это некоторые данные по Activity, которые будут жить пока мы с Activity не уйдем, т.е даже между поворотами.
Как я уже сказал выше, мы можем сделать нашу собственную реализацию ViewModel. Для этого нужно использовать метод переопределить метод
onRetainNonConfigurationInstance
. Если вы унаследовали свою Activity от любой Activity которая идет в Jetpack, у вас не получить переопределить этот метод, потому как в Activity Jetpack этот метод помечен как final.Однако поиграться все равно можно, через метод
onRetainCustomNonConfigurationInstance
(не путайте с предыдущим). В этом методе нужно вернуть объект, какой вашей душе угодно. Допустим будем возвращать HashMap<String, CustomViewModel>
. После всех переворотов, мы можем получить наши данные через метод getLastCustomNonConfigurationInstance
. Вот так, можно легко и просто сохранить данные без Bundle.Ровно такой механизм и используют ViewModel.
ViewModelStore
(который используется в ViewModelProvider) это буквально и есть HashMap<String, ViewModel>
. Разумеется если ваш процесс умрет, умрут и данные сохраненные через onRetainNonConfigurationInstance, и соответственно все ViewModel, но вы это и так знали.Метод
onRetainCustomNonConfigurationInstance
помечен как deprecated, и вместо него гугл рекомендует использовать ViewModel. По мне так это правильно, не хотелось бы приходя на проект видеть велосипед с этим методом. Значит теперь пара советов по использованию ViewModel. Не страдайте фигней и сразу подключайте ktx и используйте делегаты. Дальше за вас все сделает система, главное правильно интегрируйтесь с DI. Большинство DI сами умеют работать с ViewModel. Что почитать:
👉 Официальная дока по ViewModel это must have и в целом там очень доступно все написано
👉 Тоже с официальной доки, туториал который должен помочь
Ну и совет, не тащите LiveData, предпочитайте Kotlin Flow или Rx.
Вот теперь вопрос к публике: вспомните дурацкое поведение фрагментов, и задумайтесь, а очистится ли ViewModel в таком фрагменте?
👏28👍6
В прошлом посте я набросил на LiveData. Сейчас хочу описать почему я считаю LiveData не самым крутым решением. Давайте начнем вообще с проблемы, из-за чего появилась LiveData. Хочу тут заострить внимание, что само по себе решение довольно неплохое, оно конкретно мне не нравится и в посте постараюсь объяснить почему.
Как вы помните MVVM в целом базируется на идее о том, что у нас есть View и есть ViewModel. View подписывается на изменения с ViewModel и когда получает события изменяет элементы на экране и т.д. После того как Google стала пропагандировать MVVM встал вопрос о том, каким образом реализовать обсервабельность.
По факту можно все сделать и на обычных листнерах, но когда речь идет о кучи разных событий, становится жутко не удобно. Основную неудобность обеспечивает View которая может в любой момент умереть. Если делать все на обычных лиснерах, можно легко сделать утечку памяти.
В момент появления ViewModel моду набирало реактивное программирования. Подход когда у тебя есть потоки данных и ты декларативно это все описываешь всем пришелся по вкусу. В то время как раз начала расцветать RxJava. Однако у RxJava есть два основных минуса, почему ее не стали использовать для передачи событий из ViewModel на View.
Первый минус это ее сложность. RxJava действительно довольно сложная либа в плане понимания, особенно для начинающих. Сложная в основном потому, что нужно думать потоками данных, а в начале все думают императивно. Второй минус по дефолту RxJava не умеет отписываться когда умирает View и это нужно делать вручную, что довольно неудобно. Поэтому Гугл решить эту проблему сделав свою урезанную версию RxJava. LiveData решает все проблемы описанные выше и создает парочку своих.
Первая проблема, которую создает LiveData архитектурная. Эта проблема выходит из одного золотого правила: “Если можно наговнокодить с либой, это будет сделано рано или поздно”. LiveData идеальна для передачи данных от ViewModel к View. Все на этом идеальность кончается, однако в проектах где я был и где использовалась LiveData, она волшебным образом проникала на Domain слой. Причем Гугл в этом даже помогает, они даже в room добавили возможность возвращать LiveData чтобы следить за изменениями.
Чем плохо что LiveData может оказаться в Domain слое? В Domain слое речь идет о бизнес логике, а значит и о применении кучи различных операторов. У LiveData максимально всратое API для операторов, она тут дико проигрывает RxJava и Flow. Помимо этого LiveData очень сильно завязана на View, нельзя подписаться не на UI потоке и без View. Конечно можно подписаться без View, но только нельзя будет отписаться, прям как от email рассылок.
Ну и вторая проблема это тесты. Опять таки из-за того, что LiveData очень завязана на UI нужно во первых реализовать свою мини версию Handler чтобы вообще можно было тесты запустить. Во-вторых нельзя просто так сделать функцию которая например ждет пару секунд, и если событий нет падает. В Rx и Flow такое делается довольно просто и интуитивно, в LiveData нужно выдумывать что-то вроде такого.
Ну и естественно как же вспомнить проблему SingleLiveEvent, для которой Гугл уже 5 лет уперто не желает выпускать мелкий классик, и каждый раз нужно пилить свой.
Подводя итог. LiveData прикольный инструмент и довольно мощный. Его можно тащить в свои тестовые, его можно тащить в мелкие проекты, особенно когда вы только начали изучать разработку. Однако в уже большие проекты или когда вы уже себя уверено чувствуете лучше затаскивать RxJava, а лучше FLow потому как сейчас для View есть много расширений по работе с корутинами.
Как вы помните MVVM в целом базируется на идее о том, что у нас есть View и есть ViewModel. View подписывается на изменения с ViewModel и когда получает события изменяет элементы на экране и т.д. После того как Google стала пропагандировать MVVM встал вопрос о том, каким образом реализовать обсервабельность.
По факту можно все сделать и на обычных листнерах, но когда речь идет о кучи разных событий, становится жутко не удобно. Основную неудобность обеспечивает View которая может в любой момент умереть. Если делать все на обычных лиснерах, можно легко сделать утечку памяти.
В момент появления ViewModel моду набирало реактивное программирования. Подход когда у тебя есть потоки данных и ты декларативно это все описываешь всем пришелся по вкусу. В то время как раз начала расцветать RxJava. Однако у RxJava есть два основных минуса, почему ее не стали использовать для передачи событий из ViewModel на View.
Первый минус это ее сложность. RxJava действительно довольно сложная либа в плане понимания, особенно для начинающих. Сложная в основном потому, что нужно думать потоками данных, а в начале все думают императивно. Второй минус по дефолту RxJava не умеет отписываться когда умирает View и это нужно делать вручную, что довольно неудобно. Поэтому Гугл решить эту проблему сделав свою урезанную версию RxJava. LiveData решает все проблемы описанные выше и создает парочку своих.
Первая проблема, которую создает LiveData архитектурная. Эта проблема выходит из одного золотого правила: “Если можно наговнокодить с либой, это будет сделано рано или поздно”. LiveData идеальна для передачи данных от ViewModel к View. Все на этом идеальность кончается, однако в проектах где я был и где использовалась LiveData, она волшебным образом проникала на Domain слой. Причем Гугл в этом даже помогает, они даже в room добавили возможность возвращать LiveData чтобы следить за изменениями.
Чем плохо что LiveData может оказаться в Domain слое? В Domain слое речь идет о бизнес логике, а значит и о применении кучи различных операторов. У LiveData максимально всратое API для операторов, она тут дико проигрывает RxJava и Flow. Помимо этого LiveData очень сильно завязана на View, нельзя подписаться не на UI потоке и без View. Конечно можно подписаться без View, но только нельзя будет отписаться, прям как от email рассылок.
Ну и вторая проблема это тесты. Опять таки из-за того, что LiveData очень завязана на UI нужно во первых реализовать свою мини версию Handler чтобы вообще можно было тесты запустить. Во-вторых нельзя просто так сделать функцию которая например ждет пару секунд, и если событий нет падает. В Rx и Flow такое делается довольно просто и интуитивно, в LiveData нужно выдумывать что-то вроде такого.
Ну и естественно как же вспомнить проблему SingleLiveEvent, для которой Гугл уже 5 лет уперто не желает выпускать мелкий классик, и каждый раз нужно пилить свой.
Подводя итог. LiveData прикольный инструмент и довольно мощный. Его можно тащить в свои тестовые, его можно тащить в мелкие проекты, особенно когда вы только начали изучать разработку. Однако в уже большие проекты или когда вы уже себя уверено чувствуете лучше затаскивать RxJava, а лучше FLow потому как сейчас для View есть много расширений по работе с корутинами.
👍29👎1
🎋 Дополнение к LiveData
Сейчас я работаю над интересным постом про DRM, однако пост занял больше времени чем я рассчитывал. Поэтому чтобы вы не скучали, я вам докину еще один недостаток про LiveData, про который недавно узнал.
У LiveData есть два оставновных метода которые позволяют отправить в нее данные:
В прошлом после я уже упомянул сложности в Unit тестировании. Эти сложности как раз выходять из того, что LiveData создает Handler, который нельзя создать вне эмулятора. Эта сложность решаемая, т.к разработчики позволили заменить этот класс который под капотом использует Handler. Однако это не все сложности которые приносит метод postValue.
Вторая проблема связана с обновлением данных LiveDate. Если у LiveData нет подписчиков, то метод
Пока UI активен все хорошо, однако как только UI умирает, вы не можете получить свежие данные из LiveData. При обращении к полю вы будете получать старые данные, а это может привести к ох каким неплохим багам.
Почему сделано именно так? Ответ на этот вопрос я не знаю, возможно разработчики решили так избежать лишней нагрузки на UI поток. Мне показалось, что вам будет интересно знать про такое поведение.
Сейчас я работаю над интересным постом про DRM, однако пост занял больше времени чем я рассчитывал. Поэтому чтобы вы не скучали, я вам докину еще один недостаток про LiveData, про который недавно узнал.
У LiveData есть два оставновных метода которые позволяют отправить в нее данные:
setValue
и postValue
. Разница в том, что setValue пытается просто заметить данные в поле, и поэтому если вы вызовете этот метод не на UI потоке он упадет. Второй метод postValue уже можно использовать из любого потока, под капотом он просто вызываем метод setValue, но через Handler. В прошлом после я уже упомянул сложности в Unit тестировании. Эти сложности как раз выходять из того, что LiveData создает Handler, который нельзя создать вне эмулятора. Эта сложность решаемая, т.к разработчики позволили заменить этот класс который под капотом использует Handler. Однако это не все сложности которые приносит метод postValue.
Вторая проблема связана с обновлением данных LiveDate. Если у LiveData нет подписчиков, то метод
postValue
не будет обновлять данные до тех пор, пока они не появятся. Другими словами, вы используете LivaData, чтобы допустим, хранить состояние, и вы хотите получать эти данные и на UI и в самой ViewModel. Пока UI активен все хорошо, однако как только UI умирает, вы не можете получить свежие данные из LiveData. При обращении к полю вы будете получать старые данные, а это может привести к ох каким неплохим багам.
Почему сделано именно так? Ответ на этот вопрос я не знаю, возможно разработчики решили так избежать лишней нагрузки на UI поток. Мне показалось, что вам будет интересно знать про такое поведение.
👍43👏1
DRM
{1/2} Недавно в разговоре со знакомым разработчиком из Кинопоиска он упомянул DRM. Я раньше никогда не слышал про эту штуку, но как оказалось знать про нее must have если вы хотите работать с медиа: фильмы, музыка, книги и т.д.
Значить суть, представьте вы разработчик приложения для музыки. И рано или поздно у вас встанет вопрос, а как сделать защиту контента, который слушают или смотрят ваши пользователи. Смотрите, каждый раз когда вы прослушиваете музыку, плеер скачивает файл с музыкой, как минимум для того, чтобы быстро начать его воспроизведение если мы вернёмся назад. Помимо этого у каждого подобного приложения есть фича, позволяющая скачать файл явно чтобы после была возможность прослушать его offline.
И вот тут возникает вопрос, ведь можно накачать музыки и потом просто забрать эти файлы. Как минимум не составит большого труда зайти на флешку через File Manager и эти файлы забрать. Естественно разработчики давным давно задумались о том, а как вообще защитить подобного рода данные. Ответ на этот вопрос DRM.
DRM - digital rights management набор технических средств которые ограничивают копирование. Разрешить чтение файла, но при этом запретить копирование задача не тривиальная и в некотором смысле почти недостижимая. С этой системой все так или иначе сталкивались когда например, пытались сделать копию диска с игрой. В детстве я помню, что на некоторых дисках была защита из-за которой просто так нельзя было скопировать игру. В общем и целом, все вот эти защиты которые часто используют игры и есть DRM.
У DRM много противников, которые утверждают что это бесполезная дичь, потому как один фиг все взломают и скопируют. И много последователей, потому как ограничивая копирования вы защищаем авторские права и не теряем бабки. Можно понять обе эти группы, но нас тут интересует именно инженерная часть, а именно то, как это реализовано в Android.
DRM это общее название, конкретная система гугла называется Widevine. Однако сам Android поддерживает и другие схемы DRM, но их рассматривать не будем, ибо это скучно. Ну так вот, как это работает. Значит этот Widevine помогает зашифровать потоковый контент так, чтобы его нельзя было просто так взять и скопировать. Работает он с двумя основными протоколами, Dash и HLS. Подробнее о них расскажу как-нибудь потом, сейчас просто запомните эти протоколы помогают сделать потоковую передачу данных.
Значится, в систему Android прям на уровне системы вшит код по работе с DRM. Причем даже есть специальный фреймворк для вендоров, которые могут делать свои схемы DRM если им захочется. Другими словами это аппаратная штука и ломануть ее мамкин хацкер уже просто так не сможет. Как схематично работает эта DRM.
Гугл на своих серверах хранит лицензии контента, которые мы как создатели контента туда кладем. Помимо этого предоставляет инструменты, при помощи которых мы свой контент нарезаем на чанки, которые в последствии будем отдавать с наших серверов.
Вот вы сидите в приложении для музыки, нажимаете на иконку песни. В этот момент приложение отправляет запрос к вашему серверу на скачивание данных песни и получает первый файлик который называется InitData, в нем вшита информация о том, какая это схема DRM и еще всякая разная метаинформация. Как я уже упоминал выше, в большинстве случаев это будет Widevine.
Далее мы c этой InitData идем в системный сервис MediaDrm и получаем запрос. Получаем мы его в виде байтового массива. Берем этот запрос и по Http клиенту идем на сервер гугла, который хранит нашу лицензию. Сервер проверят запрос и отдает нам лицензию.
После мы берем эту лицензию и отправляем в MediaDrm. Далее инициализируем специальный класс MediaCodec который и отвечает на расшифровку контента. Теперь когда все настроено мы можем наконец проигрывать песню. Скачиваем небольшую часть песни которая зашифрована, передаем данные в MediaCodec он расшифровывает мы играем. И так на каждый чанк (часть файла музыки или видео). Чтобы играть офлайн, нужно при помощи специальных методов указать MediaDrm что лицензию необходимо сохранить, чтобы использовать позже.
Продолжение ниже 👇
{1/2} Недавно в разговоре со знакомым разработчиком из Кинопоиска он упомянул DRM. Я раньше никогда не слышал про эту штуку, но как оказалось знать про нее must have если вы хотите работать с медиа: фильмы, музыка, книги и т.д.
Значить суть, представьте вы разработчик приложения для музыки. И рано или поздно у вас встанет вопрос, а как сделать защиту контента, который слушают или смотрят ваши пользователи. Смотрите, каждый раз когда вы прослушиваете музыку, плеер скачивает файл с музыкой, как минимум для того, чтобы быстро начать его воспроизведение если мы вернёмся назад. Помимо этого у каждого подобного приложения есть фича, позволяющая скачать файл явно чтобы после была возможность прослушать его offline.
И вот тут возникает вопрос, ведь можно накачать музыки и потом просто забрать эти файлы. Как минимум не составит большого труда зайти на флешку через File Manager и эти файлы забрать. Естественно разработчики давным давно задумались о том, а как вообще защитить подобного рода данные. Ответ на этот вопрос DRM.
DRM - digital rights management набор технических средств которые ограничивают копирование. Разрешить чтение файла, но при этом запретить копирование задача не тривиальная и в некотором смысле почти недостижимая. С этой системой все так или иначе сталкивались когда например, пытались сделать копию диска с игрой. В детстве я помню, что на некоторых дисках была защита из-за которой просто так нельзя было скопировать игру. В общем и целом, все вот эти защиты которые часто используют игры и есть DRM.
У DRM много противников, которые утверждают что это бесполезная дичь, потому как один фиг все взломают и скопируют. И много последователей, потому как ограничивая копирования вы защищаем авторские права и не теряем бабки. Можно понять обе эти группы, но нас тут интересует именно инженерная часть, а именно то, как это реализовано в Android.
DRM это общее название, конкретная система гугла называется Widevine. Однако сам Android поддерживает и другие схемы DRM, но их рассматривать не будем, ибо это скучно. Ну так вот, как это работает. Значит этот Widevine помогает зашифровать потоковый контент так, чтобы его нельзя было просто так взять и скопировать. Работает он с двумя основными протоколами, Dash и HLS. Подробнее о них расскажу как-нибудь потом, сейчас просто запомните эти протоколы помогают сделать потоковую передачу данных.
Значится, в систему Android прям на уровне системы вшит код по работе с DRM. Причем даже есть специальный фреймворк для вендоров, которые могут делать свои схемы DRM если им захочется. Другими словами это аппаратная штука и ломануть ее мамкин хацкер уже просто так не сможет. Как схематично работает эта DRM.
Гугл на своих серверах хранит лицензии контента, которые мы как создатели контента туда кладем. Помимо этого предоставляет инструменты, при помощи которых мы свой контент нарезаем на чанки, которые в последствии будем отдавать с наших серверов.
Вот вы сидите в приложении для музыки, нажимаете на иконку песни. В этот момент приложение отправляет запрос к вашему серверу на скачивание данных песни и получает первый файлик который называется InitData, в нем вшита информация о том, какая это схема DRM и еще всякая разная метаинформация. Как я уже упоминал выше, в большинстве случаев это будет Widevine.
Далее мы c этой InitData идем в системный сервис MediaDrm и получаем запрос. Получаем мы его в виде байтового массива. Берем этот запрос и по Http клиенту идем на сервер гугла, который хранит нашу лицензию. Сервер проверят запрос и отдает нам лицензию.
После мы берем эту лицензию и отправляем в MediaDrm. Далее инициализируем специальный класс MediaCodec который и отвечает на расшифровку контента. Теперь когда все настроено мы можем наконец проигрывать песню. Скачиваем небольшую часть песни которая зашифрована, передаем данные в MediaCodec он расшифровывает мы играем. И так на каждый чанк (часть файла музыки или видео). Чтобы играть офлайн, нужно при помощи специальных методов указать MediaDrm что лицензию необходимо сохранить, чтобы использовать позже.
Продолжение ниже 👇
👍26🔥3❤1🤔1
{2/2} Примерно так это все и работает. Куча этапов и куча проверок на каждом этапе, поэтому просто так скачать видос и сохранить не получится. Помимо этого, так как MediaDrm работает на уровне системы (см. картинку) это позволяет сделать вещи вроде запрета скриншотов и записи экрана. Разумеется эти схемы обходятся, они не идеальны, но точно защитят контент от большей части пользователей.
На каждый трек и каждый видос защищённый при помощи Widevine ваш клиент делает запрос на сервера Гугл за лицензией. Что это означает? Что гугл может в любой момент не отдавать лицензию если ему ваш IP показался странным. Может на уровне операционной системы сделать так, что определенный контент вы не сможете смотреть легально. Работникам Кинопоиска или Яндекс Музыки система DRM приносит много сюрпризов и не самых положительных отзывов 😄
Если вы прочитали все это и ужаснулись, что сколько всего нужно знать, чтобы просто показать видос на устройстве не парьтесь. За вас это все уже сделано в Exoplayer, вам только его нужно передать правильные ссылки.
На каждый трек и каждый видос защищённый при помощи Widevine ваш клиент делает запрос на сервера Гугл за лицензией. Что это означает? Что гугл может в любой момент не отдавать лицензию если ему ваш IP показался странным. Может на уровне операционной системы сделать так, что определенный контент вы не сможете смотреть легально. Работникам Кинопоиска или Яндекс Музыки система DRM приносит много сюрпризов и не самых положительных отзывов 😄
Если вы прочитали все это и ужаснулись, что сколько всего нужно знать, чтобы просто показать видос на устройстве не парьтесь. За вас это все уже сделано в Exoplayer, вам только его нужно передать правильные ссылки.
👍24🔥2
Когда я был молодым и глупым у меня была идея крутой библиотеки, для работы с протоколом STOMP. Если кто не знает протокол STOMP это просто набор правил для работы с подписками на данные, который работает поверх Websocket. У этого протокола очень простая спецификация и он очень удобен когда нужно что-то сделать быстро и не охото придумывать свой велосипед.
Для бэкенда и фронта есть целый набор готовых решений. Однако для Android готовых решений нет. Хотя конечно есть, но чаще всего это забагованные, неудобные решения, разработчики которых давно забили на поддержку. И вот, казалось бы, я нашел крутую идею для библиотеки, которая решает реальную проблему. Можно просто сделать библиотеку с более менее удобным API и стать вторым Джэйком Вортоном.
Радостный сажусь я пилить эту библиотеку. Разумеется я как хороший разработчик сначала должен разработать крутую архитектуру библиотеки, чтобы не стыдно было показать. Затем желательно не завязываться на rx и корутины, а сделать отельные модули для этого, как у настоящих крутых библиотек.
Я придумал архитектуру. Как мне казалась, учел все принципы SOLID, KISS, YAGNI, FizzBuzz, CQS. После начал придумывать API, ведь библиотекой должно быть максимально удобно пользоваться. Думал очень долго иии так и не придумал. Библиотеку в итоге я так и не сделал.
Причина почему я перестал делать либу, заключается в том, что у меня началась дикая прокрастинация. Садясь разрабатывать либу, я думал не о том как быстрее решить задачу, а том как сделать супер гибкую архитектуру и код который было бы не стыдно показать.
Я кучу времени убил на чтения кода крутых библиотек, и каждый раз я начинал сравнивать свои решения с решениями авторов крутых либ. Разница была похожа на гиганскую пропасть. Казалось что создать такую же крутую библиотеку просто нереально. Код библиотек был очень вылизанный, учитывал миллионы корнер кейсов. Я пытался использовать их решения и в итоге выгорел и сдался.
Я это вот к чему, если начинаете разрабатывать какую-то либу, или вообще вы только обучаетесь не страдайте этой фигней, это так не работает. Если хотите что-то сделать и не знаете как, сделайте как-то, но сделайте и не думайте о том, как этот код будет выглядеть. Если у вас еще мало опыта, сколько бы вы не думали над архитектурой, сколько бы вы не читали чужой вы все равно сделаете хрень.
После я конечно понял почему код тех библиотек был такой крутой и обрабатывал все возможные случаи. Просто эти библиотеки уже долго на рынке и ими пользуется куча людей. Их код переписывался множество раз, и фиксилось куча багов которые прилетали от других разрабов. Серьезно вспомните сколько версий у RxJava, у OkHttp?
Невозможно сразу при создании какого-то решения учесть все, особенно когда у вас нет большого опыта. Поэтому лучше всего сначала что-то сделать, попробовать этим попользоваться. И только потом, если решение багует и оно неудобное начать его улучшать. Только в этом случае вы будете точно представлять что конкретно нужно улучить.
Крутыми разработчиками не становятся думая над архитектурой 3 года и потом выдавая супер мега решение со всеми корнер кейсами. Это работает с религией или философией, но в разработке это происходит так: Сделали какое-то решение -> Обосрались -> Порефлексировали -> Исправили косяки -> Повторили
Причем это работает не только с разработкой библиотек. В бизнесе это называют MVP. Так работает Agile, так развиваются архитектуры в уже существующих проектах. На текущем проекте где я работаю уже 3-я версия архитектуры. Это при том, что это одна из топовых мобильных команд в России.
По этой причине никто не поставит архитектором джуна, насколько бы умным он не был. Архитекторами поэтому и становятся матерые разрабы которые уже видели некоторое дерьмо и проходили этот цикл кучу раз. Их нейронка уже натренирована видеть хорошие решения и плохие которые могут в дальнейшем стрельнуть. И натренировать такую нейронку можно только пробуя и что-то делая.
Подводя некоторый итог. Хотите быстрее двигаться, делайте что-то, даже если не знаете как сделать хорошо все равно делайте.
Для бэкенда и фронта есть целый набор готовых решений. Однако для Android готовых решений нет. Хотя конечно есть, но чаще всего это забагованные, неудобные решения, разработчики которых давно забили на поддержку. И вот, казалось бы, я нашел крутую идею для библиотеки, которая решает реальную проблему. Можно просто сделать библиотеку с более менее удобным API и стать вторым Джэйком Вортоном.
Радостный сажусь я пилить эту библиотеку. Разумеется я как хороший разработчик сначала должен разработать крутую архитектуру библиотеки, чтобы не стыдно было показать. Затем желательно не завязываться на rx и корутины, а сделать отельные модули для этого, как у настоящих крутых библиотек.
Я придумал архитектуру. Как мне казалась, учел все принципы SOLID, KISS, YAGNI, FizzBuzz, CQS. После начал придумывать API, ведь библиотекой должно быть максимально удобно пользоваться. Думал очень долго иии так и не придумал. Библиотеку в итоге я так и не сделал.
Причина почему я перестал делать либу, заключается в том, что у меня началась дикая прокрастинация. Садясь разрабатывать либу, я думал не о том как быстрее решить задачу, а том как сделать супер гибкую архитектуру и код который было бы не стыдно показать.
Я кучу времени убил на чтения кода крутых библиотек, и каждый раз я начинал сравнивать свои решения с решениями авторов крутых либ. Разница была похожа на гиганскую пропасть. Казалось что создать такую же крутую библиотеку просто нереально. Код библиотек был очень вылизанный, учитывал миллионы корнер кейсов. Я пытался использовать их решения и в итоге выгорел и сдался.
Я это вот к чему, если начинаете разрабатывать какую-то либу, или вообще вы только обучаетесь не страдайте этой фигней, это так не работает. Если хотите что-то сделать и не знаете как, сделайте как-то, но сделайте и не думайте о том, как этот код будет выглядеть. Если у вас еще мало опыта, сколько бы вы не думали над архитектурой, сколько бы вы не читали чужой вы все равно сделаете хрень.
После я конечно понял почему код тех библиотек был такой крутой и обрабатывал все возможные случаи. Просто эти библиотеки уже долго на рынке и ими пользуется куча людей. Их код переписывался множество раз, и фиксилось куча багов которые прилетали от других разрабов. Серьезно вспомните сколько версий у RxJava, у OkHttp?
Невозможно сразу при создании какого-то решения учесть все, особенно когда у вас нет большого опыта. Поэтому лучше всего сначала что-то сделать, попробовать этим попользоваться. И только потом, если решение багует и оно неудобное начать его улучшать. Только в этом случае вы будете точно представлять что конкретно нужно улучить.
Крутыми разработчиками не становятся думая над архитектурой 3 года и потом выдавая супер мега решение со всеми корнер кейсами. Это работает с религией или философией, но в разработке это происходит так: Сделали какое-то решение -> Обосрались -> Порефлексировали -> Исправили косяки -> Повторили
Причем это работает не только с разработкой библиотек. В бизнесе это называют MVP. Так работает Agile, так развиваются архитектуры в уже существующих проектах. На текущем проекте где я работаю уже 3-я версия архитектуры. Это при том, что это одна из топовых мобильных команд в России.
По этой причине никто не поставит архитектором джуна, насколько бы умным он не был. Архитекторами поэтому и становятся матерые разрабы которые уже видели некоторое дерьмо и проходили этот цикл кучу раз. Их нейронка уже натренирована видеть хорошие решения и плохие которые могут в дальнейшем стрельнуть. И натренировать такую нейронку можно только пробуя и что-то делая.
Подводя некоторый итог. Хотите быстрее двигаться, делайте что-то, даже если не знаете как сделать хорошо все равно делайте.
👍36🔥12❤3
Представьте, вы смогли вернуться назад во времени, к себе только начинающему изучать программирование. Какой совет вы бы себе дали?
У нас же есть куча принципов и best practice. Возьмем например SOLID, несколько принципов составленных в одно слово. Кстати есть слух, что когда Мартин с друзьями собирал эти принципы, принципов было больше, они просто взяли те, из которых получалось слово, а остальные решили опустить). Правда я не проверял насколько это правда.
Проблема принципов SOLID в том что они очень абстрактные. У них может быть несколько трактовок и они не дают конкретики. Я точно несколько раз был свидетелем срачей по поводу того, как правильно трактовать принцип открытости/закрытости. Про принцип подстановки Барбары Лисков я вообще молчу, тут запутались вообще все. У принципов SOLID естественно есть критика советую прочитать. Некоторая критика, притянута за уши и как всегда правда где-то посередине, но стоит ознакомиться со всеми точками зрения.
По мне так, про принципы SOLID лучше уже говорить когда у разработчика есть какой-то опыт. Если мы говорим о начинающих или джунах, то им разумеется лучше давать более конкретные советы. Так будет больше профита всем.
Есть ли более конкретные принципы, которые стоит использовать, чтобы легко улучшишь свою кодовую базу? Смотрите, я уже делал посты про применение концепций функционального программирования и CQRS. Применив эти две концепции вы уже сможете писать гораздо более поддерживаемый и чистый код. К этим двум вещам, я бы еще добавил еще один.
Не бойтесь создавать объекты. Очень частая ошибка, особенно у начинающих, что они забывают про то, что у нас ООП язык и используют концепции процедурного языка. Отсюда появляются куча функций с более чем 5-ю параметрами, дублирование, избыточность, куча мутабельных полей и вот все эти головные боли.
Совет как по мне очень простой, давайте на примере. Нужно в функцию передать 4 параметра? Сделайте специальный объект для этого. Из функции нужно вернуть пару значений вместо одного? Лучше сделать свой объект, а не стандартный Pair. Со стандартным Pair легче запутаться, особенно когда объекты одного типа.
Когда мы используем объекты, мы зажимаем в некоторые тиски то, что можно передать в нашу функцию или класс. Другими словами, мы полагаемся на систему типов, которая за нас сможет отловить баги. Поэтому лучше отдавать предпочтение объектам, а не примитивам, когда появляется более менее сложная структура.
Самый банальный пример тут это дата. У вас есть несколько вариантов как хранить дату в объекте, которую вы например получаете с бэка.
Первый вариант, это просто строка с уже форматированной датой. Второй вариант это конкретный объект например LocalDateTime.
В первом варианте очень легко допустить ошибку, потому как в поле даты можно записать вообще любую строку какую душе угодно. Во втором же варианте, мы уже не сможете в поле записать что-то другое. Помимо этого вы получаете бенефит в виде того, что вы можете форматировать эту дату как вам нужно, а не как прислал бэк. И вот таких примеров на практике можно найти множество.
Часто мы пугаемся создавать объекты по причине того, что они якобы занимают память и нагружают GC. Вот тут вообще можно забить (если речь не идет о методе onDraw), это было проблемой на ранних версиях Android. Сейчас GC очень умный, настолько что порой даже создание ObjectPool получается дороже, чем просто создать объект.
У нас же есть куча принципов и best practice. Возьмем например SOLID, несколько принципов составленных в одно слово. Кстати есть слух, что когда Мартин с друзьями собирал эти принципы, принципов было больше, они просто взяли те, из которых получалось слово, а остальные решили опустить). Правда я не проверял насколько это правда.
Проблема принципов SOLID в том что они очень абстрактные. У них может быть несколько трактовок и они не дают конкретики. Я точно несколько раз был свидетелем срачей по поводу того, как правильно трактовать принцип открытости/закрытости. Про принцип подстановки Барбары Лисков я вообще молчу, тут запутались вообще все. У принципов SOLID естественно есть критика советую прочитать. Некоторая критика, притянута за уши и как всегда правда где-то посередине, но стоит ознакомиться со всеми точками зрения.
По мне так, про принципы SOLID лучше уже говорить когда у разработчика есть какой-то опыт. Если мы говорим о начинающих или джунах, то им разумеется лучше давать более конкретные советы. Так будет больше профита всем.
Есть ли более конкретные принципы, которые стоит использовать, чтобы легко улучшишь свою кодовую базу? Смотрите, я уже делал посты про применение концепций функционального программирования и CQRS. Применив эти две концепции вы уже сможете писать гораздо более поддерживаемый и чистый код. К этим двум вещам, я бы еще добавил еще один.
Не бойтесь создавать объекты. Очень частая ошибка, особенно у начинающих, что они забывают про то, что у нас ООП язык и используют концепции процедурного языка. Отсюда появляются куча функций с более чем 5-ю параметрами, дублирование, избыточность, куча мутабельных полей и вот все эти головные боли.
Совет как по мне очень простой, давайте на примере. Нужно в функцию передать 4 параметра? Сделайте специальный объект для этого. Из функции нужно вернуть пару значений вместо одного? Лучше сделать свой объект, а не стандартный Pair. Со стандартным Pair легче запутаться, особенно когда объекты одного типа.
Когда мы используем объекты, мы зажимаем в некоторые тиски то, что можно передать в нашу функцию или класс. Другими словами, мы полагаемся на систему типов, которая за нас сможет отловить баги. Поэтому лучше отдавать предпочтение объектам, а не примитивам, когда появляется более менее сложная структура.
Самый банальный пример тут это дата. У вас есть несколько вариантов как хранить дату в объекте, которую вы например получаете с бэка.
Первый вариант, это просто строка с уже форматированной датой. Второй вариант это конкретный объект например LocalDateTime.
В первом варианте очень легко допустить ошибку, потому как в поле даты можно записать вообще любую строку какую душе угодно. Во втором же варианте, мы уже не сможете в поле записать что-то другое. Помимо этого вы получаете бенефит в виде того, что вы можете форматировать эту дату как вам нужно, а не как прислал бэк. И вот таких примеров на практике можно найти множество.
Часто мы пугаемся создавать объекты по причине того, что они якобы занимают память и нагружают GC. Вот тут вообще можно забить (если речь не идет о методе onDraw), это было проблемой на ранних версиях Android. Сейчас GC очень умный, настолько что порой даже создание ObjectPool получается дороже, чем просто создать объект.
👍28🔥2❤1
🎛 Многомодульность
Когда я только начал работать в качестве 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