*** ВНИМАНИЕ: Блог переехал на другой адрес - demin.ws ***

пятница, 4 февраля 2011 г.

Стереть и переписать все нафиг!

Какая традиционная первая реакция на явно старомодный или просто дрянной код или проектное решение? «Стереть к чертовой матери и написать заново на [подставить любимый язык]».

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

Вот и возникает волшебное слово «legacy» код.

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

Ну и что, что Фортран с common-переменными повсюду или С, ну что, что там код 80-х годов с функциями на десятки экранов без каких-либо зачатков unit-тестирования, и потрогать этот код просто страшно. Но трогать его не надо, а надо понять интерфейс и изолировать его.

Лично у меня подобное «прозрение» случилось только недавно, примерно аналогично как с unit-тестированием. Слово «legacy» код не должно синонимом «стереть и переписать все нафиг!», а должно быть знаком того, что надо сделать взвешенную оценку поддержки старого «тлеющего» кода против новой разработки. Любой код начинает тлеть сразу поле релиза, но когда он вам уже достался тлеющим – это другая история.

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

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

В общем, если хотите рассматривать создание программных продуктов не просто как написание «идеального кода», а как бизнес, работа со старым кодом – это один из навыков, который придется освоить.

среда, 26 января 2011 г.

Fossil - контроль версий, баг-трекер и wiki в одном флаконе

Есть множество бесплатных хостингов для проектов с открытым кодом. Также есть места, где можно держать и закрытые проекты, но такие хостинги часто платные. Да и в целом, если уж есть какой-то закрытый проект, и есть причины его закрытости, то выкладывать его на хостинг третьих лиц было как-то нелогично.

Лично я люблю git для UNIX и mercurial для Windows. Каждая система имеет пачку удобных хостингов, как говорится, выбирай на вкус.

И вот у меня образовался закрытый проект, который мало того, что активно развивается, так еще и накапливает баг-репорты, приправленные файлами отчетов и картинками, и требует ведения документации.

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

Fossil - это распределенный контроль версий, баг-трекер и wiki в одном флаконе. Более того, его автор -- ни кто иной, как автор SQLite, борец за минимализм, простоту и надежность. Как и в случае с небезызвестной базой данных, которую кто уже только не использует, все, что вам нужно - это один единственный файл -- fossil[.exe].

Для командной строки - это просто SCM, а будучи запущенной с параметром "ui", превращается в локальный веб-сервер, в котором есть "морда" для просмотра репозитория, баг-трекера и wiki. Более того, все данные живут также в одном единственном файле-репозитории, который по сути является SQLite-базой. Для переноса его в другое место, другую операционную систему или резервного копирования, нужно просто скопировать один файл.

fossil умеет импортировать и экспортировать в git, поэтому я сначала перегнал существующий репозиторий из mercurial в git, а потом импортировал из git в fossil.

В целом, fossil хорош. Вылизанный и минималистичный. Говорят, что из-за использования SQLite в качестве хранилища, с одной стороны получаешь надежность и транзакционность любых изменений (понятно, что хоть остальные системы работают просто с файлами, у них с целостностью тоже все в порядке), но с другой стороны, по скорости может радикально проигрывать git или mercurial на больших проектах. Но для небольших "домашних", но секретных проектов - сложно представить удобнее утилиты.

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

Да, лицензия у fossil, конечно, BSD.

Итак, для дома для семьи -- очень удобно.

вторник, 25 января 2011 г.

Баг в компиляторе LCC

Благополучно нарвался на баг в компиляторе LCC.

c:\lcc\bin\lcc -v

Logiciels/Informatique lcc-win32 version 3.8. Compilation date: Dec  4 2010 13:14:58

Файл: t.c

1
2
3
4
int main() {
    char* p;
    char* s[1] = { p };
}
c:\lcc\bin\lcc t.c
Error t.c 3 Compiler error (trap). Stopping compilation

Обычно ж как бывает, начинает проявляется "баг компилятора" -- программа ведется себя странно, исключения почему-то не ловятся, наблюдаются неожиданные падения программы и т.д. В подавляющим случаев, увы, все кончается просто ошибками работы с памятью. Ничего сверхестественного.

Баги же типа этого, проявляющиеся на тривиальном примере - это всегда событие.

Из недавнего:

четверг, 20 января 2011 г.

Вопросы на интервью, на которые нельзя не знать ответы

Так сложилось, что за последние полгода, я активно участвовал в процессе интервьирования программистов в компании Bloomberg.

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

К сожалению, отказывать приходится порой из-за радикально тривиальных вещей, незнание которых просто несовместимо с профессией.

У меня накопилось несколько вопросов, незнание ответа на которые является почти стопроцентной причиной, когда я в своем отчете писал отказ.

Интервьюирование было на позицию "Senior C/C++ developer”.

Ответы приведу тут же, так как они очевидны.

  1. Сколько примерно будет 2^32? (обычно задается по телефону)

Ответ "Около четырех миллиардов" является исчерпывающим.

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

  1. Как сравнить две переменные типа double или float на равенство? (обычно задается по телефону)

Ответ "Вычесть одно из другого и сравнить результат на больше/меньше с каким-то малым числом, например 10E-6" является исчерпывающим. Конечно, много зависит от используемой библиотеки работы с числами с плавающей точкой, но смысл, в целом, одинаков.

Увы, количество неотвечающих тоже весьма значительно.

  1. (Хит!) Что распечатает данная программа? (не забываем, что собеседование на позицию разработчика на C/C++). В принципе, его тоже можно задать по телефону.
char* f() {
  char buf[100];
  strcpy(buf, "TEST”);
  return buf;
}

int main() {
  char* s = f();
  /* (1) */
  printf("%s\n", s);
}

Ответ: "Нельзя сказать с уверенностью, скорее всего мусор, но в целом это неопределенное поведение, так как локальный буфер формально прекращает существование после выхода из функции f()" является почти исчерпывающим.

Почему "почти"? Потому что обычно за ним дополнительный вопрос: "Что именно может с высокой вероятностью затирать заветное слово TEST и приводить к выводу мусора? Для конкретности: платформа x86, 32-bit, компилятор Visual Studio. Если остановить программу отладчиком в точке (1) и посмотреть, на что указывает указатель "s", то очень высока вероятность, что там будет таки "TEST", а вот printf() таки с высокой вероятностью распечатает мусор. Почему?".

Более половины собеседований, в которых я участвовал, заканчивались со знаком "минус", так как человек даже не делал попытку сказать что-то типа "В данных условиях скорее всегда слово TEST будет перезатерто параметрами функции printf(), которые передаются через стек и ложатся на то место, где был раньше размещен буфер "buf". Конечно, многое зависит от режимов оптимизации, так как аргументы могут быть переданы через регистры."

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

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

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

А у вас есть вопросы, "неответы" на которые вы лично можете считать поводом для практически однозначного отказа?

Самые глупые ответы на интервью, что я когда-либо давал

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

  1. Как-то яростно пытался доказать, что тип int является за 100% атомарным без каких-либо гвоздей на платформе x86. Не доказал (и вот почему).
  2. Вопрос: «При множественном виртуальном наследовании в С++, кто и как должен заботиться о правильном однократном вызове конструктора базового класса (подразумевается, что у этого конструктора есть параметр)?». Я сказал, что это компилятор, и вдобавок еще не смог объяснить почему.
  3. Ну и лидер нашего хит-парада. Вопрос: «В С++ таблица виртуальных функций принадлежит классу или экземпляру класса?» Барабанная дробь: я сказал, что каждый экземпляр одного класса имеет собственную копию таблицу. Почему я так сказал, я до сих пор не знаю.

Все это еще раз подтверждает факт того, что на нервах можно сморозить такое, что потом сам будешь думать о причинах сказанного.

вторник, 18 января 2011 г.

Алгоритмы на C++ и Java

Не побоюсь повториться -- две совершенно волшебные, на мой взгяд, ссылки на реализации множества алгоритмов на C++ и Java.

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

Компилятор языка С: LCC-WIN32

Потребовалось мне тут прикрутить к замечательной программе putty подсветку синтаксиса на лету в терминальной сессии. И так вышло, что на новом рабочем ноутбуке я пока еще не успел поставить Студию. У putty есть makefile’ы для Visual Studio, Borland’а, Cygwin’а и LCC. Первых двух у меня не было, и взять их было негде, Cygwin недолюбливаю из-за необходимости таскать с собой потом dll’ки, и чудом выбор пал на LCC. До этого я никогда этот компилятор не использовал.

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

Про библиотеку хочу сказать отдельно. Помимо стандартного набора libc и Win32 API, там полно всего остального. Лично я был несказанно удивлен простой, и порой столь нужной функцией ping() (и не надо больше вызывать ping.exe в скрытом окне).

В общем, с помощью также идущих в комплекте регулярных выражений, я быстро подхачил putty как мне было нужно. Попутно проронил ностальгическую слезу от программирования оконного интерфейса на чистом Win32 API и ощутил некоторые приятности С99. Например, объявление переменных не в начале блока, а где удобно, и размер автоматических массивов задавать не статически, а из переменной. C99 однозначно стоит внимательного изучения.

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

gl.h OpenGL
sqlite.h  
bignums.h Работа с числами произвольной точности
bitstring.h  
bluetoothapis.h  
d3d.h  
d3dx.h  
dynloader.h Работа с DLL’ками
gc.h Сборщик мусора (требует запуска, конечно)
getopt.h  
icmpapi.h  
int128.h  
matrix.h Работа с векторами и матрицами
mq.h IBM MQ
msi.h  
netmon.h  
netsh.h  
pcre.h Регулярные выражения в стиле Perl
ping.h PING!
ras.h  
regexp.h Простой API для регулярных выражений (regcomp() и regexec())
snmp.h  
sqlite3.h  
str.h Работа со строками в стиле C99
tapi.h  

Итак, если вам быстро нужен небольшой компилятор (дистрибутив всего шесть мегабайт), для написания программы на С99 под Windows (для графического интерфейса придется все делать на чистом Win32 API), имеющий в комплекте в дополнение к libc и Win32 API приличный набор разнообразных библиотек, то LCC – это очень сильный кандидат.

Кстати, отдельно можно скачать и 64-битную версию компилятора.

Единственное, чего я не пробовал – это линковать объектники LCC с другими компиляторами. Кто имеет опыт – поделитесь.