<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>developers.org.ua</title>
	
	<link>http://www.developers.org.ua</link>
	<description>сообщество программистов</description>
	<lastBuildDate>Mon, 08 Feb 2010 09:42:29 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<feedburner:info uri="developersorgua" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://developers.org.ua/feed/" /><feedburner:emailServiceId>DevelopersOrgUa</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><feedburner:browserFriendly>This is an full-text XML content feed for developers.org.ua. It is intended to be viewed in a newsreader or syndicated to another site, subject to copyright and fair use.</feedburner:browserFriendly><item>
		<title>Интересные ссылки №203</title>
		<link>http://feedproxy.google.com/~r/DevelopersOrgUa/~3/gtrQnOCSGIo/</link>
		<comments>http://www.developers.org.ua/archives/max/2010/02/05/weekly-linkdump-203/#comments</comments>
		<pubDate>Fri, 05 Feb 2010 10:30:51 +0000</pubDate>
		<dc:creator>Макс Ищенко</dc:creator>
				<category><![CDATA[Ссылки]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[DSL]]></category>
		<category><![CDATA[Facebook]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[md5]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[Марк Пилгрим]]></category>
		<category><![CDATA[опцион]]></category>
		<category><![CDATA[пароль]]></category>
		<category><![CDATA[стартап]]></category>

		<guid isPermaLink="false">http://www.developers.org.ua/index.php?p=6071</guid>
		<description><![CDATA[Интересные ссылки за неделю: How To Safely Store A Password, калькулятор для опционов, новая глава из книги Dive Into HTML5,  репликация в PostgreSQL, Nine Things Developers Want More Than Money, транслятор PHP->C++ от Facebook, javascript DSL for building async web apps.]]></description>
			<content:encoded><![CDATA[<p>Интересные ссылки за неделю:</p>
<ul>
<li>Оказывается, использовать хеш-функции типа md5 или даже sha1 для паролей это уже некомильфо &#8212; слишком быстро подбирается, <a href="http://codahale.com/how-to-safely-store-a-password/">How To Safely Store A Password | codahale.com</a></li>
<li>Если вас зовут в стартап, вот готовый &#8220;калькулятор&#8221; для ваших опционов, <a href="https://code.google.com/p/startupequitysimulator/source/browse/trunk/payout.py">payout.py &#8211; startupequitysimulator</a></li>
<li>Марк Пилгрим опубликовал новую главу из своей книги HTML5, на этот раз про geolocation, <a href="http://diveintohtml5.org/geolocation.html">You Are Here (And So Is Everybody Else) &#8211; Dive Into HTML5</a>. Прекрасно, как и всегда.</li>
<li>Следующий мажорный релиз PostgreSQL будет поддерживать репликацию, <a href="http://www.depesz.com/index.php/2010/02/01/waiting-for-9-0-streaming-replication/">Waiting for 9.0 – Streaming replication</a></li>
<li><a href="http://www.softwarebyrob.com/2006/10/31/nine-things-developers-want-more-than-money/">Nine Things Developers Want More Than Money | Software by Rob</a> &#8212; многовато слов, но в общем верно</li>
<li>Facebook выложил в Open Source свой транслятор PHP-&gt;C++, <a href="http://developers.facebook.com/news.php?story=358&amp;blog=1">HipHop for PHP: Move Fast</a></li>
<li>На JavaScript уже делают вещи, которые нормальному девелоперу не понять, <a href="http://www.fabjs.org/">(fab) &#8211; a pure javascript DSL</a></li>
<li>В прошлую субботу в Киеве прошел pycamp. Мои впечатления и отзывы других участников, <a href="http://maxua.posterous.com/-pycamp">http://maxua.posterous.com/-pycamp</a></li>
</ul>
<br/><a href="http://www.developers.org.ua/archives/max/2010/02/05/weekly-linkdump-203/#ratings">Оценить статью на сайте</a> | <a href="http://www.developers.org.ua/archives/max/2010/02/05/weekly-linkdump-203/#comments">0 комментариев</a>]]></content:encoded>
			<wfw:commentRss>http://www.developers.org.ua/archives/max/2010/02/05/weekly-linkdump-203/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.developers.org.ua/archives/max/2010/02/05/weekly-linkdump-203/</feedburner:origLink></item>
		<item>
		<title>Интересные ссылки №202</title>
		<link>http://feedproxy.google.com/~r/DevelopersOrgUa/~3/VLb84siIBYc/</link>
		<comments>http://www.developers.org.ua/archives/max/2010/01/29/weekly-linkdump-202/#comments</comments>
		<pubDate>Fri, 29 Jan 2010 16:53:50 +0000</pubDate>
		<dc:creator>Макс Ищенко</dc:creator>
				<category><![CDATA[Ссылки]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[gamedev]]></category>
		<category><![CDATA[geek]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[usenet]]></category>
		<category><![CDATA[интерфейс]]></category>
		<category><![CDATA[производительность]]></category>

		<guid isPermaLink="false">http://www.developers.org.ua/index.php?p=6057</guid>
		<description><![CDATA[Интересные ссылки за неделю: Linux performance basics, замечательные cookies из подписей Erik Naggum, Geek behaviors present during conversations, Amplifying C, Realism in UI Design, зачем Линус придумал концепцию staging для git.]]></description>
			<content:encoded><![CDATA[<ul>
<li><a href="http://spyced.blogspot.com/2010/01/linux-performance-basics.html">Linux performance basics</a> &#8212; если вы админите хотя бы простенький сайтик, это точно пригодится</li>
<li><a href="http://www.xach.com/naggum/usenet-sigs.txt">http://www.xach.com/naggum/usenet-sigs.txt</a> &#8212; замечательные cookies из подписей Erik Naggum</li>
<li><a href="http://www.stanford.edu/%7Epgbovine/geek-behaviors.htm">Philip Guo &#8211; Geek behaviors present during conversations</a> &#8212; какая-то заумь, но вроде интересно</li>
<li><a href="http://voodoo-slide.blogspot.com/2010/01/amplifying-c.html">voodoo slide: Amplifying C</a> &#8212; почему (некоторые?) гейм-девелоперы не любят С++</li>
<li><a href="http://ignorethecode.net/blog/2010/01/21/realism_in_ui_design/">ignore the code: Realism in UI Design</a> &#8212; излишняя реалистичность элементов интерфейса затрудняет их понимание пользователями</li>
<li><a href="http://plasmasturm.org/log/gitidxpraise/">In praise of git’s index // plasmasturm.org</a> &#8212; зачем Линус придумал концепцию staging для git</li>
</ul>
<br/><a href="http://www.developers.org.ua/archives/max/2010/01/29/weekly-linkdump-202/#ratings">Оценить статью на сайте</a> | <a href="http://www.developers.org.ua/archives/max/2010/01/29/weekly-linkdump-202/#comments">3 комментариев</a>]]></content:encoded>
			<wfw:commentRss>http://www.developers.org.ua/archives/max/2010/01/29/weekly-linkdump-202/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://www.developers.org.ua/archives/max/2010/01/29/weekly-linkdump-202/</feedburner:origLink></item>
		<item>
		<title>Что будет с зарплатами в 2010 году?</title>
		<link>http://feedproxy.google.com/~r/DevelopersOrgUa/~3/_ryaTUtI2Ts/</link>
		<comments>http://www.developers.org.ua/archives/max/2010/01/27/salaries-2010-forecast/#comments</comments>
		<pubDate>Wed, 27 Jan 2010 09:29:08 +0000</pubDate>
		<dc:creator>Макс Ищенко</dc:creator>
				<category><![CDATA[О работе]]></category>
		<category><![CDATA[зарплаты]]></category>
		<category><![CDATA[прогноз]]></category>
		<category><![CDATA[рынок]]></category>

		<guid isPermaLink="false">http://www.developers.org.ua/index.php?p=6042</guid>
		<description><![CDATA[Движение зарплат будет определяться спросом на рынке. С девальвацией гривны в 2009 ключевую роль будет играть рынок аутсорсинга и те компании, которые на нем работают.  Часть компаний вероятно попытается переориентировать на внешний рынок и, таким образом, будут увеличивать предложение, а значит снижать (удерживать) рейт для заказчика. ]]></description>
			<content:encoded><![CDATA[<p>В октябре мы проводили <a href="http://www.developers.org.ua/archives/max/2009/10/12/job-market-report/">исследования зарплат программистов в 2009 году</a> (по данным анкетирования пользователей сайта). Напомню, по данным опроса, долларовые зарплаты в 2009 оставались примерно на одном уровне с небольшими колебаниями. Наши данные это подтверждают:</p>
<p><img src="http://www.developers.org.ua/wordpress/wp-content/uploads/2010/01/salary-ukr-10-50-90.png" alt="график зарплат" width="832" height="455" style="padding:0;" /></p>
<p><small>Динамика изменений минимальной (10й перцентиль), средней (медиана), и максимальной (90й перцентиль) зарплаты.</small></p>
<p>Что же будет с зарплатами в этом году?</p>
<p>Движение зарплат будет определяться спросом на рынке. С девальвацией гривны в 2009 ключевую роль будет играть рынок аутсорсинга и те компании, которые на нем работают.  Часть компаний вероятно попытается переориентировать на внешний рынок и, таким образом, будут увеличивать предложение, а значит снижать (удерживать) рейт для заказчика. </p>
<p>Что будет в итоге &#8212; куда качнется баланс спроса и предложения &#8212; сказать пока сложно. Я думаю рост на уровне 10-15% в долларах вполне реален. Но только для тех компаний, которые получают доходы в валюте. Это вероятно приведет к тому, что эти компании будут &#8220;перетягивать&#8221; к себе сильных программистов, которые будут появляться на рынке. Для остальных &#8212; рост с небольшим опережением гривневой инфляции (т.е. до 20% в гривне). Точно плохо будет разве что тем, кто работает в госструктурах и бюджетных организациях &#8212; с госбюджетом на 2010 все очень мрачно.</p>
<p>По нашим инсайдерским данным, отдельные компании уже начали &#8220;охоту&#8221; за специалистами, &#8220;перебивая&#8221; встречные предложения по зарплате от конкурентов. Количество вакансий (по нашему сайту) растет, как бесплатных, так и платных. Безработица программистам в любом случае пока не грозит &#8212; спрос высокий. Даже если сменить работу &#8220;с прибавкой&#8221; получится не у всех, то найти работу, урезав свои притязания или согласившись на гривневую зарплату. </p>
<p><img src="http://www.developers.org.ua/wordpress/wp-content/uploads/2010/01/jobboard-stats.png" alt="количество вакансий" width="379" height="228" style="padding:0;" /></p>
<p><small>количество вакансий на нашем сайте</small></p>
<p>Чтобы ослабить давление на зарплаты и справиться с дефицитом кадров, компании будут снова запускать учебные центры. Ходят слухи, что несколько крупных компаний собираются &#8220;вскладчину&#8221; построить отдельный УЦ широкого профиля.</p>
<h3>Комментарии компаний</h3>
<h4>Земницкий Дмитрий, Pragmasoft (Харьков)</h4>
<p>Зарплаты в 2009 стали более реальными. В определенный момент, примерно весной, на рынке появилось определенное количество неплохих программистов из развалившихся компаний за вполне внятные деньги. Мы приняли на работу двух джавистов с опытом в этом году. В 2008 году такое было маловероятно, приходилось воспитывать самим.</p>
<p>Прогноз на 2010 очень сильно зависит от экономической ситуации в мире, прежде всего. Если пошатнется американская и европейская экономика, не будет заказов, зарплаты могут обвалиться вплоть до 50%. Думаю что так и будет, но не обязательно в 2010, возможно на год-два позже. Если все будет как сейчас, зависит от заказчиков. У компаний с успешными заказчиками возможно небольшое повышение зпл, и наоборот.</p>
<h4>Вадим Михневич, U-Wiss BV (Нидерланды)</h4>
<p>В 2009 компании не было смысла повышать зарплату в евро, поскольку за счет роста курса гривневый эквивалент увеличивался автоматически. В 2010 году за счет увеличения спроса из-за окончания кризисного периода, компаниям будет смысл немного увеличить зарплату, до 10% в евро. Есть общая тенденция роста курса, и давать более значительный прирост смысла нет &#8212; просто чтобы &#8220;отметить&#8221; лояльное отношение к сотрудникам и предотвратить &#8220;расползание&#8221; кадров (&#8221;О, зарплату подняли &#8212; ништяк, можно еще тут поработать, вдруг еще поднимут&#8221;). </p>
<h4>Артем Савотин, ameria.de (Германия)</h4>
<p>В целом, особенно по Киеву, в 2009 заметно упал спрос на высокие зарплаты в валюте и люди начали себя более адекватно оценивать. Можно спокойно договариваться о гривне учитывая индексацию к покупательской способности. По сравнению с другими специальностями, очень многие Project Manager-ы и Technical Writers находятся в поиске работы, несмотря на то, что у них есть постоянное рабочее место.</p>
<p>На 2010 прогнозируем рост +20-30% в гривне при условии, что национальная валюта останется на таком же уровне к иностранной, как сейчас. Если будет обвал, тогда мы будем очень оперативно реагировать и зарплаты в гривне могут вырасти намного выше. Мы видим очень большой спрос на опытных PHP разработчиков. Довольно сложно найти хороших и свободных специалистов по Plone/Zope на рынке Украины. По этому, возможно, что будем обучать ребят знающих Python.</p>
<h4>Дмитрий Сохач, ISD (Днепропетровск)</h4>
<p>Я считаю, что зарплаты по Киеву возрастут незначительно, но в регионах будет значительный в процентном отношении рост зарплат по сравнению с предыдущим годом, на 5-10% больше (в долларах).</p>
<h4> На условиях анонимности (Харьков)</h4>
<p>IT рынок в 2010 по-прежнему будет оставаться неоднородным. Какие-то компании усилят свое положение на рынке, будет оставаться часть компаний, которые не смогут дальше продолжить свой бизнес. Возможно, в 2010 году услышим о каких-то крупных слияниях или приобретении более мелких компаний. 2010 год ожидает быть более стабильным нежели уходящий. Наш прогноз &#8212; вырастут на 5-15% (в долларах) для разных категорий программистов. Общая тенденция рынка &#8212; з/п медленно будут продолжать расти. Сохранится большой спрос на профессионалов, который и будет двигать повышение з/п.</p>
<p>Востребованными программистами в этом году будут оставаться .Net Developers, Java Developers, на тестировщиков будет оставаться стабильный спрос. Возобновится интерес к менеджерам высшего звена. Как всегда будут востребованы менеджеры по продажам, маркетологи, которые смогут представить компанию в более выгодном свете, увеличить объемы продаж в перспективе. В 2010 году ожидается интерес к мобильным, переносным устройствам (и программистов, способных под них писать).</p>
<h4>Марина Вышгородских, Ciklum (Киев)</h4>
<p>Уже сейчас заметна тенденция к тому, что в 2010 запросы на специалистов будут более узкоспециализированными. Возможно, это связано с тем, что в результате кризиса с одной стороны появилась возможность найти экспертов в mainstream технологиях и в Европе, с другой – клиенты в меньшей мере готовы тратить время и ресурсы на обучение, хотят в Украине получить уже готовых специалистов.</p>
<p>В настоящий момент много запросов на iPhone and Android developers, IBM WebSphere supporters and developers, специалистов со знанием Drupal, МОSS, Google Web Toolkit, etc &#8212; причем, в качестве основных, а не «желательных» требований.</p>
<p>Я думаю, джуниорам имеет смысл заняться активным освоением относительно быстро изучаемых технологий &#8212; например, разработкой под мобильные платформы.<br />
Мы же планируем проводить курсы экспресс-подготовки узкоспециализированных специалистов, сейчас ищем партнеров для создания таких программ.</p>
<p>Что касается зарплат, думаю, возвращающийся после кризиса спрос их слегка подогреет, но не думаю, что в 2010 мы вернемся к временам зарплатного бума 2007-2008 годов. </p>
<br/><a href="http://www.developers.org.ua/archives/max/2010/01/27/salaries-2010-forecast/#ratings">Оценить статью на сайте</a> | <a href="http://www.developers.org.ua/archives/max/2010/01/27/salaries-2010-forecast/#comments">43 комментариев</a>]]></content:encoded>
			<wfw:commentRss>http://www.developers.org.ua/archives/max/2010/01/27/salaries-2010-forecast/feed/</wfw:commentRss>
		<slash:comments>43</slash:comments>
		<feedburner:origLink>http://www.developers.org.ua/archives/max/2010/01/27/salaries-2010-forecast/</feedburner:origLink></item>
		<item>
		<title>Обзор Alfresco WCM</title>
		<link>http://feedproxy.google.com/~r/DevelopersOrgUa/~3/oWljazKPR1c/</link>
		<comments>http://www.developers.org.ua/archives/jozh/2010/01/26/alfresco-wcm-overview/#comments</comments>
		<pubDate>Tue, 26 Jan 2010 12:34:43 +0000</pubDate>
		<dc:creator>Максим Михеенко</dc:creator>
				<category><![CDATA[Разработка]]></category>
		<category><![CDATA[Статьи]]></category>
		<category><![CDATA[alfresco]]></category>
		<category><![CDATA[ecm]]></category>
		<category><![CDATA[freemarker]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[liferay]]></category>
		<category><![CDATA[wcm]]></category>

		<guid isPermaLink="false">http://www.developers.org.ua/index.php?p=5970</guid>
		<description><![CDATA[В статье описано, что такое Alfresco и с чем его едят. Интеграция с порталом Liferay, описание основных составляющих, таких как WCM, Web forms, Web-scripts, поиск, Metadata extraction. Подробнее всего описана работа с веб-содержимым.]]></description>
			<content:encoded><![CDATA[<p>Итак, прошло примерно 3 месяца со времени моего знакомства с Alfresco WCM, а если точнее &#8212; попытки быстро создать на нем сайт. Русскоязычных материалов по теме мало, и я постараюсь ответить на вопросы, которые возникали у меня в процессе изучения и ответы на которые не лежат на поверхности. Статья показывает возможности применения Alfresco и,  вероятно, поможет принять решение использовать эту CMS или нет.</p>
<h3>Что такое Alfresco?</h3>
<p>Зайдя на <a href="http://wiki.alfresco.com">wiki.alfresco.com</a> мы можем скачать Community-версию, она бесплатна, на момент написания статьи выпущена версия 3.2. Как система управления веб-контентом, она достаточно просто интегрируется с open-source порталом <a href="http://www.liferay.com">Liferay</a> и призвана взвалить на себя нагрузку по управлению сложным веб-содержимым. Скачав инсталлятор, можно быстро установить уже сконфигурированную связку MySQL + Alfresco server + Alfresco virtual server. Для портала Liferay можно скачать Alfresco portlet из репозитория и развернуть Alfresco server на том же сервере приложений, что и портал.</p>
<p>Alfresco &#8212; это файло-ориентированная CMS, которая может как представлять собой файловую систему, так и расширять общепринятую функциональность через мета-данные, закрепляемые за каждым файлом и папкой.</p>
<h4>Первое знакомство</h4>
<p>При старте запускается MySQL и Tomcat с Alfresco server (занимает порт 8080, 21), а еще можно запустить Virtual Server (занимает порт 8180). Виртуальный сервер &#8212; удобное средство для предпросмотра сайта с наполнением, он обязательно должен быть запущен при работе с WCM.</p>
<p>При изучении документации встречаются обе аббревиатуры ECM и WCM. Enterprise Content Management — это ядро системы. Здесь можно создавать собственные типы данных Custom Content Model, например, тип Сотрудник с полями Имя, Фамилия, Отчество, Кабинет и Дата рождения. Эту часть системы я рассматривал поверхностно, больше копаясь в WCM, который создан как настройка над ECM.</p>
<p>Alfresco имеет свой веб-интерфейс (http://localhost:8080/alfresco/), который на самом деле является клиентом к сервисам ECM и от того позволяет выполнять лишь наиболее популярные действия, 90% конфигурирования и управления системой выполняется в .xml файлах, расположенных в 2х местах: <em>&lt;alfresco_dir&gt;/tomcat/webapps/alfresco/WEB-йINF/classes/alfresco</em> и <em>&lt;alfresco_dir&gt;/tomcat/shared/classes/alfresco/extension. </em>Там же есть множество примеров, и чаще всего эти файлы &#8212; конфигурации Spring framework, что сразу означает, что они отнюдь не прозаичны и требуют понимания архитектуры всей системы.<em><br />
</em></p>
<h3>Web Content Management (WCM)</h3>
<p>Как сказано в документации &#8212; специально для управления веб-содержимым в Alfresco разработан AVM (Advanced Versioning Manager). Это специальная модель, созданная на основе ECM, и представляющая собой версионные Файлы и Папки, плюс дополнительные сервисы. Основная идея состоит в следующем. Есть сайт (Site) и его наполнение. Каждый автор создает содержимое в его собственном хранилище, которое как слой наложено на содержимое сайта. После того как автор создал статью, он делает ей Submit и она идет по потоку утверждения. Когда статья окончательно утверждается, она переносится в общее хранилище и становится видимой всем остальным участникам работы над сайтом. Таким же образом поддерживается версионность документов. Локальные изменения каждого пользователя только после утверждения попадают в общее хранилище.</p>
<p>Этот подход имеет огромные достоинства: каждый автор имеет свой репозиторий, который всегда содержит последние общие обновления и одновременно может видеть свои изменения прямо на сайте. Добавив новость, автор может увидеть ее сразу же на своем виртуальном сайте, а другие участники могут ее увидеть только после утверждения. Однако есть и минусы. На текущий момент не решена проблема конфликтов. При появлении конфликта нет понятия merge, можно только откатить свои изменения. Таким образом, кто первый засабмитил &#8212; того и тапки. В версии 3.2 налюдается еще и глюк, когда без отката изменений нельзя засабмитить и другие, не конфликтные, файлы и еще одна проблема сабмита большого количества файлов (опытным путем установлено, что первой волной надо засабмитить только .xml файлы, выждать время, а после &#8212; все остальные).</p>
<p>Общее хранилище не является финальным для отображения, для него предусмотрена еще и публикация (deployment) на production-сервер через FSR (File System Receiver). Работает это так. При выполнении команды Deploy, создается событие отправки последних изменений. Дальше не совсем прозаично. У вас должен быть настроен получатель события, а это отдельный java-процесс (.jar и все необходимое можно скачать с официального сайта), который подписывается на deployment-события и выкладывает полученные изменения в локальную файловую систему.</p>
<h4>Файловая структура</h4>
<p>Alfresco ECM после инсталляции сконфигурирована как продвинутая файловая система с возможностью добавления метаданных к файлам и папкам, спектр которых разнообразен. Это могут быть и события, происходящие при появлении новых файлов в папке, возможность сохранения версий изменений или возможность комментирования.</p>
<p>При использовании WCM eсть нюанс. Через CMIS (протокол SMB, подключение удаленной папки) не доступны репозитории WCM, а только ECM, что означает отсутствие возможности заливать веб-контент через удаленную папку в Windows. Удаленный доступ к репозиториям AVM возможен только через FTP (этот сервис автоматически доступен при старте Alfresco), который корректно работает с русскими буквами.</p>
<h4>Web Forms</h4>
<p>Главная функция для веб-CMS, это создание и отображение содержимого сайта. Для этого в Alfresco используются веб-формы. Структура контента определяется в xml-схемах (.xsd файлы), наполнение сохраняется в формате xml, соответствующим этим схемам, а представление может быть отрендерено в разные форматы, чаще всего в html.</p>
<p>А теперь важный архитектурный момент. Рендеринг введенных пользователем данных осуществляется в момент ввода, — то есть когда пользователь создал контент типа Новость и заполнил все поля, создается несколько файлов. Например, <em>novogodnie-skidki.xml, </em><em>novogodnie-skidki</em><em>.html, </em><em>novogodnie-skidki</em><em>-short.html</em>, где первый содержит наполнение в xml (исходные данные), второй — &#8220;новость подробно&#8221; и третий — &#8220;новость кратко&#8221;, скажем, для главной страницы. Для рендеринга используются FreeMarker template или XSLT преобразования. С успехом мы можем рендерить и .jsp страницы, располагать их в разных директориях и пр.</p>
<p>Процесс рендеринга — на удивление медленный и ресурсоёмкий. Если выполнить перегенерацию всего контента, это может происходить минут 5 для 100 элементов.</p>
<p>Существует возможность на этапе преобразования использовать любые средства &#8212; обрабатывать списки контента, делать включение других шаблонов, использовать .jsp файлы, вызывать alfresco web-scripts, другими словами, можно создать абсолютно любой контент.</p>
<p>Важно понимать, что рендеринг наполнения происходит на этапе создания контента, а это говорит о следующем: созданный .html контент статичен (исключение &#8212; рендеринг .jsp страниц).</p>
<p>Еще один минус &#8212; при работе с репозиторием через стандартный веб-клиент Alfresco автор видит как нужные ему файлы (.xml), так и их представление .html, а чаще &#8212; еще и вспомогательные, .jsp и другие ресурсы. Как этого избежать, я так и не нашел.</p>
<h4>Архитектура веб-приложения</h4>
<p>Alfresco создает статический контент. На этом ее функции по созданию веб-сайтов заканчиваются. Alfresco предлагает свой Web Framework, но он крайне сложный. Чтобы упростить работу с ним, выпущен визуальный построитель сайтов Alfresco Surf platform, но даже в релизе(!) он глючит и не может получить из Alfresco список веб-контента. За разумное время мне не удалось сделать ничего толкового и было принято решение отказаться от его использования. Дальше описаны несколько наиболее применимых архитектур веб приложений.</p>
<p>Первый вариант создания сайта &#8212; чистый plain text и отсутствие динамики, который хорошо подойдет к маленьким сайтам-визиткам, где достаточно отрендерить всё в html и, возможно, сделать часть динамических страниц через .jsp. При этом деплоиться сайт может, например, в /tomcat/webapps/ROOT. Списки новостей могут так же рендериться на этапе добавлении новости и отображаться как статическая страница. Скорость работы такого сайта стремится к первой космической, а нагрузка на сервер &#8212; к нулю.</p>
<p>Второй вариант &#8212; использование фреймворка, который хорошо бы работал с уже сгенерированными страницами, например <a href="http://www.opensymphony.com/sitemesh/index.html" target="_blank">SiteMesh</a>. В этом случае достаточно просто создавать страницы .html с уже сформированными на основе контента мета-данными (title, description, keywords).</p>
<p>Третий вариант &#8212; использование Alfresco web-scripts для формирования страниц.</p>
<h4>Поиск</h4>
<p>Поиск — это ключевой компонент Alfresco, потому что только с помощью поиска можно выполнить выборку контента. Вероятно по этому видов поиска достаточно много:</p>
<ul>
<li>Open Search — доступен из веб-интерфейса, ищет только по репозиторию ECM, поэтому даже не пытайтесь найти свой первый helloworld.html в WCM репозитории;</li>
<li>Lucene — наиболее мощный вид поиска, досупен только из web scripts или джавы;</li>
<li>XPath — последовательный обход дерева элементов, что означает низкую производительность</li>
<li>CQL — обещают возможность описывать поисковые запросы по модели данных ECM в синтаксисе SQL — практического применения не видел.</li>
</ul>
<p>Из всего списка реально применим только Lucene, у него широкие возможности поиска по атрибутам и метаданным документа, но по умолчанию содержимое всех документов (и даже xml) считается сплошным текстом, что создает сложности, если мы хотим найти наличие определенного поля в документе. С другой стороны, сразу можно искать по таким полям как имя, путь к файлу, дата создания/модификации и некоторым другим.</p>
<p>Ограничением поиска Lucene является возможность искать только в общем репозитории утвержденного контента, т.е. пользовательские хранилища не доступны для поиска.</p>
<h4>Web scripts</h4>
<p>Веб-скрипты в Alfresco заслуживают отдельного внимания. Это модель MVC, один из немногих удобных механизмов а Alfresco, который представляет собой связку javascript + FreeMarker templates. Каждый скрипт имеет привязку к шаблону URL, наподобие grails, например /news/{year}?/{newsName}?. При запросе этого адреса выполняется javascript, в котором доступны почти все сервисы Alfresco. Результат рендерится в шаблонах FreeMarker.</p>
<p>Производительность не замерял, но на взгляд она не плоха, учитывая даже что javascript и FreeMarker преобразования выполняются при каждом вызове url и являются скриптами.</p>
<p>Есть одна радость — эту связку можно хоть как-то тестировать, чего не скажешь об остальных средствах.</p>
<p>Самое время обрадоваться наличию такой технологии, но и тут ложка дегтя — очень тупо реализованный &lt;include&gt; джаваскрипта. Если хочется хоть какую-то архитектуру создать, то придется делать включения нескольких javascipt-файлов, что реализовано &#8230; простым добавлением всех скриптов в один большой StringBuffer в памяти и ошибки выполнения содержат неожиданные номера строк. В этой ситуации выручает имеющийся javascript-debugger — отдельное Swing приложение, где даже можно посмотреть все сигнатуры сервис-методов.</p>
<h4>Metadata extraction</h4>
<p>Представим такую ситуацию. У нас есть тип веб-контента Новости, нам необходимо указать отображать ли эту новость на главной странице сайта или же только в общем списке. Другими словами нам нужно добавить к контенту некий признак, наличие которого означало бы необходимость опубликовать новость на главной странице. Мы можем добавить этот признак в метаданные документа, либо сделать его полем xsd структуры Новости. Если мы добавим признак в метаданные, то он будет потерян при копировании через FTP или деплое в файловую систему через FSR. Если мы добавим поле в тип Новости, то его нельзя будет найти поиском. В этом случае на помощь приходит механизм Metadata extraction — возможность настроить Lucene таким образом, чтобы при индексации контента он изымал некоторые поля из xml, чтобы можно было написать в поисковом запросе что-то вроде &#8220;+ASPECT:{cm}showOnHomePage&#8221; и найти только те новости, которые нам нужны. Идея хороша, но достаточно сложна в реализации несмотря на наличие описаний.</p>
<h4>Alfresco + Liferay</h4>
<p>Alfresco, собранную для развертывания на сервере Liferay, можно скачать из репозитория Liferay. Эта сборка включает в себя Alfresco сервер, веб-клиент, портлеты для отображения контента, администрирования и задач пользователя. Сложностей с запуском &#8212; никаких, но интеграция не полная. Не поддерживается единый вход в систему SSO (Single Sign-on). В гугле можно найти примеры полной интеграции &#8212; при регистрации пользователя в портале, он создается в Alfresco и есть возможность отображать веб-скрипты и контент под правами текущего пользователя портала. Но как показывает практика, часто это и не нужно.</p>
<h3>Выводы</h3>
<p>Статья может содержать неточности, будем надеяться, что это я что-то не до понял и не до читал, и есть более верные и красивые способы работы с Alfresco. Резюмируя, можно отметить следующее:</p>
<ul>
<li>можно легко и быстро создавать контент, тяжело из него сделать сколь-нибудь удобный сайт;</li>
<li>автоматическое тестирование практически невозможно;</li>
<li>очень слабые возможности при работе с уже созданным содержимым — только обзор директорий, файлов и поиск;</li>
<li>заявлено много рекламных решений, которые на проверку оказываются пустышками</li>
<li>наличие большого количества багов в самой системе, что свидетельствует об отношении к тестам разработчиков, и сразу же о вашей возможности тестировать свой продукт, построенный на Alfresco.</li>
</ul>
<p>Мой личный рейтинг CMS Alfresco — 3/5.</p>
<br/><a href="http://www.developers.org.ua/archives/jozh/2010/01/26/alfresco-wcm-overview/#ratings">Оценить статью на сайте</a> | <a href="http://www.developers.org.ua/archives/jozh/2010/01/26/alfresco-wcm-overview/#comments">7 комментариев</a>]]></content:encoded>
			<wfw:commentRss>http://www.developers.org.ua/archives/jozh/2010/01/26/alfresco-wcm-overview/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		<feedburner:origLink>http://www.developers.org.ua/archives/jozh/2010/01/26/alfresco-wcm-overview/</feedburner:origLink></item>
		<item>
		<title>Интересные ссылки №201</title>
		<link>http://feedproxy.google.com/~r/DevelopersOrgUa/~3/qW5WCINIUfM/</link>
		<comments>http://www.developers.org.ua/archives/max/2010/01/22/weekly-linkdump-201/#comments</comments>
		<pubDate>Fri, 22 Jan 2010 18:50:23 +0000</pubDate>
		<dc:creator>Макс Ищенко</dc:creator>
				<category><![CDATA[Ссылки]]></category>
		<category><![CDATA[CoffeeScript]]></category>
		<category><![CDATA[Continuous deployment]]></category>
		<category><![CDATA[ESI]]></category>
		<category><![CDATA[HR]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[V8]]></category>
		<category><![CDATA[Varnish]]></category>
		<category><![CDATA[видео]]></category>
		<category><![CDATA[договор]]></category>
		<category><![CDATA[кеширование]]></category>
		<category><![CDATA[фриланс]]></category>

		<guid isPermaLink="false">http://www.developers.org.ua/index.php?p=6022</guid>
		<description><![CDATA[Интересные ссылки за неделю: выразительный язык высокого уровня, который компилируется в JavaScript, How to Find and Hire Amazing People, Continuous deployment makes releases non-events, кеширование страниц сайта при помощи Varnish + ESI, шаблон договора на оказание услуг по разработке ПО или веб-сайта, пятничное видео.]]></description>
			<content:encoded><![CDATA[<p>Интересные ссылки за неделю:</p>
<ul>
<li><a href="http://jashkenas.github.com/coffee-script/">CoffeeScript</a> &#8212; попытка создать выразительный язык высокого уровня, который компилируется в JavaScript. Движок V8 дает <a href="http://shootout.alioth.debian.org/u64q/code-used-time-used-shapes.php">одну из самых быстрых реализаций</a> среди &#8220;скриптовых&#8221; языков (<a href="http://twitter.com/asolovyov/status/7903007805">via</a>)</li>
<li><a href="http://blog.adamsmith.cc/2010/01/how-to-find-and-hire-amazing-people-part-4.html?utm_source=feedburner">How to Find and Hire Amazing People, Part 4 &#8211; blog.adamsmith.cc</a></li>
<li>В продолжение прошлой темы про continuous deployment, <a href="http://www.startuplessonslearned.com/2010/01/case-study-continuous-deployment-makes.html">Lessons Learned: Case Study: Continuous deployment makes releases non-events</a></li>
<li>Очень интересный инструмент для &#8220;ускорения&#8221; сайта, <a href="http://highload.com.ua/index.php/2010/01/22/%D0%BA%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86-%D1%83%D1%81%D0%BA%D0%BE%D1%80%D1%8F%D0%B5%D0%BC-%D1%81%D0%B0%D0%B9%D1%82-%D0%B2-100/">Кеширование страниц сайта при помощи Varnish + ESI</a></li>
<li>Для фрилансеров пригодится, <a href="http://twitter.com/bashmakov/status/7895972740">шаблон договора на оказание услуг по разработке ПО или веб-сайта</a>.</li>
<li>Пятничное видео, <a href="http://stkorn.livejournal.com/285391.html">Записки одного программиста &#8212; ВНЕДРЯЙ!</a></li>
</ul>
<br/><a href="http://www.developers.org.ua/archives/max/2010/01/22/weekly-linkdump-201/#ratings">Оценить статью на сайте</a> | <a href="http://www.developers.org.ua/archives/max/2010/01/22/weekly-linkdump-201/#comments">0 комментариев</a>]]></content:encoded>
			<wfw:commentRss>http://www.developers.org.ua/archives/max/2010/01/22/weekly-linkdump-201/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.developers.org.ua/archives/max/2010/01/22/weekly-linkdump-201/</feedburner:origLink></item>
		<item>
		<title>Немного о разрыве зависимостей и TDD</title>
		<link>http://feedproxy.google.com/~r/DevelopersOrgUa/~3/YmR7kRqN184/</link>
		<comments>http://www.developers.org.ua/archives/antonmartynenko/2010/01/18/tdd-dependency-breaking-technics/#comments</comments>
		<pubDate>Mon, 18 Jan 2010 09:55:38 +0000</pubDate>
		<dc:creator>Антон Мартыненко</dc:creator>
				<category><![CDATA[Разработка]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[NUnit]]></category>
		<category><![CDATA[Rhino Mocks]]></category>
		<category><![CDATA[TDD]]></category>
		<category><![CDATA[тестирование]]></category>

		<guid isPermaLink="false">http://www.developers.org.ua/index.php?p=5989</guid>
		<description><![CDATA[Я бы хотел показать некоторые методики разрыва зависимостей, которые я использовал в работе. Их автор – Michael Feathers. В книге Working Effectively with Legacy Code он описал больше методик, чем я здесь привожу. Я считаю, что описанные здесь методики наиболее востребованные и эффективные. Все методики я привожу на языке C#.]]></description>
			<content:encoded><![CDATA[<p>TDD (Test-Driven Development) – это техника программирования, при которой разработка ведется через тестирование. Тесты пишутся до кода, либо до внесения изменений в существующий код. Эта техника предполагает написания множества юнит-тестов, которые тестируют код. Как правило, тесты выполняются во время интеграционного тестирования, что позволяет обнаружить ошибки. </p>
<p>По мере роста размера проекта тесты принимают все более важное значение, т.к. в большом проекте возникает проблемы с изменением кода. В большом проекте очень сложно менять код, не внося при этом ошибок. Чем он больше – тем сложнее понять, на что повлияет изменение, и тем выше шанс что-то сломать. При росте размера проекта важность тестов растет экспоненциально. Юнит-тесты – отличный помощник в такой ситуации.</p>
<p>Michael Feathers в его книге Working Effectively with Legacy Code вводит понятие «Унаследованный код» (Legacy code). Унаследованный код – это код без тестов, изменение которого может быть сложным из-за отсутствия автоматических регрессионных тестов. Объективно, это существенная часть кода в тех компаниях, где мы работаем (см. <a href="http://www.developers.org.ua/archives/max/2009/12/04/qa/">опрос</a> – более 70% имеет ограниченное покрытие тестами, либо не пишут тесты совсем). Не секрет, что многие проекты в начале представляют собой простые и понятные системы, над которыми работают 1-2 программиста. В таких проектах написание тестов часто считается тратой времени. Но по мере роста проекта и возрастания сложности все более ощущается отсутствие автоматического регрессионного тестирования. Дизайн все более усложняется, и становится все труднее поддерживать и развивать проект. Так появляется унаследованный код…</p>
<p>Причины, по которым люди не пишут тесты достаточно разные. Вот немногие из них: отсутствие времени, лень, сложность написания тестов, непрофессионализм и т.д. Я бы хотел заострить внимание на сложности написания тестов, и показать пару техник, которые могут помочь. Основная причина сложности написания тестов – проблемы в архитектуре и проектировании классов. Очень часто классы имеют слишком много зависимостей, и из-за этого класс или метод невозможно изолировать для тестирования. Простой пример – форма, которая обращается к базе данных за какой-либо информацией. Обсуждение архитектуры и дизайна уходят за рамки этой статьи, но я бы хотел сказать, что такие изменения не делаются быстро, а уговорить заказчика на рефакторинг очень сложно – ведь заказчику сложно понять, почему он будет оплачивать месяцы работы, и после это не получит никакого нового функционала. Программист остается один на один с унаследованным кодом, и ему надо как-то разрывать зависимости и писать тесты.</p>
<p>Я бы хотел показать некоторые методики разрыва зависимостей, которые я использовал в работе. </p>
<p>Их автор – Michael Feathers. В книге Working Effectively with Legacy Code он описал больше методик, чем я здесь привожу. Я считаю, что описанные здесь методики наиболее востребованные и эффективные:</p>
<ul>
<li>Parameterize constructor</li>
<li>Parameterize method</li>
<li>Introduce static setter</li>
<li>Extract and override call</li>
<li>Extract and override factory method</li>
</ul>
<p>Все методики я привожу на языке C#, так как я на нем пишу. Надеюсь, он будет всем понятен =)</p>
<h3>1. Parameterize constructor</h3>
<p>Эта методика предназначена для выноса зависимости при помощи конструктора. Основная ее идея – создание нового конструктора, который принимает в качестве параметра интерфейс класса, от которого зависит «унаследованный код».</p>
<p>В унаследованном коде есть метод, который необходимо протестировать, но в этом методе содержится обращение к веб-сервису, который реализован классом WebServiceFacade в виде паттерна синглтон:</p>
<pre><code>namespace ParameterizeConstructor.LegacyCode
{
    public class ClassWithDependency
    {
        public ClassWithDependency()
        {
            //some initialization
        }

        public bool DoSomething()
        {
            //...
            int caseCount = WebServiceFacade.Instance.GetCaseCount();
            if (caseCount &gt; 0)
            {
                //...
                return true;
            }
            //...
            return false;
        }
    }
}

using System.Diagnostics;

namespace ParameterizeConstructor.LegacyCode
{
    public class WebServiceFacade
    {
        private static WebServiceFacade instance;

        public static WebServiceFacade Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new WebServiceFacade();
                }
                return instance;
            }
        }

        public int GetCaseCount()
        {
            //make call to real web service
            Debug.Fail(&quot;This code makes call to real web serviсe. This is not acceptable for unit test.&quot;);
            return 42;
        }

        //...
    }
}</code></pre>
<p>Нам необходимо внести изменения в код этого метода и мы должны сначала написать тест. Но мы не можем написать юнит-тест, потому что метод будет обращаться к веб-сервису во время запуска теста. Это неприемлемо. Для разрыва зависимости нам необходимо извлечь интерфейс (Extract Interface) в классе <code>WebServiceFacade</code>:</p>
<pre><code>namespace ParameterizeConstructor.BrokenDependency
{
    public interface IWebServiceFacade
    {
        int GetCaseCount();
    }
}

using System.Diagnostics;

namespace ParameterizeConstructor.BrokenDependency
{
    public class WebServiceFacade : IWebServiceFacade
    {
        private static IWebServiceFacade instance;

        public static IWebServiceFacade Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new WebServiceFacade();
                }
                return instance;
            }
        }

        public int GetCaseCount()
        {
            //make call to real web service
            Debug.Fail(&quot;This code makes call to real web serviсe. This is not acceptable for unit test.&quot;);
            return 42;
        }
    }
}</code></pre>
<p>Теперь мы добавляем новый конструктор, не забыв оставить старый:</p>
<pre><code>namespace ParameterizeConstructor.BrokenDependency
{
    public class ClassWithDependency
    {
        /// &lt;summary&gt;
        /// We preserved original constructor
        /// &lt;/summary&gt;
        public ClassWithDependency()
            : this(WebServiceFacade.Instance)
        {
        }

        /// &lt;summary&gt;
        /// This constructor has been created for testing purposes. You can inject your dependency using this constructor
        /// &lt;/summary&gt;
        public ClassWithDependency(IWebServiceFacade webService)
        {
            this.webService = webService;
            //some initialization has gone here
        }

        private IWebServiceFacade webService;

        public bool DoSomething()
        {
            //...
            int caseCount = webService.GetCaseCount();
            if (caseCount &gt; 0)
            {
                //...
                return true;
            }
            //...
            return false;
        }
    }
}</code></pre>
<p>В старом конструкторе мы вызываем новый конструктор с параметром <code>WebServiceFacade.Instance</code> это сделано для того, чтобы гарантировать сохранение интерфейса класса для всех, кто это класс будет использовать.</p>
<p>Теперь мы можем написать тест, используя мок:</p>
<pre><code>#if TEST
using NUnit.Framework;
using Rhino.Mocks;

namespace ParameterizeConstructor.BrokenDependency
{
    [TestFixture]
    public class TestClassWithDependency
    {
        [Test]
        public void DoSomething()
        {
            var webService = MockRepository.GenerateMock&lt;IWebServiceFacade&gt;();

            webService.Expect(x =&gt; x.GetCaseCount()).Return(20);

            var classWithDependency = new ClassWithDependency(webService);
            bool result = classWithDependency.DoSomething();
            Assert.IsTrue(result);

            //...

            webService.VerifyAllExpectations();
        }
    }
}
#endif</code></pre>
<h3>2. Parameterize method</h3>
<p>Эта методика предназначена для выноса зависимости при помощи изменения сигнатуры метода. В унаследованном коде есть метод, который необходимо протестировать, но в этом методе содержится обращение к веб-сервису, который реализован классом <code>WebServiceFacade</code> в виде паттерна синглтон:</p>
<pre><code>using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ParameterizeMethod.LegacyCode
{
    public class ClassWithDependency
    {
        public bool DoSomething()
        {
            //...
            int caseCount = WebServiceFacade.Instance.GetCaseCount();
            if (caseCount &gt; 0)
            {
                //...
                return true;
            }
            //...
            return false;
        }
    }
}

using System.Diagnostics;

namespace ParameterizeMethod.LegacyCode
{
    public class WebServiceFacade
    {
        private static WebServiceFacade instance;

        public static WebServiceFacade Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new WebServiceFacade();
                }
                return instance;
            }
        }

        public int GetCaseCount()
        {
            //make call to real web service
            Debug.Fail(&quot;This code makes call to real web serviсe. This is not acceptable for unit test.&quot;);
            return 42;
        }
    }
}</code></pre>
<p>Для разрыва зависимости нам необходимо извлечь интерфейс (Extract Interface) в классе <code>WebServiceFacade</code>:</p>
<pre><code>namespace ParameterizeConstructor.BrokenDependency
{
    public interface IWebServiceFacade
    {
        int GetCaseCount();
    }
}

using System.Diagnostics;

namespace ParameterizeConstructor.BrokenDependency
{
    public class WebServiceFacade : IWebServiceFacade
    {
        private static IWebServiceFacade instance;

        public static IWebServiceFacade Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new WebServiceFacade();
                }
                return instance;
            }
        }

        public int GetCaseCount()
        {
            //make call to real web service
            Debug.Fail(&quot;This code makes call to real web serviсe. This is not acceptable for unit test.&quot;);
            return 42;
        }
    }
}</code></pre>
<p>Теперь мы добавляем новую сигнатуру метода, в старом методе делаем вызов нового с параметром <code>WebServiceFacade.Instance</code> это сделано для того, чтобы гарантировать сохранение интерфейса класса для всех, кто это класс будет использовать.</p>
<pre><code>namespace ParameterizeMethod.BrokenDependency
{
    public class ClassWithDependency
    {
        /// &lt;summary&gt;
        /// We preserved original signature
        /// &lt;/summary&gt;
        public bool DoSomething()
        {
            return DoSomething(WebServiceFacade.Instance);
        }

        /// &lt;summary&gt;
        /// This method has been created for testing purposes
        /// &lt;/summary&gt;
        public bool DoSomething(IWebServiceFacade webService)
        {
            //...
            int caseCount = webService.GetCaseCount();
            if (caseCount &gt; 0)
            {
                //...
                return true;
            }
            //...
            return false;
        }
    }
}</code></pre>
<p>Теперь мы можем написать тест:</p>
<pre><code>#if TEST
using NUnit.Framework;
using Rhino.Mocks;

namespace ParameterizeMethod.BrokenDependency
{
    [TestFixture]
    public class TestClassWithDependency
    {
        [Test]
        public void DoSomething()
        {
            var webService = MockRepository.GenerateMock&lt;IWebServiceFacade&gt;();

            webService.Expect(x =&gt; x.GetCaseCount()).Return(20);

            var classWithDependency = new ClassWithDependency();
            bool result = classWithDependency.DoSomething(webService);
            Assert.IsTrue(result);

            //...

            webService.VerifyAllExpectations();
        }
    }
}
#endif</code></pre>
<h3>3. Introduce static setter</h3>
<p>Эта методика позволяет внедрять мок-классы в синглтон и другие статические сущности. В унаследованном коде есть метод, который необходимо протестировать, но в этом методе содержится обращение к веб-сервису, который реализован классом <code>WebServiceFacade</code> в виде паттерна синглтон:</p>
<pre><code>namespace IntroduceStaticSetter.LegacyCode
{
    public class ClassWithDependency
    {
        public bool DoSomething()
        {
            //...
            int caseCount = WebServiceFacade.Instance.GetCaseCount();
            if (caseCount &gt; 0)
            {
                //...
                return true;
            }
            //...
            return false;
        }
    }
}

using System.Diagnostics;

namespace IntroduceStaticSetter.LegacyCode
{
    public class WebServiceFacade
    {
        private static WebServiceFacade instance;

        public static WebServiceFacade Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new WebServiceFacade();
                }
                return instance;
            }
        }

        public int GetCaseCount()
        {
            //make call to real web service
            Debug.Fail(&quot;This code makes call to real web service. This is not acceptable for unit test.&quot;);
            return 42;
        }
    }
}</code></pre>
<p>Для разрыва зависимости нам необходимо извлечь интерфейс (Extract Interface) в классе <code>WebServiceFacade</code> а также создать статический метод – setter:</p>
<pre><code>namespace IntroduceStaticSetter.BrokenDependency
{
    public interface IWebServiceFacade
    {
        int GetCaseCount();
    }
}

using System.Diagnostics;

namespace IntroduceStaticSetter.BrokenDependency
{
    public class WebServiceFacade : IWebServiceFacade
    {
        private static IWebServiceFacade instance;

        public static IWebServiceFacade Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new WebServiceFacade();
                }
                return instance;
            }
        }

        /// &lt;summary&gt;
        /// This method has been created for testing purposes
        /// &lt;/summary&gt;
        public static void SetInstance(IWebServiceFacade webService)
        {
            instance = webService;
        }

        public int GetCaseCount()
        {
            //make call to real web service
            Debug.Fail(&quot;This code makes call to real web service. This is not acceptable for unit test.&quot;);
            return 42;
        }
    }
}</code></pre>
<p>Класс с зависимостью остается без изменений, но теперь можно смело писать юнит-тест:</p>
<pre><code>#if TEST
using NUnit.Framework;
using Rhino.Mocks;

namespace IntroduceStaticSetter.BrokenDependency
{
    [TestFixture]
    public class TestClassWithDependency
    {
        [Test]
        public void DoSomething()
        {
            var webService = MockRepository.GenerateMock&lt;IWebServiceFacade&gt;();

            webService.Expect(x =&gt; x.GetCaseCount()).Return(20);

            WebServiceFacade.SetInstance(webService);

            var classWithDependency = new ClassWithDependency();
            bool result = classWithDependency.DoSomething();
            Assert.IsTrue(result);

            webService.VerifyAllExpectations();
        }
    }
}
#endif</code></pre>
<h3>4. Extract and override call</h3>
<p>Эта методика позволяет избавиться от зависимостей, связанных с вызовом статических методов, либо вызовов какого-либо API. Предположим у нас есть следующий код:</p>
<pre><code>namespace ExtractAndOverrideCall.LegacyCode
{
    public class ClassWithDependency
    {
        public bool DoSomething()
        {
            string customerName = string.Empty;
            //...
            //here we somehow retrieve and assign result to customerName
            customerName = &quot;Anton Martynenko&quot;;
            //...

            int caseCount = CaseCountCalculator.CalculateCaseCount(customerName);
            if (caseCount &gt; 0)
            {
                //...
                return true;
            }
            //...
            return false;
        }
    }
}

using System.Diagnostics;

namespace ExtractAndOverrideCall.LegacyCode
{
    public class CaseCountCalculator
    {
        public static int CalculateCaseCount(string customerName)
        {
            //make call to real web service
            Debug.Fail(&quot;This code makes call to real web service. This is not acceptable for unit test.&quot;);
            return 42;
        }
    }
}</code></pre>
<p>Для разрыва зависимости мы добавим публичный виртуальный метод в класс с зависимостью:</p>
<pre><code>namespace ExtractAndOverrideCall.BrokenDependency
{
    public class ClassWithDependency
    {
        public bool DoSomething()
        {
            string customerName = string.Empty;
            //...
            //here we somehow retrieve and assign result to customerName
            customerName = &quot;Anton Martynenko&quot;;
            //...

            int caseCount = CalculateCaseCount(customerName);
            if (caseCount &gt; 0)
            {
                //...
                return true;
            }
            //...
            return false;
        }

        public virtual int CalculateCaseCount(string customerName)
        {
            return CaseCountCalculator.CalculateCaseCount(customerName);
        }
    }
}</code></pre>
<p>Теперь мы можем написать тест, используя мок класса с зависимостью и подменяя во времени выполнения результаты вызова метода <code>public virtual int CalculateCaseCount(string customerName)</code>:</p>
<pre><code>#if TEST
using NUnit.Framework;
using Rhino.Mocks;

namespace ExtractAndOverrideCall.BrokenDependency
{
    [TestFixture]
    public class TestClassWithDependency
    {
        [Test]
        public void DoSomething()
        {
            var classWithDependency = MockRepository.GenerateMock&lt;ClassWithDependency&gt;();
            classWithDependency.Expect(x =&gt; x.CalculateCaseCount(Arg&lt;string&gt;.Is.Equal(&quot;Anton Martynenko&quot;)))
                .Return(31);

            bool result = classWithDependency.DoSomething();
            Assert.IsTrue(result);

            classWithDependency.VerifyAllExpectations();
        }
    }
}
#endif</code></pre>
<h3>5. Extract and override factory method</h3>
<p>Эта методика позволяет разорвать зависимости, связанные с создание экземпляров классов, основанных на промежуточных результатах выполнения метода. Посмотрим пример:</p>
<pre><code>namespace ExtractAndOverrideFactoryMethod.LegacyCode
{
    public class ClassWithDependency
    {
        public bool DoSomething()
        {
            string customerName = string.Empty;
            //...
            //here we somehow retrieve and assign result to customerName
            customerName = &quot;Anton Martynenko&quot;;
            //...
            var caseCountCalculator = new CaseCountCalculator(customerName);

            int caseCount = caseCountCalculator.CalculateCaseCount();
            if (caseCount &gt; 0)
            {
                //...
                return true;
            }
            //...
            return false;
        }
    }
}

using System.Diagnostics;

namespace ExtractAndOverrideFactoryMethod.LegacyCode
{
    public class CaseCountCalculator
    {
        public CaseCountCalculator(string customerName)
        {
            //customerName is used for initialization
        }

        public int CalculateCaseCount()
        {
            //make call to real web service
            Debug.Fail(&quot;This code makes call to real web service. This is not acceptable for unit test.&quot;);
            return 42;
        }
    }
}</code></pre>
<p>Экземпляр класса <code>CaseCountCalculator</code> создается на основе <code>customerName</code>, которое вычисляется в процессе выполнения метода. В этом случае мы извлечем интерфейс в классе <code>CaseCountCalculator</code> и используем паттерн «фабричный метод»:</p>
<pre><code>namespace ExtractAndOverrideFactoryMethod.BrokenDependency
{
    public interface ICaseCountCalculator
    {
        int CalculateCaseCount();
    }
}

using System.Diagnostics;

namespace ExtractAndOverrideFactoryMethod.BrokenDependency
{
    public class CaseCountCalculator : ICaseCountCalculator
    {
        public CaseCountCalculator(string customerName)
        {
            //customerName is used for initialization
        }

        public int CalculateCaseCount()
        {
            //make call to real web service
            Debug.Fail(&quot;This code makes call to real web serviсe. This is not acceptable for unit test.&quot;);
            return 42;
        }
    }
}

namespace ExtractAndOverrideFactoryMethod.BrokenDependency
{
    public class ClassWithDependency
    {
        public bool DoSomething()
        {
            string customerName = string.Empty;
            //...
            //here we somehow retrieve and assign result to customerName
            customerName = &quot;Anton Martynenko&quot;;
            //...
            var caseCountCalculator = CreateCaseCountCalculator(customerName);

            int caseCount = caseCountCalculator.CalculateCaseCount();
            if (caseCount &gt; 0)
            {
                //...
                return true;
            }
            //...
            return false;
        }

        public virtual ICaseCountCalculator CreateCaseCountCalculator(string customerName)
        {
            return new CaseCountCalculator(customerName);
        }
    }
}</code></pre>
<p>Теперь мы можем написать тест, используя мок класса с зависимостью и подменяя во времени выполнения результаты вызова метода <code>public virtual ICaseCountCalculator CreateCaseCountCalculator(string customerName)</code>:</p>
<pre><code>#if TEST
using NUnit.Framework;
using Rhino.Mocks;

namespace ExtractAndOverrideFactoryMethod.BrokenDependency
{
    [TestFixture]
    public class TestClassWithDependency
    {
        [Test]
        public void DoSomething()
        {
            var caseCountCalculator = MockRepository.GenerateMock&lt;ICaseCountCalculator&gt;();
            caseCountCalculator.Expect(x =&gt; x.CalculateCaseCount())
                .Return(31);

            var classWithDependency = MockRepository.GenerateMock&lt;ClassWithDependency&gt;();
            classWithDependency.Expect(x =&gt; x.CreateCaseCountCalculator(Arg&lt;string&gt;.Is.Equal(&quot;Anton Martynenko&quot;)))
                .Return(caseCountCalculator);

            bool result = classWithDependency.DoSomething();
            Assert.IsTrue(result);

            caseCountCalculator.VerifyAllExpectations();
            classWithDependency.VerifyAllExpectations();
        }
    }
}
#endif</code></pre>
<p>Конечно, стоит отметить, что в реальной жизни все намного сложнее, чем в примерах. Но комбинируя эти методики с остальными приемами Inversion of Control можно добиться значительного увеличения покрытия тестами и улучшения дизайна классов. Всем, кто заинтересован увеличением покрытия тестами унаследованного кода я рекомендую прочитать книгу Working Effectively with Legacy Code. Она содержит множество полезных советов для работы с тем, с чем множество из нас сталкивается каждый день =)</p>
<p>В примерах использовались:</p>
<ul>
<li>.NET 3.5</li>
<li>Rhino Mocks 3.5</li>
<li>NUnit 2.5.2.9222</li>
</ul>
<p><a href="http://www.developers.org.ua/wordpress/wp-content/uploads/2010/01/DependencyBreakingTechnics.zip">Исходники тут</a>.</p>
<br/><a href="http://www.developers.org.ua/archives/antonmartynenko/2010/01/18/tdd-dependency-breaking-technics/#ratings">Оценить статью на сайте</a> | <a href="http://www.developers.org.ua/archives/antonmartynenko/2010/01/18/tdd-dependency-breaking-technics/#comments">4 комментариев</a>]]></content:encoded>
			<wfw:commentRss>http://www.developers.org.ua/archives/antonmartynenko/2010/01/18/tdd-dependency-breaking-technics/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		<feedburner:origLink>http://www.developers.org.ua/archives/antonmartynenko/2010/01/18/tdd-dependency-breaking-technics/</feedburner:origLink></item>
		<item>
		<title>Интересные ссылки №200</title>
		<link>http://feedproxy.google.com/~r/DevelopersOrgUa/~3/55VjQvFUFLU/</link>
		<comments>http://www.developers.org.ua/archives/max/2010/01/15/weekly-linkdump-200/#comments</comments>
		<pubDate>Fri, 15 Jan 2010 11:45:52 +0000</pubDate>
		<dc:creator>Макс Ищенко</dc:creator>
				<category><![CDATA[Ссылки]]></category>
		<category><![CDATA[Balsamiq]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Continuous deployment]]></category>
		<category><![CDATA[Continuous Integration]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[microISV]]></category>
		<category><![CDATA[Split testing]]></category>
		<category><![CDATA[ZIP]]></category>
		<category><![CDATA[маркетинг]]></category>
		<category><![CDATA[тестирование]]></category>

		<guid isPermaLink="false">http://www.developers.org.ua/index.php?p=5983</guid>
		<description><![CDATA[Интересные ссылки за неделю: A/B тестирование, Continuous Deployment, как сделать маркетинг почти инженерной задачей, успешный путь Balsamiq, набор трюков C, ZIP File Quine.]]></description>
			<content:encoded><![CDATA[<p>Интересные ссылки за неделю:</p>
<ul>
<li>Split testing он же A/B тестирование &#8212; интересная штука. Позволяет оценивать эффективность различных элементов веб-интерфейса не &#8220;с потолка&#8221;, а на основе статистики от живых пользователей.<br />
Готовая подборка хороших ссылок по этой теме: <a href="http://www.wingify.com/conversion-blog/15-free-ab-split-testing-resources/">15+ Free A/B Split Testing</a> Resources</li>
<li>Как вам идея, что каждый успешный чек-ин идет сразу в продакшн? Безумие? <a href="http://www.infoq.com/news/2009/03/Continuous-Deployment">InfoQ: Beyond Continuous Integration: Continuous Deployment</a></li>
<li>Как сделать маркетинг почти инженерной задачей, <a href="http://www.kalzumeus.com/2009/12/31/engineering-your-way-to-marketing-success/">Engineering Your Way To Marketing Success: MicroISV on a Shoestring</a></li>
<li>Balsamiq, о которой я когда-то <a href="http://www.developers.org.ua/archives/max/2008/11/21/weekly-linkdump-152/">писал</a>, продолжает свой фантастически успешный путь, <a href="http://www.balsamiq.com/blog/2010/01/03/a-look-back-at-2009/">A look back at 2009</a>. Там же: <a href="http://www.balsamiq.com/blog/2009/10/30/tools/">Tools we use for running our startup</a>.</li>
<li>Пара ссылок от читателей:<br />
<a href="http://steike.com/code/useless/evil-c/">Набор трюков C (чуток Java), которые работают, но лучше их не использовать</a>, <a href="http://steike.com/code/useless/zip-file-quine/">ZIP File Quine — This is the ZIP file that never ends</a></li>
</ul>
<br/><a href="http://www.developers.org.ua/archives/max/2010/01/15/weekly-linkdump-200/#ratings">Оценить статью на сайте</a> | <a href="http://www.developers.org.ua/archives/max/2010/01/15/weekly-linkdump-200/#comments">0 комментариев</a>]]></content:encoded>
			<wfw:commentRss>http://www.developers.org.ua/archives/max/2010/01/15/weekly-linkdump-200/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.developers.org.ua/archives/max/2010/01/15/weekly-linkdump-200/</feedburner:origLink></item>
		<item>
		<title>Интересные ссылки №199</title>
		<link>http://feedproxy.google.com/~r/DevelopersOrgUa/~3/bjv7-KzKAh8/</link>
		<comments>http://www.developers.org.ua/archives/max/2009/12/25/weekly-linkdump-199/#comments</comments>
		<pubDate>Fri, 25 Dec 2009 17:01:25 +0000</pubDate>
		<dc:creator>Макс Ищенко</dc:creator>
				<category><![CDATA[Ссылки]]></category>
		<category><![CDATA[appstore]]></category>
		<category><![CDATA[LeWeb]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[отчет]]></category>
		<category><![CDATA[рекрутинг]]></category>
		<category><![CDATA[стартап]]></category>

		<guid isPermaLink="false">http://www.developers.org.ua/index.php?p=5955</guid>
		<description><![CDATA[Интересные ссылки за неделю: половой отбор программистов, MySQL tips, львовяне в AppStore, отчет с LeWeb.]]></description>
			<content:encoded><![CDATA[<p>Интересные ссылки за неделю:</p>
<ul>
<li>История успеха львовского разработчика,  <a href="http://habrahabr.ru/blogs/games/79036/">Путь Индейца или как моя игра стала featured в apple app store</a>. Кстати, с Максимом Гриневым можно будет пообщаться вживую в понедельник на внеочередной встрече <a href="http://startup.lviv.ua/lang/uk/maxhryniv/">Lviv StartUp Club</a></li>
<li>Подборка нетривиальных советов для пользователей мускула, <a href="http://souptonuts.sourceforge.net/readme_mysql.htm">MySQL Tips</a></li>
<li>Зачетная статья от COTOHA, <a href="http://cotoha.info/thoughts/sexual_professional_selection/">Половой отбор профессионалов at Мысли с самого низа</a></li>
<li><a href="http://startuppoint.ru/blog/taskly/12658.html">Иллюстрированный отчет с LeWeb Paris 09 / task.ly / StartupPoint.ru </a></li>
</ul>
<br/><a href="http://www.developers.org.ua/archives/max/2009/12/25/weekly-linkdump-199/#ratings">Оценить статью на сайте</a> | <a href="http://www.developers.org.ua/archives/max/2009/12/25/weekly-linkdump-199/#comments">2 комментариев</a>]]></content:encoded>
			<wfw:commentRss>http://www.developers.org.ua/archives/max/2009/12/25/weekly-linkdump-199/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://www.developers.org.ua/archives/max/2009/12/25/weekly-linkdump-199/</feedburner:origLink></item>
		<item>
		<title>Интересные ссылки №198</title>
		<link>http://feedproxy.google.com/~r/DevelopersOrgUa/~3/loue0kudkjg/</link>
		<comments>http://www.developers.org.ua/archives/max/2009/12/18/weekly-linkdump-198/#comments</comments>
		<pubDate>Fri, 18 Dec 2009 08:45:42 +0000</pubDate>
		<dc:creator>Макс Ищенко</dc:creator>
				<category><![CDATA[Ссылки]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[microISV]]></category>
		<category><![CDATA[Open source]]></category>
		<category><![CDATA[PyCamp]]></category>
		<category><![CDATA[визуализация]]></category>

		<guid isPermaLink="false">http://www.developers.org.ua/index.php?p=5926</guid>
		<description><![CDATA[Интересные ссылки за неделю: выступления Питера Зибела в офисе Google, Browser Size, open-source проекты, которые привлекли к себе внимание в 2009, обзор библиотек для визуализации данных, пара неприятных историй о небольших компаниях-разработчиков, PyCamp Kyiv.]]></description>
			<content:encoded><![CDATA[<p>Интересные ссылки за неделю:</p>
<ul>
<li>Выступления Питера Зибела (автора книги Coders at Work) в офисе Google, <a href="http://www.youtube.com/watch?v=pQy22qPH7i4">YouTube &#8211; Authors@Google: Peter Seibel</a></li>
<li>Маленькая утилита, которая поможет оценить вам видимость &#8220;первого экрана&#8221; сайта для разных пользователей, <a href="http://browsersize.googlelabs.com/">Browser Size</a></li>
<li>Несколько open-source проектов, которые привлекли к себе внимание в 2009, <a href="http://ozmm.org/posts/2009_open_source_top_ten.html">2009 Open Source Top Ten</a></li>
<li>Обзор (скриншоты) библиотек для визуализации данных (веб), <a href="http://www.insideria.com/2009/12/28-rich-data-visualization-too.html">28 Rich Data Visualization Tools</a></li>
<li>Пара неприятных историй о небольших компаниях-разработчиков:  <a href="http://blog.apparentsoft.com/business/124/is-paypal-good-for-your-microisv-business-a-short-paypal-horror-story/">“Is PayPal good for your microISV business?” A short PayPal horror story</a> и <a href="http://blog.plurk.com/2009/12/14/microsoft-rips-plurk/">Microsoft China rips off Plurk</a></li>
<li>Анонс: в конце января будет <a href="http://pycamp.org.ua/">PyCamp Kyiv</a>, ищем докладчиков.</li>
</ul>
<br/><a href="http://www.developers.org.ua/archives/max/2009/12/18/weekly-linkdump-198/#ratings">Оценить статью на сайте</a> | <a href="http://www.developers.org.ua/archives/max/2009/12/18/weekly-linkdump-198/#comments">4 комментариев</a>]]></content:encoded>
			<wfw:commentRss>http://www.developers.org.ua/archives/max/2009/12/18/weekly-linkdump-198/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		<feedburner:origLink>http://www.developers.org.ua/archives/max/2009/12/18/weekly-linkdump-198/</feedburner:origLink></item>
		<item>
		<title>Рецензия: The Web Startup Success Guide by Bob Walsh</title>
		<link>http://feedproxy.google.com/~r/DevelopersOrgUa/~3/ZQ1kRQkYbkY/</link>
		<comments>http://www.developers.org.ua/archives/kolupaev/2009/12/15/the-web-startup-success-guide-by-bob-walsh-review/#comments</comments>
		<pubDate>Tue, 15 Dec 2009 14:39:31 +0000</pubDate>
		<dc:creator>Алексей Колупаев</dc:creator>
				<category><![CDATA[Книги]]></category>
		<category><![CDATA[microISV]]></category>
		<category><![CDATA[книги]]></category>
		<category><![CDATA[стартап]]></category>

		<guid isPermaLink="false">http://www.developers.org.ua/index.php?p=5914</guid>
		<description><![CDATA[<img src="http://www.developers.org.ua/wordpress/wp-content/uploads/2009/12/1430219858.01._SCLZZZZZZZ_.png" alt="cover" width="50" height="75" align="left" />Как и Макс, я получил эту книгу в подарок от издательства, и обещал написать рецензию на нее. Не обязательно хорошую, но честную. Очень рад, что мне не пришлось стоять перед таким выбором, книга мне понравилась.]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.amazon.com/Startup-Success-Guide-Books-Professionals/dp/1430219858"><img class="alignleft" style="margin: 10px;" src="http://images.amazon.com/images/P/1430219858.01._SCLZZZZZZZ_.jpg" alt="Bob's nice book!" width="333" height="500" /></a></p>
<p><em>(Как и </em><a href="http://www.developers.org.ua/archives/max/2009/10/29/coders-at-work-review/"><em>Макс</em></a><em>, я получил эту книгу в подарок от издательства, и обещал написать рецензию на нее. Не обязательно хорошую, но честную. Очень рад, что мне не пришлось стоять перед таким выбором, книга мне понравилась.)</em></p>
<p><a href="http://twitter.com/BobWalsh">Боб Волш</a> в прошлом программист с огромным опытом, а теперь автор нескольких книг и консультант в области стартапостроения.  Его предыдущие книги касаются двух направлений – социальных медиа и идее micro ISV – то есть, «компаний», состоящих из одного человека, программиста. Не знаю, как насчет социальных медиа, но идея micro ISV для Боба явно выстрадана и проверена личным опытом. И да – нет, Боб не является автором нескольких удачных стартапов, хотя опыт у него крайне богат, но это тот прекрасный пример, когда даже очень умный и талантливый человек вовсе не обязательно добивается мгновенного успеха в стартапе. Впрочем, он продолжает пытаться – посмотрите на его проект <a href="http://startuptodo.com">startuptodo.com</a> (говорят, в золотую лихорадку больше всех заработали продавцы лопат).</p>
<p>Уверен, многие программисты так или иначе задумывались об идее micro ISV (только, конечно, не называли ее так). Идея довольно простая: что же это я пишу-пишу, а миллионы на моем коде зарабатывает кто-то другой? Или, например, так: что за дурацкие задания мне дают, кто так пишет софт! Я сам гораздо лучше понимаю, как его писать, не вырваться ли мне из-под гнета «дяди», который ничего не понимает в толковом софте?  Боб Волш прошел этот путь, набил свои шишки, натер свои мозоли, получил свои уроки, и пообщался с целой кучей умных людей на эту тему. И он написал об этом книгу!</p>
<p>То, чего, как правило, не видят программисты, это огромный мир бизнеса, в котором не так уж важно, какой именно продукт ты делаешь. Очень быстро оказывается, что блестящий код и смелые решения – это только часть, и далеко не самая большая. Что продажи и маркетинг, пожалуй, важнее интерфейса и отсутствия багов. Что для победы в конкурсе красоты мало быть красивым – нужно еще и принять участие в конкурсе красоты. Как же быть? Один из вариантов – найти себе партнера, который сможет выполнить эти функции, и тогда, вдвоем, или втроем, вы сможете быть сильной командой, имеющей таланты по всем важным для молодой компании направлениям. Второй вариант, micro ISV – стать специалистом и в остальных, недостающих областях. Почему бы одному человеку не быть и программистом, и маркетологом, и продавцом, и многими другими виртуальными работниками в своей компании-одного-человека? Конечно же, Боб Волш не знает, что такое ведение бухгалтерии и налогового учета в Украине, поэтому считает, что все это возможно совместить в одном талантливом человеке.</p>
<p>Что вы найдете в этой книге? Ни слова о программировании, вы сами решите какой продукт и как создавать. Гораздо важнее понимать ценность вашего продукта, а это не так уж очевидно. Если вы делаете стартап, micro ISV, назовите как вам удобно – вы начинаете игру в деньги. Их надо заработать, и тот, кто делает это лучше, вовсе не обязательно лучше программирует. Он лучше понимает, кто их заплатит и почему. Он лучше знает, как найти потребность и удовлетворить ее. Как говорить со своими клиентами, и, хотя бы, дать им знать, что продукт существует.  Как найти деньги в тот момент, когда они нужны. Как быть очень-очень продуктивным, ведь успех дается только упорным и кропотливым трудом. Почему инвесторы делают инвестиции, и кому они их не дадут.</p>
<p>В книге не только размышления Боба на эти темы, в ней также множество интервью с теми, кто преуспел в этом. Не все имена известны в наших широтах, но Гай Кавасаки и Джоел Спольски одобряют и дают интервью этому человеку. Это не то, чтобы гарантия качества, но все-таки – авторитеты, что называется, тянут мазу. Отдельно я хочу упомянуть выдающуюся подборку эпиграфов к каждой главе – это очень емкие и интересные цитаты.</p>
<p>Я считаю, что каждый программист, который больше трех раз думал о том, чтобы сделать что-нибудь эдакое, стартапчик там, или еще какой замут, мог бы потратить двадцать баксов и пару дней, чтобы прочитать эту книгу, стать умнее, и, возможно, никогда больше не думать об этом. Или продолжать думать,  но уже гораздо более трезво. Это гораздо дешевле и быстрее, чем набивать все те же шишки самостоятельно. Неспроста самая первая фраза на обложке гласит: from developer to founder.  И, конечно, не стоит забывать, что самые смелые и громкие проекты сделаны скорее вопреки, чем благодаря книгам, учебникам и наставлениям. Для чего их, разумеется, надо прочитать.</p>
<p>Знание – сила!</p>
<p style="text-align: right;">Алексей Колупаев,</p>
<p style="text-align: right;"><a href="http://kolupaev.com">kolupaev.com</a></p>
<br/><a href="http://www.developers.org.ua/archives/kolupaev/2009/12/15/the-web-startup-success-guide-by-bob-walsh-review/#ratings">Оценить статью на сайте</a> | <a href="http://www.developers.org.ua/archives/kolupaev/2009/12/15/the-web-startup-success-guide-by-bob-walsh-review/#comments">10 комментариев</a>]]></content:encoded>
			<wfw:commentRss>http://www.developers.org.ua/archives/kolupaev/2009/12/15/the-web-startup-success-guide-by-bob-walsh-review/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		<feedburner:origLink>http://www.developers.org.ua/archives/kolupaev/2009/12/15/the-web-startup-success-guide-by-bob-walsh-review/</feedburner:origLink></item>
	</channel>
</rss>
