Как стать автором
Обновить
40
0.3
Valentin Nechayev @netch80

Программист (backend/сети)

Отправить сообщение
Property-based это само по себе сложно. Мы пробовали (на PropEr). В большинстве случаев получалось, что описать необходимое поведение отдельной функции в тех терминах, что ему нужно, во много раз сложнее, чем написать саму функцию. ;(
А вот что в нём хорошо — поиск маргинальных случаев. Но можно и не дождаться.
> Что у него будет в TDD? Да ровно то же, что и в коде. В тестах он тоже решит, что «не меньше — это больше».

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

И Ваше правило

> Никогда не тестировать свой собственный код.

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

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

> Тесты (что железа, что софта) бывают двух видов. Одни — чтобы доказать, что ошибок нет. Другие — чтобы найти ошибки. TDD дает лишь тесты первого рода.

Полностью согласен, но я бы переформулировал. Слово «доказать» некорректно — разве что «подтвердить», «подкрепить уверенность». Доказательство возникает только в результате верификации, пусть и ручной (глазной? не знаю, как лучше сказать), тесты же обеспечивают выполнение условий этой верификации (рядом уже писал то же более сжатой формулировкой).

> оценка работы программеров — по тому, чтобы багов было меньше и самой малой значимости.

И они в этих условиях сами себя не тестируют? Что-то я сильно сомневаюсь. Может, тестируют, но подпольно? :)

> Но массовое использование TDD, где надо и где не надо — это карго-культ.

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

Первое: тут уже упоминалось, но повторю: формально TDD никак не требует собственно писать по ТЗ, оно требует только удовлетворения теста. Поэтому, если мы напишем умножение в виде

multiply(0, 0) -> 0;
multiply(2, 0) -> 0;
multiply(2, 2) -> 4.

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

Для объяснения этого вообще нужно вернуться к тому, зачем же тест нужен. Тут лучшая формулировка, что я слышал, дана коллегой landerhigh с RSDN: «Тесты — не для поиска багов, а для написания верифицируемого в контролируемых условиях кода.» Под верификацией имеется в виду любая верификация, начиная с банального просмотра глазами. Код должен обеспечивать своим содержимым выполнение ТЗ так, чтобы это могли проверить средства верификации (сам автор, коллеги-ревьюеры, автоматические средства...), а тест — чтобы ловить то, что верификация не ловит: человек не заметил опечатку или крайний случай; вместо символа ';' шутник подставил ';' (U+037E); не выловлен какой-то крайний случай; и тому подобное.

Далее, в классическом TDD объявляется, что
1) пишутся тесты до кода («test first»);
2) тесты должны быть проверены на то, что они не проходят (тупо — запустили, увидели отказ и только после этого имеем право двигаться дальше);
3) пишется код для их удовлетворения.

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

Чтобы допустить в принципе ситуацию, когда код меняется, надо допустить, если использовать правило «test first», что новые тесты соответствуют TDD, но старые — нет, часть тестов может работать и до новой разработки, и после. А это значит, что они уже нарушают этот принцип — их нельзя проверить на корректность той проверкой, что «тест до написания упал => он, вроде, правильный».

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

Также с этим напрямую связано, что TDD никак не решает вопрос качества самого тестового окружения. Например, что будет, если в результате сбоя в одном файле assertEqual() станет проверять не равенство результата ожидаемому, а просто наличия результата?

Пригодный к длительному сопровождению (а не к одиночному «хренак, отдали заказчику и забыли») тестовый комплект должен быть таким, чтобы корректность самого тестирования можно было проверить автоматизированно в любой момент без ручного вмешательства. Например, мы вводили инверсии тестов — аналог «мутационного тестирования» именно для кода тестов, в явно продуманных местах. Общая идея: пусть тест ожидает, что если на входе A, на выходе B. Модифицируем код посылки на вход так, чтобы посылалось C, но так, что верхний уровень контроля теста ничего про это не знает. Тест должен упасть (а вокруг него строится другой, который проверяет факт падения вложенного теста). Ошибка инверсного теста у нас срабатывала очень редко, но каждый такой случай означал серьёзные скрытые проблемы в коде.

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

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

Вы вначале решите, что должен пройти тест для (1,2) и (2,1). OK, сравнили два элемента, прошли. Теперь добавляем третий… четвёртый… двадцатый… во что превратилась функция? В лучшем случае вы получите сортировку вставками (если кодер очень умён), а скорее всего это будет «пузырёк». При совсем тупом кодере это вообще не будет читаемо даже после ста грамм. И Вы никак не получите ни метод Хоара, ни тем более метод Бэтчера. Потому что для них надо сначала реализовать алгоритм, хотя бы в уме, со всеми циклами, ветвлениями и тому подобным, а уже затем тестировать.
Можно сказать тут, конечно, что алгоритм в уме и алгоритм в коде — разные. Но когда вы заранее знаете, какое именно ветвление вы напишете и почему — вы уже действуете не под тест, а под алгоритм. Идея предварительного разделения на два подмассива, как у Хоара, а тем более математически подобранная сортировка подпоследовательностей, как у Бэтчера — тестом не решится.

В вычислительной математике таких случаев ещё больше. Тестирование решения СЛАУ через построение треугольной матрицы — очень хреново ложится на TDD, а тонкие эффекты на долях эпсилона в принципе не могут быть заранее просчитаны.

Следующее — проблема уже состоявшихся требований. Пусть есть изменение ТЗ — добавилось новое требование. Но что будет, если оно уже реализуется кодом? Например, требование — чтобы сортировка была устойчивой — но она уже такая в реализации. Такая ситуация в принципе не покрыта TDD. Решение я уже описал выше — отказ от заложения на важность теста его изначальной неработой.

В каком же случае TDD может идеально работать, и даже быть полезным, в его каноническом виде? Это
1) написание только нового кода, или расширение на безусловно новые требования;
2) полное отсутствие необходимости R&D, весь проект ясен с самого начала (сюда входит и вариант изменения ТЗ на ходу — просто таких начал становится несколько);
3) большое количество низкоквалифицированных сотрудников и аналогичного управления ими, которое способно сэкономить на тестах, но для которого угроза административного наказания за нарушение инструкций важнее проблемы собственно качества выходного кода.

То есть это идеальная технология для классической оффшорной галеры. Антиполюс — то, где TDD способно только навредить — разработка собственного наукоёмкого продукта. (Как раз случай коллеги Jef239, поэтому я не удивляюсь его отношению. И мой случай для всех прошлых и нынешних работ.)

Резюмирую: я никак не против отдельных функциональных тестов всех уровней (от юнит- и до интеграционных верхнего уровня). Они должны быть. Более того, они должны быть и для нормального заказчика (который обязан не верить, пока не покажут работающее), и для собственного контроля. И разработчик должен сам писать тесты на все подозрительные и крайние случаи, и контроль должен это поощрять и требовать. Но требование 100% покрытия и «test first» — это то, что превращает разумный подход в религию.

И ещё один PS — речь не про тесты для прямой проверки ТЗ (я их отношу к BDD, а не TDD).

Чуть сумбурно получилось, но, надеюсь, понятно.
> Потому что return — это таки сахар поверх goto с гарантиями

return только частный не самый интересный для обсуждения случай. Интереснее с break. До сих пор масса языков позволяет им выйти только с самого нижнего уровня. Пример Java, где можно даже выйти из тела именованного if, до сих пор остаётся приятным исключением.

> Поэтому, например, писать руками код с goto в 2017 году на каких-нибудь даже плюсах я особых причин не вижу.

На плюсах — тот же выход из множества вложенных циклов (когда неудобно порождать отдельную функцию; хотя с появлением лямбд стало значительно проще делать их эффективно). На C — объединение веток очистки за собой является стандартнейшим и полезнейшим приёмом (альтернатива чудовищно хрупка).
> Изменение ТЗ — это исправление ошибок.

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

> на случай атомной войны планировалась автономность до года

Р-радикально. :) Впрочем, тогда и не такие взлёты мысли были. И устройство было упрощено до предела. Не думаю, что кто-то из современных конструкторов сейчас возьмётся повторять этот подвиг в варианте с хоть каким-то расчётом на более современные средства, а не уровня древней Спарты.

> Ну тоже примерно так. Ну может 5-7, а не 3, но примерно так…

Ook.

> Во втором варианте проще вставить дополнительные операции между вызовом процедуры и использованием её результата.

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

> По ссылке все-таки, не по указателю. Указателей на данные там вообще не было.

Это я переупростил для ясности. С точки зрения C++-like терминологии это таки ссылка, даже если внутри чистейший указатель.
Тритовые — тем, что будут умножать и делить на 2, а не на 3.
А ещё есть вопрос операций типа count leading (trailing) zeros (sign zeros, etc.). Аналог сохранится, но кроме него надо будет ещё другие варианты продумывать.
Для этого нужен следующий шаг — увеличение количества миллибит в бите. Увеличение всего на 2.4% (1000 -> 1024) позволит повысить плотность упаковки картинки на 4-8%. Скорость на каналах, понятно, повысится во столько же раз.
Так Вы тогда и спецификацию Си прочитайте. В C11 (final draft) — пункт 6.2.6.2. Достаточно объёмная цитата, форматирование я уложил в плоское:

=== cut here ===
For unsigned integer types other than unsigned char, the bits of the object
representation shall be divided into two groups: value bits and padding bits (there need not be any of the latter). If there are N value bits, each bit shall represent a different power of 2 between 1 and 2, so that objects of that type shall be capable of representing values from 0 to 2**N − 1 using a pure binary representation; this shall be known as the value representation. The values of any padding bits are unspecified.

For signed integer types, the bits of the object representation shall be divided into three groups: value bits, padding bits, and the sign bit. There need not be any padding bits; signed char shall not have any padding bits. There shall be exactly one sign bit. Each bit that is a value bit shall have the same value as the same bit in the object representation of the corresponding unsigned type (if there are M value bits in the signed
type and N in the unsigned type, then M ≤ N ). If the sign bit is zero, it shall not affect the resulting value. If the sign bit is one, the value shall be modified in one of the following ways:
— the corresponding value with sign bit 0 is negated (sign and magnitude);
— the sign bit has the value −(2**M ) (two’s complement);
— the sign bit has the value −(2**(M − 1)) (ones’ complement).
Which of these applies is implementation-defined, as is whether the value with sign bit 1 and all value bits zero (for the first two), or with sign bit and all value bits 1 (for ones’ complement), is a trap representation or a normal value. In the case of sign and magnitude and ones’ complement, if this representation is a normal value it is called a negative zero.
=== end cut ===

То есть свобода по сравнению с Java, да, есть — нежёсткие размеры, возможность signed быть короче соответствующих им unsigned, и выбор метода представления отрицательных чисел не только дополнительным кодом. Но двоичность — в полный рост, слово bit(s) во всех определениях, и никакой троичной альтернативы нет.

> Понятно что можно запустить виртуальную машину на чём угодно, но вот скорость у этого будет…

И то же самое для Си. Чтобы получить язык, удобно переносимый на троичную логику, надо что-то ближе к Ada, где основные рабочие типы задаются диапазонами. И особенно надо тщательно продумать, что будет с некоторыми операциями вроде битовых сдвигов, count_leading_zeros…
А отдельной подсистемы DOS — нету, она считается «Win16 console».

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


в консольной DOS-программе под Windows NT можно использовать то, что было в WIn16, но отсутствовало в DOS. Например — обращаться к сетевым файлам.

Ну там очень много чего можно было использовать через спец. интерфейсы. Например, то, что запускалось через DOS4G[W] и тому подобные экстендеры, под Win95 запускалось нативно (а даже если программа просила экстендер, тот просто "встраивал" свои действия в Windows). Для сети Win95 эмулировала SMB интерфейсы стиля Lantastic и аналогов. Но если это не выглядело Win бинарником, а был просто досовским .exe — то прямой путь к Windows-интерфейсам был недоступен (можно было требовать её dllʼки, но там требовалась особая осторожность).
Вообще, этот слой там чуть ли не высшее произведение инженерной осторожности за всю историю осестроения — заслуживает отдельного рассказа (увы, я знаю оттуда дай бог чтобы 2%, и не возьмусь).

Давайте не путать изменения ТЗ с дополнениями, которые потребовали рефакторинг из-за непродуманности структуры.

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


100% не предусмотришь, но 95% — вполне.

В 95% мы вполне вкладывались. Оставшихся 5% хватало, чтобы последствия обсуждать ещё много лет. :) И Туполеву наверняка не ставили задачи в виде "самолёт должен летать год без посадки".


И я об этом же. Если система к движению в какую-то сторону не готова — это ошибка аналитика. Или команда программистов вообще не видит дальше спринта (а надо смотреть на 5-10 лет вперед).

Реально получалось видеть с достаточным качеством на год, в общих чертах — на три.


double V = GetV());
printf("V=%.1f\n",V);
Но второй вариант проще и в отладке и в модификации.

Ну, опустим, что они неравнозначны (вот в варианте

printf("V=%.1f\n", (double) GetV())
есть действительно полное соответствие). Если отладка это распечатать V и/или не спутать пошаговый проход GetV() с printf() — да, согласен.
Но с модификацией связи не вижу. Наоборот, первый кажется проще для модификации — за счёт отсутствия лишних сущностей. Ту же переменную V надо проследить, чтобы её значение нигде дальше не использовалось. Вот если бы был оператор типа unvar (del в Python), чтобы поставить после printf и после этого чтобы V была неизвестна — тогда не надо было бы смотреть вниз.


Указателей в алголе-60 и фортране-IV вроде не было (или не было в тех реализациях, с которыми я работал).

В Фортране IV всё передавалось по указателю. Передача по значению появилась позже. От этого возникал ряд неприятных эффектов. Но я понял идею этого пункта, спасибо.

МДА. Ну или у вас аналитики действуют методом тыка, или вы путаете изменения ТЗ с дополнениями, из у вас какое-то "настоящее" программирование типа поддержи сайта.

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


Да, значительная часть хотелок превращалась в дополнения, но были и такие, которые требовали рефакторинга с самого нижнего слоя.


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

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


Точно так же я понимаю, что на компе может не быть часов, а в байте неожиданно может оказать 32 бита,

У нас без часов там не бывало, и 32 бита на системе наблюдения — тоже, хотя для этого не нужно гоняться за изделиями загадочной российской военщины — простой Cray тоже отлично подойдёт на это. Некоторым приближением к таким диверсиям были вычислительные модули на Cell.


Так что если я решил, что какого-то изменения не будет, а оно произошло — это моя ошибка.

Вот тут принципиально расходимся. Не ошибка. Ошибка — это когда на такое изменение не успеешь отреагировать. А когда оно просто непредсказуемо, но ты сохраняешь способность реагировать на новости — это как раз нормальное рабочее состояние.


В качестве мини-примера: одно из таких направлений, очень перспективное, сорвалось из-за того, что генподрядчик попал под санкции. (Это было ещё в 11-м году, так что с нынешней политикой прямо не связано.) В результате были запущены другие направления развития. Несмотря на материальность сферы, предсказать такое невозможно. Можно только быть готовым держать систему наготове к движению в любую разумную сторону.
И да, мы могли предсказать, что направление A потребует такого-то изменения, B — другого. Но пока нет никаких достоверных данных о движении в эту сторону — браться менять смысла нет. А изменения между собой слабо совместимы, скорее конфликтуют. Так зачем спешить вводить одно, даже если видишь, куда и как оно могло бы двинуться? Чем проще и прямее реализация на каждом этапе под его требования и под гарантированную (а не предполагаемую) перспективу, тем лучше.


Чтобы было понятней — вспомните дискуссию про goto сороколетней давности. Код с goto -очень гибкий. Но и некрасивый.

Я скорее отношусь к тем, кто "GOTO considered harmful" considered harmful. Потому что и return из середины, и break/continue это смягчённое goto, и автогенерация кода может их рожать пачками, и в машинном коде оно, а не if/while, и ваще. Сам иногда применяю в типичных шаблонах (например, на голом C — объединить код cleanup-веток некоторой функции). У меня критерий к нему — чтобы код был понятен тем, кто не заражён религиозной неприязнью.


И вот это


Код с goto -очень гибкий. Но и некрасивый.

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


Делая код эффективным — мы теряем в его универсальности. Делая код красивым — опять теряем в универсальности.

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


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

Не знаю, как это соотносится с моей юностью, но начинал я на Fortran. Common-блоки использовались скорее для удобства не-передачи контекста каждый раз и для замены не существовавших тогда структур, чем для собственно эффективности.
Процедуры на 5 страниц и более там и сейчас норма (на днях копался в BLAS), но в специфическом контексте, где это почти не мешает. В системном программировании такие или там, где ну очень тяжело разрезать иначе (как tcp_input() в классическом BSD), или в очень специфическом стиле (sendmail).
А что было неэффективным в случае иной связи? Слишком много возни с указателями?

RTFM, NTVDM — это основной компонент подсистемы WoW16:

Это никак не следует ни из процитированного Вами текста, ни из просто определения win-on-win. Скорее надо говорить, что NTVDM — нижележащее средство для WoW16.


Почитайте "Inside Windows 95" или Unofficail windows 95 — поймете, что только титаническим трудом они смогли заставить Win95 работать настолько стабильно.

Читал, и именно об этом и говорю. И там как раз написано, почему так — не потому, что просто вдруг оказался там кривой код (с их ресурсами они всё это исправили бы), а потому, что само построение ОС не давало шансов сделать беспроблемное устройство.


А линейка Win 3.1 загнулась после крайне неудачной WinMe:

Которую мало кто ставил.

Неужели непонятно, что красота и эффективность идет за счет специализации кода (по условиям ТЗ)?

EPARSE, простите. Красота и эффективность — следствие специализации кода, или они страдают (ухудшаются) из-за специализации кода?


Что вся декомпозиция — хороша лишь, пока программист верно понимает ТЗ и спектр его изменений?

Нет, непонятно. На практике можно считать, например, что через год изменятся 10% требований, а за десятилетний цикл развития — чуть менее, чем все. (Реальные цифры с одной из моих прошлых работ.) Но каким образом из этого следует рекомендация писать всё так, что не заботиться о его сопровождении? Ведь даже если 20% изменилось, остальные 80% те же, а сопровождать надо каждый день.


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

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


И вот вам пример, чем быдлокод лучше. Известная "Задача о восьми ферзях": Простой вариант с 8 циклами — легко обеспечивает переход к доскам непрямоугольной формы. А вот быстрый алгоритм Дейкстры…

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


Давайте разделим:
Быдлокод — это простой, некрасивый код, написанный в лоб.

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

Вы путаете разные вещи.

Нет, не путаю. Я явно указал, что тут два разных этапа избавления от этого наследия.


Одно дело — подсистема WoW

Про WoW я ничего не говорил. Говорил про то, что в линии NT называется NTVDM, а в 9x как-то иначе. Сейчас уточнил — да, оно в 32-битных версиях вроде бы есть и сейчас.


Но сказалась усталость кода…

Или скорее то, что альтернативный перспективный код был давно стабилизирован и готов к использованию.

Вы таки удивитесь, но таки Win2000 из линейки nt.

Я таки удивился, что Вы считаете, что я считаю иначе. Я не дал к этому никакого повода.


95 (+апгрейды), 98 (+апгейды), миллениум.

Спасибо, кэп. :)

На современной элементной базе полноценный barrel shifter дешевле, чем раскладка сдвига на много бит в цикл по одному биту.
Хотя x86 и сейчас не гарантирует быстрое выполнение для всяких нелепостей вроде RCR на несколько бит.

И любой работающий быдлокод — в 100 раз лучше неработающей красоты.

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


Пример с Doom и куском для NeXT, кстати, тут очень хорош дидактически :) С одной стороны, там очевидные странности в виде магических констант, которые по обычным нормативам надо описать в виде #define. Но я бы тут вместо этого поставил комментарий с описанием смысла конкретного числа.
С другой стороны, стиль, рекомендуемый во многих местах, потребовал бы сделать тут безусловно вызванное макро, которое было бы определено в непустое действие только для NeXT. А вот это уже хуже потерей локальности — надо идти неизвестно куда только для того, чтобы узнать, что для 99.999% случаев макро просто пустое.

Но верно и то, что линейка Win95 загнулась, потому что просто не смогла развиваться.

Связано ли это с качеством её кода? Или всё-таки с тем, что принципиально беззащитный дизайн перестал котироваться, а компы дотянули до мощности, когда NT-based решения перестали быть аццким тормозом?
Насколько я в курсе, там существенные затраты шли таки на проблемы совместимости с DOS. Избавление от неё сначала при переходе на Win2000, а затем (в Vista) при устранении DOS mode и 16-битки — были громкими праздниками.

насчет диверсии это слишком — конечно недостатки есть в любом промышленном объекте, причем исходная разработка была направлена не на мирный атом.

Именно. Перевод на "мирные рельсы" без адекватного обеспечения (хотя и начальная разработка в таком опасном виде).


Но несоблюдение правил и непрофессионализм в том смысле о котором я сказал — и есть причина аварии

Неотъемлемая компонента причины, вместе с проблемами конструкции.

Кстати вы представляете себе, что такое переделать действующий реактор

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

Информация

В рейтинге
2 259-й
Откуда
Киев, Киевская обл., Украина
Дата рождения
Зарегистрирован
Активность