Кажется, про TDD давно всё известно: сперва тест — потом код — получаем покрытие. Но на деле его суть понимают неправильно — как критики, так и сторонники.
Эта статья — не инструкция и не религиозная проповедь. Это разбор заблуждений. Причём речь пойдёт не только о критиках TDD, но и о его сторонниках.
TDD часто воспринимают как способ добиться максимального покрытия или как дисциплину «писать тесты вперёд». Но настоящая цель — не в тестах, а в итеративном проектировании поведения и архитектуры.
Если вам кажется, что TDD — это занудно, медленно, не подходит к реальному коду или убивает гибкость — возможно, вы всё делали правильно.
Но с совершенно другими целями.
Именно поэтому вам не понравилось.
Разберёмся, что такое TDD на самом деле — и почему вы, скорее всего, не знаете TDD.
1. Недопонимание цели TDD — главное заблуждение
Ошибка: TDD — это способ добиться тестового покрытия.
Реальность: TDD — это способ проектирования.
Чаще всего TDD воспринимают как технику написания тестов до кода, чтобы удостовериться, что всё протестировано. На практике же его основная цель — не сами тесты и не метрики покрытия, а итеративная разработка системы через формулирование желаемого поведения. Тесты здесь выполняют роль спецификаций, а не проверки. Разработчик получает не просто проверку работы, а возможность обдумать и уточнить интерфейсы, зависимости, структуру модулей. В результате сама разработка продукта становится более технологичной: проектирование и реализация сливаются в управляемый процесс, где каждое поведение вводится осознанно и поддаётся проверке.
2. Ожидание мгновенного повышения качества
Ошибка: Если писать по TDD, багов не будет.
Реальность: Качество — это не только тесты.
TDD действительно снижает количество дефектов — за счёт итеративности, простоты и чётких целей. Но само по себе написание тестов не заменяет мышление. Даже в рамках TDD можно писать плохие тесты, дублировать код или закреплять ошибки в логике. TDD — это инструмент, а не волшебная кнопка качества. Эффект от него зависит от зрелости подхода и понимания сути.
3. Использование TDD как догмы
Ошибка: TDD обязателен для любого проекта.
Реальность: TDD — инструмент, а не религия.
TDD подходит далеко не всегда: он работает лучше всего там, где можно чётко выразить поведение — бизнес-логику, правила, трансформации. В экспериментальном коде, UI, интеграциях с внешними сервисами TDD может быть неэффективен или даже мешать. Навязчивое следование методике без учёта контекста приводит к разочарованию.
4. Путаница с Unit Testing
Ошибка: TDD — это просто юнит-тесты.
Реальность: TDD — это процесс, а не тип тестирования.
Можно писать юнит-тесты без TDD. Можно применять TDD, используя end-to-end или интеграционные тесты, если они описывают поведение системы.
Можно даже вообще обойтись без автоматизированных тестов — формулировать ожидаемое поведение и проверять его вручную. Такой вариант, конечно, не является общепринятым и вызывает споры, поскольку нарушает привычную для многих автоматизированную природу TDD. Тем не менее, если сохраняется порядок действий и намерение — сначала убедиться, что поведение не реализовано, затем реализовать, затем убедиться, что работает, и только потом рефакторить — такой подход с определёнными допущениями можно считать разновидностью TDD.
Тип тестов зависит от уровня, на котором мы проектируем. Главное — не наличие теста как такового, а то, что именно он фиксирует и в каком контексте создаётся. Суть TDD — в порядке действий и в намерении.
5. Беспокойство о «медленности»
Ошибка: TDD замедляет разработку.
Реальность: TDD экономит время на отладке и переосмыслении.
На старте действительно может показаться, что TDD замедляет разработку: приходится писать тесты, продумывать интерфейсы, проводить рефакторинг. Но эти усилия окупаются уже в среднесрочной перспективе. Код становится проще, понятнее и устойчивее к изменениям. Возникает меньше багов, проще менять требования. TDD — это инвестиция.
К тому же, даже если вы не пишете тесты, вы все равно так или иначе тестируете создаваемое поведение, запуская программу после внесения в нее изменений. А это тоже требует времени, что дополнительно нивелирует разницу в трудозатратах.
6. Неправильное понимание цикла «тест → код → рефакторинг»
Ошибка: Сначала пишем тесты и реализацию целиком.
Реальность: Маленькие шаги позволяют проектировать поведение.
Многие нарушают ритм TDD, пытаясь сразу написать всё: тест и полноценную реализацию. Это приводит к переусложнению, пропущенному рефакторингу и неустойчивой архитектуре. Настоящий цикл TDD — это серия маленьких шагов: пишем минимальный тест, реализуем минимум для его прохождения, затем улучшаем структуру. Этот ритм помогает контролировать сложность и сохранять гибкость кода.
7. Написание тестов ради тестов
Ошибка: Тесты нужны ради покрытия.
Реальность: Тесты фиксируют поведение.
Когда тесты пишутся «на всякий случай» — ради галочки или ради отчёта — они не несут пользы. Более того, такие тесты часто ломаются от безобидных изменений и мешают развитию. В TDD тест — это прежде всего способ выразить, что система должна делать. Это инструмент мышления. Он фиксирует предположения о поведении — и помогает их проверять.
8. Игнорирование рефакторинга
Ошибка: Реализация — цель.
Реальность: Качественный код — часть процесса.
Часто после прохождения теста разработчик двигается дальше, забывая про третий шаг — рефакторинг. В итоге код начинает обрастать дублированием, усложняться, терять структуру. В TDD рефакторинг обязателен. Это не «посмотрим потом», а часть цикла. Именно в этом шаге рождается хорошая архитектура: без изменения поведения, но с улучшением формы.
9. Неподходящее применение TDD
Ошибка: TDD применимо везде.
Реальность: Некоторые задачи требуют других подходов.
TDD трудно применять там, где невозможно заранее определить поведение: например, в UI, в обработке нестабильных API, в исследовательском коде. Здесь либо тесты получаются слишком хрупкими, либо разработка идёт вслепую. В таких случаях лучше использовать другие инструменты: прототипирование, мануальное исследование, интеграционные тесты. TDD — не серебряная пуля.
10. Подмена проектирования тестами
Ошибка: Архитектура «вырисуется» сама собой.
Реальность: Высокоуровневый дизайн требует осознанного подхода.
Иногда можно услышать: «Я не продумываю архитектуру — я просто следую TDD, и всё складывается само». Это рискованный путь. TDD помогает проектировать детали — интерфейсы, взаимодействие компонентов. Но он не заменяет продуманного подхода к архитектуре системы, разграничению ответственности, выбору моделей. Без общей картины можно прийти к локально хорошему, но системно хрупкому решению.
TDD — не про тесты. Это про мышление. Про то, как маленькими шагами идти к поведению, которое можно формализовать, проверить и изменить. И если вы когда-то попробовали TDD и разочаровались — вполне возможно, вы всё делали правильно. Просто вы решали не те задачи.
Комментарии (60)
viordash
20.08.2025 08:16спасибо, хорошее раскрытие техники разработки ПО.
6. Неправильное понимание цикла «тест → код → рефакторинг»
я с годами вернулся к такой последовательности, код > тест > рефакторинг > ...
Тест, написанный первым получался довольно синтетическим и был практически бестолковым.
iv660 Автор
20.08.2025 08:16Ваша последовательность вполне имеет право на существование. Однако это не TDD.
Ваша методика обеспечивает хорошие тесты.
Для TDD не так важно, какие в результате получаются тесты. Гораздо важнее, какая получается реализация. Такая последовательность дает стимулы, чтобы реализация была минимально необходимой для закрытия имеющихся требований.
Суть TDD — «сформулировал требование → реализовал → привел к товарному виду». Тест — это всего лишь инструмент для фиксации требований и факта их закрытия реализацией.
DasMeister
20.08.2025 08:16Вы не правы. Важна не последовательность код->тест->рефакторинг. А важна последовательность red-green-refactoring.
Кент Бек это много раз демонстрирует в своей книге. Получите красный тест, почините проведите рефакторинг снова получив красный тест.
Рефакторинг это и не только классическое изменение формы, но и собственно написание кода, который в текущем окне контекста приведёт к красным тестам. После чего он сам идёт и уточняет тесты.
В TDD не важно, что предшествует тест или код, важно пойти в цикл Green-Red-Refactoring.iv660 Автор
20.08.2025 08:16Согласен: ядро TDD — именно цикл red → green → refactor. И я как раз описываю его как «тест → код → рефакторинг», а не «код → тест → …». Судя по вашей последовательности действий, это ответ скорее на вот этот комментарий: https://habr.com/ru/articles/933090/comments/#comment_28730776.
По поводу «рефакторинга, который делает тесты красными». По определению это не рефакторинг. Рефакторинг — это изменение внутренней структуры без изменения внешнего поведения, поэтому он не должен переводить зеленые тесты в красные. Если тест «покраснел», значит вы либо сломали поведение, либо начали вводить новое — и это уже новая red-фаза, которой должен предшествовать добавленный/измененный тест. См. Фаулер: https://martinfowler.com/books/refactoring.html
Кент Бек действительно показывает микрошаги, но он не смешивает фазы: красный — зафиксировать требование, зеленый — реализовать, рефакторинг — улучшить форму, сохраняя зеленый. Иногда большие изменения идут как цепочка R-G-R-G (несколько циклов), но «второе R» тут — это новый красный шаг, а не рефакторинг, меняющий поведение.
Итого: спор не о важности RGR, а о терминологии. В моем тезисе речь ровно о дисциплине: поведение задается тестом (red), код доводит до соответствия (green), улучшения формы делаются на зеленом (refactor).
DasMeister
20.08.2025 08:16Если вы меняете структуру приложения, то i9n тесты и e2e будут проходить без проблем, т.к. поведение в сумме, возможно, не поменялось. Но если вы выделили метод или целый компонент из существующего компонента и собрали новую композицию связей, то у существующего кода тесты моментально станут красными.
У всего приложения вы не поменяли поведение (возможно), а у компонента для которого был комплект малых тестов - да.Именно это и есть рефакторинг и он может и должен приводить к красным тестам. Если вы работаете с кодовой базой, а не с юнитом.
Да вы действительно правы, что R-G фаза набор экспериментов с кодом, не связанная с рефакторингом. Однако изначально Кент Бек вкладывал в неё свой личный подход и просил не возводить его опыт в догму.
Не для каждого кода нужно получить фазу red в начале. Она была нужна в 2000 году, т.к. у них не было инструментария который позволял работать иначе.
Например сейчас я могу задекларировать в IDE функцию/метод с сигнатурой и выбросить исключение "Not Implemented", нажать комбинацию клавиш и мне сгенерируется тест сьют, нажать пару раз tab и получить тест вызывающий нереализованную функцию.
Это всё ещё TDD или нет? Если нет - добро пожаловать в догматики и ваше дело проиграно. А если да, то вы нарушили свои же утверждения - пропустив ненужную фазу.
Часть кода вообще не имеет смысла тестировать описывая контракт до того как сформирован код. Существует проксируемый код. Например бизнес-сервис который вызывает единственный метод репозитория. Совершенно всё равно в какой последовательности вы это сделаете.
И все эти "исключения" существуют в рамках понимания того, что вы делаете. Если вы знаете какой код вы пишете, можете писать его до теста. Главное войти в цикл eventualy. Но если вы пишете код который не понимаете как должен работать, то в цикл надо входить до него.
Пример для объяснения предыдущего абзаца:
Есть сложная структура данных, её нужно преобразовать в сложный аналитический запрос. По факту, вы пишете планировщик и оптимизиатор запросов. Вы понятия не имеете как это сделать и к каким запросам это приведёт. Но вы понимаете, что на вот эти входные данные получите из базы вот эти выходные данные. Самое время написать тест до кода. Т.к. после того как вы напишите вторую имплементацию - у вас всё сломается в первой или вообще перестанет хоть как-то функционировать.
Иными словами TDD это про итерации в первую очередь, а не про то в какой последовательности они выполняются. Единственное что важно, что перед рефакторингом вы должны оказаться в зоне "GREEN".iv660 Автор
20.08.2025 08:16Например сейчас я могу задекларировать в IDE функцию/метод с сигнатурой и выбросить исключение "Not Implemented", нажать комбинацию клавиш и мне сгенерируется тест сьют, нажать пару раз tab и получить тест вызывающий нереализованную функцию.
Это всё ещё TDD или нет? Если нет - добро пожаловать в догматики и ваше дело проиграно. А если да, то вы нарушили свои же утверждения - пропустив ненужную фазу.
Очень хорошая иллюстрация и замечание по поводу слепого следования догмам.
Я считаю, что описанный вами подход вполне укладывается в концепцию TDD. Почему? Да потому что тест был написан до начала работы на реализацией. Это означает, что теперь вы не забудете, что этот метод у вас не реализован.
С другой стороны, в данной ситуации не вижу существенной разницы: сначала объявлять метод, а потом писать/генерировать тест, или наоборот. То есть следование догме в данном случае ситуацию никак не ухудшило бы. Более того, если бы вы стачала написали тест, вызывающий несуществующий метод, то вы могли бы на этом и остановиться, считая фазу Red завершенной. Необходимости объявлять метод и кидать в нем исключение уже не было бы. То есть, и тут вы получаете некоторую, пусть и минимальную, но экономию времени.
viordash
20.08.2025 08:16вызывающий несуществующий метод
а если разработка идет в стеке где нельзя вызвать несуществующий метод? Например C\C++\C#. Я поэтому и отошел от принципа сначала тест, потому как глупо писать метод-заглушку, с кучей warnings о не использованных аргументах.
iv660 Автор
20.08.2025 08:16Существует мнение (я за него не агитирую — просто констатирую факт), что ошибка компиляции — это достаточное условие, чтобы считать этап Red завершенным.
Если же этот подход не годится, тогда вообще все равно, что писать сначала — тест или метод заглушку.
В любом случае, даже если вы будете догматично следовать принципам TDD, будет либо чуть лучше (не потратите время на заглушку), либо так же. Ничего не потеряете.
Поскольку я работаю на стеке, который позволяет так делать, меня вариант с ошибкой вызова несуществующего метода вполне устраивает.
Throwable
20.08.2025 08:16Реальность: TDD — это способ проектирования.
Ну вот ни разу. Тем более, что вы сами себя опровергли в п10. TDD к проектированию имеет такое же отношение как автомастерская к автопроизводителю. Простой, чистый и понятный код -- это и есть способ проектирования.
Реальность: TDD экономит время на отладке и переосмыслении.
TDD обнаруживает часть ошибок до того, как они будут репортированы клиентом. Тесты не инвестиция, а мусор, кодовая база которого может до 10х раз превышать бизнес код. Инвестиция -- функционал и качественный код, к которому TDD не имеет никакого отношения. Говнокодить на ура можно и по TDD.
Код становится проще, понятнее и устойчивее к изменениям. Возникает меньше багов, проще менять требования.
Вот эта основная херь, на которую ведутся миллионы. Все с точностью до наоборот. Мелкие итеративные шаги ведут к многократному покрытию одного фрагмента. Поэтому минимальное изменение требований функционала валит стопицот тестов, ковырять которые приходится долго и рутинно. Поэтому любой рефакторинг вообще кладется в долгий ящик, т.к. никому не уперлось возиться мегабайтами свалившихся тестов, при всем при том, что это лишь внутреннее архитектурное изменение, которое не трогает заявленный функционал.
Реальность: Маленькие шаги позволяют проектировать поведение.
Для того, чтобы построить небоскреб, нужно сложить кирпичи в стену и проверить ее на прочность. В следующей итерации подумаем что с этим делать. Проектирование поведения происходит в бизнес коде. Тесты -- для фиксирования поведения и проверки корректности (соответствии поведения заявленному контракту). А уж никак не для проектирования.
Вывод: пишите функциональные тесты, которые проверяют внешнее поведение (API, спецификацию, т.д.). Избегайте многократного покрытия. Проектируйте без TDD.
iv660 Автор
20.08.2025 08:16Понимаю ваши аргументы, но тут вопрос именно в терминах. Когда я пишу «TDD — это способ проектирования», речь не про «архитектурное проектирование» на уровне системы, а про итеративное проектирование поведения кода. Маленькие шаги в TDD — это не про «построить небоскреб кирпичами», а про уточнение интерфейсов и контрактов по мере их написания.
Что до «мусорных тестов» — да, при механическом следовании «пиши тесты вперед» легко получить горы ненужного кода. Но это не цель и не сущность TDD. Суть в том, чтобы тесты были инструментом мышления и проверки гипотез. Если тесты не помогают, а мешают — это уже не TDD, а просто неудачная реализация практики.
Поэтому возражение «проектируйте без TDD» работает только если понимать TDD исключительно как «наклепать кучу мелких юнит-тестов». Но в изначальном смысле TDD — это как раз дисциплина, которая помогает проектировать код так, чтобы он был удобен для изменения и расширения.
Throwable
20.08.2025 08:16Да, я понимаю. Но в этом и состоит основная проблема. Сначала вы пишите мелкие компоненты, тестируете каждый из них. Затем тестируете из связку. Затем добираетесь до функционального уровня, например API и тестируете уже его. В итоге функционал одного и того же компонента покрывается многократно, что затрудняет будущий рефакторинг и вообще любые изменения. При всем при том, что ваш внутренний дизайн никому особо не упал, важно, чтобы был соблюдён контракт на уровне API.
viordash
20.08.2025 08:16нужно сложить кирпичи в стену
вот как раз юнит-тест и гарантирует что ваш кирпич правильной формы, и он гарантировано встанет ровно и надежно вместе с другими кирпичами.
А зачем покрывать один и тот же функционал в разных тестах? Честно говоря не пойму для чего. Для меня, функциональное тестирование, бывает нужным, если в связке есть либа\система которая не вызывает доверия. А если все модули уже надежны, то функциональное тестирование это так сказать "шлифовка". Но предполагаю, что это все зависит от области применения.
ws233
20.08.2025 08:16Пирамида тестирования утверждает, что каждая единица поведения должна быть проверена ровно 1 раз и на том самом нижнем уровне, на котором её можно проверить. Не все можно проверить на самом нижнем уровне – уровне модульных тестов без интеграций. Но если уж вы проверили что-то на более низком уровне, то на более высоком дублирующих тестов быть не должно, это же очевидно.
Как результат, ситуация "функционал одного и того же компонента покрывается многократно" невозможна. Более того, если тесты пишутся именно на поведение, а не на детали внутренней реализации, то проблем с рефакторингом не будет. Почему, очень хорошо объяснено в книге Хорикова про модульное тестирование.
Throwable
20.08.2025 08:16Пирамида тестирования не указывает, что такое единица поведения и где провести адекватную границу. Некоторые следуют правилу один класс -- один тест. Некоторые тестируют геттеры-сеттеры, тогда как другие -- полностью загруженное приложение. Третьи -- делают "срезы" вообще по принципу быстро (юнит) -- медленно (интеграционный) и тупо мокают базу данных. Поэтому если есть возможность выделить и изолировать функциональную единицу как модуль, то здесь и должен проходить уровень тестов.
Если же вы работаете по классическому TDD, то вы как правило пишите тест для каждого из компонентов вне зависимости от функционала и уровня, ибо для тестов уровня модуля будет слишком большая итерация. Отсюда берется многократное покрытие.
ws233
20.08.2025 08:16Вы правы, подходов много.
Но все же пирамида тестирования утверждает, что тест должен быть реализован на самом нижнем уровне, на котором это возможно. (Иначе пирамиды не выйдет, очевидно же?)
Пример. Валидация введенной суммы перевода.
Можно проверять прям от UI через сервер и до появления красной картинки обратно на UI. Получится сложный, медленный, дорогой, хрупкий e2e-тест, который тестирует поведение.
Но самый ли нижний это из возможных уровней, где проводится валидация?
Нет, конечно!
Мы можем убрать UI и проверять, допустим, что вход в функцию валидации какого-то значения, возвращает или не возвращает ошибку (которая потом бы в UI превратился в красную картинку). Так мы опустили проверки на уровень ниже. Проверяется ли при этом нужное поведение? Да, если к нему добавить 2 интеграционных теста, которые проверят, что значение из UI корректно в функцию записывается, а её возвращаемое значение корректно в UI устанавливается.
Что мы получили? Интеграция проверена 1 раз. Валидация введенного знания - 1 раз. Многократного покрытия нет.
Что касается TDD, то, наверное, Вы правы, что сначала будет написан один высокоуровневый e2e тест. Но ничто не мешает Вам на стадии рефакторинга рефакторить не только код, но и тест (или мешает? вопрос автору статьи, кстати @iv660 подразумевает ли рефакторинг и рефакторинг ранее написанного теста? ), разбивая 1 большой тест на несколько меньших, опуская все возможные проверки на более низкие уровни. Так вы избежите многократного тестирования и получите надежные, дешевые, быстрые тесты, которые не боятся рефакторинга, ибо каждый ваш тест будет соответствовать тому уровню, на котором реализовано соответствующее поведение.
У нас вроде работает \Разведенные в сторону руки/
skovoroad
20.08.2025 08:16Для того, чтобы при тестировании связки не закладываться на функциональность каждого мелкого компонента, вроде бы тыщу лет назад и придумали интерфейсы и DI?
В результате функционал одного и того же компонента не покрывается многократно, потому что в тестах связки участвует не сам этот объект, а его мок.
Throwable
20.08.2025 08:16Это все сферический конь в ваккууме. Как это бывает реально:
Вы тестируете компонент. DI вам говорит, что компонент завязан на entity репозиторий базы данных. И вы здраво хотите его мокнуть. В репозитории стопицот queries. Нужно ли их всех мокать? Нет. В итоге вы судорожно пытаетесь найти какие из них реально используются компонентом, чтобы отмокать только их. Потом замечаете, что некоторые
findByXXX()
репозитория параметризированы, и пытаетесь разобраться в SQL логике, чтобы точно определить что же делает база. Затем в тесте пытаетесь симулировать какой-то сценарий. В итоге получается 'given' простыня из предусловий и моков, понятная только вам, здесь и сейчас. Ни один читающий после вас эту лабуду не поймет на лету почемуfindByXXX()
должен возвратить именно 42. После многочисленных попыток отдебажить сценарий и установить правильное поведение моков, вы, наконец, получаете зеленый тест, запускате приложение и... бум! ничего не работает. Ошиблись в моке, не учли поведние бд, ретроградный меркурий, еще что-нибудь.На следующей неделе компоненту вместо
findByXXX()
нужно использовать более продвинутыйfindByYYY()
. Одна строчка изменений в бизнес коде и двести рутинных поправок в тестовых моках.
iv660 Автор
20.08.2025 08:16Вы сами очень точно сказали:
Тесты -- для фиксирования поведения и проверки корректности (соответствии поведения заявленному контракту).
Я бы уточнил: для фиксирования требований и проверки соответствия этим требованиям. Когда вы сначала формулируете требования, а только потом проектируете, — у вас есть все шансы этим требованиям соответствовать. В этом и состоит суть TDD.
DasMeister
20.08.2025 08:16Мне вот любопытно. А сколько вы проработали на одном месте, где потворствовали идее писать функциональные тесты. Лет 5-10 удалось хотя бы?
Вот прямо на месте написания кода, а не повышения в тимлиды, архитекоры, руководители групп и так далее?
Текст вашего комментария просто в каждом пункте демонстрирует не понимание проблем которые таким образом создаются.Простой, чистый и понятный код -- это и есть способ проектирования.
Не существует простого и чистого и понятного кода.
Код бывает или примитивный - т.е. многословный и монотонный, что приводит к пропуску ошибок - т.е. не бывает понятным (только создаёт такое впечатление). Либо бывает с когнитивной нагрузкой - библиотеками и/или DSL. Что сразу перестаёт быть "простым", зато становится понятным.
Чистый код, это сотни итераций рефакторинга, которые и создают в итоге элегантность и чистоту. Но хотелось бы посмотреть как вы отрефакторите какой-то компонент и воспользуетесь "функциональными тестами" и хотелось бы глянуть, сколько времени это займёт.TDD обнаруживает часть ошибок до того, как они будут репортированы клиентом.
Тесты не обнаруживают ошибки. Тесты фиксируют поведение и ожидания - создают контракт. Они документируют то, что ожидается от кода. Это и есть контракт вашего компонента, а не "соблюдение API".
Потому что если вам репортирует клиент - он может уже быть мертвым в некоторых ситуациях. А в других - его бизнес будет мертв.Поэтому любой рефакторинг вообще кладется в долгий ящик, т.к. никому не уперлось возиться мегабайтами свалившихся тестов, при всем при том, что это лишь внутреннее архитектурное изменение, которое не трогает заявленный функционал.
Я зарабатываю на рефакторинге. Каждый раз когда мне нужно что-то отрефакторить я беру это и делаю. А вот проекты у которых 0% покрытия и 5 лет разработки, после которой кодер стал архитектором - в них вот никогда ничего не рефакторят.
т.к. никому не уперлось возиться мегабайтами свалившихся тестов, при всем при том, что это лишь внутреннее архитектурное изменение, которое не трогает заявленный функционал
Всё верно. Именно поэтому существует Open Close Principe. Который говорит - создай новое поведение, а не меняй старое. Если комонент перестает работать в рамках контракта тест упадёт, т.к. функционал компонента - изменился. Создайте новый компонент и выведите старый из эксплуатации.
Изменение же поведения 1 компонента не приводит к развалу всего стека, только самого компонента и i9n теста. Ну. Если вы умеете конечно программировать и писать чистый код, о котором упоминали. Тот у которого всегда - Single Responsibility. Благо это не сложно, для тех кто в продуктовых командах то работает.Throwable
20.08.2025 08:16Не существует простого и чистого и понятного кода.
Если ваша команда не может генерировать простой и понятный код, или же вы используете, средства разработки, которые не способствуют простой и понятной огранизации кода и помимо бизнес логики привносят стороннюю когнитивную нагрузку, то боюсь, никакое TDD вам здесь не поможет. Весь ваш проект с самого начала -- это мусор.
Чистый код, это сотни итераций рефакторинга, которые и создают в итоге элегантность и чистоту.
Как правило за рефакторинг никто вам платить не собирается. Продакт всегда требует новых фич. Если команда изначально создает технический долг, в надежде, когда придут хорошие времена и можно будет его спокойно отрефакторить, то они ошибаются -- такие времена никогда не настанут. Проще будет переписать весь продукт с нуля.
Они документируют то, что ожидается от кода. Это и есть контракт вашего компонента,
Полная херь (не в обиду вам). Документирует документация. Контракт определяется дизайном, и такими средствами как WSDL, OpenAPI, UML, etc. А вот тесты могут проверить соответвтвие имплементации дизайну. Ибо никому в здравом уме не впилось разбираться с вашими
whenSomeShitHappend_thenShouldReturnXXX()
, написанными на кривом языке с кучей непонятных предусловий. Есть конечно BDD и жесткая спека, когда вы пишите софт для NASA, но это, думаю, не ваш случай.Потому что если вам репортирует клиент - он может уже быть мертвым в некоторых ситуациях. А в других - его бизнес будет мертв.
Тем не менее, все конторы тестируют на хомячках. А по поводу бизнеса и к великому сожалению даже жизни, поймите одно простое правило -- всем пофиг. Защита бизнеса клиента -- дело рук самого клиента (SLA, fallback, etc.). В большинстве же случаев клиентский сектор уже настолько приспособился к случайным сбям и ошибкам, что это перестало быть проблемой.
А вот проекты у которых 0% покрытия и 5 лет разработки, после которой кодер стал архитектором - в них вот никогда ничего не рефакторят.
Не совсем понимаю почему 0% покрытия? Я говорил о функциональных тестах, которые тестируют лишь заявленный функционал, а не внутреннюю кухню. Например, API тесты микросервиса. Для последующего рефакторинга это как раз идеально -- вы смело можете хоть поменять базу данных, хоть перелопатить весь внутренний код, не боясь, что это затронет консьюмеров.
Изменение же поведения 1 компонента не приводит к развалу всего стека, только самого компонента
Это наивное идеализированное представление о реальной разработке. Уверяю вас, что уже на следующей итерации вы получите новое бизнес-требование, причем тривиальное, которое как серпом по яйцам пройдется по всей вашей стройной архитектуре, требуя перелопатить большинство контрактов, начиная от API и кончая базой. И вместе с этим еще стопицот бесполезных тестов.
ws233
20.08.2025 08:16Для конкретизации п.5 вот даже целую детальную статейку набросали, почему так выходит...
iv660 Автор
20.08.2025 08:16Спасибо за ссылку — хороший кейс в поддержку п.5. Статья говорит про модульные тесты (не про TDD напрямую), но выводы те же: ранняя фиксация требований и быстрый фидбек сокращают общий цикл.
ws233
20.08.2025 08:16Ну, Вы сами написали, что TDD можно писать с любыми тестами. Но пирамида тестирования утверждает, что модульных (юнитов) должно быть больше всего. Поэтому замените в названии статьи модульные тесты на TDD - логическая цепочка не нарушится, смысл не поменяется.
Krasnoarmeec
20.08.2025 08:16TDD хороша уже тем, что она есть.
Поясню. Если нашлась ошибка, то я для её фиксации пишу тест, который, конечно же, поначалу падает. Ну, правлю код так, чтобы не падал, это очевидно. Но если архитектура построена так, что там нет места тестам, то получается сплошная боль - тестирование только вручную, а это медленно и не гарантирует, что следующая правка кода не разрушит функциональность, ранее протестированную вручную.
agoncharov
20.08.2025 08:16приходится писать тесты, продумывать интерфейсы, проводить рефакторинг. Но эти усилия окупаются уже в среднесрочной перспективе. Код становится проще, понятнее и устойчивее к изменениям
Как это окупается по сравнению с подходом когда вы сначала реализуете, а потом фиксируете поведение тестом? Ведь пока вы над реализацией работать не закончили, вы будете переделывать свои тесты множество раз, никогда не видел чтобы было по другому.
Krasnoarmeec
20.08.2025 08:16Попробую ответить за автора статьи.
Потому что, когда после написания приложения озаботитесь тестами, архитектура приложения будет такой, что просто так тестов в неё уже не встроить. Многочисленные зависимости от других модулей, требующие моков, зависимость от железа, не предусматривающая режима симуляции - это только то, что пришло в голову за минуту.
agoncharov
20.08.2025 08:16Я глубоко убежден что если архитектуру нужно подстраивать под тесты — то нужно выбрасывать к чертям такие тесты
Krasnoarmeec
20.08.2025 08:16Моё мнение, что если архитектура не позволяет встраивать в себя тесты без танцев с бубном (обилие моков и DI), то надо пересматривать архитектуру (как не отвечающую принципам SOLID).
agoncharov
20.08.2025 08:16Если тестам нужна какая-то специально спроектированная под них архитектура, значит тесты на завязаны на детали реализации и тестируют не то что нужно
iv660 Автор
20.08.2025 08:16А вот с этим утвреждением я полностью согласен.
Но мы говорим не о «специально спроектированной архитектуре», а об архитектуре, допускающей достаточную степень гибкости.
GidraVydra
20.08.2025 08:16Представьте, что вы специалист QC на заводе электроники, и вам приносят на проверку залитую компаундом плату без тестовых пинов, и говорят: тестируй. Вы говорите, что по ISO номер ХХХ такие платы надо тестировать через JTAG, а вам в ответ: выбрасывай к чертям свои тесты и свой ISO и изобретай новые, мы ничего в архитектуре платы менять не будем. Норм тема, а?
Zukomux
20.08.2025 08:16Это называется по-другому - я что-то накодил такое, что нормально нельзя покрыть юнит-тестами, но переделывать не буду, т.к. тесты отстой, а я гений!
dkuzminov
20.08.2025 08:16На самом деле последовательность изначально была другая. Вначале шли требования к продукту. Если вы изначально не зафиксировали, что вы хотите получить, то вы получите "что-то", и оно будет пахнуть. Требования -- это не всякое утвердление, а недвусмысленное, понятное, реализуемое, и, главное, тестируемое.
Если есть требования перед началом работы -- можете кодировать без тестов, они появятся после. Ведь ваши требования, как я писал выше, тестируемые, то есть вы представляете, как вы будете проверять соответствие продукта требованиям? Значит и архитектуру менять не придется.
С появлением разных сортов эджайла requirement books ушли в прошлое. Сама необходимость никуда не отпала, но по факту этого стали избегать (хотя эджайл как раз настаивает на наличии требований, даже при том, что они будут меняться). В итоге, поскольку необходимость тестирования все-таки никто не отрицал, требования решили заменить тестами, но писать их заранее. А отсюда уже и влияние их на архитектуру.
Ну а если вам по душе ничем не ограниченная свобода действий кодировать то, что пришло в голову только с утра, и так каждый день... то выбрасывать, скорее всего, придется не тесты, а продукт.
iv660 Автор
20.08.2025 08:16Тесты — не самоцель (каковую мысль я также пытался донести в своей статье).
Если у вас возникают проблемы со встраиванием тестов, это индикатор того, что с архитектурой не все в порядке. Если у вас возникают проблемы со встраиванием тестов, то у вас возникнут проблемы и со встраиванием нового функционала, и изменением имеющегося, и с рефакторингом.
qeeveex
20.08.2025 08:16Я глубоко убежден что если архитектуру нужно подстраивать под тесты — то нужно выбрасывать к чертям такие тесты
Разбитую на слои архитектуру априори легко тестировать. Самим же потом удобно будет делать доработки.
Если у вас связанный код не покрытый тестами, то любые изменения в коде могут тянуть за собой проблемы. Самый прикол что они вылазят в пограничных состояниях, которые как раз легко тестировать unit тестами.
Krasnoarmeec
20.08.2025 08:16Про переделку тестов.
Да, иногда их надо переделывать. Зависимости всякие менять, но это нормально.
Мне в основном приходится работать со всякими моделями. И, если модель меняется, то меняются и выходные значения. Вот тут тесты просто спасение - можно посмотреть где рушится и на сколько изменилось значение. Требовалось получить 10 см, а модель выдала 10 нм - тут точно что-то пошло не так. Ну, а если вместо 10 см выдала 10.1 см, то тоже стоит проверить модель на вшивость, хотя и без особого фанатизма.
iv660 Автор
20.08.2025 08:16На практике тесты приходится менять крайне редко. Если вы пишете тесты так, чтобы они отражали требования к системе, и в процессе работы над реализацией вам захотелось изменить тест, задумайтесь - а действительно ли дело в тесте? Чаще всего оказывается, что вы неправильно пишете реализацию.
agoncharov
20.08.2025 08:16Просто разработка это итеративный процесс. Как в точности компоненты должны взаимодействовать между собой в полной мере становится понятно только по ходу разработки. Потому что по ходу дела выясняются новые нюансы, уточняются требования и т.д.
iv660 Автор
20.08.2025 08:16Да, именно поэтому взаимодействие между компонентами удобно организовывать через ограниченное количество точек взаимодействия (A. K. A. интерфейсов).
Если требования меняются в ходе разработки, мы скорее добавляем в компоненты новые интерфейсы, а не меняем старые. Те интерфейсы, реализации и, соответственно, тесты, которые теряют актуальность, просто удаляем.
mvv-rus
20.08.2025 08:16Если требования меняются в ходе разработки, мы скорее добавляем в компоненты новые интерфейсы, а не меняем старые.
А почему бы эти интерфейсы не поменять, пока никакие другие модули, кроме тех, которые разрабатываете персонально вы, пока от них не зависят? Зачем так стого держаться первоначального выбора, если вы в нём ошиблись? Нелогично IMHO.
Те интерфейсы, реализации и, соответственно, тесты, которые теряют актуальность, просто удаляем.
То есть, работаем на мусорную корзину? Думаю, начальники будут в восторге! (на самом деле, нет)
iv660 Автор
20.08.2025 08:16Я имел в виду под интерфейсами очень мелкие единицы — например, отдельный метод публичного API. Они изолированы и покрывают минимальное число требований. Поэтому менять контракт чаще всего просто не имеет смысла: проще и безопаснее заменить его новым, а старый удалить, когда он теряет актуальность.
Ключевой момент в том, что интерфейс отделен от реализации. Добавление нового метода не требует больших усилий и обычно не ломает существующую систему. В реальных проектах редко бывает так, что и реализацию интерфейса, и клиентский код пишет один и тот же разработчик. Поэтому «пока от него никто не зависит» — ситуация скорее исключительная.
Конечно, «чаще всего» не значит «всегда». Если изменения действительно минимальны и есть стопроцентная уверенность, что интерфейс еще нигде не используется (или что все места легко скорректировать), то изменить его допустимо. Но в общем случае добавление нового метода куда безопаснее, чем переписывание старого — именно поэтому я и делаю на это акцент.
aamonster
20.08.2025 08:16Сложное какое-то объяснение... Лучше на практике въехать: когда попадаешь в ситуацию, что без тестов требуемый функционал ну никак не вырисовывается (пишешь, запускаешь, 100500 действий, чтобы попробовать – нет, не то, и так раз за разом) – очень быстро соображаешь и про tests first, и про то, как их лучше писать.
grand1987
20.08.2025 08:16Я бы добавил еще один пункт: код ревью. Код ревью пересекается с другими пунктами отмеченными автором. Я например делаю код ревью по тестам на сам код. Нет отчета ТДД с покрытием изменений, код не пройдет.
iv660 Автор
20.08.2025 08:16Код-ревью — тоже важная штука.
Я не рассматривал его в статье по той причине, что он работает на более крупном уровне, чем TDD.
Об этом я также упоминал в ответе на комментарий по поводу Agile.
mvv-rus
20.08.2025 08:16Тесты здесь выполняют роль спецификаций, а не проверки.
Не кажется ли вам, что тесты - это слишком громоздкий и трудоемкий способ писать спецификации, по сравнению с другими способами писать спецификации? Мне - кажется. И главное - что это ещё и слишком трудоемкий способ править спецификации, при том, что при написании нетривиальной программы, в которой сразу не очевидно разбиение на модули и определение функций и зон ответственности этих модулей, спецификации нередко приходится корректировать в процессе разработки.
Другое дело, когда архитектура пограммы в целом уже ясна, и нужно не ошибиться в многочисленных деталях реализации (типа правильности перемещения данных из JSON в БД и обратно, с надлежащими проверками) : там IMHO да, специификации в виде теста уместна - если вообще принято решение тесты писать (трудоемкие они).
ws233
20.08.2025 08:16Нет, не кажется. Это лишь частично спецификация. Второй частью – это автоматизация проверки соответствия результата этой спецификации. Т.е. заменять тесты только спецификацией – в корне неверно. Вы еще обязаны для полного замещения организовать автоматизацию на другой тип написания спецификации. Если другие типы написания спецификации и будут проще, то вот их автоматизация – навряд ли. Соответственно и сумма спецификации в другом типе и её автоматизации выйдет дороже, чем тесты.
Проблема изменения тестов при изменении внутренней реализации давно решена: тестируются не детали внутренней реализации, а единица поведения. Подробнее в книге Хорикова про модульное тестирование.
mvv-rus
20.08.2025 08:16Второй частью – это автоматизация проверки соответствия результата этой спецификации. Т.е. заменять тесты только спецификацией – в корне неверно.
А может, лучше "суп отдельно, а мухи отдельно" - в смысле спецификация отдельно, а проверка ее отдельно? Потому что объективно нужны эти вещи в разные моменты времени: спецификация - в процессе создания программы, а тесты - уже на этапе контроля, когда программа создана. И потому что в процессе написания программы спецификации ее частей подвержены изменениям. А потому для работы со спецификаций лучше иметь что-нибудь более легковесное.
А использование тестов в качестве спецификации имеет тот недостаток, что тесты - это код. Код по сравнению с текстом слишком многословен, то есть имеет больший объем, причем объем кода тестов (в строках и знаках) обычно в разы больше объема покрываемого ими кода приложения (правда сложность - цикломатическая и т.п. - кода тестов обычно меньше). В коде, опять же, надо уделять внимание соблюдению церимониалов используемых языков/библиотек/фреймворков (boilerplate),. Код, даже "самодокументируемый", труднее читать, чем текстовое описание, наконец.
Вы еще обязаны для полного замещения организовать автоматизацию на другой тип написания спецификации.
А она точно нужна, эта автоматизация? А то вот мне тут сразу вспоминается акроним YAGNI. Или - Леонов в роли Уэфа, с фразой "А Скрипач не нужен, родной." Потому что спецификация склонна меняться в течение процесса первичной разработки, и чем менее программа тривиальна, тем эти изменения неизбежнее. А проверка реально нужна после завершения этапа первичной разработки, грубо говоря - один раз (не, я в курсе про регрессионное тестирование, но оно будет не на этом этапе, а потом). Так что часто имеет смысл сэкономить на сложности работы со спецификацией в процессе разработки, а не на завершающем этапе. А один раз тесты можно и вручную написать. Или, если спецификация сделана на чем-то распространенном, типа OpenAPI AKA Swagger - использовать кем-то уже сделанные средства автоматического написания тестов по спецификации. Ну, а в наше время - использовать суррогат таких средств на базе LLM: говорят (я про это пока ничего сказать не могу, ибо на грабли с тестами от LLM самолично не понаступал), что с написанием тестов LLM справляется прилично.
Проблема изменения тестов при изменении внутренней реализации давно решена: тестируются не детали внутренней реализации, а единица поведения. Подробнее в книге Хорикова про модульное тестирование.
Мне даже нравится чем-то этот ваш оптимизм - что проблема решена. Решена-то она на бумаге на бумаге. А забыли про овраги: в процессе начальной разработки - всего приложения или какой-то отноительно самостоятельной его функции, проводимом без предварительного детального проектирования (то есть - в обычном практическом случае) меняться может многое, почти всё. В том числе и поведение, и разбиение его на единицы. И уж всяко - интерфейсы, через которые взаимодействуют модули. И чем менее тривильна разрабатываемая программа, тем чаще такие изменения происходят.
PS А вот когда реализация программы тривиальна (типа какого-нибудь проверить какой-нибудь очередной JSON отправить его на обработку, получить результат и вернуть его) и думать не надо, а что делать понятно заранее - там можно и с TDD заморочиться. Другой вопрос, правда - а оно вам хочется заниматься такой работой? И не пугает ли вас при этом перспектива замены на AI?
ws233
20.08.2025 08:16А может, лучше "суп отдельно, а мухи отдельно" - в смысле спецификация отдельно, а проверка ее отдельно? Потому что объективно нужны эти вещи в разные моменты времени: спецификация - в процессе создания программы, а тесты - уже на этапе контроля, когда программа создана.
2 момента:
Тесты нужны ДО создания программы и ВО ВРЕМЯ её создания тоже. Об этом вся статья про TDD, в комментах к которой мы общаемся (например, пп.1,6,7). Об этом же и Ваш пункт про СПЕЦИФИКАЦИЮ, который мы сейчас обсуждаем. СПЕЦИФИКАЦИЯ подразумевает, что она должна быть написана ДО. Значит, и тесты должны быть написаны ДО.
Также Вы выше пишите, что тесты нужны только на стадии ФИНАЛЬНОГО контроля. Но вот теория менеджмента подразумевает, что контроль бывает не только финальный. Он еще бывает входящий (ДО начала выполнения задачи, т.е.разработки программы), промежуточный и переодический (последние 2 - ВО ВРЕМЯ разработки программы).
Отсюда снова следует, что тесты должны быть созданы как минимум ДО начала работы программы, если мы их хотим использовать для входного контроля, или ВО ВРЕМЯ, если хотим использовать промежуточный и/или переодический.
Автору в п.4 и отдельным пунктом можно добавить про типы контроля и как TDD помогает про них не забывать...Конечно нужно разделять!
Разделение мух от котлет выглядит так: фиксируем мы требования и поведение через написание теста, а контролируем потом через его исполнение. Вас не устраивает такое разделение?
Код по сравнению с текстом слишком многословен, то есть имеет больший объем, причем объем кода тестов (в строках и знаках) обычно в разы больше объема покрываемого ими кода приложения (правда сложность - цикломатическая и т.п. - кода тестов обычно меньше). В коде, опять же, надо уделять внимание соблюдению церимониалов используемых языков/библиотек/фреймворков (boilerplate),. Код, даже "самодокументируемый", труднее читать, чем текстовое описание, наконец.
Ниже в этой же ветке уже ответили, что это не всегда так. Я лишь добавлю, что перечисленные Вами "недостатки" давно придумано как нивелировать и устранять. Учитывая это я бы даже сказал, что запись спецификации в виде тестов ДОВОЛЬНО ЧАСТО получается даже дешевле и проще, чем обычным человеческим текстом.
Отсюда мы возвращаемся к тезису про более дешевую сумму записи спецификации и её автоматизации и получаем, что автотесты становятся практически самым дешевым и эффективным вариантом, как бы со стороны не казалось иначе. Такой вот парадокс, да.А она точно нужна, эта автоматизация?
Про это п.4. И мои комментарии выше. Я тоже LLM не использовал для генерации тестов, но прекрасно знаю некоторые своды правил, которые позволяют сделать так, что требования из ТЗ, записанные в виде кода теста, получаются проще, чем в тексте самого ТЗ. Возможно, Вы умеете писать ТЗ круче наших аналитиков (в этот вопрос я с вами в дискуссию вступать не стану).
А забыли про овраги: в процессе начальной разработки - всего приложения или какой-то отноительно самостоятельной его функции, проводимом без предварительного детального проектирования (то есть - в обычном практическом случае) меняться может многое, почти всё. В том числе и поведение, и разбиение его на единицы. И уж всяко - интерфейсы, через которые взаимодействуют модули. И чем менее тривильна разрабатываемая программа, тем чаще такие изменения происходят.
Ну, я же не зря Вам целую книгу порекомендовал, наверное? Вот Вы спорите даже не прочитав, похоже. Собственно, как и всю статью... че уж я про книгу-то сетую...
Пожалуй, на этом я остановлюсь, на дальнейшие Ваши комментарии отвечать не стану. Спасибо Вам за содержательную дискуссию.
iv660 Автор
20.08.2025 08:16Код по сравнению с текстом слишком многословен, то есть имеет больший объем, причем объем кода тестов (в строках и знаках) обычно в разы больше объема покрываемого ими кода приложения (правда сложность - цикломатическая и т.п. - кода тестов обычно меньше).
Давайте сравним.
Спецификация метода
sum(a, b)
Описание
Метод
sum(a, b)
принимает два целых числа и возвращает их сумму.Параметры
a
(int): Первое целое число.b
(int): Второе целое число.
Возвращаемое значение
(int): Сумма двух целых чисел
a
иb
.
Предусловия
Параметры
a
иb
должны быть целыми числами.
Постусловия
Метод возвращает целое число, которое является результатом сложения
a
иb
.
Исключения
Если
a
илиb
не являются целыми числами, метод должен выбрасывать исключениеTypeError
.
Тесты (достаточные для разработки этого метода в соответствии с принципами TDD и требованиями спецификации)
Function test_sum_with_two_integers() result = sum(3, 5) assert result == 8 Function test_sum_with_non_integer_a() try sum(3.5, 5) expect TypeError Function test_sum_with_non_integer_b() try sum(3, "5") expect TypeError
ws233
20.08.2025 08:16простите, но Вы забыли постусловие с переполнением и тест на него.
а еще тесты бы граничными условиями дополнить, чего не написано даже в ТЗ (а должно быть, как программа себя ведет в граничных случаях?).sum(-MAX_INT, -MAX_INT)
- это переполнение или успех?
а в остальном - все отлично!
iv660 Автор
20.08.2025 08:16К комментарию выше мне остается только добавить, что далеко не всегда описать требуемое поведение словами будет проще и дешевле, чем оформить это поведение в виде теста.
dkuzminov
20.08.2025 08:16И снова я про Cynefin Framework. TDD идеально с ним согласуется. Начинаем мы в домене Chaos: фичи нет, и как она должна выглядеть -- мы не знаем. После написания теста мы фактически формулируем требование (с тех пор, как вести Requirements Book стало моветоном, тесты стали единственным адекватным способом сформулировать требования). Теперь мы знаем, чего хотим, но не знаем, достижимо ли это: мы в домене Complex. В процессе первой "грязной" реализации мы фактически изучаем предмет: можно ли реализовать фичу, какие есть сложности, правильно ли сформулировано требование на предыдущем этапе... В конце, когда тест наконец проходит, мы приходим к пониманию и того, чего мы хотим, и того, как это нужно реализовывать. Остается выбросить весь грязный код и оставить только это кровью и потом добытое понимание: поздравляю, мы в домене Complicated. А дальше начинается та чистая инженерия, к которой мы, по идее, должны стремиться: что делать -- ясно, как устроена система вокруг нужного кода -- понятно. Остается только взять и написать хороший код. Такой, чтобы читающие его люди (или мы сами через пару лет) оказались бы в домене Clear: чтобы не было ни двусмысленности, ни костылей.
Но на практике мало кто понимает даже что такое тест...
fo_otman
Маленькие шаги - это вроде agile называется, не? Спринты по 2 недели, ценность, ретро, демо для этого и были придуманы, чтобы идти на ощупь в условиях создания чего-то нового, неизвестного и потому непонятного.
noavarice
Маленькое шаги - это принцип, agile можно считать применением этого принципа по отношению к организации разработки в команде. TDD можно считать применением того же принципа по отношению к организации работы отдельно взятого разработчика - разрабатываем продукт в команде, но в конечном счёте код пишет каждый отдельно
Neka_D
Agile буквально переводится как гибкость или маневренность. Все перечисленное было придумано чтобы снизить цикл обратной связи, а так же чтобы с каждой обратной связью улучшать процесс и продукт.
TDD идеально вписывается в этот концепт. Обратная связь моментальная и информативная и ты не движешься дальше вперёд, пока не обработаешь и не примешь во внимание обратную связь.
iv660 Автор
Вы точно подметили сходство.
Agile и TDD — это одна и та же идея, но на разных уровнях: Agile работает на уровне продукта и команды, а TDD — на уровне кода и архитектуры.
grand1987
Как правильно подметел автор статьи, многие неправильно понимают смысл некоторых вещей. Посмотрите, например, лекцию "Дядюшки Боба", одного из авторов Agile и возвожно поймете суть.
Если не хотите слушать автора, то кратко, "Гибкость" не про процессы в компании, а "гибким" должен быть сам програмист. Прошли те времена когда один пишет код, другой его тестирует, а третий думает архитектуру. Сегодня этого недостаточно. Програмист должен быть силен во всех областях и должен сам проверять свой код перед тем как отправить его в прод. От этого, между прочим может зависить чья-то жизнь. Вот про что Agile.