Хабрахабр

[Перевод] Стоит ли высокое качество ПО затрат на его разработку?

Часто в процессе реализации проектов команды сталкиваются с вопросом: чему следует уделять больше внимания – выпуску новых фич или повышению качества кода? Обычно менеджеры делают выбор в пользу фич. Зачастую разработчики таким положением дел недовольны, считая, что им выделяется недостаточно времени для работы над архитектурой и качеством кода.

Те, кто знаком со мной лично, знают, что я не разделяю эту мысль. Закон Беттериджа гласит: «На любой заголовок, который заканчивается вопросительным знаком, можно ответить словом нет». Такая постановка вопроса предполагает, что существует компромисс между затратами и качеством. Но в этой статье я хочу пойти ещё дальше и доказать, что постановка вопроса из заголовка этой статьи просто не имеет смысла. В этой статье я докажу, что к миру разработки компьютерных систем этот компромисс не применим и, в действительности, создавать ПО высокого качества оказывается в конечном счёте дешевле. И необходимо постоянно соблюдать баланс.

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

Мы привыкли к тому что нужно выбирать между ценой и качеством

Как я писал ранее, при разработке ПО постоянно приходится делать выбор между качеством продукта и затратами на его разработку. Когда вы покупаете новый смартфон, у вас есть выбор. Заплатить больше денег и получить более быстрый процессор, больше памяти и улучшенный экран, или заплатить меньше, но пожертвовать некоторыми функциями. Из этого правила бывают исключения: иногда продукт более высокого качества стоит дешевле. А иногда люди даже не могут объективно сравнить два продукта и выбрать более качественный. Например, не замечают разницы между экранами, которые изготовлены по совершенно разным технологиям. Тем не менее, утверждение: «Высокое качество стоит дороже» – обычно справедливо.

Качество ПО это о многом

Говоря о качестве ПО, следует начать с определения критериев качества. Что такое качественное ПО? С этого момента всё несколько усложняется, ведь у любой компьютерной системы есть множество критериев, по которым можно оценивать её качество. Можно оценивать UI и UX: насколько быстро и просто пользователь может решить свою задачу? Можно оценивать надёжность: есть ли в программе баги, которые ведут к неправильному и нестабильному поведению? Ещё один критерий – это архитектура: насколько исходный код программы структурирован, насколько просто и быстро программист может найти нужный ему в данный момент участок кода?

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

Самое важное их отличие в том, что пользователи могут оценить внешнее качество, но не могут понять, насколько хороша (или плоха) внутренняя архитектура системы. Поэтому я разделяю критерии качества на две категории: внешние (например, UI/UX или наличие багов) и внутренние (архитектура).

На первый взгляд внутреннее качество не имеет значения для пользователей (но только на первый)

Если пользователи не могут оценить внутреннее качество ПО, является ли этот критерий важным? Давайте представим гипотетическую ситуацию, что две команды разработчиков, независимо друг от друга, решили создать приложение для отслеживания и прогнозирования задержек авиарейсов. Одной командой управляю я, а второй руководит Ребекка. Набор базовых функций у приложений примерно одинаковый, интерфейс у обоих приложений тоже получился достаточно удобный и продуманный, критических багов в приложениях нет. Единственное отличие заключается в том, что исходный код приложения от Ребекки чётко структурирован и организован, а код, созданный моей командой, представляет из себя беспорядочный набор классов и методов с непонятными именами и ещё более непонятной логикой того, как этот код связан между собой. Есть и ещё одно отличие: я продаю своё приложение за $6, а Ребекка продаёт почти такое же приложение за $10.

Иными словами – зачем переплачивать за внутреннее качество, которое не имеет никакого значения для пользователей? Поскольку пользователям недоступен исходный код приложений, а качество кода никак не влияет на пользовательский опыт, зачем пользователям платить лишние $4?

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

Программы с высоким внутренним качеством проще расширять

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

Добиться ситуации, когда код станет сложным для понимания, на самом деле очень просто. Высокое качество кода позволяет программистам быстро в нём ориентироваться. Названия, которые Тони дал переменным и функциям 6 месяцев назад, возможно, были понятны ему, но также непонятны новому разработчику, как и мотивы побудившие Тони покинуть компанию. Логические условия могут переплетаться, связи между структурами данных могут быть сложными и неявными. Разработчики обычно называют это «техническим долгом» (техдолгом), или иными словами, разницу между текущим состояние кода и идеальным состоянием, в котором он может быть.

Когда приложение разделено на модули, программисту не нужно изучать все 500,000 строк исходного кода и он может быстро найти нужные ему в данный момент несколько сотен строчек. Одним из основных преимуществ, которые даёт высокое качество кода является то, что программист может быстро понять, как работает система и внести нужные правки. Если структуры данных в программе совпадают с терминологией из доменной области бизнеса, то программисту легко соотнести запрос на новый функционал с тем, как устроена система. Когда программисты дают понятные имена переменным, функциям и классам, можно легко понять, что делает каждый отдельный фрагмент кода без необходимости глубоко вникать в контекст. Также возрастает вероятность ошибиться. Техдолг же увеличивает время, которое требуется для работы с кодом. А если баг не будет замечен сразу, то это приведёт к появлению проблем в production-коде и тому, что придётся потратить ещё больше времени на исправление этих проблем в будущем. В случае появления багов из-за плохого качества кода, потребуется дополнительное время на то, чтобы локализовать проблему и исправить её.

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

Пользователи хотят получать новые фичи как можно быстрее

Мы подходим к важному моменту, а именно: к ответу на вопрос, почему же для пользователей всё таки важно внутреннее качество ПО? Высокое внутреннее качество способствует более быстрому выпуску новых фич, потому что их проще, быстрее и дешевле делать. Наши с Ребеккой приложения выглядят сейчас почти одинаковыми, но через несколько месяцев высокое качество кода Ребекки позволит ей выпускать каждую неделю по новой фиче, а я застряну на месте, стараясь справиться с техдолгом и пытаясь запустить хотя бы одну новую фичу. Я не смогу конкурировать с Ребеккой по скорости развития, и её приложение быстро обгонит моё по функциональности. В конечном счёте пользователи удалят моё приложение и будут пользоваться приложением Ребекки, несмотря на то, что оно стоит дороже.

Визуализация влияния внутреннего качества

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

Обычно кривая выглядит примерно так: На графике ниже схематично изображено, как можно представить соотношение функциональности и времени, которое требуется на его разработку.

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

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

Однако есть одна хитрость. На начальных стадиях разработки игнорирование качества кода оказывается более эффективно, чем следование высоким стандартам. Но когда же заканчивается этот период?

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

Опытные разработчики считают, что плохое качество кода начинает замедлять работу уже через несколько недель после начала проекта. Но вернёмся к вопросу, в какой момент стоит начать задумываться о качестве кода. На ранней стадии проекта на красоту архитектуры и кода можно не обращать внимания.

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

Даже лучшие команды иногда пишут плохой код

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

В тот момент он как раз закончил работать над проектом, который все считали очень успешным. Чтобы наглядно это показать, хочу рассказать вам о беседе с одним из наших лучших тим-лидов. Команда тоже была довольна выполненным проектом. Заказчик был в восторге от новой системы, причём, как от новых возможностей, так и от ресурсов, которые были потрачены на её разработку. Я спросил у него: «Но как же так, ты ведь один из наших лучших архитекторов?». Технический лидер команды тоже был весьма доволен результатом, но признался, что на самом деле архитектура системы получилась не такая уж удачная. Он ответил так, как ответил бы любой опытный архитектор: «Мы принимали хорошие решения, но только сейчас понимаем, как нужно было делать правильно».

Видимо, поэтому опытных разработчиков называют «архитекторами». Многие люди сравнивают создание сложных систем с проектированием небоскрёбов. Типичные заказчики плохо понимают, чего они хотят от программы и начинают понимать это только в процессе работы над ней. Но в процессе создания ПО всегда есть некоторая неопределённость, нехарактерная для других областей деятельности, в которых неопределённости гораздо меньше. Элементы, из которых создаются программы (языки программирования, библиотеки, платформы), меняются раз в несколько лет. Чаще всего, в тот момент, когда им показывают первые версии программы. Ситуация усложняется ещё больше, когда выясняется что технологии, используемые для производства бетона, его физические свойства и характеристики обновляются каждые 2 года. Проводя аналогию со строительством небоскрёба, можете ли вы представить ситуацию, когда заказчик просит архитектора добавить ещё с десяток этажей и изменить планировку у нижних, при том, что половина здания уже построена?

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

Это помогает проекту быстро развиваться и выпускать новые фичи как можно оперативнее. Профессиональная и организованная команда отличается от менее организованной тем, что в процессе работы над системой создаёт меньше технического долга, а также параллельно избавляется от существующего. Участники такой команды постоянно работают над поддержанием высокого качества кода и быстро избавляются от плохого кода, пока он не начал мешать двигаться вперёд. Такая команда вкладывается в создание автоматизированных тестов, что помогает быстрее выявлять проблемы и тратить меньше времени на поиск и исправление багов. В качестве метафоры можно привести уборку на кухне после приготовления пищи. CI-системы тоже способствуют этому, особенно в ситуации, когда множество людей параллельно работает над разными задачами. Если вы не почистите их сразу, то грязь засохнет и потом будет гораздо сложнее её отмыть. Невозможно что-то приготовить, не испачкав при этом стол, посуду и другие кухонные принадлежности. И когда в следующий раз вы захотите что-нибудь приготовить, вам будет намного сложнее это сделать потому что сначала придётся помыть гору посуды.

Исследование DevOps Research and Assessment (DORA)

Компромисс между стоимостью и качеством – не единственный в мире разработки ПО, который кажется простым на первый взгляд, но на практике всё оказывается несколько сложнее. Также широко обсуждается вопрос, что лучше выбрать – быстрые темпы разработки и релизов или более медленные темпы и тщательное тестирование. Считается, что использование второго подхода позволяет добиться более высокой стабильности продакшн-систем. Однако в исследовании DORA было доказано, что это не так.

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

Поддержка систем с хорошей архитектурой стоит дешевле

Самые важные пункты из того, о чём мы говорили выше:

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

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

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

У Мартина Фаулера есть ещё одна статья про технический долг. Небольшой тизер:

Чистка технического долга – это как выплата процентов по кредиту. Дополнительное время, которое тратится на добавление новых фич, можно сравнить с процентами по банковскому кредиту. Но может создаться ложное ощущение, что техническим долгом можно достаточно просто управлять. Эта метафора хорошо описывает суть. Однако в реальности это намного сложнее.

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»