Приключение на двадцать минут
Наверняка я не ошибусь, если предположу, что многие, задумываясь о карьере программиста, думают, что купят пару-тройку книг по языку программирования, прочитают их, пройдут пару курсов и начнут покорять IT-олимп. Во всяком случае одним из таких людей когда-то был я.
Вдаваться в подробности своей карьеры до Java я не буду, так как в то время она была связана с веб-разработкой с использованием технологий, характерных для второй половины нулевых: PHP, MySQL, JavaScript, HTML и CSS. Не скажу, что в то время я не тратил времени на изучение указанных технологий, но сейчас оно выглядит ничтожно малым по сравнению с тем временем, которое потрачено на изучение тем, связанных с enterprise-разработкой. А ведь самое интересное в моей карьере началось как раз в тот день, когда я решил окунуться в волшебный мир Java и enterprise-разработки. И началось оно с того самого легкомысленного: «Куплю пару-тройку книг по Java, прочитаю и погнали!».
После изучения Java по книгам «Философия Java» и «Java. Полное руководство» пришлось изучать базы данных, Spring Framework, Hibernate и Java EE, системы сборки, различные библиотеки, алгоритмы, структуры данных, шаблоны проектирования, всевозможные принципы вроде SOLID и GRASP, тестирование. И вишенкой размером с арбуз на этом торте стало предметно-ориентированное проектирование. А ведь ещё были OSGi, JPMS, очереди сообщений и так далее.
Многие темы имеют весьма существенное развитие, как, например, тестирование. Сначала выясняется, что написанный код можно проверить тестами, а далее оказывается, что можно сначала писать тесты, а уже потом реализуемый код, что называется разработкой через тестирование. А в конце концов вы узнаёте, что есть разработка через контракт и разработка на основе поведения. И это не говоря уже о доступном многообразии видов тестирования.
А ведь ещё есть моменты, когда на практике не получается совместить совместимые на первый взгляд вещи. У меня, например, достаточно долго не получалось состыковать принципы SOLID и концепции предметно-ориентированного проектирования, да и получилось в итоге с нюансами и компромиссами.
В итоге вот это вот «Куплю пару-тройку книг по Java, прочитаю и погнали!» у меня длится уже двенадцать лет, я не перестаю узнавать и изучать что-то новое, связанное с моей профессией, хотя стараюсь сильно далеко не отходить от мира Java и enterprise-разработки. И я более чем уверен, что в других направлениях разработки программного обеспечения картина не сильно отличается.
Применение теоретических знаний на практике
Но изучение чего-то нового требует практического применения знаний для их закрепления и получения соответствующего опыта. Что-то вполне можно внедрять в уже разрабатываемые проекты, а что-то получится внедрить только в каких-то будущих проектах. Но я стараюсь не экспериментировать на рабочих проектах, ибо у таких экспериментов есть своя цена. Вместо этого я регулярно создаю проекты-песочницы с какой-нибудь хорошо известной мне предметной областью и пытаюсь применить при его разработке как можно больше своих знаний. В своё время такой подход мне помог таки состыковать принципы SOLID и предметно-ориентированное проектирования, проверить концепции CQRS, event sourcing и распределённых систем в целом.
С точки зрения разработчика всё это действительно кажется полезным и важным: тесты делают ваш код более стабильным и защищают от ошибок, принципы SOLID позволяют более комфортно вести разработку, в том числе и параллельную, фреймворки и библиотеки экономят время, предоставляя уже готовые решения от других разработчиков. Но пару лет назад я начал задумываться над тем, как это всё выглядит со стороны заказчика, и вот тут не всё так однозначно.
При внедрении в процесс разработки любых нововведений стоит помнить, что нет абсолютно бесплатных решений. Да, фреймворки и библиотеки могут существенно сократить время разработки, но эта экономия напрямую зависит от опыта и знаний разработчиков. Если перед нами начинающий разработчик, только завершивший курс по Spring, то высока вероятность того, что он может быстро накидать MVP-версию проекта, но будет постоянно сидеть в документации и на тематических ресурсах для решения каких-то новых для него проблем. В то же время опытный разработчик сделает всё быстрее, он и стоить такой разработчик будет больше.
Даже принципы SOLID, которые имеют минимальное влияние на объём кода и время разработки, имеют свою стоимость, так как надо потратить некоторое время на обучение им всех разработчиков в команде. Да, опытная команда, работающая в одном составе продолжительное время, будет тратить минимум ресурсов на применение этих принципов. Однако когда-то все разработчики этой команды потратили время на их изучение и эксперименты с ними.
С тестами ситуация аналогична: на одну строчку тестируемого кода может приходиться 3-5 строк тестов, а то и больше, да и написание тестов тоже требует и времени, и квалификации. А наверху этого торта, как я уже говорил, находится предметно-ориентированное проектирование, которое требует значительного времени и соответствующей квалификации у разработчиков.
Исходя из всего вышесказанного я для себя сделал простой вывод, что применение всех моих знаний в рамках одного проекта нецелесообразно, так как разработка такого проекта будет просто невероятно дорогой и долгой. Впрочем, если речь идёт о сработавшейся команде профессионалов, которые имеют значительный опыт разработки и применения большого арсенала инструментов, подходов и технологий, то такая команда вполне может быстро и эффективно разрабатывать проекты большой сложности, однако потянуть такую роскошь могут только ИТ-гиганты (увольняющие сейчас десятки тысяч работников). Да и проект должен быть весьма масштабным, чтобы был смысл применения всего арсенала технологий.
Поэтому, прежде чем внедрять что-то в процесс разработки, нужно попытаться определить финансовые и временные издержки на внедрение, а так же побеспокоиться о том, чтобы вся команда смогла работать с нововведениями эффективно. И нужно помнить, что каждый используемый инструмент должен решать какую-то конкретную задачу в рамках проекта — нагрузочное тестирование бесполезно в проекте, не предполагающем хоть какой-нибудь нагрузки, ровно, как и применение Kubernetes для развёртывания проекта, не требующего масштабирования.
Стоит помнить, что экспертный вес разработчика собирается из знаний технологий, практического опыта их применения и понимания того, когда и в каком объёме их действительно следует применять.
Конфликт разработчиков и заказчиков
Наверняка кто-то из вас слышал байки о том, что у некоторой компании есть критический баг в одной из информационных систем, а её разработчики вместо реального решения бага спорят о том, какой шаблон проектирования должен быть применён или как должны называться классы. К сожалению, такие ситуации действительно имеют место в нашей индустрии. И с точки зрения заказчика, будь он хоть внешним, хоть внутренним, такие ситуации разработчиков не красят. Но эта ситуация — уже крайность, а я хочу поговорить о вещах более обыденных и повседневных.
Для заказчиков важно, чтобы разрабатываемая для них информационная система работала корректно и стабильно, её разработка укладывалась в обозначенные сроки, а в идеале — опережала их. И в коммерческой среде это всё действительно очень важно. Если ваша система работает нестабильно или некорректно, то пользователи перестанут ей пользоваться и уйдут к конкурентам. Если новые возможности в ней появляются позже, чем у конкурентов, то часть пользователей тоже уйдёт к конкурентам. А если конкурентов нет, то они появятся, так как обязательно найдётся тот, кто захочет сделать всё правильно. В бизнесе работает принцип «кто раньше встал, того и тапки» и задача разработчиков предоставлять заказчикам возможность «вставать раньше», если у последних нет очень крутого отдела маркетинга.
Я уже рассказал, что все технологические решения имеют некоторую цену, выражаемую во времени или деньгах. Заказчик может нанять команду профессионалов для разработки проекта — это будет максимально дорого, но в то же время, скорее всего, максимально быстро. Но каковы варианты, если заказчик хочет сэкономить? Он может нанять менее опытных разработчиков, что может увеличить время разработки, либо понизить качество кода, а может привести и к обоим последствиям. И для достижения максимальной эффективности бизнеса заказчик вынужден балансировать между стоимостью проекта, временем его разработки и уровнем его качества, так чтобы проект имел приемлемую стоимость, разрабатывался в сроки и обладал достаточным качеством.
И из этого неизбежно возникает конфликт между разработчиками и заказчиками. Разработчики стремятся применить все свои знания и сетуют на то, что представители заказчика не понимают, насколько важно, например, написание сквозных тестов, на которое разработчики тратят значительное время при написании кода. А заказчики в свою очередь недовольны тем, что разработчики из-за своих заморочек не могут достаточно быстро решать появляющиеся задачи.
ИТ-индустрия в ответ на динамику рынка и его потребности явила миру в общем-то хорошие инструменты вроде гибких методологий разработки, девопс, MVP, но не обошлось и без ложки дёгтя в виде «индусского кода» и «индусов-программистов». Существование и процветание (если так можно сказать) последних двух объясняется тем, что многим заказчикам результат нужен здесь и сейчас, а качество кода их не волнует, если он работает правильно. Особенно это характерно для так называемых стартапов, когда зачастую заранее нет понимания того, что должно получиться, оправдает ли проект инвестиции и как долго он будет существовать.
Поэтому со стороны разработчиков очень важно выбирать те инструменты, которые решают в рамках проекта какие-то реальные проблемы или задачи, и использовать их в достаточном объёме, а так же объяснять доступным языком представителям заказчиков, почему это важно, но в то же время слышать потребности заказчика и уметь корректировать свою работу в зависимости от изменяющихся требований.
Джентльменский набор инструментов
Если речь идёт о серверных приложениях, то лично у меня базовый набор инструментов для разработки сформировался уже давно и выглядит он следующим образом.
Принципы SOLID и чистая архитектура. Да, на первом месте именно они, так как их применение упрощает работу с кодом и его понимание, а это в свою очередь имеет значительно положительное влияние на скорость разработки. При этом я стараюсь максимально изолировать от внешних зависимостей внутреннюю логику проекта.
Функциональный дизайн. Я уже давно не пишу в своих проектах сервисы, контроллеры и репозитории на десятки методов и тысячи строк кода, так как их поддержка со временем превращается в ад. При параллельной разработке с такими классами и интерфейсами возникают конфликты при слиянии. Их чтение без навигатора по методам затруднено. А сложнее всего тестами — если у вас получился класс-репозиторий на тысячу строк, то код его тестов будет на ТРИ тысячи строк в лучшем случе, и работа с такими тестами тоже будет затруднена. Вместо этого я пришёл к использованию функциональных интерфейсов и классам, которые реализуют только один интерфейс, следовательно, содержат только один публичный метод. Класс, который реализует только какую-то одну функциональность, невероятно прост, его легко читать и с ним легко работать, ровно, как и с его тестами. Да, кто-то может возразить тем, что при таком подходе появляется много классов и интерфейсов, и в них со временем станет сложно ориентироваться. Но на мой взгляд эти проблемы легко решаются правильным раскладыванием классов по пакетам, а если у вас действительно много классов, то вполне стоит задуматься над разделением большого сервиса на меньшие (микросервисы).
Spring и его экосистема. Я работаю со Spring Framework с 2011 года, за это время я успел его полюбить, возненавидеть и расположить там, где он мне помогает, а не диктует то, как должен разрабатываться проект. Экосистема Spring невероятно богата и предоставляет практически всё необходимое для разработки проектов любых масштабов и сложности. Да, есть много других фреймворков и экосистем вроде Jakarta EE, Helidon, Micronaut, Quarkus и т.д. Я их все по мере возможности изучаю, но так и не нашёл ничего, что мне заменило бы Spring.
Тестирование является неотъемлемой составляющей процесса разработки программного обеспечения. В идеале разработка должна вестись через тестирование, это серьёзно снижает вероятность возникновения багов, особенно, если критерии прохождения тестов сформулированы максимально подробно. В качестве стандартного минимума я использую модульные и интеграционные тесты. При этом модульными тестами я проверяю корректность работы не каждого компонента по отдельности, а всей внутренней логики приложения, так как рассматриваю её компоненты как единое целое. Интеграционные же тесты я стараюсь сделать ближе к сквозным, например, вместо встраиваемых БД вроде H2 или Derby использую разворачиваемую в Docker СУБД, которую намерен использовать в реальной эксплуатации. Это делает интеграционные тесты более репрезентативными, так как создаются условия максимально приближённые к реальным.
Основы в виде чистой архитектуры, функционального дизайна, экосистемы Spring и тестирования на мой взгляд более чем достаточно для проектов любых масштабов от небольших проектов до проектов корпоративного сегмента. По мере необходимости можно к этому списку добавлять недостающие элементы.
Выводы и тезисы
В первую очередь я бы хотел порекомендовать разработчикам подходить более ответственно к разработке архитектуры и инфраструктуры проектов, выбирать те практики и подходы, которые позволят разрабатывать проекты с долгим циклом разработки с максимальной средней скоростью.
- Старайтесь следить за индустрией и поддерживайте в актуальном состоянии свои знания
- Старайтесь не экспериментировать на проектах, находящихся в реальной эксплуатации
- Используйте проекты-песочницы для проверки практического применения новых знаний
- В проектах-песочницах старайтесь проверять не только возможности новых инструментов, но и их совместимость с остальными вашими практиками.
- При внедрении нововведений в проекты делайте это в достаточном объёме и старайтесь объяснить заказчикам, для чего это нужно, и какие проблемы это решает, особенно, если это требует дополнительных финансовых или временных затрат
- Умейте корректировать процесс разработки в зависимости от изменяющихся требований
- Применяйте в проектах подходы и инструменты, с которыми ваша команда сможет работать эффективно