вторник, октября 31, 2006

О самонадеянности

Мне просто нравятся загрузочные экраны Windows. Всё взято с http://www.guidebookgallery.org/screenshots/splash. Версия 1.0 у меня была. Я запустил её на компьютере с 386м процессором. Она поработала секунд 10 и сдохла с неясной диагностикойПишешь что-нибудь на пару раз попользоваться, оно разрастается, начинает использоваться людьми и теряешь возможность внести какие-то исправления, потому что, оказывается, на базе этого уже построили свою функциональность. Но это ещё не самое плохое. Владелец кода in-house разработки может сказать: всё, меня пробило на улучшения: всё поноют, но сделать ничего не смогут. Главное - не смогут помешать изменять. И, в зависимости от количества дипломатических оборотов, которые хочет и может потратить программист, замена лучшего на более лучшее (слышал я такой оборот как-то от барана неместного) пройдёт более или менее безболезненно.

2.0 у меня не было, врать не буду.Но если программа (пусть это даже и целая система) писалась в сумерках разума (не важно, по какой причине) и эта программа ушла в production!.. Чтобы умилостивить богов, 90% участников проекта просто обязано застрелиться на рабочих местах (в оригинале - в кабинетах). В самом деле. Допустим, что есть некая вполне тонкая библиотека, абстрагирующая пользователя от того, как выполняется работа с базой данных. Она берёт на себя управление транзакциями, соединениями и прочее всякое. Работает. При тестировании работает just fine. Все вызовы абстрактного сервера данных надёжно защищены критическими секциями, железобетонная корректность открытия и закрытия транзакций также не вызывает никаких сомнений. Работает. Казалось бы.

А теперь посмотрим.

public virtual object SelectScalar()
{
lock(m_CommitSync)
{
int threadId = ThreadId.Value;
object result = null;
try
{
GenericConnection.Open(SQLDataAdapter, threadId);
result = SQLDataAdapter.SelectCommand.ExecuteScalar();
}
catch (Exception ex)
{
DumpException(ex, SQLDataAdapter.SelectCommand.CommandText);
}
finally
{
GenericConnection.Close(threadId);
}

return result;
}
}

А вот с 3.0 я натешился. Нестабильность, в которую я сейчас даже не верю, явно обозначенная прожорливость, но при этом - работает! Первая моя программа для Windows была написана и собрана на 3.0То есть, шансов нарушить выполнение нет (см. lock в начале метода). + соединение всегда закроется, это с гарантией. Всё, в общем, работает отлично. Но заметили: если выпустить программу в дикую природу, то начинаются проблемы c deadlocks, "timeout expired prior to completion" и т.д. Библиотеку протестировали вдоль и поперёк. Трудно придумать, какой тест я не написал, чтобы удостовериться, что всё ok. Но оно не работает.

Так как я себе верю (а ещё больше - зелёной полоске), то библиотека выводится из круга подозреваемых. Остаётся:

  1. Процессор
  2. SQL Server
  3. Данные

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

  1. Неправдоподобно длинные методы для Insert, Update, Delete
  2. Для чего-то производятся постоянные выборки всех записей (ок. 600000) из одной популярной таблицы

Да, и ещё ThreadId.Value возвращало Thread.CurrentThread.ManagedThreadId;

Почти день я прозанимался этой проблемой, свалившейся на меня (всё время всё сваливается на меня) из FB (приоритет 1, конечно). Что я могу на её счёт сказать? Раз уж сегодня всё пронумеровано, то вот:

  1. Длинные методы писались без понимания того, как это будет работать. Всё-таки один клоун Вася (Петя, Коля, Вова и пр.), запуганный своим кодом до того, что боится даже лишний раз нажать на кнопку в готовой (по его мнению) программе - это одно, а толпа тёток, которые без прелюдий ошибаются (кто бы мог подумать) и закрывают окна, когда они начинают скрывать их пасьянс (не говоря уж о том, что им просто обязательно провести собственный обезьяний тест) - это нечто радикально другое. Когда тётки наваливаются на несчастную чахлую тушку программы и начинают её накрячивать вдесятером - тут и выясняется, что у программиста ваще не было представлений о том, что он пишет, для чего и кто использует.
  2. Излишняя, преступная и вредительская самонадеянность ("такого никогда не может случиться"). Тут и расшифровывать даже не хочу.

Код нельзя просто написать, скомпилировать и дальше неделю пить пиво и пялиться по выходным в телек на новую серию "The Sopranos". Это так примитивно, что неловко даже писать: код должен развиваться. Его надо пересматривать. Он не должен отражать ваше понимание проблемы двухмесячной или двухгодовой давности. Для историй у нас есть SourceOffSite, а для ностальгических соплепусканий ("ах, как всё было просто много лет тому назад"- хрена с два! просто никогда не было!) - http://www.guidebookgallery.org/screenshots/

суббота, октября 28, 2006

Снова про ошибки

Мне нравится, как исчезают ошибки из FogBUGZ. Сначала на мне их было 20 (и это только в одном проекте), сейчас их 3. И к понедельнику их будет 0. Некоторые исправлять очень трудно, но после исправления появляется труднопередаваемое ощущение, что что-то стало лучше. Я успел поработать во многих организациях, некоторые из них были сертифицированы по ISO9000 и CMMI 4 и нигде, ни в одной я не видел методического, систематического или какого угодно ещё подхода к исправлению ошибок. Даже к исправлению ошибок. Организация с сертификатом ISO9000 занималась бесконечными совещаниями на тему что лучше: .NET или J2EE (при этом я работал в команде, которая писала огромный кусок системы на C++, использовала DCOM и MSSQL2000 - куда тут засунуть J2EE?). Контора с CMMI 4 не имела ни одной спецификации, на которую новый человек мог бы посмотреть, чтобы понять, что от него ждут. В этой же организации мне пришлось принимать уже заваленный проект, который сдох потому, что у менеджера и "руководителя департамента" не было никакого образования: 1 курс литинститута, 1 курс меда, профессиональный опыт: 1 (одна!) база данных для сайта (можно себе представить, что это было такое) и сколько-то там форм на php (до сих пор не жалею, что не знаю, что это такое). В итоге $60,000 мы заработали, но ещё 20 тысяч долларов пришлось подарить, поскольку команда (на 90% состоящая из студентов) никак уже не могла решить проблем, которые на неё наваливались.

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

У Макконнелла вышла книжка "Профессиональная разработка программного обеспечения". Написана сухо и жёстко, но читать можно. У него я нашёл пару строк сатиры на деятелей разработки вроде З., который записывает непосредственно в офисе неизвестные ранее звуки му. Мне показалось, что это забавно:

Группа разработчиков бортового ПО для космических кораблей "Shuttle"... Там нет гор коробок из-под пиццы, пирамид банок из-под кока-колы, отвесных стен для альпинистов, площадок для катания на роликовых досках или подобных элементов пейзажа "продвинутых" организаций-разработчиков ПО. Там не в игрушки играют, а стремятся создавать безукоризненное ПО
Иногда любители качественно отдохнуть в рабочее время настаивают на том, что всё должно быть весело и "клёво". Мне почему-то кажется, что они отдыхают и раскайфовываются в рабочее время по единственной причине: работа завалена и никакого просвета в ней не видно; как ни вкалывай, только дети и получаются хорошими. Ну, в таком случае у ребят и правда нет выбора, кроме как отдыхать.

среда, октября 25, 2006

Анонс

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

понедельник, октября 23, 2006

SMO

Предыдущую блестящую, конгениальную и отточенную версию этого поста сожрал не то сам гугл, в лице своего агента blogger.com, не то microsoft посредством http404 в ie, а так как мне не достичь повторно такого просветления, то скажу просто, в одно предложение.

С DMO работать в .NET 2.0 невозможно, SMO не умеет правильно расставлять GO при скриптовании ограничений и ключей, а наша программа для записи живой копии БД (в виде 3х скриптов .sql) для включения в состав дистрибутива - чихать на это хотела и прекрасно работает.

суббота, октября 21, 2006

Мне не хватает указателей


Больше трёх лет я не работаю с указателями. И мне их не хватает. Java, C# и прочее - хорошие языки, но это не то, что требуется для получения удовольствия. Ну, какое удовольствие, в самом деле, от того, что не нужно писать delete или delete[]? А если при этом учесть, что сильные ссылки от событий надо разрывать вручную, через -=, то и вовсе задумаешься - для чего.
Мне нравилось писать многопоточные COMпоненты в ATL, выделять и забирать память. Мне нравилось то, что я знаю, для чего нужно выравнивание по границе, что компилятор создавал очень быстрый код по моим текстам и то, что указателями я был надёжно ограждён от проффесоров, которые не в курсе, что такое TLS, EAX и DMA.

пятница, октября 20, 2006

Отступники

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

Написана и давно работает библиотека, позволяющая рассматривать хранилище данных, например, базу MS SQL Server или Oracle, или набор XML-файлов, как совокупность объектов со связями. Это удобно, просто и понятно. Все обычно понимают суть спустя минут 5 после начала объяснения. Ну, к тому же мы написали генератор кода по определениям, как это сделали в своё время ребята из ThoughtWorks для библиотеки QuickFIX - для создания 5 классов, 3х процедур, одного view и таблицы требуется написать xml с описанием объекта и сгенерировать нужное. Итого - примерно минута or so. В общем, плодим сущности при всяком признаке надобности быстро и весело. И нам за это почти ничего не бывает, если только мы не отступаем от генеральной линии использования. Но некоторые случаи, такие, как тот, на исправление которого ушло 30 часов вместо 1го запланированного, вызывают у меня депрессию. Смотрите.

Объект со связями, находящимися в однотипной коллекции может понадобиться не всегда. Поэтому загрузка связей выполняется по требованию: если к свойству не обращаются, то и грузиться данные не будут (это экономия времени и памяти). Но если к свойству обращаются, то программист обязан придать результату вызова свойства детерминистичности: у одного и того же объекта вне зависимости от плотности потока космических лучей всякий вызов должен возвращать один и тот же набор данных. А также программист обязан не допускать деградации свойств объекта, удерживая объект на возможно более высокой ступени организации. Вроде два правила. Но что бывает, если от них отступить (а такие отступления всегда случаются в спешке, горячке, когда, по идее, ничего похожего и близко нельзя делать) - вот этого я бы никому не пожелал.

Отложенная загрузка выполняется так:


public Channel AssociatedChannel
{
get
{
if (m_Channel == null)
{
if (!IsNew)
{
IAdvancedRelationMining m = new RelationEngine();
m_Channel = m.GetOwners(this, null).First;
}
else
m_Channel = new Channel();
}
return m_Channel;
}
set
{
m_Channel = value;
MakeDirty();
NotifyPropertyChanged("AssociatedChannel");
}
}


То есть, при получении свойства надо удостовериться в том, что 2 условия выполняются:
1. Получаемое поле - null, т.е., к нему ещё не обращались
2. Объект, содержащий поле - не новый (он у нас новый, если его Id < 0)

Если нарушить вышеприведённые правила, например, позволить объекту-контейнеру деградировать с инверсией Id, то получить то же самое значение свойства, которое было у этого объекта с ешё не инвертированным Id - не получится: поведение становится недетерминистическим, как GETDATE() в T-SQL или сумма на чеке, который я приношу из магазина. Последствия отступления от правил:
1. Из отведённого на поиск и исправление ошибки 1(одного) часа потрачено 30 (тридцать)
2. Проблема заключалась в инверсии, применявшейся к объекту, у которого ещё не вызывалось это свойство, и соответствующее поле объекта было равно null.

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

среда, октября 18, 2006

"Разработка через тестирование"?

Александр зимой презентовал мне "Разработку через тестирование". Хорошая, с чувством написанная, я перечитал её пару раз, наверное. Ну и так, акцидентально, обращаюсь. Уже несколько лет мы стараемся применять тесты для получения дополнительных гарантий качества кода и иногда это неплохо получается. Всё, что может помочь упростить код или уменьшить его количество - применяется. Тесты не исключение. Но поскольку много лет назад, как и многие другие, я прочитал Мифический человеко-месяц, а после него - "... Серебряную пулю", то верить в то, что тесты - это то самое я не мог. И не зря. На днях была выпущена большая новая версия: много нового кода, много исправлений, в том числе и базы данных. Перед выпуском всё проверялось и неоднократно. Код проверялся тестами, интерфейс - протыкиванием, обновления базы данных - нашими утилитами обновления рабочих баз. После сборки msi мы проверили и его(её?). Версия под панфары была отправлена по адресу.

Утро следующего дня началось с письма:

После загрузки последного обновления вся моя работа пропала. Что делать?
Как она могла пропасть, если мы всё проверяли?! Правда, пользователь попался умный и не вредный - дал нашему автоматическому отчёту о сбое отправить состояние стека. 5 минут изучения, проверка обновления БД и вот: отсутствие обратной совместимости в очередном исправлении БД. У нас оно работало потому что база заполнялась-стиралась-заполнялась. Перед применением исправления она всякий раз оказывалась пустой и после того, как patcher её обновлял - программа работала как ни в чём не бывало. А у пользователя там были данные. Всё тоже обновилось, без ошибок, правильно. И умерло.

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

понедельник, октября 16, 2006

Об асинхронности в asp.net 2.0

Чуть больше года назад мы выпустили первую версию нашего продукта для сектора госуправления. По итогам опытной эксплуатации была добавлена функция, позволившая сэкономить пользователями сотни и даже, может быть, тысячи часов рабочего времени. Некоторые страницы содержали (и продолжают содержать) списки, состоящие из тысяч записей, с тенденцией к постоянному расширению. Загрузить такой список через web и тем более - найти в нём что-то это задача для людей, которые уже сделали все распоряжения на случай своего самовозгорания на работе. Мне лично трудно возиться со списком, в котором есть хотя бы 20 элементов, а если больше, то эту и такую программу я стараюсь больше не открывать. Очевидно, что наши пользователи не более терпеливы, чем я. Поэтому было решено пойти по пути гугла, а ещё раньше - программы, разработанной неизвестными героями в дебрях ФНС, которая подсказывала варианты по мере набора пользователем слова. Набрал пользователь "у", ему предложили в качестве варианта страны: "УГАНДА, УКРАИНА...". В списке, реализованном как div поверх текста, пользователь стрелками или мышью выбирает то, что ему нужно. Хорошо, удобно, быстро. Отвечал за эту часть общей работы я и сделал так:
1. В HTML указал, что буду использовать webservice.htc
2. Написал свой htc, в котором определил клиентский HTML-элемент управления, который умеет цепляться к строке ввода, получать оттуда данные, отправлять их веб-сервису, получать от него отклик и выводить в таком типа списке элементов с подсветкой.
3. Само собой, надо написать WS
Проверили, всем понравилось.

Ситуация с развёртыванием этой инсталляции была неидеальной, высокая текучесть администраторских кадров на объекте внедрения, общая суматоха, в общем, в результате многочисленных несанкционированных изменений конфигурационного файла веб-сервис стал работать через раз. Мы, поддавшись на обещания одной компании, решили использовать механизм асинхронных вызовов из ASP.NET 2.0 В самом деле, если это такой революционный, радикальный и драматический прорыв, то почему бы не воспользоваться.

Надо сказать, что в эту болтовню я с некоторых пор не верю - очень мало есть компаний, если они вообще есть, в которых маркетологи и рекламисты понимают, что делают технические люди
Целью, которую мы преследуем, является исключение веб-сервиса из списка компонентов, так как в каждой точке расширения какой-нибудь гений администрирования всё сделает и настроит обязательно не так; функциональность WS передаётся обратно в ASPX. Переделка на самом деле проста.
1. В списке наследования страницы указывается ICallbackEventHandler
2. Реализуются 2 его метода, public void RaiseCallbackEvent(string eventArgument) и public string GetCallbackResult()
3. В первом методе выполняется разбор строки, сформированной в браузере, во втором браузеру асинхронно отдаётся ответ.

Сделать это было всё несложно и вообше - дело пяти минут, я дольше вспоминал, как получить поля из web-формы c использованием JavaScript: работа велась автоматически и бессознательно. Когда всё было сделано, я осмотрелся и память услужливо предложила свой вариант увиденного: 1998й год, лето. Я пишу на asp маленькую задачу с получением результата поиска билетов без перерисовки страницы у клиента. RSExecute, RSEnableRemoteScripting и прочее, кто понимает. Прошло 8 лет, даже 8 с половиной и ничего не изменилось, всё то же самое. Клиентские скрипты, серверные скрипты, один шлёт, другой слушает... Где радикальная новь?

Поэтому приходится признать, что в плане асинхронности 2.0 предлагает не больше, чем ASP 9 лет назад. Это не хорошо, и не плохо. Это - правда.

суббота, октября 14, 2006

Категории ПО

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

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

У нас в работе находится несколько проектов. Часть из них (если быть точным, то 3)- коробочные. Два - то, что он называет корпоративным или то, что я называю - одной инсталляции. Для определённости посчитаю прибыль в спичках, учитывая, что в коробке примерно их 40 штук. Первые мы продаём (все суммы условные, отражающие только порядки) или будем продавать по цене от половины (гравицапа поспички стоит!) до трёх спичек. Вторые меньше, чем за три коробка мы продавать не намерены. При этом, несмотря на исследование рынка, которое проводилось перед началом работ над приложениями из первой группы, мы можем с равной вероятностью продать 10 и 1000 копий. Вторые мы уже продали. 40 спичек * 3 * 2 = 240 спичек. Значит, для того, чтобы получить столько же, сколько мы получили от двух инсталляций в организациях, нужно в среднем продать 240 коробок с программами первой группы. Стоит ли ради этого привести эти 2 инсталляции в состояние, сравнимое по качеству и удобству с коробочным ПО? Ведь над коробочными решениями мы стараемся, как в последнем бою, даже не зная ещё, сколько продадим. Спольский говорит, что это не выгодно, так как это не отобьётся. Возможно, что в Америке это так. Программисты по 100 тысяч в год, дикое по сравнению с нашим налогообложение и пр. Но у нас - я могу спорить на деньги - отдавать качественное ПО даже в случае единичных инсталляций --это выгодно.

Пробиться на рынок корпоративных продаж архисложно. Уж не сравнить, во всяком случае, с типичным путём зарабатывания денег на shareware:
а) Выложить на shareware.ru свою программу
б) ждать поступления денег на счёт.
Деньги могут повалить тоннами, если ты угадал или не поступить на счёт никогда, если программа оказалась не нужна никому или проигрывает по набору функций уже имеющемуся. Но, скорее всего, сумма будет невелика в любом случае. Потому что во-первых, $19.99, а во-вторых - Асталависта (и это просто удивительно, насколько плохо программисты защищают свою прибыль). Да, я отвлёкся. Пробиться на корпоративный рынок трудно, но если пробился, то можно существенно увеличить норму прибыли, с обычных 20-30 до сотен процентов. Я помню, как в одном проекте, ещё когда я работал не в нынешней своей компании, за баннер одна американская корпорация заплатила $500. За один единственный баннер. И этих баннеров были десятки. Такой же баннер для малобюджетного сайта Клуба любителей вислоухих кроликов обойдётся дай бог чтоб хотя бы в $5 и то, на условиях его бесплатной переделки по требованию пользователей любое количество раз в течение полугода. Выгодно ли делать в условиях финансового дождя негодное по качеству ПО? Ведь обе стороны понимают, что то, что предлагается, просто не стоит своих денег. Другой вопрос, конечно, откуда берутся такие суммы. Но я никогда не заседал в советах директоров и совершенно без понятия, почему на крупных заводах нет бесплатных автобусов для рабочих, обед стоит для них денег, а $600000 за базу данных из двадцати таблиц и Java-интерфейс для редактирования некоторых полей - не считается безумной тратой огромных денег. Может ли быть в таких условиях невыгодным шлифовать этот интерфейс до идеального состояния? Каковы могут быть причины этого? Я считаю, что не может. Как и причин для этого. Потому что получая большие деньги и не давая пользователю ощущения качества, надёжности и стабильности продавец роет себе яму и вкапывает в её дно острые колья. О том, что паршивое нестабильное и подверженное сбоям ПО - признак некомпетентности и важный знак необходимости ухода с рынка - я уж и не говорю.