Чи мають бути твердження у версії версій


20

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

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

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

assert(ptr != nullptr);

що матиме невеликий вплив на більшість кодів.

Це призводить мене до питання: чи повинні твердження (маючи на увазі концепцію, а не конкретну реалізацію) бути активними у складах випусків? Чому ні)?

Зауважте, що це питання не стосується того, як увімкнути твердження у версіях версій (наприклад, #undef _NDEBUGабо використовувати самостійно визначену реалізацію). Крім того, мова йде не про включення тверджень у сторонній / стандартний код бібліотеки, а в контрольований мною код.


Я знаю глобального гравця на ринку охорони здоров’я, який встановив налагоджену версію своєї лікарняної інформаційної системи на сайтах клієнтів. Вони мали спеціальну угоду з Microsoft про встановлення бібліотек налагодження C ++ і там. Що ж, якість їхнього продукту була ...
Бернхард Гіллер

2
Існує давня приказка, що "зняття тверджень - це щось подібне до того, як носити рятувальну куртку для занять у гавані, але потім залишати рятувальні жилети позаду, коли ваш корабель відправляється у відкритий океан" ( wiki.c2.com/?AssertionsAsDefanishProgramming ) . Я особисто тримаю їх активованими у версіях версій за замовчуванням. На жаль, ця практика не дуже поширена у світі C ++, але принаймні відомий ветеран C ++ Джеймс Канзе завжди використовував аргументи на користь збереження тверджень у версії версій: stackoverflow.com/a/12162195/3313064
Christian Hackl

Відповіді:


20

Класичний assert- це інструмент зі старої стандартної бібліотеки С, а не з C ++. Він все ще доступний на C ++, принаймні з міркувань зворотної сумісності.

У мене немає точної часової шкали стандартних ліфтів C під рукою, але я впевнений, що вона assertбула доступна незабаром після часу, коли K&R C вийшов наживо (близько 1978 року). У класичному C, для написання надійних програм, додавання тестів указівників NULL та перевірки меж масиву потрібно робити частіше, ніж у C ++. Сукупність тестів вказівників NULL можна уникнути, використовуючи посилання та / або розумні покажчики замість покажчиків, а за допомогою std::vectorперевірки меж масиву часто непотрібно. Більше того, хіт вистави в 1980 році був, безумовно, набагато важливішим, ніж сьогодні. Тож я думаю, що це дуже ймовірно, що причина "твердження" була розроблена таким чином, щоб бути активною лише в налагодженнях за замовчуванням.

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

  • перевіряє деякий стан (і зупиняє виконання в області, де умова не вдається)

  • надає чітке повідомлення про помилку, якщо умова не виконується

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

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

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

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

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

Однак у С важко реалізувати хороший, загальноприйнятий стандартний механізм поводження з помилками з усіма згаданими особливостями через відсутність винятків як частини мови програмування. Тож більшість програм C мають власні механізми обробки помилок із поверненнями кодів, або "goto" s, або "long jumpings", або їх сумішшю. Це часто прагматичні рішення, які підходять до певного виду програми, але не мають "загального призначення", щоб вписатись у стандартну бібліотеку С.


Ах, зовсім забув про спадщину С (зрештою її #include <cassert>). Це зараз має сенс.
Ніхто

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

1
@ChristianHackl: Я погоджуюсь, що існують ситуації, коли припинення програми є єдиним життєздатним варіантом, але тоді це має відбуватися і в режимі випуску, а також assertце неправильний інструмент для цього (кидання винятку може бути і неправильною реакцією ). Однак я впевнений, що насправді assertвін також використовувався для безлічі тестів на C, де в C ++ сьогодні використовуються винятки.
Док Браун

15

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

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

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

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

Я говорю не про конкретну реалізацію тверджень, а про концепцію твердження. Я не хочу зловживати твердженням або плутати читачів. Я мав намір запитати, чому саме так в першу чергу. Чому немає додаткового випуску_сервісу? Чи немає в цьому потреби? Яке обґрунтування твердження про відмову у звільненні? - Ніхто

Чому немає relase_assert? Чесно кажучи, тому, що твердження недостатньо хороші для виробництва. Так, є потреба, але нічого не відповідає тому, що потрібно. О, звичайно, ви можете створити свій власний. Механічно ваша функція кидання, якщо потрібно просто розгорнути і виключити, щоб кинути. І це може відповідати вашим потребам. Але ви дійсно обмежуєте дизайн. Ось чому мене не дивує, що у вашій бібліотеці мов немає такої твердження, як система кидання винятків. Це, звичайно, не те, що ти не можеш цього зробити. Інші мають . Але розгляд справи, коли все піде не так, - це 80% роботи для більшості програм. І поки що ніхто не показав нам хороший розмір, який підходить для кожного рішення. Ефективна робота з цими випадками може ускладнитися. Якби у нас була консервована система release_assert, яка не відповідала б нашим потребам, я думаю, це зробило б більше шкоди, ніж користі. Ви просите про гарну абстракцію, яка б означала, що вам не доведеться думати над цією проблемою. Я теж хочу його, але це не схоже на те, що ми ще там.

Чому твердження вимкнено у звільненні? Твердження були створені у розпал епохи кодексу риштування. Код, який нам довелося видалити, бо знав, що ми не хочемо його у виробництві, але знав, що хочемо запустити розробку, щоб допомогти нам знайти помилки. Затвердження були чистішою альтернативою if (DEBUG)шаблону, який дозволив нам залишити код, але відключити його. Це було до того, як тестування одиниць було знято як основний спосіб відокремити код тестування від виробничого коду. Дослідження досі застосовуються навіть тестерами експертних підрозділів як для того, щоб зрозуміти очікування, так і покрити випадки, які вони все-таки роблять краще, ніж тестування.

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


2
Я думаю, моє питання: Чому цей код потрібно видалити перед випуском? Перевірки не є великими збитками від продуктивності, і якщо вони не вдаються, то, безумовно, є проблема, про яку я б віддав перевагу більш прямому повідомленню про помилку. Яку перевагу пропонує використання виключення замість ствердження? Напевно, я не хотів би, щоб незв'язаний код міг catch(...)його мати.
Ніхто

2
@Nobody Найбільшою перевагою в тому, що не використовувати твердження для потреб, які не вимагають, є не бентежити читачів. Якщо ви не хочете, щоб код вимкнено, не використовуйте ідіоми, які сигналізують, що він буде. Це так само погано, як використання if (DEBUG)для керування чимось, окрім налагодження коду. Мікрооптимізація може нічого не означати у вашому випадку. Але ідіому не слід підривати лише тому, що вона вам не потрібна.
candied_orange

Я думаю, що я не зробив свого наміру досить чітким. Я говорю не про конкретну реалізацію, assertа про концепцію твердження. Я не хочу зловживати assertчи бентежити читачів. Я мав на увазі запитати, чому саме так. Чому немає додаткового release_assert? Чи немає в цьому потреби? Яке обґрунтування твердження про відмову у звільненні?
Ніхто

2
You're asking for a good abstraction.Я не впевнений у цьому. Я, головним чином, хочу розібратися з питаннями, в яких немає відновлення, і цього ніколи не повинно відбуватися. Візьміть це разом із, Because production code needs to [...] not format the hard drive [...]і я б сказав, що у випадках, коли інваріанти розбиті, я б погодився звільнитись над UB у будь-який час.
Ніхто

1
@Nobody "Проблеми, де немає відновлення", можна вирішувати, кидаючи винятки, а не ловлячи їх. Ви можете зазначити, що вони ніколи не повинні траплятися в тексті повідомлення про виняток.
candied_orange

4

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


4

assertє такою формою документації, як коментарі. Як і коментарі, ви зазвичай не надсилаєте їх клієнтам - вони не належать до коду випуску.

Але проблема з коментарями полягає в тому, що вони можуть застаріти, і все-таки вони залишилися. Ось чому твердження хороші - їх перевіряють у режимі налагодження. Коли затвердження застаріває, ви швидко виявите його і все одно будете знати, як виправити ствердження. Той коментар, який застарів 3 роки тому? Хтось здогадається.


1
Тож переривання невдалого інваріанта, перш ніж щось станеться, не є дійсним випадком використання?
Ніхто

3

Якщо ви не хочете, щоб «ствердження» було вимкнено, сміливо напишіть просту функцію, що має подібний ефект:

void fail_if(bool b) {if(!b) std::abort();}

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


1
Я це добре знаю. Питання детальніше пояснюється тим, чому дефолт є таким, яким він є.
Ніхто

3

Безглуздо сперечатися, що повинен робити аргумент, він робить те, що робить. Якщо ви хочете чогось іншого, напишіть власну функцію. Наприклад, у мене є Assert, який зупиняється на відладчику або нічого не робить, у мене є AssertFatal, який завершить збій програми, у мене є функції bool Asserted та AssertionFailed, які стверджують і повертають результат, щоб я міг як стверджувати, так і вирішувати ситуацію.

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


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

Гм, це не verify(cond)буде нормальним способом стверджувати та повертати результат?
Дедупликатор

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

2

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

Він також розроблений таким чином, щоб його не можна було отримати зі стійких версій версій з будь-яких причин, які розробники можуть вважати корисними: естетику, продуктивність, що б вони хотіли. Це частина того, що відокремлює збірку налагодження від збірки випуску, і за визначенням збірка випуску позбавлена ​​таких тверджень. Таким чином, є підрив дизайну, якщо ви хочете випустити аналогічну "збірку випуску з твердженнями на місці", яка була б спробою складання випуску з _DEBUGвизначенням препроцесора і не NDEBUGвизначена; це насправді вже не збірка версій.

Дизайн поширюється навіть на стандартну бібліотеку. Як дуже базовий приклад серед численних, багато реалізацій std::vector::operator[]буде assertперевірка здоровості, щоб переконатися, що ви не перевіряєте вектор поза межами. І стандартна бібліотека почне працювати набагато, набагато гірше, якщо включити такі перевірки у версії версії. Орієнтир vectorвикористанняoperator[]і ctor заповнення з такими твердженнями, що входять до простого старого динамічного масиву, часто показуватиме динамічний масив значно швидшим, поки ви не відключите такі перевірки, тому вони часто роблять ефективність впливу далеко не тривіальними способами. Перевірка нульового вказівника тут і перевірка поза межами дійсно можуть стати величезними витратами, якщо такі перевірки застосовуються мільйони разів у кожному кадрі в критичних петлях, що передують коду, просто, як перенаправлення розумного покажчика або доступ до масиву.

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

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

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

Як було зазначено вище, перевірки не обов'язково тривіальні з точки зору ефективності. Багато хто, ймовірно, тривіальні, але знову ж таки, навіть стандартна версія використовує їх, і це може вплинути на ефективність неприйнятними способами для багатьох людей у ​​багатьох випадках, якщо, скажімо, обхід випадковим доступом std::vectorтриває 4 рази довше в тому, що повинно бути оптимізованою версією версії через його межі, які перевіряють, що ніколи не повинно провалюватися.

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

Але також це просто так asserts, як вони розроблені. Якщо вони не мали б такого величезного впливу на продуктивність у всьому платі, то я можу прихилити це, якби вони були розроблені як більш, ніж функція збирання налагодження, або ми могли б використовувати, vector::atщо включає межі перевірки навіть у версії версій та викидах за межі доступ, наприклад (але з величезним хітом продуктивності). Але в даний час я вважаю їх дизайн набагато кориснішим, враховуючи їхній величезний вплив на продуктивність у моїх випадках, як функцію, що працює лише налагодження, яка опускається, коли NDEBUGвизначено. Що стосується випадків, з якими я працював, принаймні, це має величезну різницю для складання випуску, щоб виключити перевірки правильності, які насправді ніколи не повинні бути збитими.

vector::at vs. vector::operator[]

Я думаю, що відмінність цих двох методів лежить в основі цього, як і альтернативи: винятки. vector::operator[]реалізацій, як правило, assertщоб переконатися, що вихід поза межі викликає помилку, що легко відтворюється, коли намагається отримати доступ до вектора поза межами. Але реалізатори бібліотеки роблять це з припущенням, що це не буде коштувати ні копійки в оптимізованій збірці випусків.

Тим часом vector::atпередбачено, що завжди перевіряє поза межами меж і кидає навіть у складі релізів, але за це виконує покарання за ефективність до того моменту, коли я часто бачу набагато більше коду, vector::operator[]ніж використання vector::at. Багато дизайну C ++ перегукується з думкою "платити за те, що ти використовуєш / потребуєш", і багато людей часто віддають перевагу operator[], що навіть не турбується про те, що межі перевіряють версії версій, виходячи з того, що вони роблять не потрібно перевіряти межі в їх оптимізованих версіях випусків. Якщо раптом у версії версій увімкнено твердження, продуктивність цих двох буде однаковою, а використання вектора завжди буде повільніше, ніж динамічний масив. Отже, величезна частина дизайну та користі тверджень базується на ідеї, що вони стають вільними у складанні релізів.

release_assert

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

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

Насправді є деякі випадки, коли я можу виявити важкий збій із номером рядка та повідомленням про помилку, бажано витончено відновитись після викинутого винятку, що може бути досить дешевим, щоб зберегти у випуску. І є деякі випадки, коли неможливо відновити виняток, як-от помилка, яка виникає при спробі відновлення з існуючого. Там я знайшов би ідеальне пристосування для, release_assert(!"This should never, ever happen! The software failed to fail!");і, природно, це буде брудним дешевим, оскільки перевірка буде виконуватися в першу чергу на винятковому шляху і не коштуватиме нічого при звичайних шляхах виконання.


Мій намір не полягав у тому, щоб увімкнути довідки у коді третьої сторони, дивіться уточнюючу редакцію. Цитата з мого коментаря не відповідає контексту мого запитання: Additionally, the performance argument for me only counts when it is a measurable problem.Отже, ідея полягає у вирішенні кожного конкретного випадку, коли брати твердження з релізу.
Ніхто

Однією з проблем останнього коментаря є те, що стандартна бібліотека використовує те саме, assertщо покладається на ті самі характеристики _DEBUG/NDEBUGпрепроцесора, що і те, що ви використовували б при використанні assertмакросу. Тому немає можливості вибірково включати твердження лише для одного фрагмента коду, не вмикаючи його і для стандартної лібрики, наприклад, якщо припустити, що він використовує стандартний.

Існує перевірка ітератора STL, яку ви можете відключити окремо, що вимикає деякі твердження, але не всі.

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

vector::operator[]і vector::at, наприклад, матиме практично таку ж ефективність, якби твердження були включені у версії версій, і більше немає сенсу використовувати operator[]з точки зору продуктивності, оскільки це все-таки буде перевіряти межі.

2

Я кодую в Ruby, а не C / C ++, і тому я не буду говорити про різницю між твердженнями та винятками, але я хотів би поговорити про це як про те, що зупиняє час виконання . Я не згоден з більшістю відповідей, поданих вище, оскільки зупинення програми з друком заднім числом для мене добре працює як техніка оборонного програмування.
Якщо є спосіб для затвердження рутини (абсолютно незалежно від того, як це написано синтаксично і якщо слово "assert" використовується або взагалі існує в мові програмування або dsl), це означає, що слід виконати певну роботу і продукт одразу повертається з "випущеного, готового до використання" до "потребує виправлення" - тепер або перепишіть його на реальну обробку винятків, або виправте помилку, яка спричинила появу неправильних даних.

Я маю на увазі, що Assert - це не річ, з якою вам слід жити і часто дзвонити - це сигнал зупинки, який вказує на те, що вам слід зробити що-небудь, щоб це більше не повторилося. І сказати, що "у складанні випусків не повинно бути тверджень" - це як сказати "у складанні випуску не повинно бути помилок" - чувак, це майже неможливо, боротися з цим.
Або подумай про них як про "невдалі одиничні тести, зроблені та запущені кінцевим користувачем". Ви не можете передбачити все, що користувач буде робити з вашою програмою, але якщо smth занадто серйозно піде не так, це повинно зупинитися - це схоже на те, як ви будуєте конвеєри - ви зупиняєте процес і не публікуєте, чи не так? ? Затвердження змушує користувача зупинятись, повідомляти і чекати вашої допомоги.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.