вторник, 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). Она как! :)

Отправить комментарий