вторник, 4 августа 2009 г.

[comp.flame] Correct by construction говорите?

Многие верят, что существуют или же могут быть придуманы методики и приемы разработки качественного софта. На просторах программистких форумов апологеты функционального подхода пытаются доказывать это неудобочитаемыми фрагментами Haskell-ового кода, твердя одну и ту же мантру: “Написанная однажды корректная чистая функция остается корректной всегда”. Методологи разработки ПО не останавливаются и плодят все новые методологии. Вот и сейчас Айвар Якобсон заявляет, что создает новую теорию, которая должна позволить создавать great software. Блажен, кто верует, однако.

Почему я так скептически отношусь к этому? Да потому, что никто не принимает в расчет внезапных “затмений”, которые случаются у разработчиков. Но которые оказывают очень и очень сильное влияние на качество кода.

В качестве примера приведу фрагмент кода, который мой коллега вчера раскопал в недрах переписываемого нашей командой проекта:

unsigned long f( unsigned long a ) {
 while( (1 << 24) < a )
  a -= (1 << 24);
 return a;
}

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

Наша версия – оставить в числе только младшие 24-ре бита. Т.е занулить 8-мь старших бит в 32-х битовом числе. Зачем для этого пришлось писать цикл с вычитанием и нельзя было воспользоваться обычным битовым AND-ом (ну или хотя бы остатком от деления) – тайна сия велика есть.

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

Может ли любой хороший программист совершить подобный ляп программируя даже на самом супер-пупер функциональном языке в рамках самой мега-супер-пупер методологии? Да запросто!

PS. А ведь бывает и более сурово. Когда ступор настигает тебя не при написании кода, а еще при разработке алгоритма. Вчера я у себя нашел очень серьезный баг. В процедуре выборке новых строк из БД и распределении их по заинтересованным в них клиентам я допустил ошибку из-за которой часть строк могла быть помечена как обработанные, но не попасть к клиентам. Причем ошибка была именно в алгоритме и мне повезло на нее наткнуться, поскольку счастливым для меня образом в одном из тестов совпали нужные количества строк, клиентов и размеры пакетов для клиентов. А если бы не совпали? Пришлось бы кому-нибудь лет через шесть приводить в своем блоге облагороженный кусок моего кода с вопросом “какой кретин эту байду написал и в каком он был состоянии?!”… :(

Update. После публикации этого фрагмента на govnokod.ru в нем выяснилась интересная особенность. Если младшие три байта числа отличны от нуля, то он действительно обнуляет старшие байты (например: 0x3000001 -> 0x2000001, 0x1000001, 0x000001). А вот если три младших байта равны нулю (т.е. число кратно 1^24), то оно сокращается до 1^24 (например: 0x3000000 -> 0x2000000, 0x1000000). Она как! :)

6 комментариев:

Miroslav комментирует...

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

зы буквально вчера нечто подобное ( ошибка в алго) случайно поймал сосед в моем коде. Там проверка "нет ничего в данном окне в данном слое" была заменена на "нет ничего в данном окне", что естественно работало на тестах тк много слоев в тестах "во всех сочетаниях" не бывает.

eao197 комментирует...

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

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

Didro комментирует...

Относительно свежая статья в тему, что называется - http://lambda-the-ultimate.org/node/3543
Рассказывает об языке Guru (http://code.google.com/p/guru-lang/), который как раз предлагает подход proven-by-construct.

eao197 комментирует...

За последние два месяца я написал что-то в районе 10K строк кода на C++ для решения одной, в общем, не сильно сложной задачи. В которой используется несколько процессов, взаимодействие с несколькими БД, логирование, мониторинг, конфигурирование. Сильно сомневаюсь, что все это было бы возможно на языке, который бы заставлял доказывать что и был к тому же чистым функциональным языком. Но сделанное мной, как я надеюсь, обеспечит мне нормальное существование на какое-то время в будущем. Так что, похоже, пока я занимаюсь суровой практикой, где-то там развивают теорию в виде COQ, Guru, да и того же Haskell-я. Когда эти наработки дойдут до мейнстрима, я уже точно буду старым толстым старпером предпенсионного возраста.

И я не верю в то, что языки типа Haskell-я будут играть в ближайшие годы хоть сколько-нибудь заметную роль. А гибриды вроде Scala/F#/OCaml не сильно далеко ушли от C++/Java. И не предполагают correction by constructions.

Kodt комментирует...

Запости этот щедевр на govnokod.ru, пусть люди порадуютя.

eao197 комментирует...

Отправил :)