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

Это плавающие ошибки
Это плавающие ошибки

Если ты владелец IT-продукта и болеешь за свое дело, то без качественных технических заданий не обойтись. В больших нетиповых проектах постановка и формирование ТЗ становится нетривиальной. Нужно описать много условий, ветвлений и прочих сложных логических конструкций, которые «с наскоку» не реализовать, ведь на бумаге это выглядит лишь как набор абстракций. А так уж устроен наш мозг, что ему нужна конкретика и образы, а не полотна с текстом!

Взять, например, разработку фриланс-портала. У проекта обычно несколько статусов. Всякий статус доступен для пользователей с разными ролями, у каждой роли есть набор атрибутов и свойств, характерных только для этого статуса и этой роли. А еще есть дополнительные, вроде бы, автономные сущности такие как «Переписка», «Оплата», «Отзывы». Они вроде бы самостоятельные, но, так или иначе, перекликаются со статусами и ролями. Все это при пересечении дает очень большой набор вариантов, который нужно запрограммировать.

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

Есть всего два возможных базовых варианта. Остальное - их комбинации.

Вариант 1. Подумать и сформулировать набор правил для каждой сущности. Например, берем сущность «Переписка» и формулируем правила по ее доступности. Это может выглядеть следующим образом: 

Если между специалистом и заказчиком была переписка, то она блокируется при следующих условиях:

a.      Проект получает статус «Заблокирован»

b.      Проект получает признак «Снят с публикации»

c.       Проект получает статус «Закрыт» (полное закрытие)

d.      Специалист отказался от выполнения проекта на этапе поиска исполнителя

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

Кстати, как вы думаете, какой сюрприз (явно ненужный нам) можно получить по этому правилу? Напишите в комментариях, если заметите!

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

Лайфхак: это лучше зафиксировать в проектной документации в качестве принципа работы!

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

Но что, если вы не смогли чего-то учесть?

Тут на помощь приходит второй вариант постановки задачи…

Вариант 2. Рассмотреть все частные случаи разрабатываемого процесса, разложив его на максимально подробные составляющие. Здесь проблема состоит в том, что процесс в сложных системах может быть настолько большим, что представить его полным набором мелких составляющих, мягко говоря, затруднительно. А, порой, и вовсе невозможно. 

Вот вам абстрактный пример. У нас есть:

  • 3 состояния проекта S = (S1, S2, S3)

  • 3 роли R = (R1, R2, R3)

  • и три атрибута А = (А1, А2, А3), которые зависят от того, как пересекаются S и R.

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

Например, мы подумали, и у нас получилось так: 

S1 + R1 = А1

S1 + R2 = А2

S1 + R3 = А3

S2 + R1 = А1

S2 + R2 = А2

S2 + R3 = А3

S3 + R1 = А1

S3 + R2 = А2

S3 + R3 = А3 

Таким образом, пересечение двух небольших множеств, состоящих всего из трех объектов дало нам выборку из 9 вариантов! 

А если множество статусов S получит еще статус S4? Сколько будет вариантов? Уже 12!

А если размеры обоих множеств станут равны 4-м? Тогда вариантов будет 16!

Это я еще не привел пример, когда нам нужно пересечь 3 множества или больше!

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

Неполная карта частных случаев из одного фриланс-проекта
Неполная карта частных случаев из одного фриланс-проекта

Применимость на практике и "плавающие" ошибки?

Мы рассмотрели два способа подачи информации для ТЗ в сложных проектах. Я считаю, что у обоих способов есть свои преимущества и недостатки.

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

Кстати, бывают случаи, когда правило видоизменяется по ходу проекта, например, если мы что-то не учли изначально или в проекте появляются новые вводные. Такое бывает по невнимательности или по Agile:)

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

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

Другими словами это, когда:

  • после проверки было: S1 + R1 = А2 (неправильно), а S1 + R2 = А2 (правильно)

  • после исправления стало: S1 + R1 = А1 (правильно), а S1 + R2 = А1 (неправильно)

Таким образом, наша ошибка уплыла из случая S1 + R1, но приплыла в случай S1 + R2.

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

Для руководителя проекта такие ошибки - страшный сон, ведь по мере столкновения с ними, возникает перспектива повторного тестирования всего проекта! В то же время программист просто меняет условие в коде и ждет реакции.

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

При таком подходе снять с себя ответственность уже не получится, а это уже прямое указание на чью-то возможную некомпетентность или лень!

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

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

Буду рад, если вы поделитесь в комментариях своим опытом борьбы с плавающими ошибками! Как решали подобные ситуации? Были ли конфликты на этой почве?

Комментарии (8)


  1. DrinkFromTheCup
    27.08.2022 02:30
    +2

    Частично или полностью эту задачу можно (и нужно!) поручить тестировщикам

    Кхм... Ну, хоть не техподдержке - и на том спасибо...

    Рассмотреть все частные случаи разрабатываемого процесса

    По мере перебора крайностей документация всё пухнет-пухнет, а вероятность поймать дефект логики в конечном продукте всё не падает и не падает...

    Подумать и сформулировать набор правил для каждой сущности.

    Вы использовали слишком общие сущности в качестве примера. У читателя могут возникнуть стрёмные идеи по итогу. Идеи, коих Вы в виду не имели, надеюсь.

    Я полагаю, что Вы задумывали написать что-то о разделении единого кома проекта на конечное количество самодостаточных И НЕ ДЕЛИМЫХ МЕЛЬЧЕ без потери контекста сущностей.

    Т. е. набор сущностей для примера должен быть не "«Переписка», «Оплата», «Отзывы»" (что слишком крупно для адекватного ТЗ), а что-то вроде "письмо в переписке", "транзакция в системе оплаты" и "отзыв".
    Причём, каждая из этих сущностей имеет при себе не только перечень правил обработки, но и ПРИМЕРНЫЙ перечень обязательных атрибутов (в примере - ID автора для всех трёх, привязка к задаче для всех трёх, текстовое содержание для всех трёх (с указанием, где зачем текст будет использоваться), цифровое значение рейтинга для отзыва (и его способ отображения на фронтэнде)), etc-etc.

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

    Но вот беда...

    А так уж устроен наш мозг, что ему нужна конкретика и образы, а не полотна с текстом!

    Риск услышать ненавистное мне "Лень читать!" (о, если бы только это...) в этом случае и правда велик.

    Причём, независимо от способа отображения и последующей интерпретации ТЗ.

    Способа заставить программиста "попасть" в ТЗ (и при этом не наделать дефектов логики, именуемых Вами как "плавающие ошибки"), при этом не заставляя читать ТЗ, мне не известно.


    1. XaBoK
      27.08.2022 14:06

      Документация и должна пухнуть, если пухнет кодовая база и бэклог. Тут больше игра на оптимизацию. При удачном решении большого роста не будет ни там ни там.


    1. sshmakov
      27.08.2022 18:13
      +2

      Я полагаю, что Вы задумывали написать что-то о разделении единого кома проекта на конечное количество самодостаточных И НЕ ДЕЛИМЫХ МЕЛЬЧЕ без потери контекста сущностей.

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

      А вот умение разделить сложную функциональность на атомарные сущности, каждую из которых можно полностью протестировать отдельно - навык хорошего аналитика.


  1. lxsmkv
    27.08.2022 10:43

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

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


    1. lxsmkv
      27.08.2022 11:42

      Я бы копал в сторону т.н. Business Rules Engine


    1. XaBoK
      27.08.2022 14:04

      Конечные автоматы и юнит тесты как раз и есть варианты 1 и 2 из поста. Юнит тест обязан перебрать все варианты для 100% покрытия. Описания состояний и переходов между ними - state machine. Не понятно только, почему предлагают выбирать между ними...


  1. SergeyUstinov
    27.08.2022 15:34
    +3

    требует от нас предварительно проделать достаточно большой объем аналитической работы

    Опыт подсказывает, что без "достаточно большого объема аналитической работы" любая система средней сложности и выше не взлетит.


  1. ads83
    27.08.2022 23:34

    Кстати, как вы думаете, какой сюрприз (явно ненужный нам) можно получить по этому правилу? Напишите в комментариях, если заметите!

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


    И встречный вопрос: а какой баг заложили вы?