На Хабре (да и в реальной IT жизни) встречаeтся много вопросов вида:
- Надо ли обновлять систему (или зависимости в приложении), если и так всё работает?
- Нужны ли вообще тесты (автотесты) в приложении (вы ведь на них потратите своё время и деньги заказчика)?
- Если ли смысл в паттернах и выделении абстракций (ведь подобное размазывает код, приводит к снижению производительности и т.д.)?
Ключевой вопрос во всех примерах ниже: что вы разрабатываете: товар или сервис? Как ни странно, но как только вы ответите на этот вопрос о товарах и сервисах, все сомнения о необходимости тестов, абстракций и т.д. отпадут сами собой.
Термины
В них есть принципиальная разница: товар обладает законченностью, его можно продать, а потом забыть о его существовании. В случае сервиса, покупатель и продавец общаются долго (по аналогии с подпиской, которая является этим самым сервисом).
Примеры продуктов:
- Строительство моста. Собственно, мост построен, сдан (это принципиально важный шаг), компания-строитель забыла о нем. В реальности еще существует гарантия на постройку, однако для сферичности эксперимента лучше пока сделать вид, что она отсутствует (или же очень короткая). При строительстве будет абсолютно нелогично менять планы в процессе (например, вернуть привезенную ограду и купить другую, более эффективную).
- Продажа табуретки. Всё аналогично мосту, самое главное — продать эту самую табуретку. После этого продавец забудет про покупателя очень надолго (по крайней мере, в большинстве случаев).
- Продажа квартиры (особенно на вторичном рынке). Тут опять-таки, самое важное — продать квартиру так, чтобы она не развалилась в первые месяцы. А что будет дальше — продавцу абсолютно неважно.
Примеры сервисов:
- Банковское обслуживание. Клиент платит за обслуживание раз в месяц, банк предоставляет сервис весь этот месяц. Клиент и банк помнят друг о друге как минимум весь месяц, а зачастую и несколько лет. Нет смысла продавать неликвид, ведь клиент откажется от обслуживания довольно быстро.
- Сервис такси (т.е. Яндекс Такс, Gett и пр.). Несмотря на то, что поездки носят законченный характер, компании делают основные деньги на постоянных клиентах, на тех, которые возвращаются. Поэтому нет никакого смысла обманывать покупателя в первую же покупку (в стиле бомбил), так как отношения здесь длительные.
- Супермаркет. Опять-таки, нам длительность общения продавца и покупателя намного важнее сиюминутной выгоды. А этом значит, что вместо продажи испорченного хлеба, магазину выгоднее утилизировать партию. Иначе покупатель купит хлеб в последний раз и не придет в магазин вообще. Более того, в отличии от примера с мостом, для супермаркета абсолютно нормально проводить "рефакторинг" — анализ того, насколько стеллажи стоят эффективно и т.д.
Зачем нам знать разницу между товаром и сервисом?
Идея проста: если вы рассматриваете свою программу как товар (то есть ваша связь с ней прервется после первого релиза), то нет никакого смысла тратить лишнее время ни на тесты, ни на рефакторинги, ни на соответствие стилям кодирования. Ведь если вы потратите своё время и сделаете "на отлично", то ваш продукт просто удорожает. А в дальнейшем эти абстракции будут просто вам не нужны (ведь вы-то прекратите работать над программой).
Однако если вы оказываете сервис для пользователей (например, делаете аналог Facebook), то у вас будут задачи обновления зависимостей, у вас будут задачи добавления/удаления функций, а потому тесты всё-таки будут, ибо они снизят риски в долгосрочной перспективе. Более того, вам потребуется выделять абстракции, как минимум для того, чтобы в будущем встраивать новую логику. Итого, если вы рассматриваете свою разработку как сервис, то вам необходимо обновлять зависимости, писать тесты, выделять абстракции и делать немало другой работы по уходу от legacy и минимизации рисков ошибки в будущем.
Пособие: как сделать из программы legacy своими руками
В контексте этой статьи можно очень легко вывести формулу того, как можно очень просто из практически любого разрабатываемого ПО сделать legacy, причем следуя этой формуле успеха достигает сам разработчик этой самой программы, без чьей либо помощи. Формула проста: чтобы получить legacy софт, вам необходимо относится к разработке сервисного ПО так, как будто вы делаете товар.
Или другими словами: если вы видите, что участвуете в разработке сервиса (то есть вы надолго в этом проекте, вы будете еще несколько лет добавлять новые функции в проект, адаптировать его к новым реалиям), однако есть желание сделать из проекта истинное legacy (то есть программу, в которую невероятно сложно вносить изменения, которая неспособна работать на более новой ОС/железе и т.д.), то просто начните относиться к проекту как к товару. Просто рассматривайте каждый релиз, как последний. Чаще употребляйте слова "ну всё, продали версию". Как можно активнее делайте маленькие костыли и хаки, вместо переработки кода. И напоследок: побольше ручного труда (забудьте про TeamCity/Jenkins), и никогда не пишите документаций, спецификаций и разных комментариев в коде.
Довольно интересно, что если буквально чуть-чуть изменить отношение к ПО, то оно само будет становится страшным legacy, причем сделанным своими руками.
Пособие: как же не делать из своих программ legacy
Как ни странно, однако для того, чтобы не получить ужасное ПО на руках, необходимо всего лишь задавать себе вопрос раз в месяц/квартал: а продукт, который я делаю, является товаром или сервисом? И запомнить/записать этот ответ хотя бы на некоторое время. В дальнейшем любые вопросы о тестах, рефакторинге, документации и пр. будут отпадать сами собой.
Примеры:
- Что нам лучше сделать с тестами, которые постоянно падают при каждом обновлении зависимостей и при каждом релизе? Причем падают они не по делу.
- Для программы-товара: лучше просто удалить эти проблемные тесты. Всё равно от них сейчас уже больше проблем, чем пользы. А в дальнейшем они будут никому не нужны.
- Для программы-сервиса: есть смысл починить/исправить тесты, чтобы уменьшить их false-positive срабатывание. Каждая починка тестов — это наше время, однако наличие этих самых тестов уменьшит риски пропустить ошибку в будущем.
- Стоит ли обновиться на Spring Boot 2, или есть смысл оставаться на 1.5?
- Для программы-товара: строго нет. Мы скоро закончим проект и никогда не вернемся к этому заказчику. У нас не будет задач ни по поддержке этой программы, ни по добавлению новых функций, ни по обновлению на новую Java с исправлениями безопасности.
- Для программы-сервиса: строго да. Ведь эта версия будет неподдерживаемой уже через год. Более того, чтобы запускаться на бесплатной Java, нам необходимо будет обновиться на Java 11 уже очень скоро, однако ряд модулей из Spring Boot 1.* (как минимум — ASM) не поддерживает байткод от Java 9+.
- Стоит ли нам обновить техническую документацию по продукту?
- Для программы-товара: а нам за это заплачено? Если нет — лучше вообще удалить, так как в ней могут быть ошибки, которые потом придется исправлять "по гарантии". А если нет документации — то нет и ошибок.
- Для программы-сервиса: конечно да. Через пару лет эта документация нам очень пригодится, когда новым разработчикам придется объяснять, как и почему у нас всё так работает.
Товар или сервис продает IT аутсорсер
Несмотря на то, что аутсорсер технически является IT компанией (более того, основная часть послужного состава — это люди, относящиеся к IT), самые важные шаги в проекте:
- Подписан договор с заказчиком
- Заказчик подписал "приемо-сдаточный акт" (или по-другому — готовый товар продан)
То есть все остальные действия сотрудников компании крутятся только вокруг этих пунктов. И именно эти две вещи прямо влияют на то, что же на самом разрабатывает аутсорсер — товар или сервис.
Например, для государственных заказов зачастую разрабатываются товары. И ни разу не сервисы (хотя и такое тоже бывает). А потому, в таких проектах нет никакого резона делать документацию, ускорять работу программы (то есть делать заказчика довольными). А значит получаем правило: если вы разрабатываете, тестируете или настраиваете ПО в фирме аутсорсере, который забудет про контракт после подписания акта, то нет никакого смысла даже задумываться о тестах, документации, выделении абстракций и пр.. Более того, если вы будете советовать менеджеру среднего звена "сделать софт лучше", то он абсолютно не поймет, зачем вы в принципе задумываетесь о таком. Ведь компания мало того, что не получит никакой прибыли от рефакторингов, она зачастую может понести вполне реальные убытки то того, что разработчик тратит время не пойми на что.
Более того, если компания-аутсорсер делает подобный софт на заказ, то у неё зачастую нет никакого стимула делать программы безопасными (ведь в будущем всегда можно просто наехать на блогера, как это делали в 90е, верно? а все убытки так или иначе понесет заказчик)
Так почему же получается legacy?
Прочитав статью, невольно рождается мысль: ведь все эти идеи до боли просты. Почему же тогда у нас получается legacy софт? Почему у одной компании/команды продукты легки в поддержке, а у других программы тормозят, а разработчики тратят кучу времени на поддержку?
Один (из многих) ответов — это отношение самих людей в командах к программам как к товару, или же как сервису. Причем здесь важно понимать, что в данном контексте, команда — это все люди, которые имели отношение к разработке софта, то есть и тестировщики, и разработчики, и аналитики, и менеджмент. Суммарная позиция всех людей и дает позицию команды. И она может быть как и "мы продаем товар", так и "мы делаем сервис".
Примеры конфликта, когда команда разрабатывает сервис, однако относится к нему, как к товару:
- Если участники проекта не заинтересованы в долгой работе с этим проектом. Например, человек может планировать уйти из фирмы в ближайшее время. Или в компании высокая текучка (в том числе когда люди часто переходят из команды в команду), которая приводит к тому, что участники проекта могут не ассоциировать себя с этим проектом. Т.е. если ты знаешь, что через полгода будет работать с другими людьми и над другими задачами, то какой смысл вкладывать силы в текущую программу? Зачем писать документацию, зачем делать авто-тесты и пр.? Лучше просто сделать побыстрее, получить премию за ускоренную пятилетку и пойти работать в другое место.
- Метод кнута и пряника в команде/компании построен так, что намного выгоднее чаще отчитываться о создании новых продуктов и о замене старых. Такое может получиться, если все бонусы в компании выдаются за то, что "был сделан продукт А" или за то, что "был переделан продукт Б", а не за то, что "продукт С постоянно развивается и помогает зарабатывать деньги". В этом случае все разумные люди будут избегать долгих сроков (за это не дадут пряник). Ключевая разница — в презентации будут превуалировать законченные формы глаголов, по отношению к проектам (т.е. программа сделана, проект завершен), вместо длительных (сервис позволяет зарабатывать, команда обеспечивает инфраструктуру).
- И не забываем про стандартный саботаж (или job security) — зачем делать документацию к продукту, который ты хорошо знаешь? Ведь иначе менеджер легко найдет тебе замену. Зачем упрощать жизнь при поддержке ПО? Ведь если поддержка ПО очень простая, то тебя можно заменить на кого-нибудь другого. Этот пункт очень пересекается с первым, который описывал компанию с большой текучкой. Однако стоит понимать, что условная "job security" мало перекликается с идеей товаров или сервисов, просто это зачастую является хорошим объяснением многих процессов, так что нельзя не упомянуть.
Примеры обратного конфликта, когда команда на самом деле создает товар, однако относится к нему как к сервису:
- Непонимание бизнеса. Даже очень честный аутсорсер редко признается, что его прибыль — это разница между тем, сколько платит заказчик за проект, и тем, сколько он может стоить в минимальном исполнении, при условии, что заказчик готов принять его. То есть основной заработок — это возможность сэкономить там, где в долгосрочном периоде экономить было бы неразумно. Отсюда и возникает конфликт — если менеджер среднего звена говорит заказчику о том, что "мы делаем лучший в мире софт", то разработчик может начать принимать всё это за чистую монету.
- Всегда так делали. Если команде enterprise java разработчиков (делающих сервисы, которые потом улучшаются и дополняются в течении десятилетия) дать задание "сделать временный сервис по копированию файлов из пункта А в пункт Б", то велик шанс того, что этот простейший сервис будет переусложненным. Да, он будет копировать файлы, однако само копирование будет спрятано за 10ю слоями абстракции (а через пару месяцев — за 11ю).
Влияние товаров и сервисов на языки и технологии
На хабре зачастую есть немало споров по части того, какой язык программирования лучше. Или же какая технология лучше. Как ни странно, немало из них возникает из-за того, что у сторон разное понимание того, чем является программа — товаром или сервисом.
Если у вас одноразовая задача (например, перекопировать файлы из пункта А в пункт Б с некоторыми условиями и минимальными преобразованиями), то довольно глупо будет выбирать технологии, рассчитанные на долгую поддержку, дающие длинную обратную совместимость. Для таких задач Go/Python будет идеальным решением.
И наоборот — если в вашу задачу входит длительное оказание сервиса (с частыми обновлениями безопасности и т.д.), то на первое место выходят такие плюсы платформы, как обратная совместимость, легкость обновления, простота патчинга и т.д. Вам уже становится абсолютно неважно, легко или сложно написать Hello World на выбранном языке, так как такие программы будут создаваться раз в несколько лет.
И как это использовать?
В заключении — как использовать то, что программа может являться как товаром, так и сервисом.
- Раз в месяц просто вспоминайте, что вы делаете: товар или сервис.
- Не пытайтесь развивать товар. Его продают один раз и забывают. Этот подход невероятно понятен бизнесу. Работайте по системе "сделал — забыл".
- Не относитесь к сервисным проектам как к товару. Вы будете долго общаться с пользователями, им в этом случае важнее долговременное сотрудничество, а не сиюминутная выгода.
Автор: Игорь Манушин