[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Стабильная система?



Артём Н. -> debian-russian@lists.debian.org  @ Thu, 01 Oct 2015 23:12:30 +0300:

 >>> Как измеряется надёжность?
 >> Количеством, гм, ВНЕЗАПНЫХ глюков в единицу времени.
 АН> Если серьёзно говорить об измерении, это не является показателем.

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

На этой задаче.  Если бы это была реальная авионика, 

 >>> не исключена просто логическая ошибка в алгоритме, которую без тестов вы не
 >>> сможете отловить (да знаю, это очевидно).
 >>
 >> Ну да, естественно.  Вот это как раз обычно лечится ручным прогоном.
 АН> Либо вы несколько противоречите сами себе, либо кода не так много.
 АН> В таком случае, чтобы проверить все пути выполнения, вам надо выполнить столько же
 АН> операций, сколько выполнят тесты. Только вручную.

Я ел этих устриц.

Тут есть три момента.

Первый - это то, что ручной прогон по каждому месту делается один раз
(ну, пять раз, но в один промежуток времени - пока пишется и проверяется
это место в коде).  В смысле, если с умом писать на хаскеле, этого, как
правило, достаточно, потому что куски достаточно хорошо изолированы.  В
парадигме с mutable state, конечно, дело обстоит намного хуже.

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

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

 >> Кстати, в оригинальном приборе (который стоит в реальных вертолетах)
 >> таки благополучно перепутали в одном месте плюс с минусом, и отловлено
 >> это было (мной) на стенде при ручном прогоне.
 АН> Фактически, в данном случае стенд являлся тестом для реальной системы,
 АН> а реальная система тестом для стенда.

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

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

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

Да, там это, возмоно, имело бы смысл, потому что реальный прибор писан,
вероятно, на C++, в лучшем случае на C, с mutable state, и гонять потом
этот тест следовало бы регулярно.  Угадайте с трех раз, стали ли это
делать разработчики прибора?

 >> Вообще мне приходилось работать в режиме TDD, когда я не знал про
 >> хаскель.  Там, собственно, я и узнал на практике, что прекрасная в
 >> теории идея TDD на практике выражается в квадратичном количестве тестов
 >> и многочасовом их прогоне.
 АН> Да, я тоже это знаю.

 АН> Ну, по факту, странный "спор". По-моему, вы сами прекрасно знаете, что
 АН> без тестов никогда нельзя гарантированно сказать, что система не работает
 АН> на некотором подмножестве случаев после её изменения.
 АН> И тесты нужны. Хотя писать их лень и оверхед.

Видите ли, информация о том, что система НЕ работает - это не та
информация, которая нужна.  Нужна достаточная степень уверенности, что
система РАБОТАЕТ.  Эту уверенность можно получить разными способами.

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

 >> Позже, уже в Транзасе, мне коллеги рассказывали, что они изначально тоже
 >> писали тесты, и автоматически прогоняли их каждое утро, когда приходили
 >> на работу и запускали smalltalk.  Профессиональных параноиков среди низ
 >> не было, с количеством тестов они не перенапрягались, но все равно через
 >> несколько месяцев прогон тестов стал занимать столько времени, что был
 >> отключен.
 АН> Может проблема в Smalltalk?
 АН> Я представил себя на месте пилота (если это не тренажёр).
 АН> Наверное, если бы летал я, для себя я бы не стал отключать тесты...

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

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

 АН> Да, и может проблема не в тестах?
 АН> Если изменяется кусок, который сильно изолирован, так ли часто надо прогонять _все_ тесты?

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

 >> Благодаря иммутабельности блокировки надо делать не на каждый чих, а
 >> только при _явной_ операции записи, которых в иммутабельной парадигме
 >> немного.
 АН> Всё-таки принципиально ничего не меняется, и для записи они нужны...

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

Мне рассказывали про обратную ситуацию, очень поучительная была история.

Меняли несколько лет назад в ЦБ систему межбанковских платежей.  Старая
система, написанная еще программистами мейнфреймов, с кастомным
хранилищем данных, работала по схеме "оптимистичной блокировки" - она
сначала проводила все транзакции, потом подсчитывала балансы, и если в
результате на каком-то счету баланс оказывался отрицательным, начинала
откатывать транзакции, в которых с него что-то перечислялось.  Ситуация
редкая, и система работала.  Новая была написана с современной БД,
программистами, обученными по современным стилям, и не особо думавшими
над границами их применения.  Она проверяла баланс (вероятно,
триггерами) при каждой транзакции, и откатывала транзакцию, если в ее
результате баланс оказывался отрицательным.  Если транзакцию провести не
удавалось, она переставлялась в конец очереди, и т.д.  А вот эта
ситуация, как ВНЕЗАПНО выяснилось, частая, и в результате новая система
тупо не успевала провести все транзакции за день.  То есть в теории
работала, а на практике - нет.

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

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

Сам я такого не писал, а вот тот знакомый, который мерил, как раз писал.
Он не просто так взялся мерить.

 >>   >> В смысле, человека берут решать сложную
 >>   >> задачу, а на чем он ее решает - это уже его личное дело.
 >>   >> Задача сложная, "программист на XXX" ее не решит.
 >>   АН> Как правило, всё-таки декларируют определённый язык.
 >>   АН> Решите вы задачу и уйдёте.
 >>   АН> А кто будет поддерживать ваш код затем?
 >>
 >> Поскольку задача сложная, то поддерживать будет человек со сравнимым
 >> интеллектом.  Собственно, я не единственный сотрудник Транзаса,
 >> способный поддерживать код на хаскеле.
 АН> Но есть ещё "умение разбираться в чужом коде".
 АН> Хотя, в данном случае, наверное Хаскелю будет плюс, в целом.
 АН> Сложно найти людей, которые с ним работают, чтобы поручить
 АН> им поддерживать кусочки задачи (не думаю, что интеллект измеряется знанием или незнанием
 АН> Haskell, я вот его не знаю, так что, я автоматически попадаю у вас в категорию "тупой"?).

Я же сказал, "будет дешевле выучить".  Умный человек отличается от
тупого не тем, что он уже знает, а тем, что он может узнать, когда
понадобится.

 >> Человеку со сравнимым интеллектом будет дешевле выучить хаскель и
 >> разобраться в хаскельном коде, чем разобраться в коде того же
 >> назначения, написанном на C++, который он, предположим, уже знает.
 >>
 АН> Вероятно. Это зависит от того, кем, как и с какой целью так написан C++ код.
 АН> Есть C++ код, в некоторой степени написанный для того, чтобы показать "какая
 АН> крутая у меня квалификация".

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

 >> А если учитывать, что "поддерживать" часто начинается с "рефакторить",
 >> то тут преимущество хаскеля с его "как только после рефакторинга
 >> скомпилировалось, то почти наверняка работает правильно" становится
 >> заметным.
 АН> Странная поддержка...

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

 >> Если задача простая, то ситуация, разумеется, иная.
 АН> На C++ любую задачу возможно решить сложно.

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

 >> Я пробовал, довольно много. ООП как парадигма - это duck typing
 АН> Да не обязательно. Это выражение больше напоминает Python.
 АН> И всё вытекающее: нестрогую типизации прежде всего, прототипное "наследование" и т.п..

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

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

В хаскеле, кстати, элемент ООП тоже есть, в языке называется class, а в
описаниях чаще - typeclass, чтобы отличать от понятия class в
традиционных ООП-языках, а на практике соответствует тому, что в Java
называется interface.  (А в парадигме, насколько я понимаю, под классом
понимается именно интерфейс.)  Но в хаскеле оно применяется чаще
все-таки с подразумеванием нижележащей математической модели, в то время
как в ООП скорее с подразумеванием, гм, некоего представления в голове
конкретного программиста, из-за чего интерфейсы бывают странны.

 >> следствие, вышеупомянутое квадратичное количество тестов.  Собственно,
 >> это оно у хорошо примененной ООП квадратичное, у спагетти-кода оно
 >> экспоненциальное.
 АН> И на хаскеле возможно так написать. :-\

Не вопрос.  Просто хаскель провоцирует более аккуратный стиль.

 >>  А вот
 >> функциональная парадигма с богатой системой типов позволяет поднять
 >> удерживаемый в голове уровень сложности задачи на следующую ступень.
 АН> Разве не проще оперировать объектами в терминах какого-нибудь DSL?

Так объектами или DSL? :)

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

Это нередко имеет смысл, но и в этом случае - угадайте, на каком языке
удобнее преобразовывать грамматику из головы в парсер?

Но чаще делают не так.  Чаще делают EDSL (embedded DSL), т.е. строят DSL
поверх базового языка.  Если не надо парсить внешние файлы, то это
намного дешевле.  И вот тут богатая система типов позволяет сделать
более выразительный EDSL.

 >>> Ещё тут рядом на Erlang пытались что-то делать, не пошло.
 >>> Некому это поддерживать.
 >>> Просто людей не найти, кто станет на этом писать.
 >>
 >> У меня коллега уехал в Швецию как раз на позицию, где был нужен Erlang.
 >> Идет.
 АН> Не именно потому он уехал в Швецию, что специалистов фиг найдёшь? :-)

Нет.  Он вообще-то до того как раз на хаскеле и смоллтоке писал, а
Erlang изучал уже на месте.

 >> Но насколько я его понял, Erlang нишевый по назначению, для
 >> программирования общего назначения на нем писать неудобно.
 АН> Вполне универсальный язык, насколько я знаю.

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


Reply to: