суббота, 19 июня 2010 г.

[prog.flame] Выдерну фразу Чарльза Петзольда из контекста

Ув.тов.Alex поделился ссылкой на статью Чарльза Петзольда от 2005 года “Does Visual Studio Rot the Mind?” Позволю себе выдернуть оттуда два предложения:

But Visual Studio is not interested in having you write good code. It wants you to write code fast.

Что в моем переводе на русский язык будет звучать так:

Но Visual Studio не заинтересована в том, чтобы вы писали хороший код. Она хочет, чтобы вы писали код быстро.

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

пятница, 18 июня 2010 г.

[prog] Обалденная презентация Роба Пайка о языке Go

Another Go at Language Design – 56-ти страничные слайды одноименного доклада Роба Пайка от 28 апреля 2010.

Роб Пайк очень умный мужик. Презентация сделана что надо. Читаешь и просто ощущаешь в себе растущее желание попрограммировать на языке Go. Спасает только то, что его пока еще не зарелизили :)

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

…Просмотрел презентацию, впечатлился. Потом полез в старый C++ный исходник чтобы продумать добавление в него новой функциональности. А там и STL-левские контейнеры, и мой шаблонный код, и использование сторонних библиотек, и всего этого до фига, и все это работает… Да, подумалось мне, каким бы хорошим и продвинутым Go не стал со временем, вряд ли мне придется это код когда-нибудь на Go портировать :)

PS. Еще один прикольный момент из презентации. Сейчас апологеты новых языков любят приводить примеры того, как кусок проекта переписали, скажем, на Scala и код получился в N раз короче, чем на Java. Ну это понятно – новички против старичков. А вот в презентации озвучен отзыв одного из пользователей Go: он переписал программку из 6K строк на Scala на Go и получил аналогичный результат всего 3K строк на Go. Вот так вот! Новички против новичков! :) Запасаемся попкорном ;)

PPS. Ссылка на презентацию была найдена здесь: http://blog.golang.org/2010/05/new-talk-and-tutorials.html

четверг, 17 июня 2010 г.

[prog] Возвращаясь к спору о важности борьбы с ошибками

Продолжение темы, начатой несколько дней назад. Расскажу о том, почему спор с лисперами в лице тов.archimag и тов.vsevolod оставил у меня плохие впечатления.

Отправной точкой стало заявление archimag-а:

vshabanov> ты не очень себе представляешь, от каких ошибок может защитить система типов

archimag> Просто не верю в сказки. Unit-тесты кроме улучшения дизайна код и изменения стиля разработки также ещё способствуют и предотвращению появления ошибок. Зачем нужно что-то ещё "в обычном программировании" мне не понятно.

Тут я привел archimag-у два примера: добавление в язык Eiffel механизма обеспечения т.н. void safety и планируемое добавление в C++0x ключевого слова noexcept. И попросил archimag-а объяснить, как же с помощью unit-тестов обеспечить такие же гарантии, как два этих изменения в системах типов языков Eiffel и C++. Archimag не ответил, ссылаясь на то, что он не понимает, зачем все это вообще нужно, что у него подобных ошибок никогда не было и т.д. Хотя ответ тут очевиден – unit-тесты в принципе не могут дать гарантии отсутствия конкретного типа ошибок.

Поскольку я это заварил, то расскажу от каких именно ошибок данные нововведения в Eiffel и C++ защищают. Примеры, для простоты, будут приводить на Java-подобном псевдоязыке.

Суть void safety в том, что на уровне языка обеспечивается отсутствие обращения к объектам по нулевым ссылкам. Что для языков вроде Eiffel, Java, C# и т.д. даже важнее, чем для C++. Поскольку в этих языках любой объект ссылочного типа должен быть создан через new. А раз new вызывает программист, то программист легко может забыть это сделать. Откуда и порядочное количество исключений NullPointerException в Java.

Представьте, что у вас есть класс с несколькими полями и несколькими конструкторами. В каждом конструкторе вам следует инициализировать ссылочные поля:

class Demo
   {
      SomeReferenceType firstField;
      SomeAnotherReferenceType secondField;
      ...
      YetAnotherReferenceType lastField;

      public Demo() {
         this.firstField = new SomeReferenceType();
         this.secondField = new SomeAnotherReferenceType();
         ...
         this.lastField = new YetAnotherReferenceType();
      }

      public Demo(BunchOfInitializationParams params) {
         this.firstField = new SomeReferenceType(params./*что-то-там*/);
         this.secondField = new SomeAnotherReferenceType(params./*что-то-там*/);
         ...
         this.lastField = new YetAnotherReferenceType(params./*что-то-там*/);
      }

      public Demo(AnotherInitializationParams params) {
         this.firstField = new SomeReferenceType(params./*что-то-там*/);
         this.secondField = new SomeAnotherReferenceType(params./*что-то-там*/);
         ...
         this.lastField = new YetAnotherReferenceType(params./*что-то-там*/);
      }
   }

Чем больше атрибутов будет в вашем классе, тем больше шансов, что какой-то из них вы забудете проинициализировать. Такой атрибут получит значение null и первое же обращение к нему приведет к NullPointerException. Особенно часто подобные ошибки у меня возникают при сопровождении старого кода – вводишь новый атрибут, в нескольких местах его инициализацию добавляешь, а где-то забываешь. А потом хоп! И при каком-то наборе входных данных приложение падает.

Так вот void safety позволяет компилятору бить вас по рукам при попытке обращения к ссылке (указателю), которая не была проинициализирована (или может быть null-ссылкой по замыслу разработчика).

Лично я вижу здесь большое преимущество перед unit-тестами. Хорошо, когда есть unit-тесты, покрывающие 100% исходного кода. Такие unit-тесты позволят быстро выловить null-ссылку. Но как часто мы имеем такое покрытие тестами? Как дорого оно обходится при разработке? А при сопровождении? Теперь сравните с тем, что компилятор вообще не дает нам обращаться к нулевым ссылкам. Т.е. совсем.

Теперь по поводу noexcept. Как я понимаю, это воплощение в C++0x того, что когда-то в моем блоге уже обсуждалось. Если noexcept будет нормально проверяться компилятором в compile time, то с помощью noexcept будет гораздо проще писать безопасный по отношению к исключениям код. В первую очередь, в C++ можно будет получать гарантию не бросающих исключений функций swap(). Но есть и еще одна, не менее важная штука: облегчение написания обработчиков исключений. Например, пишем мы блок catch и должны вызывать в нем функцию cleanup для очистки ресурсов. Как понять, должны ли мы писать catch так:

private void cleanup() {
   cleanFirstResource();
   cleanSecondResource();
   ...
}
try {
   /* Какие-то действия */
}
catch(SomeException x) {
   cleanup();
   /* Остальные действия по обработке исключения */
}

или же нам нужно писать так:

try {
   /* Какие-то действия */
}
catch(SomeException x) {
   try {
      cleanup();
   } catch {
      /* Сделать все равно ничего не можем, поэтому
       * просто выбрасываем возникшее исключение. */
   }
   /* Остальные действия по обработке исключения */
}

Если мы вызовем cleanup как в первом варианте, то что делать при сопровождении, когда кто-то разрешит cleanFirstResource бросать исключение?

Так вот noexcept как раз защищает нас в этой ситуации. Стоит объявить cleanup как noexcept и компилятор уже не должен позволить вызывать в ней функции без noexcept. Поэтому, если кто-то модифицирует cleanFirstResource и начнет бросать оттуда исключения, то наш cleanup попросту не будет скомпилирован.

Я решил привести именно такой пример полезности noexcept потому, что именно такой “граничный” код, который вызывается только в особенных случаях, очень тяжело тестировать. И чем больше компилятор помогает нам в этом, тем лучше.

После неудачного общения в ЖЖ тов.thesz, дискуссия переместилась в блог archimag-а. Там речь зашла о такой ошибке, как целочисленное переполнение. На что vsevolod заявил, что поскольку в Lisp-е целочисленные переменные имеют возможность автоматически расширяться в зависимости от значения, то в Lisp-е ошибок с переполнением int-ов быть вообще не может. Понятное дело, что я тут же привел контрпример, который вообще не зависит от языка программирования. Но от него отмахнулись. Мол, это частный случай, который ничего не доказывает, а в общем случае никаких проблем нет.

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

Вероятность возникновения ошибки – это совсем не тоже самое, что вероятность повстречать динозавра на улице. К ошибкам, думаю, нужно относиться по другому. Дело не в вероятности ее возникновения – закон подлости (в купе с законом бутерброда и генеральским эффектом) обеспечит ее устойчивый рост вплоть до единицы. Т.е. ошибка рано или поздно проявится. Гораздо важнее время ее возникновения и ее последствия.

В качестве примера последствий хочу рассказать про свою самую памятную ошибку с переполнением целочисленных переменных (точнее, с переходом через 0 к UINT_MAX для unsigned int переменной).

Сделал я тогда специальный компонент в нашем шлюзе коротких сообщений – он обеспечивал нормализацию всплесков трафика от партнеров, чтобы не переполнять выделенные нам каналы. Т.е. если пришлет провайдер сразу 1000 сообщений, то мы плавно распределим эту тысячу в течении нескольких секунд по каналу с пропускной способностью 200 сообщений в секунду. Проработал этот компонент несколько месяцев, даже под суровой нагрузкой. Но однажды ночью меня разбудил экстренный звонок из службы техподдержки. Мол, один из наших каналов заткнулся, очереди сообщений от провайдеров пухнут, а ничего по каналу от нас не идет. Пока я проснулся, пока шарманку раскочегарил, пока разобрался что к чему… В общем, минут 30 канал стоял колом. За что мы потом гневный факс от клиента получили. А дело оказалось в том, что при определенной ситуации с тайм-аутами и отрицательными ответами счетчик активных транзакций переходил через ноль в обратную сторону. Понятное дело, что такого быть не должно было, по замыслам-то. Но случилось.

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

Кстати, это практически закономерность: если в системе 24x7 внезапно проявляется какой-то ядреный баг, то происходит это почти всегда почему-то ночью ;)

Напоследок приведу еще одну цитату из archimag-а:

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

К сожалению, я не знаю, что archimag называет “нормальным процессом разработки”. Но если в это понятие входит строгая дисциплина с выделенными ролями архитекторов, разработчиков, тестировщиков, с круглосуточно работающими continuous integration серверами, с централизованными bug tracker-ами и прочими атрибутами промышленной разработки ПО… То лично мне кажется, что очень все это недешево. И если бы средства программирования позволили бы уменьшить объем тестирования и сократили бы время на локализацию и устранение ошибок, то разработка ПО стала бы обходиться дешевле. К счастью, потихоньку в этом направлении и движемся.

[bugs] Вот такой вот WSJ’s Photo Journal сегодня

Обычно в WSJ’s Photo Journal публикуются фотографии небольшого размера (порядка 960 пикселей по горизонтали). Поэтому было очень удивительно увидеть там сегодня просто огромные по меркам Photo Journal-а снимки (более 4000 пикселей по ширине). Вот как это выглядело в Opera в режиме масштабирования (чтобы самая большая картинка помещалась в окно):

Маленькая картинка под снимком с пожарным – это изображение нормального для Photo Journal размера.

Вот только не могу с уверенностью утверждать, косяк ли это верстальщиков WSJ или же очередной глюк Opera.

среда, 16 июня 2010 г.

[work] Особенность работы с крупными клиентами

При работе с крупными клиентами (масштаба МТС или Сбербанка) есть одна неприятная особенность – их спецов очень трудно убедить в том, что они неправы. Особенно в случае, когда они действительно неправы.

К сожалению, это не анекдот. Иногда кажется, что ты должен знать раза в два-три больше коллеги с другой стороны для того, чтобы найти убедительные аргументы в защиту своей точки зрения. Поскольку логика в их словах простая: “У нас все работает. Таких клиентов, как вы, у нас море. И у них все работает. И лишь у вас не работает. Так что ищите проблему на своей стороне.” Уж не знаю, что на людей так действует – громкое имя собственной конторы, большая ответственность или бюрократия, или же конкурентная среда внутри крупной шараги делает людей профессионалами в поиске крайних. Но явление имеет место быть.

Вот и сегодня завершилась почти двухнедельная эпопея поиска проблемы с одним очень из себя VIP клиентом. Сначала менеджеры поставили на уши меня, затем я поставил на уши большую половину нашей службы техподдержки. Мы вместе и по отдельности угрохали туеву хучу времени. Чтобы админы клиента поменяли настройки на своей стороне и все после этого заработало. Оно конечно радостно, и груз большой с плеч упал, но настроение какое-то паршивое…

[life.humour] Перл из рекламы

Сегодня в телевизионной рекламе услышал замечательную по своей маразматичности фразу: “Даже если у вас нет лишних килограммов, но вы боретесь с полнотой…”

вторник, 15 июня 2010 г.

[life.sport] Когда же эти дебилоиды на стадионах перестанут дудеть в вувузелы?

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

понедельник, 14 июня 2010 г.

[prog.thoughts] По следам разговоров о важности ошибок в программах

В конце прошлой недели довелось встрять в несколько споров, в которых ряд товарищей-лисперов (уж не знаю, случайно это или нет) высказывались в том плане, что борьбе с ошибками уделяется незаслуженно много внимания. Началось все в ЖЖ Сергея Зефирова (aka thesz), откуда я выцепил замечательную цитату. Продолжение последовало в блоге Андрея Москвитина (aka archimag).

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

Пока же меня поразили две вещи:

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

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

2. Постоянно говорится о каких-то прикладных областях, в которых ошибки в софте не представляют большой угрозы. Я честно пытался представить себе, что это за области. Это точно не системное программирование. Это не телекоммуникации. Это не системы АСУТП. Это не банковсковский или биржевой софт (уж очень сильный резонанс вызывают ошибки в каких системах). Это не встроенное ПО. Вряд ли это игры (хотя я здесь полный дилетант). Вряд ли это коробочный софт – все таки пользователи не будут долго платить за глючный хлам.

Что остается? Какой-нибудь бесплатное для пользователей ПО. Вроде интернет-браузеров. Когда сбоит GMail или в очередной раз падает Opera – это неприятно. Но тут уж ничего не поделать – бесплатный сыр он и есть бесплатный.

Может быть это какой-то внутрикорпоративный софт? Пишет себе контора какой-нибудь складской учет. Ну будет подглючивать время от времени АРМ у зав.сладом. Ну пожалуется, ну исправят. Главное, что он данные вводить в БД может. А то ведь требования постоянно меняются, новые типы товаров, новые виды отчетности. Программа должна успевать за ними. Если успевает, то глюки можно потерпеть. Вероятно, речь идет именно о таких проектах (косвенным подтверждением я считаю рассказ о разработке софта для сети гипермаркетов).

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

Хотя, с другой-то стороны… Если лично я буду озабочен качеством производимого мной кода, то я буду более ценным специалистом. Так что лучше я продолжу борьбу с ошибками, и буду дальше культивировать в себе нетерпимое отношение к ним. А остальные пусть сами решают, хотят ли они конкурировать с такими как я :)