Как и зачем мы переписали Тяжеловато на Тайпскрипт

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

Почему поддерживать код стало дорого

Было несколько причин.

Не было тестов

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

Не было нормальной архитектуры

Приложение, я напомню, я написал за 2 недели на коленке, там было не до архитектуры. (Ох уж этот дух стартапа!) По-хорошему, после запуска надо было взять пару недель и отрефакторить там всё хорошенько, но что-то (всё?) пошло не так.

Не было документации

Которая бы напомнила, что и как работает, и почему именно так. Казалось бы приложеньице маленькое, что там помнить-то, но нет! Как хранятся даты?.. Это поле стейта за что отвечало? Так, это-то с чем связано? Господи, а это что ещё за хрень? И прочее-прочее-прочее.

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

И вот в 2019 году — случилось. Мне бомбануло окончательно, и я начал думать, как это можно изменить.

Как это можно было изменить

Мне было понятно, что если я начну исправлять ситуацию, меня будут преследовать две мысли.

Я это уже делал. Зачем тратить силы на то, что уже сделано?

Вообще-то это здравая мысль — действительно, переписывать ради переписывания нерентабельно. Я хотел найти для себя дополнительные стимулы и интерес. Интересом стали технические ограничения, которые я себе выдумал, и попытка поуправлять процессом разработки. А в дальнейшем, возможно, — реанимация Тяжеловато как проекта.

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

Поуправлять разработкой — это передать опыт, попробовать посмотреть на процесс шире, выйти в надсистему. У меня как раз на примете было два человека, кто хотел и мог бы помочь: Виктор Дёмин и Фёдор Кузнецов. Собственно, с ними мы и затеяли весь этот карнавал.

Реанимация проекта — я не то чтобы рассчитывал на это, этот пункт скорее был бонусным. Меньше ожиданий — лучше. Однако, проект ожил.

Не навреди!

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

То есть, нельзя было пофлексить что-то, что уже работало. Нельзя было выпилить фичу просто так, на то должна быть веская причина. И, разумеется, нельзя было просрать пользовательские данные. (Если вдруг что-то пошло не так, напишите, пожалуйста, на support@fuckgrechka.ru.)

Всё это грозило парализовать проект, потому что он начал бы казаться слишком большим. Мы с ребятами решили не «упарываться по-стартаперски», а дать себе бесконечное количество времени. Вот прямо сколько нужно, чтобы написать нормально, столько и берём. Торопиться — запрещено.

Дальше оставалось определить стек и технические ограничения.

Стек и технические ограничения

От Реакта уходить не хотелось. С Ангуляром я так и не подружился, а Вью мне почему-то казался всё ещё сыроватым. Палец отрезать не стали. Оставили Реакт.

Одним из технических ограничений «со звёздочкой» было минимальное количество зависимостей и размер бандла. Вторым — отсутствие классов: у нас только функциональные компоненты.

Второе действительно заставляет думать про отделение бизнес-логики от административной. Хуки помогли не размазывать логику по жизненному циклу компонентов, а собрать её в одной функции — SRP, все дела.

Последним (и по совместительству определяющим ход разработки) ограничением был выбор между двумя стульями: JS или TS. Сейчас расскажу, почему выбрали Тайпскрипт.

Почему выбрали Тайпскрипт

Мне хотелось нормальных типов, а не 0 !== '0' и вот этого всего. Хотелось проектировать, как нормальный человек, а не писать тонны jsdoc к каждой функции. Хотелось, чтобы код сам говорил, что он делает, а не прятался за комментариями, которые один хрен устареют и потом сиди-думай, чему верить. В Тайпскрипте всё это в какой-то мере есть.

Типы

Да, присвоить переменной типа string значение типа number уже не выйдет — и это прекрасно! Потому что за пределами классической песочницы-туду-листа, я буду путать не string и number, а структуры данных и сущности. И если редактор мне подсказывает: «дорогой, у тебя здесь должен быть просто Record, а не Spend», то я готов писать хоть на C#.

Если в коде есть Enum, автозаполнение подскажет, что там может быть
Если в коде есть Enum, автозаполнение подскажет, что там может быть

Цикл обратной связи короче, чем с JS, потому что TS проверяет мой код, пока я его пишу, и ошибку я увижу гораздо раньше.

Автоматическая документация

Идеальная документация — это та, которой нет, а её функция выполняется. Типы, интерфейсы и сигнатуры функций — это и есть такая документация.

Мы, люди, пользуемся именами, чтобы ссылаться на явления и сущности — нам так удобнее думать. Когда сущности в коде названы в соответствии с предметной областью, код читать становится проще.

Типы и интерфейсы помогают называть сущности в соответствии с предметной областью
Типы и интерфейсы помогают называть сущности в соответствии с предметной областью

Ещё Тайпскрипт позволяет использовать тип-алиасы для примитивов. Например, DayStartTimeStamp — тип, который описывает временную метку в UTC, отсылающую к началу определённого дня.

Это алиас для обычного number, просто он назван понятно. Я мог быть указать где-то в комментарии, что startDate указывается в миллисекундах по UTC… но если у меня есть возможность указать это прямо в коде — то я лучше так и сделаю.

Тип-алиасы делают описания структур данных понятнее и ближе к предметной области
Тип-алиасы делают описания структур данных понятнее и ближе к предметной области

Код, покрытый типами, алисами и интерфейсами можно писать, как предложение. Вот функция, которая проверяет, находится ли переданная запись внутри заданного промежутка времени.

Находится ли трата в промежутке от начала до конца сегодняшнего дня?
Находится ли трата в промежутке от начала до конца сегодняшнего дня?

А вот так эта функция потом используется:

Трата сегодняшняя, если она в промежутке от начала до конца сегодняшнего дня
Трата сегодняшняя, если она в промежутке от начала до конца сегодняшнего дня

Понимать такой код потом получается быстрее и менее напряжно.

Архитектура

А этот параграф — ода интерфейсам. Если вы однажды упоролись по SOLID, то обратной дороги уже нет, проектировать систему без абстракций становится больно.

Можно даже создать почти-каноничную-луковичную структуру
Можно даже создать почти-каноничную-луковичную структуру

Интерфейсы помогают продумывать отношения между сущностями. Это настолько офигенно, что за первые пару дней я не написал ни строчки продуктового кода — только интерфейсы, типы и сигнатуры функций.

Тесты и рефакторинг

Для разработки мы выбрали методологию TDD. Здесь Тайпскрит тоже пришёлся кстати, потому что имея типы и интерфейсы проще создавать (или генерировать, кому как больше нравится) стабы и моки.

В рефакторинге же самое кайфовое — использовать встроенные инструменты IDE: rename symbol, extract function и прочее. Мне не нужно беспокоиться, переименовал ли я все сущности в проекте — об этом позаботится IDE. Моё дело — подобрать правильное название, дальше — пусть потеют роботы.

Минусы

Самый жирный минус Тайпскрипта — он требует времени. Много. Дополнительного. Времени.

Оно будет уходить на то, чтобы покрыть функции и методы типами, привести в соответствие с типами все аргументы, понять, почему какой-то тип не работает для метода из сторонней библиотеки. (Здесь шутка про «нельзя просто так взять и написать быстрый черновик функции, не используя any».)

Второй минус, чуть поменьше, — не у каждой библиотеки есть описание для Тайпскрипта в DefinitelyTyped. Основные решения там есть, конечно, но для менее известных библиотек описаний может не быть. Например, для clsx его не было.

Если вы проводили по 5 часов за тем, чтобы покрыть типами стороннее API, вы понимаете эту боль.

Окей-окей, а когда обновление-то?

А оно в продакшене уже 🙃

  • Если вы пользуетесь веб-версией, то скорее всего, приложение на телефоне обновилось само.
  • Обновление для Android появилось в магазине пару недель назад.
  • Обновление для iOS появится совсем скоро.

Апргейд версии должен был пройти гладко и без потерь, но пишите, как-оно-что. Вдруг мы всё-таки что-то поломали по пути.

Ссылки

О приложении:

Скачать БЕЗ РЕГИСТРАЦИИ И СМС:

Крутые ребята:

Хвастаюсь книгами:

Всякое-техническое: