May 18, 2022

О себе

Кто я. Меня зовут Артур, я Android-разработчик.

Откуда я. Я родом из Киева.

Образование. Образование получал тоже в Киеве, в Политехническом Институте, у меня диплом магистра по инженерии программного обеспечения - в декабре закончил это образование.
Но я бы не сказал, что это основной источник моих знаний, потому что я много времени уделяю самообразованию, это и курсы, и посещение конференций (пока в роли слушателя) - в принципе я бы сказал что больше знаний именно от этого получил и получаю.

Где сейчас живу. Сейчас я живу в Дании, в Копенгагене, у меня тут живет сестра и немало родственников которые убежали от войны.

Опыт. Вообще я уже успел поработать на 3х ролях - сначала был фронтенд (месяц), потом - бэкенд, по нему я закончил курсы и на опенсорсе участвовал в проекте, параллельно готовясь к собеседованиям. Потом в какой-то момент появилась вакансия у знакомых на Android, я подумал - почему бы и нет, предлагали хорошие условия, и я согласился.

Прошлое место работы. Компания называлась КарМани, через какое-то время разработка ушла в отдельную компанию ТехМани, и собственно я пришел туда джуном и дорос со временем до синьора и плюс уже менторил джуна.
Приложение было в сфере ФинТеха, всего примерно 200 тысяч скачиваний и несколько тысяч ежедневных пользователей.
Команда состояла из 3-4 разработчиков, по-разному было, плюс к нам были прикреплены 1-2 ручных тестировщика, тоже по-разному бывало.

Почему ушел с прошлого места. Тут все просто, я ушел в первый же день войны, фактически по двум причинам:

  • чисто с юридической точки зрения, стало опасно работать
  • с моральной точки зрения, я не мог больше работать на компанию, которая платит налоги в стране, которая на эти деньги убивает жителей моей страны.

Личные качества.

  • целеустремленность
  • желание всегда докопаться до сути
  • командный игрок
  • лень (и плюс, и минус)
  • перфекционизм (скорее минус)

Достижения на прошлом месте работы (КМ / ТехМани)

  • поднятие оценки в маркете с 4,1 до 4,8, за счет:
    • фиксов багов - crash-free rate увеличился примерно с 92 до 97-98 процентов
    • новых фич
    • проработки review flow (в какие моменты запрашивать оценку)
    • внедрения in-app review (увеличило кол-во оценок в 3 раза)
  • повышение разных бизнес-метрик, таких как:
    • процент регистрирующихся пользователей
    • процент пользователей которые переходят на следующий шаг в нашем основном пошаговом флоу
    • процент клиентов которые покупают те или иные услуги
  • повышение качества кода и легкости разработки:
    • код-ревью
    • работа над код-стайлом (разработка общих файлов конфигураций, в планах было добавить автоматические lint-проверки)
    • рефакторинги (рефакторинг хранения локальных данных, рефакторинг самого большого адаптера)
  • улучшения UI-я:
    • добавление темной темы
    • добавление возможности менять тему
    • темизация статус бара (подстраивание его цвета под экран)
  • добавление разных фичей:
    • подсказки в виде инстаграм-историй
    • переделывание флоу обращения в саппорт
    • загрузка фото из галереи
    • загрузка данных контакта
    • показ видео (через ютуб)
    • добавление огромного нового флоу (фактически нового проекта) внутри приложения в короткие сроки

Самые интересные задачи

1) Рефакторинг главного экрана

Описание: Экран состоял из большого вертикального списка, в котором отображались много разных сущностей, которые приходят из БД. И соответственно когда данные в БД меняются, нужно обновить и UI.

Какие были проблемы:

  • за это все отвечал один адаптер, который фактически взял на себя много обязанностей, и плавно разросся до огромных размеров с огромными деревьями решений. Это привело к тому, что чтобы добавить карточку какой-то новой сущности в список нужно было потратить очень много времени чтобы разобраться куда вставлять новый код и убедиться не заденет ли он старые карточки (из-за механизма переиспользования вьюхолдеров)
  • при обновлении одной из сущностей весь список полностью перерисовывался, просто потому что мы не можем делать иначе в базовой версии адаптера
  • в будущем нужно было предусмотреть другие источники данных для адаптера, например чтобы по какому-то критерию показывалась определенная карточка - это тоже было довольно сложно сделать, так как адаптер был сильно завязан на конкретный источник данных.

Как решил проблемы:

  • разбил адаптер на множество мелких адаптеров, по одному на каждую сущность, с использованием библиотеки от Ганса Дорфмана - adapter delegates
  • вынес разные ответственности в разные сущности:
    • вьюмодели, которые хранили данные которые будут отображаться на экране
    • адаптеры-делегаты, которые знали как отрисовать конкретную вьюмодель на конкретной UI-карточке
    • converter, который знал как превратить бизнес-модели во вьюмодели
    • оркестратор, который всем руководил. Внутри него еще работали 2 дополнительных сущности:
      • сущность запроса данных
      • сущность подписки на данные.

Сам оркестратор делал следующее: позволял скормить ему разные источники данных, подписывался на них и при обновлении любых данных он скачивал в background-потоке данные из конкретного источника, конвертировал их во вьюмодели и кэшировал в общем списке всех данных. Делал он это все на едином потоке для того, чтобы избежать гонок. И после этого он отправлял данные на Main-поток где сетил их в адаптер.

  • использовал адаптер с DiffUtil-ом, который позволил перерисовывать только те карточки, данные которых изменились
  • еще какие-то мелкие оптимизации, которые не играют большой роли

Вывод:

  • получилось больше сущностей и кода в целом
  • но при этом:
    • легко добавлять новые типы карточек, при этом есть уверенность что они никак не заденут старые
    • легко добавлять любые типы источников данных, фактически для них надо реализовать несколько сущностей с четкими ответственностями
    • при обновлении данных перерисовываются только те карточки, данные которых поменялись
    • при транзишнах в background и обратно и в другие фрагменты и обратно данные не запрашиваются заново из БД и других источников так как они кэшируются
    • адаптеры для конкретных сущностей можно переиспользовать, как и вьюмодели

То есть фактически код стал намного более расширяемый, переиспользуемый и чистый.

Короткая версия:

Это была задача по рефакторингу главного экрана в приложении, который отображает большой список разных карточек. Проблем было 3:

  • в него становилось все тяжелее и тяжелее вносить какие-либо изменения
  • в нем происходило больше перерисовок, чем нужно
  • в него нельзя было добавить новые источники данных

Для того чтобы устранить все эти проблемы пришлось написать больше кода, чем было до этого. Основная идея была в том чтобы создать несколько разных сущностей вместо одной, каждая из которых будет иметь свою зону ответственности. Также я использовал библиотеку которая называется DiffUtil - она помогает экономно работать со списками, перерисовывая только те элементы данные которых поменялись.
Таким образом после этого рефакторинга код стал намного более расширяемым, переиспользуемым и чистым.

2) Рефакторинг хранения пользовательских данных

Описание: Была сущность (Preferences Data Source), которая отвечала за хранение пользовательских настроек в виде примитивов - например это были таймстемпы обновлений и версии словарей, текущая выбранная тема и т.д. И соответственно этот класс позволял сохранять эти переменные и получать их обратно, и также он в нужные моменты очищал часть из этих переменных (например при логауте).

Какие были проблемы:

  • бойлерплейт - при добавлении новой переменной нужно было писать как минимум 2 метода для её сеттинга и получения. Если же переменная была списком, то методов выходило еще больше и бывало что чтобы добавить одну новую переменную надо написать аж 40 строк. В общем в итоге код этого класса довольно прилично рос.
  • можно было легко забыть дописать обнуление переменной в те методы в которых обнуляются все переменные
  • неудобство API для асинхронного чтения (listener, который надо было оборачивать например в callbackFlow) и некоторые странности его работы (например при обнулении всех префов апдейт не приходил)

Как решил проблемы:

  • основная проблема - бойлерплейт - решилась использованием делегатов - я создал их иерархию, в основе лежал базовый делегат который умел сохранять и получать свою переменную из хранилища, поверх него был делегат для работы с nullable-переменными (и flowable-делегат тоже был), и было много других делегатов, которые внутри себя использовали nullable-делегат - это и non-null, и числовой, списочный, маповый, был отдельный делегат с interop-ом для RxJava, который возвращал апдейты в виде Flowable.
  • проблема того что можно забыть обнулить переменную в нужных методах решилась прокси-методами, создающими делегаты - фактически в этих методах ключ добавлялся в специальный список, и в обнуляющем методе просто мы проходили по этому списку и обнуляли все переменные.
  • неудобство API решилось миграцией на DataStore - новую библиотеку от Google, которая как раз позиционируется как замена для Shared Preferences. Она:
    • имеет удобнее api (интеграция с корутинами, flow и т.д.)
    • позволяет ловить возможные возникающие ошибки
    • безопасна для вызова на Main-потоке, так как выносит работу в Dispatchers.IO

Вывод:

В итоге чего удалось достичь:

  • для добавления новой переменной нужно написать всего одну строку
  • невозможно забыть указать правила обнуления переменной
  • в целом работа класса стала стабильнее (ловля ошибок, безопасность на мейн-потоке)