Обновить
11

Программист, техлид

3
Подписчики
Отправить сообщение

Вы предлагаете в код в виде комментариев вносить требования (ТЗ)? Ну это такое.

Про ТЗ соглашусь. Попытка вывести прямое, однозначное соответствие между ТЗ и кодом может привести к сомнительным решениям. ТЗ лучше проецируется на приемочные тесты, чем на код.

Спасибо за аргументированный комментарий. Со многими мыслями в нём я согласен.

Про напоминания — да, полностью поддерживаю. Это действительно хорошая и уместная роль комментариев: сохранить знание, которое сейчас вроде бы ни на что не влияет, но может оказаться критичным при изменениях. Такие вещи в код "не впишешь", и комментарий тут вполне уместен.

Что касается встроенной документации — я предпочитаю отделять её от обычных комментариев в коде. То, что оформляется через Javadoc, PHPDoc, Doxygen и т. п., — это всё-таки документация к интерфейсу, и она действительно должна описывать семантику, ограничения, контракты, примеры использования и прочее. У себя на проекте я как раз продвигаю такую практику, и она обязательна для публичных методов.

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

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

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

Многое, с чем я могу согласиться, например, есть в этом комментарии: https://habr.com/ru/articles/929600/#comment_28604372 — очень точно сказано и про приоритет читаемости, и про то, что в большинстве проектов производительность — не первое, о чём стоит беспокоиться.

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

Про производительность: в подавляющем большинстве случаев на неё гораздо больше влияют не структура кода или количество методов, а операции ввода-вывода — файловые операции, работа с базой, сетевые задержки и т. п. Разбиение метода на 2–3 вспомогательных почти никогда не станет узким местом.

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

Да, оба варианта возможны, тут нужно смотреть по контексту.

От себя дополню, что предложенная вами формулировка NotPercent может оказаться не вполне однозначной. Я бы предлжил fractionalTaxRate.

Плюс самодокументируемого кода в вашем кейсе становится понятен, если тело метода с расчетом оказывается относительно большим. Тогда расстояние от места объявления аргумента или локальной переменной (там, где она описана комментарием) до места его использования может оказаться достаточно большим, чтобы вызвать непонимание у человека, читающего код. В случае самодокументируемого кода имя переменной говорит само за себя, в каком бы месте оно вам ни встретилось.

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

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

Удивительно, что как противники, так и адепты TDD - и автор статьи - не исключение, в своих рассуждениях о предмете исходят из ложной предпосылки, что основным продуктом TDD является тестовое покрытие.

Между тем, с пониманием того, что реальная цель TDD - разработка функционала, соответствующего спецификации, все становится на свои места. Цель - корректно реализованный функционал, средство - "материализация" спецификации в виде тестов. Вопросов, почему спецификацию надо писать до написания кода, а не после, я почему-то особо не наблюдаю.

А тестовое покрытие является хоть и полезным, но всё же побочным продуктом этого подхода.

Единственная цель TDD — как можно быстрее получить обратную связь. Если можно получить обратную связь, без написания тестов, то TDD не нужен (хотя тесты писать надо!).

Далеко не единственная и даже не главная.

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

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

REPL, возможно и позволяет как-то контролировать результаты на текущем шаге разработки, однако не решает следующих проблем:

  1. Он никак не помогает сформулировать и зафиксировать требование спецификации до начала разработки.

  2. Контроля регресса.

И еще одно распространенное заблуждение: TDD используют потому, что надо писать тесты. Как раз наоборот: если ты используешь TDD, то получается, что (некоторые) тесты тебе писать уже не надо - они являются побочным продуктом методологии разработки и появляются "сами собой". А само TDD помогает писать не тесты, а конечный программный продукт.

В своей аргументации вы отталкиваетесь от очень распространенного заблуждения, что целью TDD является тестирование. Это абсолютно не соответствует действительности.

Как намекает название, методология Test Driven Development (разработка посредством тестов) имеет целью упорядочивание процесса разработки, и тесты в ней применяются лишь как средство. Тест в ней является не средством тестирования, а способом соотнести разрабатываемый функционал и список требований к нему. В TDD вы пишете тест, чтобы быть уверенным, чтобы тот код, который вы собираетесь написать, закрывает определенное требование из спецификации. Именно поэтому тест нужно писать до начала кодирования, а вовсе не потому, что после его завершения у вас будет соблазн забить на тесты и от этого пострадает тестовое покрытие.

То, что в результате разработки по TDD увеличивается тестовое покрытие, - это всего лишь побочный эффект. И если все получившиеся тесты по окончании разработки просто взять и удалить, TDD все равно выполнит свою основную функцию - систематизацию процесса разработки. (Другое дело, что мы этого не делаем, потому что расширение тестового покрытия - это положительный, полезный побочный эффект.)

Просто возьмите нормальную книгу по теме.

Ключевое слово "нормальную".

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

Речь о "системе управления многоквартирным домом". Тут всё еще проще: номер квартиры.

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

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

Конечно, это касается только серьезного рефакторинга. Если речь о переименовании переменных, то это, конечно, на отдельную задачу не тянет.

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

Когда припрет - ну, например, когда код настолько макаронный, что новую фичу в него не вставить никак, - матерятся, но рефакторят.

С другой стороны, знаю людей, которые и вполне сознательно рефакторят. В том числе в рамках подхода "Red - Green - Refactor".

При рефакторинге... производительность растет

Правильно ли я понимаю, что речь здесь о производительности команды разработчиков? Ведь рост производительности кода в моем скромном понимании - задача не рефакторинга, но оптимизации.

На еду остается более 100 рублей в день.

И целый рубль еще на развлечения остается.

Все эти подходы просто придумываются людьми совершенно далекими от разработки.

Вопрос: от какой именно разработки?

Есть разработка, которая про «настрочить скриптец на коленке», когда полученный программный продукт не то что релизить, а запускать второй раз никто не собирается; а есть, которая «кровавый энтерпрайз», когда софтина живет и развивается десятки лет, а в разработке участвуют много-много независимых команд. И есть всё, что где-то между ними: там тебе и веб-студии, и аутсорсеры-фрилансеры, и разной мелкости корпоративные иэрпишечки-сиэрэмочки, и все другое прочее.

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

отдельный тип задач, где ТДД (а точнее, написания теста до реального кода) помогает

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

$client = ClientClass::create();
Нужно исходить из нашей задачи. А состоит она, в соответствии с принципом открытости-закрытости, в том, чтобы исключить необходимость изменять класс ClientClass при добавлении нового функционала. Обрабатывая входные данные прямо в классе ClientClass, каждое изменение функционала будет приводить к изменению этого класса. Поэтому наша задача в том, чтобы вынести из ClientClass все, что будет изменяться.

Так вы же все равно существующий код поменяли, добавив обработку массива filters.

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

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

ClientClass работает на уровне репозитория, а фильтры — на уровне бизнес-логики. На уровне контроллера мы только выбираем подходящую конфигурацию.

Выглядит так, как будто у вас в контроллерах много логики.

Как раз наоборот. Логики в контроллерах у меня нет вообще — только конфигурация.

Информация

В рейтинге
Не участвует
Откуда
Россия
Зарегистрирован
Активность

Специализация

Бэкенд разработчик
Ведущий