Це обмеження розвитку тестово керованих (і Agile взагалі) практично актуально?


30

У Test Driven Development (TDD) ви починаєте з неоптимального рішення, а потім ітераційно виробляєте кращі, додаючи тестові випадки та рефакторинг. Етапи повинні бути невеликими, це означає, що кожне нове рішення якось буде в сусідстві з попереднім.

Це нагадує математичні методи локальної оптимізації, такі як спуск градієнта або локальний пошук. Загальновідоме обмеження таких методів полягає в тому, що вони не гарантують знайти глобальний оптимум або навіть прийнятний локальний оптимум. Якщо ваша вихідна точка відокремлена від усіх прийнятних рішень великою областю поганих рішень, дістатися до неї неможливо, і метод вийде з ладу.

Якщо бути більш конкретним: я думаю про сценарій, коли ви реалізували цілий ряд тестових випадків, а потім виявляєте, що наступний тестовий випадок потребує конкурентно іншого підходу. Вам доведеться відкинути попередню роботу і почати заново.

Ця думка фактично може бути застосована до всіх спритних методів, які проходять невеликими кроками, не тільки до TDD. Чи має запропонована аналогія між TDD та локальною оптимізацією якісь серйозні недоліки?


Ви маєте на увазі під-техніку TDD під назвою тріангуляція ? Під "прийнятним рішенням" ви маєте на увазі правильне чи доречне / елегантне / читабельне?
guillaume31

6
Я думаю, що це справжня проблема. Оскільки це лише моя думка, відповіді я не напишу. Але так, оскільки TDD рекламується як дизайнерська практика , це недолік, який може призвести до локальних максимумів або взагалі немає рішення. Я б сказав, загалом TDD НЕ підходить для алгоритмічного проектування. Дивіться пов’язану дискусію щодо обмежень TDD: Розв’язування судоку з TDD , в якому Рон Джеффріс робить дупу з себе під час бігу по колах і "робити TDD", а Пітер Норвіг надає фактичне рішення, фактично знаючи про предмет,
Андрес Ф.

5
Іншими словами, я б запропонував (сподіваюся) суперечливе твердження, що TDD добре допомагає мінімізувати кількість класів, які ви записуєте в "відомі" проблеми, тому створює більш чистий і простий код, але не підходить для алгоритмічних задач або для складних проблем, де насправді дивитись на велику картину та володіти доменними знаннями корисніше, ніж писати тестові деталі та "відкривати" код, який ви повинні написати.
Андрес Ф.

2
Проблема існує, але не обмежується TDD або навіть Agile. Зміни вимог, які означають дизайн раніше написаного програмного забезпечення, повинні постійно змінюватися.
RemcoGerlich

@ guillaume31: Не обов’язково тріангуляція, але будь-яка техніка, що використовує ітерації на рівні вихідного коду. До прийнятного рішення , яке я маю в виду той , який проходить всі випробування і може підтримуватися досить добре ..
Frank Puffer

Відповіді:


8

Загальновідоме обмеження таких методів полягає в тому, що вони не гарантують знайти глобальний оптимум або навіть прийнятний локальний оптимум.

Щоб зробити ваше порівняння більш адекватним: для певних проблем алгоритми ітеративної оптимізації, ймовірно, дають хороші локальні оптими, для деяких інших ситуацій вони можуть вийти з ладу.

Я думаю про сценарій, коли ви реалізували ряд тестових випадків, а потім виявите, що наступний тестовий випадок потребує конкурентно іншого підходу. Вам доведеться відкинути попередню роботу і почати заново.

Я можу собі уявити ситуацію, коли це може статися насправді: коли ви вибираєте неправильну архітектуру таким чином, вам потрібно знову відтворити всі наявні тести з нуля. Скажімо, ви починаєте реалізовувати свої перші 20 тестових випадків на мові програмування X в операційній системі А. На жаль, вимога 21 передбачає, що вся програма повинна працювати в операційній системі B, де X недоступний. Таким чином, вам потрібно викинути більшу частину вашої роботи та повторного впорядкування мовою Y. (Звичайно, ви б не викинули код повністю, а перенесли його на нову мову та систему.)

Це вчить нас, навіть при використанні TDD, добре заздалегідь зробити загальний аналіз та дизайн. Однак це справедливо і для будь-якого іншого підходу, тому я не вважаю це притаманною проблемою TDD. І для більшості завдань програмування в реальному світі ви можете просто вибрати стандартну архітектуру (наприклад, мова програмування X, операційна система Y, система бази даних Z на апаратному XYZ), і ви можете бути відносно впевненими, що ітеративна чи спритна методологія, як TDD не приведе вас у глухий кут.

Посилаючись на Роберта Харві: "Ви не можете виростити архітектуру з одиничних тестів". Або pdr: "TDD не тільки допомагає мені прийти до найкращого остаточного дизайну, але допомагає мені потрапити туди за меншу кількість спроб."

Тож власне те, що ви написали

Якщо ваша відправна точка відокремлена від усіх прийнятних рішень великою областю поганих рішень, дістатися до неї неможливо, і метод вийде з ладу.

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

З іншого боку, коли ви робите загальне планування заздалегідь і вибираєте потрібну архітектуру, використання TDD має бути подібним до запуску ітеративного алгоритму пошуку в тій області, де ви можете розраховувати на досягнення "глобального максимуму" (або принаймні достатньо хорошого максимуму) ) за кілька циклів.


20

Я не думаю, що TDD має проблему локальних максимумів. Код, який ви пишете, може бути, як ви правильно помітили, але саме тому рефакторинг (код перезапису без зміни функціональності) діє. По суті, у міру збільшення ваших тестів ви можете переписати значні частини вашої об'єктної моделі, якщо вам потрібно, зберігаючи поведінку незмінною завдяки тестам. Випробування істинних істин щодо вашої системи, які, таким чином, повинні бути дійсними як в локальних, так і в абсолютних максимумах.

Якщо вас цікавлять проблеми, пов'язані з TDD, я можу згадати три різні, про які я часто думаю:

  1. Проблема повноти : скільки тестів необхідно, щоб повністю описати систему? Чи "кодування прикладами випадків" є повним способом опису системи?

  2. Проблема загартовування : незалежно від тестування інтерфейсу, він повинен мати незмінний інтерфейс. Тести представляють інваріантні істини , пам’ятайте. На жаль, ці істини взагалі не відомі для більшості написаного нами коду, у кращому випадку лише для зовнішніх об'єктів.

  3. Проблема з пошкодженням тесту : щоб зробити твердження перевіреними, нам може знадобитися написати неоптимальний код (наприклад, менш ефективний). Як ми пишемо тести, щоб код був максимально гарним?


Відредаговано на адресу коментаря: ось приклад пошуку локального максимуму для "подвійної" функції за допомогою рефакторингу

Тест 1: коли вхід 0, поверніть нуль

Впровадження:

function double(x) {
  return 0; // simplest possible code that passes tests
}

Реконструкція: не потрібна

Тест 2: коли вхід 1, поверніть 2

Впровадження:

function double(x) {
  return x==0?0:2; // local maximum
}

Реконструкція: не потрібна

Тест 3: коли вхід 2, поверніть 4

Впровадження:

function double(x) {
  return x==0?0:x==2?4:2; // needs refactoring
}

Реконструкція:

function double(x) {
  return x*2; // new maximum
}

1
Однак я зазнав, що моя перша конструкція працювала лише для простих випадків, і я пізніше зрозумів, що мені потрібно більш загальне рішення. Розробка більш загального рішення вимагала більше тестів, тоді як оригінальні тести для особливих випадків знову не працюватимуть. Я вважав прийнятним (тимчасово) видаляти ці тести, поки я розробляю більш загальне рішення, додаючи їх ще раз, коли час буде готовий.
5gon12eder

3
Я не переконаний, що рефакторинг - це спосіб узагальнити код (звичайно, поза штучним простором «дизайнерських моделей») або уникнути локальних максимумів. Рефакторинг прибирає код, але він не допоможе вам знайти краще рішення.
Андрес Ф.

2
@Sklivvz Розумів, але я не думаю, що це працює так, як поза прикладами іграшок, як ті, які ви розмістили. Також вам допомогло те, що вашу функцію назвали "подвійною"; таким чином ви вже знали відповідь. TDD безумовно допомагає, коли ви більш-менш знаєте відповідь, але хочете написати "чисто". Це не допомогло б виявити алгоритми або написати дійсно складний код. Ось чому Рон Джеффріс не зміг вирішити судоку таким чином; ви не можете реалізувати алгоритм, який вам не знайомий, використовуючи TDD, випускаючи його з незрозумілості.
Андрес Ф.

1
@VaughnCato Добре, зараз я можу довіряти тобі або бути скептичним (що було б грубо, тому не будемо цього робити). Скажімо, на мій досвід, це не працює так, як ви кажете. Я ніколи не бачив, щоб досить складний алгоритм склався з TDD. Можливо, мій досвід занадто обмежений :)
Андрес Ф.

2
@Sklivvz "Поки ви можете написати відповідні тести" - це саме те, що це звучить як запитання до мене. Я говорю, що ви часто не можете . Подумати про алгоритм чи розв’язувач не полегшиться спочатку написання тестів . Ви повинні спочатку переглянути всю картину . Спроба сценаріїв, звичайно, необхідна, але зверніть увагу на те, що TDD не стосується написання сценаріїв: TDD - це тестовий привід дизайну ! Ви не можете керувати дизайном розв'язувача судоку (або нового рішення для іншої гри), попередньо написавши тести. Як анекдотичний доказ (чого недостатньо): Джеффріс не зміг.
Андрес Ф.

13

Те, що ви описуєте математично, - це те, що ми називаємо малювати себе в кутку. Ця поява навряд чи є винятковою для TDD. У водоспаді ти можеш збиратись та висипати вимоги місяцями, сподіваючись, що ти можеш побачити глобальний макс лише, щоб дістатися та зрозуміти, що є краща ідея лише наступного пагорба.

Різниця полягає в спритному середовищі, яке ви ніколи не очікували бути ідеальним на даний момент, тому ви більше ніж готові кинути стару ідею та перейти до нової ідеї.

Більш специфічний для TDD є техніка, щоб уникнути цього з вами, коли ви додаєте функції в TDD. Це пріоритетне приміщення трансформації . Якщо TDD має для вас формальний спосіб рефакторації, це формальний спосіб додавання функцій.


13

У своїй відповіді @Sklivvz переконливо стверджував, що проблеми не існує.

Я хочу стверджувати, що це не має значення: основна передумова (і причина розвитку) ітеративних методологій в цілому і Agile, особливо TDD, зокрема, полягає в тому, що не тільки глобальний оптимум, але і локальні оптимуми також не є ' t відомо. Інакше кажучи: навіть якщо це була проблема, то в жодному разі неможливо це зробити ітераційним шляхом. Якщо припустити, що ви приймаєте основні умови.


8

Чи можуть обіцяти TDD та Agile створити оптимальне рішення? (Або навіть "гарне" рішення?)

Не зовсім. Але це не їх призначення.

Ці методи просто забезпечують "безпечний перехід" з одного стану в інший, визнаючи, що зміни є трудомісткими, складними та ризикованими. І суть обох практик полягає в тому, щоб програма та код були життєздатними та довели, що вони швидше та регулярніше відповідають вимогам.

... [TDD] протистоїть розробці програмного забезпечення, що дозволяє додавати програмне забезпечення, яке не доведено, щоб відповідати вимогам ... Кент Бек, якому приписують розробку або «повторне розкриття» техніки, заявив у 2003 році, що TDD заохочує просте проектує і вселяє впевненість. ( Вікіпедія )

TDD фокусується на тому, щоб кожен "фрагмент" коду відповідав вимогам. Зокрема, це допомагає забезпечити, щоб код відповідав раніше існуючим вимогам, на відміну від дозволу на те, щоб вимоги визначалися поганим кодуванням. Але, це не обіцяє, що реалізація в будь-якому випадку є "оптимальною".

Що стосується Agile процесів:

Робоче програмне забезпечення є основним показником прогресу ... Наприкінці кожної ітерації зацікавлені сторони та представник замовника переглядають прогрес та переоцінюють пріоритети з метою оптимізації рентабельності інвестицій ( Вікіпедія )

Спритність не шукає оптимального рішення ; просто робоче рішення - з метою оптимізації рентабельності інвестицій . Це обіцяє робоче рішення швидше , ніж пізніше ; не є "оптимальним".

Але це добре, адже питання неправильне.

Оптимали в розробці програмного забезпечення - нечіткі, рухомі цілі. Вимоги, як правило, в нормі і пронизані секретами, які тільки з'являються, на ваш стурбованість, в конференц-залі, повному ваших начальників. А "властивість архітектури та кодування рішення" оцінюється розділеними та суб'єктивними думками ваших колег та ваших управлінських сюзеренів - ніхто з яких насправді нічого не може знати про хороше програмне забезпечення.

У принаймні, TDD і Agile практики визнають труднощі і спробувати оптимізують дві речі , які є об'єктивними і вимірними: . Робоча v Чи не Робоча і рано V Пізніше ..

І навіть якщо ми «працюємо» і «швидше» як об'єктивні показники, ваша здатність оптимізувати їх в першу чергу залежить від майстерності та досвіду команди.


До речей, які ви могли б розтлумачити, як зусилля виробляють оптимальні рішення, включають такі речі, як:

тощо.

Чи справді кожна з цих речей виробляє оптимальні рішення, було б ще одним великим питанням!


1
Щоправда, але я не писав, що мета TDD або будь-який інший метод розробки програмного забезпечення - це оптимальне рішення в розумінні глобального оптимуму. Мене єдине занепокоєння полягає в тому, що методології, засновані на невеликих ітераціях на рівні вихідного коду, у багатьох випадках можуть взагалі не знайти прийнятного (досить хорошого) рішення
Френк Пуффер

@Frank Моя відповідь призначена для охоплення як локальних, так і глобальних оптимістів. І відповідь у будь-якому випадку - "Ні, саме для цього не розроблені ці стратегії - вони розроблені для покращення рентабельності інвестицій та зменшення ризику". ... або щось подібне. І це частково пов’язано з тим, на що доходить відповідь Йорга: "оптимальні" - це рухомі цілі. ... Я б навіть зробив крок далі; Вони не тільки рухаються цілями, але й не є цілком об'єктивними або вимірюваними.
svidgen

@FrankPuffer Можливо, варто доповнити. Але, головне, ви запитуєте, чи досягнуть ці дві речі те, чого вони взагалі не розроблені або не призначені. Більш того, ви запитуєте, чи можуть вони досягти чогось, що навіть неможливо виміряти чи перевірити.
svidgen

@FrankPuffer Bah. Я спробував оновити свою відповідь, щоб сказати це краще. Я не впевнений, зробив це краще чи гірше! ... Але мені потрібно вийти з SE.SE і повернутися до роботи.
svidgen

Ця відповідь гаразд, але проблема, яку я маю з цим (як і деякі інші відповіді), полягає в тому, що "пом'якшення ризику та покращення рентабельності інвестицій" не завжди є найкращими цілями. Вони насправді самі по собі не є цілями. Коли вам потрібно щось попрацювати, пом’якшення ризику не скоротить. Іноді відносно непрямі невеликі кроки, як у TDD, не спрацюють - ви зведете до мінімуму ризик, але в кінцевому підсумку ви не досягнете корисного місця.
Андрес Ф.

4

Одне, чого поки ніхто не додав, - це те, що опис, який ви описуєте, дуже абстрактний та нереальний. Це може бути, як у математичній програмі, де ви оптимізуєте алгоритм, але цього не буває багато в бізнес-програмах, над якими працює більшість кодерів.

У реальному світі ваші тести в основному застосовують і підтверджують бізнес-правила:

Наприклад - якщо замовником є ​​30-річний некурящий з дружиною та двома дітьми, то категорія преміум - "x" тощо.

Ви не збираєтесь ітераційно змінювати механізм розрахунку премії, поки він не буде правильним дуже довго - і майже напевно, поки програма не працює;).

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


1
По-перше, прочитавши вашу відповідь, я подумав «так, це головна суть». Але після переосмислення питання я подумав, що це не обов'язково настільки абстрактно чи нереально. Якщо хтось наосліп вибирає абсолютно неправильну архітектуру, TDD не вирішить цього, не після 1000 ітерацій.
Док Браун

@Doc Браун погодився, це не вирішить цю проблему. Але це дасть вам набір тестів, які здійснюють кожне припущення та правило бізнесу, щоб ви могли ітеративно вдосконалити архітектуру. Архітектура настільки погана, що потрібно випрацювати перезаписати, щоб виправити дуже рідко (я сподіваюся), і навіть у такому крайньому випадку тести одиниць бізнес-правил стануть гарною відправною точкою.
мкот

Коли я кажу "неправильна архітектура", маю на увазі випадки, коли потрібно викинути існуючий набір тестів. Ви читали мою відповідь?
Док Браун

@DocBrown - Так, я. Якщо ви мали на увазі "неправильну архітектуру" на "змінити весь тестовий набір", можливо, ви повинні були це сказати. Зміна архітектури не означає, що вам доведеться скинути всі тести, якщо вони базуються на бізнес-правилах. Ймовірно, вам доведеться змінити їх для підтримки будь-яких створених вами нових інтерфейсів і навіть повністю переписати деякі, але бізнес-правила не заміняться технічними змінами, тому тести залишаться. Безумовно, інвестиції в одиничні тести не повинні бути недійсними через маловірогідну можливість повністю покращити архітектуру
mcottle

звичайно, навіть якщо потрібно переписати кожен тест новою мовою програмування, не потрібно все викидати, принаймні один може перенести існуючу логіку. І я погоджуюсь з вами на 100% для великих реальних проектів, припущення у питанні цілком нереалістичні.
Док Браун

3

Я не думаю, що це заважає. У більшості команд немає когось, хто здатний придумати оптимальне рішення, навіть якщо ви написали це на своїй дошці. TDD / Agile не заважає.

Для багатьох проектів не потрібні оптимальні рішення, а ті, що роблять, необхідний час, енергія та фокус будуть зроблені в цій галузі. Як і все, що ми прагнемо будувати, по-перше, змусити його працювати. Тоді зробіть це швидко. Ви можете зробити це з якимось прототипом, якщо продуктивність настільки важлива, а потім відновити всю справу за допомогою мудрості, накопиченої за багато ітерацій.

Я думаю про сценарій, коли ви реалізували ряд тестових випадків, а потім виявите, що наступний тестовий випадок потребує конкурентно іншого підходу. Вам доведеться відкинути попередню роботу і почати заново.

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

Крім того, поставте це в контексті вашої стурбованості недостатньою оптимізацією, і ви не можете не витрачати час на оптимізацію речей, яких у вас не повинно бути, і на створення негнучких рішень, оскільки ви були настільки гіперцентризовані на їх продуктивності.


0

Застосовувати математичну концепцію на кшталт "локального оптимуму" до розробки програмного забезпечення може бути оманливим. Використовуючи такі терміни, звучання розробки програмного забезпечення звучить набагато більш кількісно і науково, ніж є насправді. Навіть якщо для коду існував "оптимальний", ми не можемо його виміряти, а отже, і не знати, чи досягли ми його.

Швидкий рух був дійсно реакцією проти віри в те, що розробку програмного забезпечення можна планувати та прогнозувати математичними методами. На краще чи гірше, розробка програмного забезпечення більше схожа на ремесло, ніж на науку.


Але чи була це занадто сильна реакція? Це, безумовно, допомагає у багатьох випадках, коли суворе планування заздалегідь виявилося громіздким та дорогим. Однак деякі проблеми з програмним забезпеченням повинні вирішуватися як математична проблема та заздалегідь розроблена. Ви не можете їх TDD. Ви можете TDD інтерфейс користувача та загальний дизайн Photoshop, але ви не можете TDD його алгоритми. Вони не є тривіальними прикладами, такими як отримання «суми» або «подвійного» або «пороху» у типових прикладах TDD [1]). Напевно, ви не можете дражнити новий фільтр зображень із написання деяких тестових сценаріїв; ви абсолютно повинні сісти і написати та зрозуміти формули.
Андрес Ф.

2
[1] Насправді я майже впевнений fibonacci, що я бачив, що я використовував як приклад / підручник з TDD, - це більш-менш брехня. Я готовий поставити під сумнів, що ніхто ніколи не виявив "FD" або будь-яку подібну серію від TDD'ing. Усі починаються з уже знаючого полі, який обманює. Якщо ви спробуєте відкрити це за допомогою TDD'ing, ви, швидше за все, досягнете тупику, про який розпитувала ОП: ви ніколи не зможете узагальнити серію, просто написавши більше тестів і рефакторинг - ви повинні застосувати математичну міркування!
Андрес Ф.

Два зауваження: (1) Ви праві, що це може бути обманом. Але я не писав, що TDD - це те саме, що і математична оптимізація. Я просто використовував це як аналогію чи модель. Я вірю, що математику можна (і слід) застосовувати майже до всього, якщо ви знаєте про відмінності між моделлю та реальною справою. (2) Наука (наукова робота) зазвичай навіть менш передбачувана, ніж розробка програмного забезпечення. І я навіть сказав, що інженерія програмного забезпечення більше схожа на наукову роботу, ніж як ремесло. Ремесла зазвичай вимагають набагато більш рутинної роботи.
Френк Пуффер

@AndresF.: TDD не означає, що вам не доведеться думати або розробляти дизайн. Це просто означає, що ви пишете тест перед тим, як написати реалізацію. Це можна зробити за допомогою алгоритмів.
ЖакБ

@FrankPuffer: Гаразд, яке вимірне значення має "локальний оптимум" у розробці програмного забезпечення?
ЖакБ
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.