С тех пор как вам доверяют не только кодить под строгим контролем, но и принимать даже минимальные решения, вы становитесь в полной мере ответственны за будущее проекта. В том числе, за стоимость его последующей поддержки. Имея опыт с действительно долгосрочными историями, мы собрали несколько советов о том, как не “стрелять в ноги” себе, своим коллегам и тем, кто придет на проект после вас.

Бывалым наши советы могут показаться очевидными. А вот новичкам настоятельно рекомендуем к прочтению. Потратьте время на воплощение этих идей в своих проектах, чтобы потом не тратить еще больше на бесконечный рефакторинг.

Сходные идеи можно высказать практически в любой сфере разработки, но мы будем говорить о них на примере проектов на React-е.

image

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

Задумайтесь о типизации


Какие бы вольности не допускали языки разработки, на больших долгосрочных проектах статическая типизация очень полезна. Если по какой-то причине на таком проекте не получается использовать статическую типизацию, то даже JSDoc существенно поможет поддерживать качество кода.

Но мы не будем однозначно советовать всегда использовать типизацию. Выше не зря была сделана оговорка о больших проектах, поскольку типизация — это в первую очередь помощь команде. Но ее организация и поддержка (то же изменение типов в соответствии с изменениями на бэкенде) требует определенных ресурсов. В маленьком краткосрочном проекте, где работает 1-2 человека, эти вложения бессмысленны. Но они необходимы, когда команда большая и будет расширяться.

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

По мере развития проекта типизации надо придерживаться, даже если у вас уже давно прогорают все сроки. Потом вы себе скажете спасибо за этот перфекционизм.

Разделите код на блоки, выделите логику


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

Очень наглядно польза от этого принципа была видна на проекте с большими формами, о котором мы недавно писали (https://habr.com/ru/company/maxilect/blog/511322/). В том проекте проверки в блоках и видимость этих блоков изначально были связаны между собой. Но когда мы собрали всю связь полей в одном месте, отслеживать изменения логики стало гораздо легче. А главное мы смогли переиспользовать код в новом похожем проекте.

Единого стандарта на структуру проекта не существует — тут придется опираться на свои знания. Есть два подхода, которые могли бы подойти для многих проектов: File Type First и Feature First.
Чтобы нащупать отправную точку для поиска структуры, удобной именно в вашем случае, рекомендуем обратиться к документации. Там обычно описаны best practice. Например, React и Redux в документации предлагают стандарты организации логики и файловой структуры проекта. При наличии определенного опыта можно экспериментировать и отклоняться от стандартов, если это позволяет обойти какие-то ограничения для конкретного проекта, но для большинства случаев в этом нет необходимости. И, конечно, не стоит забывать о таких “базовых” принципах, как SOLID. Неплохая статья о том, как применять эти принципы в React-е: https://habr.com/ru/company/ruvds/blog/428079/. Хорошая статья о “чистой” архитектуре: https://habr.com/ru/post/499078/.

По организации кодовой базы в React и связанным с этим вопросам есть неплохая, хоть и давно не обновляемая, подборка материалов: https://github.com/markerikson/react-redux-links/blob/master/project-structure.md.

Сформулируйте соглашение о коде


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

Придерживайтесь выбранной парадигмы


Если вы на старте решили придерживаться какого-то подхода, к примеру атомарного веб-дизайна, не надо отказываться от него, как только сроки начинают поджимать. Начатая и брошенная поддержка парадигмы порой чуть ли не хуже, чем ее полное отсутствие. Если немного “дать волю хаосу”, навести порядок будет крайне сложно — придется потратить время на возвращение к парадигме. И на другую быстро “перескочить” уже не получится, поскольку в проекте уже сформируется бесформенная каша.

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

Помните о тестах


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

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

Отдельно хочется отметить интеграционные и end-to-end тесты.

Интеграционные тесты необходимо запускать каждый раз после исправления багов или добавления новых фич, чтобы убедиться, что корректировки ни на что не повлияли. Мы на своих проектах стараемся запускать их автоматом, когда заливаем результаты своей работы (мы это реализовали через hook). Не отработали тесты — не стоит заливать изменения в проект. Сначала надо все поправить.

Но интеграционные тесты касаются только фронтенда. End-to-end тесты — более медленные и проверяют взаимодействие приложения со всеми зависимыми элементами. Эти тесты имитируют действия пользователя. Тут мы ничего не мокируем, а действительно ждем ответов бэкенда и проверяем взаимодействия внутри всей экосистемы проекта, используя Cypress (в качестве аналога можем порекомендовать также Puppeteer).

Думайте перед обновлением, но не отказывайтесь от него


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

Как и типизация, о которой мы говорили выше, любое обновление стоит ресурсов (т.е. косвенно — денег). Но есть несколько моментов, из-за которых нельзя полностью отказаться от обновлений.

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

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

Обновления стоит воспринимать, как часть естественного порядка вещей. Но перед сменой библиотеки или обновлением фреймворка всегда необходимо проводить исследование.
Подойдет ли вам новая версия? Как вообще организовать переход? И конечно, планировать все нужно заранее.

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

А хорошо ли вы делаете сейчас?


Мерилом того, насколько эффективно вы разрабатываете свой проект, может быть соотношение времени, которое идет на разработку новых фич, и времени, уходящего на баги. В зависимости от подхода этот показатель может быть больше или меньше, поэтому не возьмемся задавать “целевые” уровни. Вы и сами можете сравнить ситуацию до и после каких-либо преобразований.
Помимо не численных характеристик, вроде “удовлетворенности разработчиков от проекта”, есть такой показатель, как время подключения к проекту нового человека. Это характеристика того, насколько проект понятно описан через документацию, тесты и соглашение о коде.

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

Быть может, у вас есть свои советы? Оставляйте в комментариях!

P.S. Мы публикуем наши статьи на нескольких площадках Рунета. Подписывайтесь на наши страницы в VK, FB, Instagram или Telegram-канал, чтобы узнавать обо всех наших публикациях и других новостях компании Maxilect.

P.P.S. Заглавная картинка с прошедшего недавно архитектурного конкурса Playhouse Competition.