пятница, февраля 29, 2008

Тяжелый день 29 февраля

Сегодня упали модульные тесты на сборке. Потому что 29 февраля.
4 года назад тесты тоже падали, по той же причине, хотя и проект был другой и работал я в другом месте...
Тяжелый день.

5 инструментов

Эстафета "5 инструментов без которых я не могу работать продуктивно" пришла ко мне от Юрия Волкова (Deepen C++).
Оговорюсь сразу, что, во-первых, я буду говорить только о программных инструментах (без телефона и принтера я тоже не могу работать продуктивно). Во-вторых, речь пойдет не о работе на компьютере вообще, а именно о работе архитектора - проектировщика. Итак:
1. Outlook. C этим инструментом у меня сложные отношения. Outlook мне никогда не нравился, но два с половиной года назад я попал - на новой работе Outlook корпоративный стандарт. Теперь весь мой рабочий день вертится вокруг этой штуки, и со временем я стал находить, что работу свою он делает хорошо. Помимо почты, это еще и организация митингов, резервирование переговорок, интегрированный hrelp desk, система папок проектной переписки и т.д. Кроме того, вся моя личная корреспонденция редиректится на Gmail, а оттуда дублируется на корпоративный Exchange, и в конце концов сваливается в Outlook. После того как я настроил синхронизацию календарей Outlook с телефоном (ActiveSync), стал реже опаздывать на митинги.
2. Virtual PC (до этого был VMWare Workstation, но опять же - корпоративные стандарты). Постоянно использую виртуальные машины, на диске живет целый выводок образов различных ОС и конфигураций. Задачи приходится решать самые разные: тестирование развертывания продуктов на разных окружениях, всевозможные proof of concepts - проверки реализуемости различных решений, тестирование и изучение новых продуктов, и т.д. и т.п.
3. Rational Rose. Пробовал много разных инструментов моделирования, но классика победила :)
4. Notepad. Как это не покажется странным но по частоте использования обычный блокнот вообще вне конкуренции. XML и SQL, конфиги, логи, скрипты и нередко исходники - все читаю и правлю в блокноте. Конечно хорошо было-бы иметь подсветку синтаксиса и спелчекер для борьбы с моей хронической неграмотностью, но невозможно все это таскать за собой от компьютера у компьютеру. А блокнот есть везде...
5. Visual Studio. Одновременно на разных проектах приходится использовать три версии VS2003 VS2005 и VS2008. К сожалению программировать в последнее время приходится все меньше, но все равно считаю студию выдающимся инструментом для программиста, а в VS2008 еще и для архитектора появились неплохие инструменты.

Хотелось бы услышать с чем работают:
not-a-kernel-guy
kuklaora
Ленивый программист
ivaliy(хотя ему сейчас, видимо, не до того...)
Бороздин Андрей

P.S. Да, совсем забыл SVN с Тартилой. Без них работа станет. Но свободные номера в списке уже закончились...

четверг, февраля 21, 2008

Agile и Offshore development.

Нашел в MSDN статью Андрея Филева (Andrew Filev) Успешное внедрение гибких методик разработки в оффшорной разработке программного обеспечения.
Честно говоря не ожидал прочитать что либо подобное в MSDN. Статья больше об offshore, чем об agile. Особо рекомендую прочитать тем, кто продолжает считать офшорные разработки чем-то вроде работы второго сорта.

Подборка материалов по WCF

В процессе сбора информации по WCF у меня сформировалась большая подборка ссылок. Негоже добру пропадать, решил я.
По ссылкам статьи (большинство в русском переводе) из MSDN, MSDN Magazine, GotDotNet.ru и из блогов.
По моему про WCF написано уже все :) Осталось все это прочитать :))

1.Эффективные методики управления экземплярами в WCF-приложениях

2.Discover Mighty Instance Management Techniques For Developing WCF Apps(оригинал предыдущей статьи на английском)

3.Serialization in Windows Communication Foundation

4.Security in Windows Communication Foundation

5.What You Need To Know About One-Way Calls, Callbacks, And Events

6.Service Factory для WCF

7.Создание службы ответов WCF в очереди(рус.)

8.Защитите приложения ASP.NET и службы WCF с помощью Windows CardSpace

9.Основные положения системы обмена сообщениями WCF

10.Распространение транзакций WCF

11.Подробно об адресации WCF

12.WCF Bindings In Depth

13.Декларативная безопасность WCF

14.Расширение служб WCF за пределы HTTP с помощью WAS

15.Контексты синхронизации в WCF

16.Расширение WCF при помощи настраиваемых поведений

17.Программирование HTTP с использованием WCF и .NET Framework 3.5

18.Интеграция блока Policy Injection Application Block со службами WCF

19.Что нового для WCF в Visual Studio 2008

20.Learn The ABCs Of Programming Windows Communication Foundation

21.Обработка ошибок в Windows Communication Foundation (WCF)
Первый взгляд на Windows Communication Foundation

22.Обзор архитектуры Windows Communication Foundation

23.Интеграция Windows Workflow Foundation и Windows Communication Foundation

24.Размещение и использование служб Windows Communication Foundation

25.Windows Communication Foundation (Compact Edition) and the story of the Lunch Launcher

понедельник, февраля 18, 2008

Результаты опроса "Что важнее для успеха в разработке ПО"

Опрос закончился. Приняло участие 88 человек. Итак в чем же, по мнению сообщества, роются секреты успешной разработки софта?
65 (73%) человек отметили хорошую постановку задачи.
59 (67%) человек выделили важность хороших программистов
42 (47%) человека (менее половины) верят в важность хорошего менеджмента
39 (44%) человек отмечают архитектуру
34 (38%) человек считают важными тесты
10 ((11%) человек отметили важность методологии.

В результате важность перечисленных аспектов распределилась следующим образом:

Согласно общественному мнению секрет успеха прост. Надо взять хороших программистов, толково объяснить им поставленную задачу и половина успеха уже у вас в кармане. Для полной уверенности в том, что все пойдет как надо, можно поставить на проект хорошего менеджера. Если не забыть про архитектуру и тесты, то успех будет полным. А методология, это штука из разряда nice to have, главное чтоб работать не мешала :)

Что я хочу сказать от себя. Меня определенно радует сфокусированность подавляющего большинства (73%) на требованиях. Ну а мои личные предпочтения распределились следующим образом:
1. Хороший менеджер, и менеджмент в целом. Мне, как человеку техническому, потребовалось определенное время (годы) для того, чтобы осознать, техническое совершенство продукта несмотря не на что играет в достижении успеха важную но все же вторичную роль. Проект - это в первую очередь управление, без него даже самый совершенный код останется бесполезными килобайтами на жестком диске.
2. Хорошая постановка требований. Требования - это наше все. Нельзя создать хороший софт не зная точно, что он должен делать.
3. Хорошая архитектура. Технический аспект важен. но всего лишь на третьем месте.
4. Хорошие программисты. Программисты нужны :) Причем нужен хотя бы один, способный обеспечить выполнение пункта 3. Не более того. Есть очень малое число проектов. для которых действительно нужны выдающиеся программисты.
5. Хорошее тестирование. Без тестирования невозможно выпустить хороший продукт. Это как воздух, без воздуха нельзя жить, но никто не говорит о роли воздуха в достижении счастья.
6. Хорошая методология очень важна. Но методология определяется менеджементом (извините за некрасивое слово). Методология - это стиль управления. Не столь важно какой будет стиль, главное чтобы управление было эффективным.

P.S. Диаграма построена при помощи Google Chart API

воскресенье, февраля 17, 2008

Простой дизайн

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



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

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

Другой подход утверждает, что опытный проектировщик руководствуется не только текущими требованиями при создании дизайна системы. Привлекая свой опыт, он может создать дизайн, способный удовлетворить требованиям, которые могут появиться в дальнейшем при развитии системы. Так сказать, дизайн на вырост. Благодаря этому можно избежать переделок дизайна в ходе развития системы. Сторонники этого подхода также предпочитают использовать готовые framework-и, фабрики ПО, мотивируя это тем, что «там уже есть все необходимое».

Один из коллег высказал такую мысль:
«Это, в общем, вопрос компромисса:
Слишком частные решения как правило ограничивают масштабируемость приложения и увеличивают затраты на переработку. В том числе переработку архитектуры.
Слишком общие решения могут безосновательно увеличить стоимость разработки и снизить производительность.
Так что тут больше вопрос интуиции.
Начинающие разработчики, как правило, делают первую ошибку, разработчики среднего уровня — вторую»


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

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

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

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

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

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

Очень сложно получить хороший дизайн сразу и окончательно, хотя бы потому, что это всегда результат компромисса. Еще сложнее получить хороший дизайн, когда помимо реально существующих требований мы закладываем в него возможности «на будущее». Наоборот, выделив главное, отбросив несущественное, у нас появляется гораздо больше шансов создать предельно простой дизайн, который в тоже время наилучшим образом удовлетворяет самым важным требованиям. Наиболее ярко этот подход проявился в методологии XP, в знаменитом принципе YAGNI (You Aren't Gonna Need It) – «тебе это не пригодится».

Оппоненты данного подхода обычно говорят: «зачем я буду несколько раз переделывать свою систему, когда я сразу могу внести в нее все необходимые свойства, благо есть архитектурные паттерны, framework-и, software factory и т.д.». Ответ прост. Проектирование процесс «грязный» (подверженный ошибкам) и итеративный. Создать «сразу» окончательный дизайн невозможно. Поэтому наиболее выигрышной стратегией будет не «предвосхищение требований», а «ожидание изменений».

Совершенно показателен в этом плане пример использования готовых библиотек и «фабрик ПО». Несколько лет назад Microsoft начал публиковать так называемые Application Blocks, исходные коды библиотек для решения типовых задач, возникающих при построении приложений: конфигурирования, логирования, кэширования, бизнес логики и т.д. Это были действительно интересные библиотеки, которые обобщали наиболее передовой опыт. Затем все эти блоки были сведены в Enterprise Library. Это уже был своего рода каркас для построения распределенных корпоративных приложений. Сейчас на основе Enterprise Library созданы Software factory – библиотеки дополнены шаблонами и мастерами design time для Visual Studio. Само название «Фабрика ПО» подразумевает, что она позволяет максимально упростить и ускорить создание приложений, предоставляя каркас содержащий «все необходимое». На практике получается не все так гладко. Разработка идет очень быстро и хорошо, до тех пор, пока не возникает задача, на которую не были рассчитаны базовые библиотеки. Тут приходится вносить изменения в эти библиотеки, а поскольку они рассчитаны на максимально широкий класс задач, то они имеют достаточно сложный и нетривиальный дизайн, и вносить изменения в них весьма не просто. Это повторяется из проекта в проект – резкий старт, огромная экономия времени на рутинных аспектах и затем такой же резкий стопор в разработке, погружение в детали библиотек, переделки, а зачастую дублирование для реализации недостающих функций. В результате проект занимает примерно столько же времени, как и при разработке с нуля. А дизайн системы представляет собой огромный блестящий каркас, заполненный функционалом едва ли на десятую часть, окруженный достаточно неприглядными подпорками.

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

А как же framework-и и фабрики ПО? Это замечательные вещи, но использование их в проекте, особенно впервые, это значительный риск. И в тоже время, если вы уже использовали тот или иной каркас, вы зачастую можете обнаружить, что именно для вашего конкретного случая этот каркас, как говорится, именно то, что доктор прописал. Вот в чем роль опыта :) И в тоже время каркасы, это просто кладезь best practice архитектурных решений и поэтому они достойны всяческого внимания и изучения.

понедельник, февраля 11, 2008

«Пилите пули, Шура, они серебряные…»

или "В цепких лапах библиотек"


Поиски серебряной пули в разработке ПО продолжаются.
Сегодня читаю в «Блоге об IT бизнесе» Виктора Ронина статью «Серебряная пуля в разрезе». Если в прошлых постах автор искал источники повышения производительности разработки софта в мотивации программистов, то теперь он ищет серебряную пулю в open source software. Вот несколько цитат:

«Однако, я напоследок оставил то, что действительно является «серебряной пулей».
Это – библиотеки. Точнее не только библиотеки, но и разнообразные платформы. За последние двадцать лет появилось гигантское количество открытого доступного кода или хотя бы платформ с открытыми интерфейсами.
...
2008 год: RFID ворота на складе, подключенные к машинам на которых установлен Ubuntu с программой, которая выгребает данных с RFID считыватели и по сети закатывает в БД. На сервере крутится программа, которая выгребает данные из БД и через SOAP загружает их в Sales Force Automation...

Дай бог 0.01% [второй] системы написано программистом, а остальные (сайт sales force, библиотеки по работе с RFID, база данных, tcp/ip, xml, soap, графический интерфейс, броузер и т.п.) досталось ему фактически забесплатно. Если бы он пытался написать все сам (даже «срезая углы») у него это заняло бы всю жизнь.

...мы и не заметили серебряной пули, хотя она находится у нас под носом."


Хоть Виктор и цитирует Брукса, но, похоже, старик его не слишком убедил.
Повторное использование кода, библиотеки функций и переносимые компоненты - все это, наряду с интегрированными средами разработки и языками высокого уровня, является основным двигателем прогресса в программной инженерии за последние 20 лет.

С одной стороны, прогресс вроде бы есть. Ранее интерфейс типичного бизнес приложения мерцал зелеными буквами и цифрами на мониторе PDP терминала, и представлял собой последовательность меню в стиле «Нажмите 1 для просмотра баланса; Нажмите 2 для пополнения счета; Нажмите Esc…». Сегодня, мы можем мышкой перетаскивать активы из Нью-Йоркского филиала в Нижегородский, на интерфейсе, представляющем собой географическую карту, и смотреть отчеты в своем мобильном телефоне. Т.е. тысячи новых возможностей, интерактивность, мобильность, красочность и т.д.
С другой стороны, прогресса никакого и нет. Как требовалось раньше 10 человеко-лет на разработку среднего бизнес приложения, так и сегодня требуется 10 человеко-лет. Как строился квартальный отчет 45 минут, так он и строится 45 минут.

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

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

  • несоответствие дизайна системы и подключаемой библиотеки. Наверное, все писали обертки, враперы, дополнительные слои и т.д. Яркий пример COM и .Net

  • наличие ошибок в библиотеках и компонентах. И обязательно эта ошибка проявляется именно у вас.

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

  • неэффективное использование библиотек из-за недостаточного знания ее внутреннего устройства. Например, мы можем, по незнанию использовать один экземпляр на все приложение интерфейса «Соединение» из внешней библиотеки, и получим кучу проблем с блокировками, просто потому, что мы не знали о том, что наилучшей стратегией использования этой библиотеки является создание отдельного экземпляра «Соединения» для каждого вызова.


Я думаю, все сталкивались с подобными проблемами. Многие склонны списывать эти проблемы на «кривые ручки» тех, кто создает библиотеки и компоненты, либо на собственные «кривые ручки». Некоторые уверены, что по настоящему «классные» компоненты лишены всех этих недостатков. Есть также мнение, что библиотеки и компоненты с открытым кодом способны решить все эти проблемы. Это очень распространенное мнение, и я с ним не согласен. Сейчас попытаюсь объяснить почему.

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

  • компоненты с открытым кодом имеют меньше ошибок. Даже если это и не так, у нас есть исходный код, и мы можем быстро исправить ошибку.

  • компоненты с открытым кодом открыты (извините за тавтологию), мы можем добавить нужный нам функционал, выкинуть не нужный.


Все это так. Но все это несущественно для решения тех проблем, которые порождаются использованием компонентов. Дело в том, что библиотеки и компоненты не в силах побороть трудности создания программных систем, обусловленные основными свойствами этих систем: сложностью, согласованностью, изменяемостью и незримостью [Брукс, Мифический человеко-месяц, глава 16]. Как бы мы не делили код на библиотеки, он все равно будет скомпилирован, слинкован и выполнен в едином адресном пространстве. Мы ничего не можем сделать с изменчивостью, - меняется внутреннее устройство библиотек, меняется интерфейс – это объективно. Мы не можем отменить согласованности, совместно работать могут только согласованные интерфейсы. Если мы используем не ту версию компонента, мы получаем runtime error. И все мы знаем, что такое “dll hell”. В этом плане, не имеет никакого значения, доступны исходные коды компонент в процессе создания программы, или не доступны.

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

Многим из вас, вероятно, приходилось сталкиваться с системами, как бы застывшими в своем развитии. Вот стоит система, созданная на основе «Турбо-Москаль» версии 5.1. «А чего вы не переведете ее на новую версию, ведь давно вышел «Гипер-Москаль» версии 9.0?». «Да ребятам там в пятом «Турбо-Москале» много чего пришлось переделать руками. Когда попробовали перейти на восьмую версию - даже не скомпилировалось. А ребята то уже уволились. Стали смотреть, что они там наменяли, но ты же знаешь, в пятом «Москале» двадцать мегабайт исходников. Разве там разберешься!». Знакомая картина?

Поэтому при использовании библиотек с открытым кодом используют специальные практики. Свои изменения необходимо, абсолютно необходимо ввести в общий код библиотеки (компонента). А для этого вы должны пойти на сайт сообщества, которое разрабатывает нужный вам компонент, и зарегистрировать в багтрекере (bugtracker) или в бэклоге (backlog) проекта баг (bug) или фичу (feature) или ишью (issue) - по обстоятельствам. Затем вам надо подключиться к репозиторию исходников проекта и взять рабочую копию исходного кода. Потом внести свои изменения в код, протестировать, интегрировать, затем создать патч (patch) и отправить его координатору проекта. Если координатор сочтет, что ваш патч удачен (а если не сочтет?), он включит его в код проекта и он войдет в следующий unstable релиз. Только так вы избавите себя от проблем с неподдерживаемыми изменениями в исходном коде сторонних компонент. Но вы почувствовали, как запахло жареным? Нет? Вы чувствуете, что вы начали заниматься разработкой компонента с открытым кодом, вместо того, чтобы заниматься разработкой своей системы. Оно вам надо!

В общем, что компоненты без исходных кодов, что с исходными кодами – проблем с ними предостаточно. Конечно аргумент «если писать все самому, это займет всю жизнь» принимается и с ним никто не спорит. Но если рассматривать проблему шире, ведь бизнес приложения живут десятилетиями, живут и изменяются, то ясно, что библиотеки это просто один из инструментов, позволяющий нам создавать то, что называется «современное программное обеспечение». Не больше и не меньше. Как было 10 человеко-лет на систему, так и осталось.

Ну а что же может повысить эффективность создания программного обеспечения? Я думаю то, что помогает преодолевать основные факторы сдерживающие эффективность: сложность, изменчивость, связанность и неосязаемость. Заменить подключаемые библиотеки на связываемые автономные сервисы (слышали про SOA), для борьбы со сложностью. Вместо монолитных компилируемых приложений использовать распределенные, конфигурируемые в runtime для борьбы со связанностью и изменчивостью. Либо более радикальные решения в этом же направлении, связанные с применением динамических языков, позволяющих изменять интерфейсы взаимодействия непосредственно во время исполнения. С неосязаемостью программного обеспечения и связанными с этим проблемами помогают бороться проектирование на основе моделей (Model Driven Design) и разрабатываемые методики статического и динамического анализа кода. Конечно, решение одних проблем породит новые. Но мы научимся за стандартные 10 человеко-лет, создавать такие программы, о которых сейчас даже не мечтаем.
А серебряной пути нет.

суббота, февраля 09, 2008

Реальные оценки

Возвращаясь, в очередной раз, к теме оценки сроков проектов (project estimation), я хочу поговорить не о точности, техниках и методиках оценки, а совершенно о других вещах. Я хочу поговорить о честности и смелости.

Первоначальная оценка проекта практически всегда происходит на фоне конфликта интересов. Менеджер (и в его лице вся проектная команда) заинтересован в том, чтобы все было долго и дорого. Это понятно, ведь «вес» менеджера прямо пропорционален размеру бюджета, который он осваивает. Заказчик хочет, чтобы все было быстро и дешево. Ситуации, когда разработка проекта ведется в условиях практически неограниченного бюджета, чрезвычайно редки.
Как делается оценка проекта. Сначала менеджер собирает технические оценки, которые делают программисты. Затем, он учитывает некоторые показатели, вроде размера команды, времени на обучение и отпуска и т.д. и выдает окончательную оценку.
Когда заказчик внешний, то оценка проекта, это фактически предмет торговли, со всеми вытекающими. Проект надо продать подороже, но не перегнуть палку, чтобы заказчик не ушел. Либо заказчик очень важен для компании, и необходимо начать проект любой ценой.
Когда заказчик внутренний, то обычно все еще хуже, чем с внешним. Внутренний заказчик, это чаще всего начальник менеджера или начальник начальника. Какие уж тут торги.

Вот тут самое время поговорить о честности и смелости. Ведь что часто происходит? Начинается новый проект, менеджер приносит оценки - «это можно сделать командой в восемь человек за девять месяцев». На что получает немедленный и категоричный ответ начальника: «Любой аутсорсер сделает это в два раза быстрее и в два раза меньшим количеством людей». Чем не повод поговорить о смелости и честности?
Бывает, конечно, и не так плохо. Вместо категоричного ответа можно услышать что-то вроде: «Вы знаете, что у нас ограничения по бюджету. Вот если бы вы смогли сделать это за шесть месяцев командой из шести человек, было бы здорово». В любом случае менеджер проекта (или просто программист, если проект маленький) испытывает на этом этапе очень сильное давление со стороны начальства, заказчика, отдела продаж, и т.д.

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

Существует и другой путь, под девизом «Главное начать. А потом они никуда не денутся». Есть мнение, что следует соглашаться на сокращение предварительных оценок, чтобы склонить руководство или заказчика к тому, чтобы начать проект. После того как проект будет начат и в него будут вложены определенные средства, то им придется довести его до конца, несмотря на перенос сроков. А объективные причины, объясняющие причины сдвига сроков мы всегда найдем. Надо сказать, что это весьма и весьма распространенный подход. Это путь смелый, но не честный, и надо сказать весьма рискованный. Соглашаясь сделать проект на $250 000, отчетливо сознавая, что потребуется не менее $500 000, вы подставляете человека, принимающего решение о старте проекта (будь то ваш начальник или представитель заказчика) на серьезную сумму, и если по результатам проекта вам придется всего лишь расстаться с работой - это можно считать довольно легким исходом.

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

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

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

Практически всегда, предметом торговли может быть объем функционала. Можно пожертвовать второстепенными функциями, можно пересмотреть системные требования. Например, можно отказаться от поддержки многоплатформенности, или кроссбраузерности, обычно это позволяет значительно сократить затраты.

В случаях, когда критичны сроки, но позволяет бюджет, можно увеличить размер команды. Если это делать в начале проекта, а не тогда, когда проект уже провален, это помогает.

И, наконец, просто напрашивается вариант изменить методику ведения проекта. Вместо того, чтобы мучительно согласовывать сроки проекта и объем функционала, можно воспользоваться гибкими методологиями, вести проект инкрементально. Выпустить как можно раньше версию с минимальными функциями и представить ее заказчику. А далее повторить планирование и определить круг функций для следующего релиза. Это позволяет снять саму причину противоречия между сроками и объемом функционала. Когда через месяц вы представите заказчику первую версию продукта, которая, вероятно, будет выполнять всего одну или две функции из требуемого набора, это изменит многое. Закзчик видит овеществленный результат вложения своих денег. Продукт еще не функционален, но он есть, и если реализовать фичу А и фичу Б, то это будет уже что-то. Когда через пару недель он видит новую версию продукта с реализованными фичами А и Б, заказчик получает уверенность в том, что он может управлять процессом и получить в итоге именно то, что ему требуется.
Конечно вы все равно не сможете сделать все в урезанные сроки. Но к назначенному времени ситуация будет коренным образом отличаться. При традиционном подходе мы бы имели незаконченный проект с неясными перспективами. А в нашем случае у нас на руках работоспособный продукт, в котором реализованы еще не все функции. Как говорится почувствуйте разницу.

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

пятница, февраля 08, 2008

Entity Framework - Identity, Computed и ключевые столбцы

Таблицы БД могут содержать столбцы, которые изменяются в момент сохранения изменений на стороне БД. В SQL Server это столбцы IDENTITY и вычисляемые (computed) столбцы. Как с такими таблицами работает Entity Framework?

Рассмотрим фрагмент Storage Schema (это Xml описание схемы БД в составе EDM)



<EntityType Name="Invoice">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="int" Nullable="false" StoreGeneratedPattern="Identity" />
<Property Name="Customer" Type="nvarchar" Nullable="false" MaxLength="50" />
<Property Name="Cost" Type="money" Nullable="false" />
<Property Name="Discount" Type="money" Nullable="false" />
<Property Name="Total" Type="money" StoreGeneratedPattern="Computed" />
</EntityType>



Это описание таблицы Invoice. Столбец “Id” объявлен identity, а столбец “Total” - это вычисляемый столбец ([Cost]-[Discount]). Мы видим, что визард при генерации EDM добавил в описания этих столбцов атрибут StoreGeneratedPattern. Именно этот атрибут сигнализирует EF о том, что значение данного столбца вычисляется в БД.
StoreGeneratedPattern имеет три значения: “None”, “Identity”, “Computed”. Значение “None” принято по умолчанию. “Computed” обозначает, что столбец содержит вычисляемое значение, и при вставке и обновлении EF вместо того, чтобы передавать значения для этого столбца в БД, получает его значение после обновления. “Identity” соответственно обозначает столбец Identity. По смыслу он практически идентичен значению “Computed”, но заставляет EF генерировать специальный SQL код для получения значения этого столбца после вставки:


select [Id] from [dbo].[Invoice]
where @@ROWCOUNT > 0 and [Id] = scope_identity()


Свойства классов, замапленные на вычисляемые столбцы хот и имеют аксессор set, никогда не сохраняют свои значения в БД. Что бы мы ни присваивали свойству Invoice.Total после сохранения изменений, там будет записано значение, прочитанное из БД.

В этом отношении свойства классов, которые входят в состав Entity Key - ключа сущности, отличаются еще более странным поведением. Логично было бы предположить что Invoice.Id будет вести себя подобно Invoice.Total. Поскольку это свойство замаплено на identity столбец, то какие бы значения мы ему не присваивали. После сохранения мы получим значение, установленное БД. Это так лишь отчасти:


01 using (TestEntities context = new TestEntities())
02 {
03 Invoice inv = new Invoice();
04 inv.Id = 1;
05 inv.Total = 10;
06 context.AddToInvoice(inv);
07 context.SaveChanges(true);
08 Console.WriteLine(inv.Id);
09 inv.Id = 1000;
10 context.SaveChanges(true);
11 Console.WriteLine(inv.Id);
12 }


В приведенном коде мы сперва создаем экземпляр Invoice, а затем присваиваем значения его свойствам Id и Total. Затем мы сохраняем изменения в БД. После сохранения, в строке 8 мы видим в свойстве Id значение назначенное БД. Однако, когда в строке 9 мы пытаемся изменить значение Id то получаем исключение:

"The property 'Id' is read-only because it is part of the entity's key."

Вот так. В строке 4 присваивание Id проходит нормально, а здесь сваливается с исключением. Изменять значения ключевых свойств (свойств, которые входят в состав ключа) можно только, если состояние объекта “Detached” или “Added”. Такое поведение появилось только в beta 3. В предыдущем CTP изменения значений ключевых свойств тупо сохранялись в БД или игнорировались для identity столбцов.

четверг, февраля 07, 2008

Опрос - что важнее всего в разработке ПО?

Разрабатывать софт интересно, но сложно. У каждого из нас есть свой положительный и отрицательный опыт в этом деле. Что, по вашему мнению, наиболее важно для успеха в разработке ПО? Форма для голосования слева на сайдбаре.
Голосуем в течении недели, а потом посмотрим результаты, и я расскажу, что самое важное по моему мнению. Заодно и сравним :)

P.S. Просьба указывать три, ну максимум четыре варианта, иначе опрос теряет смысл.

вторник, февраля 05, 2008

Entity Framework - производительность

Для всех, кого интересует производительность Entity Framework, рекомендую почитать пост Brian Dawson в ADO.NET team blog - "Exploring the Performance of the ADO.NET Entity Framework - Part 1".

Там нет сравнительных характеристик, но зато подробно расписано из чего складывается время выполнения запроса, что кэшируется и как, и каким образом это время можно сократить. Очень интересные вещи, понимание которых, помогает писать правильный код. Например, Object context construction занимает 1.38% времени при самом худшем раскладе. А загруженные в MetadataWorkplace метаданные EDM сохраняются в глобальном кэшэ (на уровне приложения). Это, например, означает, что нет никакой надобности кэшировать контекст в ASP.Net сессии, а лучше создавать свой экземпляр для каждого ASP.NET запроса.