[Перевод] Семь смертных грехов разработки ПО
Перевод статьи Seven Deadly Sins of a Software Project автора Yegor Bugayenko.Сопровождаемость — это наиболее ценное достоинство современной разработки программного обеспечения. Сопровождаемость может измеряться, в основном, рабочим временем, которое требуется новому разработчику, чтобы вникнуть в проект, до того, как он начнет вносить значимые изменения. Чем больше времени это занимает, тем меньше уровень сопровождаемости. В некоторых проектах это время близко к бесконечности, что означает, эти проекты практически не сопровождаемы. Я хочу рассказать вам о семи смертных грехах, которые делают программный продукт несопровождаемым.
Анти-паттерныК сожалению, языки программирования, которые мы используем, слишком гибкие. Они слишком много позволяют и слишком мало запрещают. К примеру, Java никак не запретит вам разместить код всего приложения в одном классе с парой сотен методов. Технически, приложение скомпилируется и запустится. Но это — хорошо известный анти-паттерн Божественный объект.Таким образом, анти-паттерн — это технически допустимый способ спроектировать приложение так, что бы оно было заведомо неправильным. В каждом языке программирования существует достаточно большое количество анти-паттернов. Их присутствие в вашем проекте схоже с опухолью в живом организме. Однажды она начала расти и её уже очень трудно остановить. В конце концов живой организм погибает. В конце концов ваш проект становиться несопровождаемым, и его необходимо переписать.
Однажды вы допустили парочку анти-паттернов, и их количество, как опухоль, будет только расти.
Это особенно актуально для объектно-ориентированных языков (Java, C++, Ruby, Python), в основном, потому что они много чего унаследовали от процедурных языков (C, Fortran, COBOL). Именно поэтому ООП-разработчики имеют тенденцию думать в процедурном и императивном стиле. К сожалению.
Кстати, в дополнение к существующему списку всем известных анти-паттернов, я хотел бы добавить эти несколько моментов от себя, которые я считаю плохими подходами к разработке.
Мой практический совет здесь — это читать и учиться. Возможно, эти книги помогут вам. Всегда относитесь скептически к качеству вашего кода и не расслабляйтесь, когда оно «просто работает». Как и в случае с раком, чем раньше его диагностируешь, тем выше шанс выжить.
Бесследные изменения Когда я смотрю на историю коммитов, я должен иметь возможность рассказать о каждом изменении: что было изменено, кто внес эти изменения и почему эти изменения были внесены. Более того, время, которое требуется для того, что бы ответить на эти три вопроса, должно измеряться в секундах. В большинстве проектов этого нет. Вот несколько практических рекомендаций: Всегда используйте тикеты. Не важно, на сколько проект или команда малы, даже если вы один, создавайте тикеты (GitHub Issues) для каждой проблемы, которую вам предстоит решить. Кратко опишите проблему в тикете. Используйте тикетную систему для промежуточных мыслей, что бы потом было понятно, к чему были «те несколько коммитов».
Связывайте тикеты и коммиты между собой. Само собой, каждый коммит должен сопровождаться сообщением. Коммиты без сообщений — это грязная практика, и я даже не буду обсуждать, почему. Но одно только сообщение — этого не достаточно. Каждое сообщение должно начинаться с номера тикета, с которым вы работаете. GitHub (А я уверен что вы используете его) автоматически свяжет тикеты и комиты, давая возможность лучше прослеживать изменения.
Ничего не удаляйте. Git позволяет нам делать «push --force», что перезаписывает целую ветку, которая до этого существовала на сервере. Это всего лишь один пример того, как вы можете уничтожить история разработки. Часто я видел, как люди удаляют свои комментарии на GitHub discussions, что бы их тикеты выглядели более «чистыми». Это просто-напросто неправильно. Никогда ничего не удаляйте; оставьте свою историю, не важно, на сколько она плохая (или некрасивая) на ваш взгляд.
Сложные релизы Каждый кусок программного продукта должен быть упакован, прежде чем он будет доставлен конечному пользователю. Если это Java библиотека, она должна быть упакована в *.jar-файл и выпущена на каком-то репозитории; Если это — web-приложение, то оно должно быть развернуто на какой-то платформе, и т.д. Не имеет значения, на сколько проект большой или маленький, всегда должны быть стандартные процедуры, которые тестируют (test), упаковывают (package), и разворачивают (deploy).Идеальным решением будет автоматизация этих процедур до такого уровня, что их можно будет запустить одно строчной командой:
./release.sh или mvn deploy Большинство проектов далеки от этого. Их процесс релиза всегда включает в себя немного магии, где сотрудник, ответственный за это (так же известный, как DevOp), должен прокликать какие-то кнопки тут и тут, где-то авторизоваться, проверить какие-то метрики и т.д. Такой сложный процесс релиза все ещё является вполне типовым грехом всей индустрии разработки ПО.
Я могу дать лишь один практический совет: Автоматизируй это. Я использую rultor.com для этого, но вы можете использовать любые инструменты на свой вкус. Здесь важно то, что вся процедура полностью автоматизирована и может быть выполнена при помощи командной строки.
Добровольный статический анализ Статический анализ — это то, благодаря чему наш код выглядит лучше и, следовательно, лучше работает. Но это случается только тогда, когда вся команда принудительно (!) следует правилам, диктуемым статическим анализатором. Я писал об этом в Strict Control of Java Code Quality. Я использую qulice.com для проектов на Java и rubocop для проектов на Ruby, но, помимо этого, существует множество вариантов для каждого языка.Вы можете использовать любой анализатор, но сделайте это обязательным! Во многих проектах, где задействованы статические анализаторы, разработчики пишут красивые репорты и продолжают писать код как раньше. Такой «добровольный» подход не дает никакой пользы проекту. Более того, это создает иллюзию качества.
Я говорю о том, что статический анализ кода должен быть обязательным шагом вашего производственного конвейера. Сборка не должна проходить, если какой-либо статический анализатор указал на нарушение правил.
Неизвестное покрытие тестами Проще говоря, покрытие тестами — это процент кода, который тестируется модульными или интеграционными тестами. Чем выше процент покрытия, тем больше кода прогоняется во время тестирования. Разумеется, чем больше процент покрытия, тем лучше.Однако, многие разработчики, просто-напросто не знают процент покрытия тестами. Они не замеряют эту метрику. У них могут быть тесты, но никто не знает, на сколько глубоко они проникают в код и какая часть кода не покрыта тестами вообще. Эта ситуация гораздо хуже, чем низкий процент покрытия, который можно измерить.
Высокий процент покрытия — это не гарантия высокого качества. Это очевидно. Но неизвестный процент покрытия — это четкий признак проблем с сопровождаемостью. Когда новый разработчик присоединяется к проекту, он должен иметь возможность вносить некоторые изменения и видеть, как тесты реагируют на это. В идеале, процент покрытия тестами должен измеряться таким же образом, как и в случае со статическим анализом, сборка не должна проходить, если значение падает ниже какого-то заранее определенного порога (обычно около 80 процентов).
Бесконечная разработка Под «бесконечной» я имею в виду разработку без этапов (майлстоунов) и релизов. Без разницы, какое ПО вы пишете, вы должны периодически присваивать версии и делать релизы. Проект без четкой истории релизов — это несопровождаемое месиво.В больше степени, сопровождаемость — это когда я могу понять вас, прочитав ваш код.
Когда я смотрю исходный код и историю его комитов и релизов, я должен видеть, каковы были намеренья автора, что происходило с проектом год назад, куда он идет сейчас, какой его план развития и т.д. Вся эта информация должна быть в исходном коде и, важнее всего, в Git истории.
Git tags и GitHub release notes — это два мощных инструмента, которые предоставляют мне всю информацию. Используйте их по полной. Так же не забудьте, что каждая бинарная версия продукта должна быть доступна для немедленной загрузки. Я хочу иметь возможность скачать версию 0.1.3 и протестировать ее прямо сейчас, даже если продукт сейчас находится в версии 3.4.
Недокументированные интерфейсы Каждый кусок программного продукта имеет свой интерфейс, через который его следует использовать. Если это Ruby gem, то должны быть классы и методы, которые я хочу использовать, как конечный пользователь. Если это web-приложение, то должны быть web-страницы, которые конечный пользователь будет видеть и использовать. Каждый программный продукт имеет интерфейсы, и они должны быть аккуратно документированы.Как и все вышесказанное, это так же относится к сопровождаемости. Как новый программист на проекте, я сразу начну изучать его интерфейсы. Я должен понимать, что он делает и пробовать использовать его самостоятельно.
Я говорю здесь о документации для пользователей, а не для разработчиков. По идее, я против документации внутри ПО. В этом я полностью поддерживаю Agile Manifesto — работающий программный продукт гораздо важнее, чем подробное документирование. Но это не относится к «внешней» документации, которая предназначена для конечных пользователей, а не для разработчиков.
Итак, взаимодействие конечных пользователей с вашим программным продуктом должно быть четко документировано.
Если вашим продуктом является библиотека, тогда его конечный пользователь — это программист, который будет ее использовать — не развивать ее, а именно использовать, как «черный ящик».
Это критерии, которые используются для оценки проектов с открытым исходным кодом в нашем award competition.