Logo    
Деловая газета CitCity.ru citforum.ck.ua - все об Open Source Форумы Все публикации Учебный центр Курилка
CitForum    CITForum на CD    Подписка на новости портала Море(!) аналитической информации! :: citforum.ck.ua
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Wednesday, 02-Jan-2008 21:03:39 EET

Google
WWW citforum.ck.ua
С Новым годом!
2007 г.

Пересекая границы: специфика разработки ПО распределенной командой

, http://artemkondratyev.net

Эта статья написана на основе опыта работы в нескольких проектах по разработке ПО, где участники проектной команды были в большей или меньшей степени удалены друг от друга: начиная от варианта «одна команда в двух офисах в одном городе», и заканчивая вариантом «3 команды в разных странах – из них одна на другом континенте». Как появляются такие проекты? В малых компаниях ценность конкретного специалиста для проекта часто выше, чем возможные расходы на коммуникацию, и его местожительство не берется в расчет. В крупных компаниях, наоборот, появляется большой объем работ, не требующих высокой квалификации, и задачи реализуются с привлечением субподрядчиков, в том числе из других стран. Проблемы возникают при этом очень похожие, и современные средства коммуникации не всегда оказываются панацеей.

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

Введение

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

  1. Управляющая информация
  2. Способы коммуникации
  3. Инструментарий
  4. Области коллективного владения
  5. Проблемные участки

Основные аспекты взаимодействия распределенных команд

Управляющая информация

В любом проекте самым надежным каналом связи всегда будет канал «управляющий центр – подчиненные подразделения» («Руководитель – исполнители»). Если отсутствие связи между участниками команды еще можно себе представить, то отсутствие связи с руководителем означает, что проект скорее мертв, чем жив. Наряду с управляющим воздействием, по этим каналам можно распространять и информацию, рассчитывая на то, что она действительно дойдет до всех членов команды.

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

Требования
Зафиксированные в каком бы то ни было виде, требования позволяют каждому участнику проекта четко понимать, что ему нужно сделать, а значит и позволяют делать прогнозы относительно сроков выполнения задач. В условиях распределенной команды, зафиксированные требования – часто вообще единственный способ добиться какого-либо результата. Несмотря на кажущуюся банальность этих утверждений, удивительно, насколько часто этим пренебрегают, поручая людям делать что-то неопределенное.
Планы и бизнес-цели
Имеются в виду высокоуровневые планы и высокоуровневые бизнес цели. Отвечают на вопросы, соответственно, «к какому сроку» и «зачем». Высокоуровневые планы содержат даты, когда наличие разработанной функциональности становится критичным для бизнеса заказчика, бизнес-цели объясняют, ради чего, собственно, все работы ведутся. Для технического специалиста эти знания по важности стоят после требований, но позволяют реализовывать последние гораздо более осмысленно и эффективно.
Структура команды
Людям нужно знать, кто какие позиции занимает в проекте и за что отвечает – для того, чтобы задавать друг другу вопросы, чтобы просить о выполнении некоторых работ или сообщать о проблемах. Должны быть известны и способы связи – телефон, адрес электронной почты, имя (номер) в системе обмена мгновенными сообщениями. Не считайте, что все и так всех знают – когда-нибудь обязательно выяснится, что это не так.
Ответственность команд
Логичное следствие из предыдущего пункта. Если в проекте участвует несколько команд, нужно знать, какая команда за что отвечает и кто является контактным лицом от каждой команды.
Глоссарий
Вроде бы мелочь. Но однажды может оказаться, что участники разных команд говорят «на разных языках». С одной стороны, это ведет к тому, что появляются различные названия для одних и тех же элементов в пользовательском интерфейсе приложения, в коде, в структуре данных. С другой – это приводит к разночтению требований и ошибкам в логике приложения. Еще более актуальным наличие глоссария становится тогда, когда участники проектных команд действительно говорят на разных языках.

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

Способы коммуникации

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

Skype, Icq и другие системы мгновенного обмена сообщениями
Я отвожу этому способу общения ведущую роль даже в тех проектах, где участники располагаются в одном офисе. По моему опыту, большая часть общения в проектах происходит именно с помощью этих средств. Это не во всех случаях так же эффективно, как телефонный звонок, не так официально, как электронное письмо, но это то средство, которое всегда «под рукой» и при этом позволяет общаться «асинхронно», т.е. задавать вопрос тогда, когда он появился, и отвечать тогда, когда есть момент. В случае, когда телефонная связь между командами является слишком дорогой, это вообще первое средство. Я думаю, дело еще и в том, что этот способ общения наиболее похож на живой разговор двух людей, эффективнее которого, по-моему, только телепатия.
Телефон
При всем удобстве систем мгновенного обмена сообщениями, иногда наступает момент, когда проще сказать в двух словах, чем описывать в нескольких предложениях. Телефон, из всех средств удаленного общения - еще и самый лучший способ заставить другого человека тебе ответить (телефонный звонок сложнее всего проигнорировать). Казалось бы, зачем описывать преимущества способа связи, которому уже больше века? Тем не менее, в условиях высоких цен за междугородние и международные разговоры, телефонная связь может показаться излишеством. Стоит, однако, хорошо подумать, прежде чем отказаться от такого высокоэффективного средства.
Системы учета заданий и ошибок (Tasktracking, Bugtracking systems)
В процессе разработки ПО одним из важнейших средств коммуникации становится именно система учета ошибок (заданий). Значение таких систем часто недооценивают. Тем не менее, такая система позволяет собрать всю информацию об ошибке (задании) в одном месте. Хранение вместе с описанием ошибки всего хода ее обсуждения и всех связанных файлов дает прекрасную возможность исправить ошибку, не прибегая больше ни каким источникам информации, и, при необходимости, просмотреть всю историю принятия решений. Надо помнить, что ошибки часто исправляются участниками, пришедшими в проект намного позже момента их обнаружения. Иногда старые ошибки открываются заново спустя значительное время после того, как были исправлены в первый раз.
Электронная почта
Многими людьми именно этот способ общения считается наиболее предпочтительным. Да, у этого способа есть несколько очевидных преимуществ: письма имеют достаточно официальный статус, долговременно хранятся на сервере, копия письма может быть отправлена руководителю. Электронная почта прекрасно подходит для передачи объемных сообщений, для одновременной рассылки информации нескольким участникам и для сообщения окончательных решений. Однако зачастую эти преимущества могут превратиться в недостатки: письмо не требует мгновенного ответа, поэтому письма часто откладываются, после чего про них забывают вовсе. Письма часто объемны, поэтому многие читают их «по диагонали». Письма достаточно официальны, поэтому в них не будет сиюминутных мелких деталей, из письма часто трудно почувствовать настрой команды и действительное состояние дел. В этом смысле письма похожи на проектную документацию, с той лишь разницей, что документация читается по необходимости, а письма приходят сами.
Живой контакт
И все-таки, ничего не заменит личного общения людей. По своему опыту знаю, что к человеку начинаешь относиться совсем по-другому, если видел его лично. В условиях распределенной команды сделайте все возможное, чтобы участники встретились хотя бы раз. Даже одна командировка может дать отличный импульс к более успешному взаимодействию сотрудников.
Видеоконференция
Видеоконференция – это когда несколько человек смотрят друг друга по телевизору и не более того, по крайней мере, в условиях отсутствия сверхбыстрых каналов передачи видеосигнала. Не могу сказать по своему опыту, чтобы видеоконференция могла помочь решить хотя бы один по-настоящему сложный вопрос. Так как видеоконференция обычно устраивается для одновременного общения многих людей, некоторые люди в таких условиях предпочитают хранить молчание, особенно, если обсуждение вопроса для них невыгодно. При этом, молчать по другую сторону экрана - гораздо легче, чем сидя за одним столом. Для многих людей видеоконференция – непривычный способ общения, поэтому, несмотря на кажущуюся схожесть с живым разговором, она может казаться людям менее удобной, чем переписка по электронной почте или обмен сообщениями.
Инструментарий

Этот пункт касается решений, связанных с выбором и конфигурацией средств коллективной разработки.

Система контроля версий
Наличие такой системы я рассматриваю как обязательное условие профессиональной разработки ПО. Говорить о преимуществах такой системы я не буду – эта информация есть в обилии в других источниках. Скажу только о том, что выбор конфигурации системы контроля версий оказывает существенное влияние на процесс разработки в целом. С одной стороны, использование только локально доступной системы контроля версий (свой собственный репозиторий у каждой локальной команды) имеет определенные преимущества – нет необходимости согласовывать время внесения изменений, нет задержек, связанных с плохой работой сети при интеграции со средствами разработки. С другой стороны, решение об использовании локального репозитория требует наличия модульной архитектуры разрабатываемой системы и увеличивает интеграционные риски.
Треккеры
Имеются в виду все средства учета задач, требований, ошибок и т.д. Как я уже говорил, система учета ошибок является одним из важнейших средств коммуникации проектной команды. Соответственно, такое средство должно у проектной команды быть.
Система хранения документов
Обеспечивает коллективный доступ к проектной документации и позволяет хранить версии документов. Если исходный код почти всегда помещается в систему управления версиями, то документы часто хранятся просто на общем сетевом ресурсе. Тем не менее, использование системы управления версиями для хранения документации не менее полезно, чем для хранения кода. Кстати, ничто не мешает использовать для хранения кода и документации один и тот же репозиторий, не приобретая дополнительной системы.
Общий сетевой ресурс
Самый минимум того, что нужно иметь проектной команде для того, чтобы хоть как-то обмениваться данными.
Система планирования
Обеспечивает общий доступ к плану проекта.
Простейший web-ресурс с фото и краткой информацией о каждом участнике проекта
Казалось бы, мелочь, но позволяет создать у участников проекта хотя бы какое-то ощущение общности. Видя свои фото в одном месте, люди начинают верить, что они работают в одном проекте. Чем больше конкретных деталей один человек знает о другом, тем менее абстрактным он ему кажется, тем легче этим людям общаться друг с другом. Кроме всего, такой сайт – еще и удобное место для того, чтобы указать позицию, зону ответственности и контактную информацию каждого из участников.
Области коллективного владения

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

Исходный код
Промежуточный плод совместных усилий и, собственно, самое важное из того, для чего нужны такие совместные усилия. Как бы ни были далеки друг от друга участники команды, они неизбежно пересекутся здесь. Поэтому так важно планировать и организовывать процесс командного внесения изменений в код. Существует несколько подходов к этому вопросу:
  1. Коллективное владение кодом. Подход хорошо описан в литературе по экстремальному программированию (XP), и в теории обладает целым рядом преимуществ, включая непрерывный обмен знаниями о системе, взаимозаменяемость участников команды, однообразие получаемого кода и непрерывное его улучшение. На практике, чтобы получить все эти преимущества, надо иметь либо по-настоящему сплоченную команду единомышленников, либо ограничиться вариантом, когда один ведущий разработчик выдает конкретные задания нескольким исполнителям и сам определяет степень их вмешательства в работу друг друга. С моей точки, коллективное владение кодом подходит лишь для очень небольших команд – от 1 до 5 человек. В любом случае надо быть готовым к тому, что коллективное владение кодом очень быстро может превратиться в бесконтрольное. В самом плохом сценарии это приведет к тому, что одни разработчики будут исправлять написанный другими код, не вникая в его детали или откатывать сделанные другими изменения, другие будут до последнего защищать свою часть кода от вмешательства со стороны. Такая ситуация порождает безответственность и напряженные отношения в группе.
  2. Каждый пишет свою часть кода. Разделяя зоны ответственности программистов, мы имеем плюсы в виде довольных программистов, часто предпочитающих работать в одиночку, и персональной ответственности за выделенную часть функциональности. Однако недостатков тоже хватает: каждый программист зачастую пишет в своем стиле, плохо понимает чужой код, а потому, когда один программист уходит из проекта, его код иногда оказывается невозможно сопровождать. Кроме того, малое внимание уделяется взаимодействию модулей, написанных разными программистами, которых больше заботит внутреннее устройство модуля, чем его интеграция с модулями других разработчиков.
  3. Худшей комбинацией обоих подходов видится мне ситуация, когда один разработчик пишет код, другой исправляет ошибки. В этом случае получаем безответственность первого программиста и разочарование второго. Должен отметить, что если исправить ошибку в собственном коде для программиста – дело чести, то доделывать некачественную работу других – крайне неприятное занятие.
  4. Наиболее привлекательной мне кажется другая комбинация подходов, возможная при компонентной архитектуре: каждый компонент разрабатывается только одной локальной командой, внутри команды – коллективное владение кодом. Взаимозаменяемость и обмен знаниями обеспечивается на уровне каждой команды, причем локальность команды гарантирует быстрое и безболезненное решение возможных спорных вопросов. Границы компонентов препятствуют непосредственному вмешательству одной команды в работу другой. В то же время ориентация на компонентную архитектуру предполагает, что вопросам взаимодействия компонентов (интерфейсам) будет уделено должное внимание.
Процедура внесения изменений в исходный код (Check-in)
При совместном написании кода одним из наиболее критичных моментов становится внесение сделанных изменений в репозиторий исходного кода. Необходимо четко определить условия, при выполнении которых допустимо внесение изменений в код (код компилируется, модульные тесты выполняются и т.д.).

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

Архитектура
Как бы ни было организовано в проекте владение кодом, в любом случае, вся разработка должна подчиняться некоторому более общему замыслу, подходу. Можно выделить как минимум три таких подхода:
  1. Монолитное приложение. Разработчики реализуют каждый свою часть функций системы, о взаимодействии как-то договариваются по ходу разработки. Может быть подходящим решением для локальной команды, но гораздо менее подходящим - для распределенной. Есть существенный риск недопонимания или разногласий между удаленными членами команды.
  2. Платформа разработки и проекты на базе платформы. Позволяет естественным образом разделить ответственность разработчиков, но отнюдь не способствует их сближению. Как раз наоборот, разработчики платформы более интересуются общими задачами, разработчики на платформе – более частными, и их точки зрения на одну и ту же функциональность могут существенно различаться. Кроме того, разработчики платформы становятся в некотором роде «привилегированной кастой», что может приводить к напряжению отношений между группами.
  3. Компонентный подход. Команды отвечают каждая за свой компонент, но совместно определяют интерфейсы взаимодействия компонентов. Если имеется несколько команд в разных частях света, компонентная архитектура будет наиболее точно соответствовать структуре проектной группы. Сложность только в том, что для реализации компонентной архитектуры нужно достаточно уверенное владение данным подходом к проектированию.
Знания о разрабатываемой системе
Это те знания, которые участники команды разделяют между собой. Сюда относятся как знания об архитектуре и исходном коде, так и то общее представление о разрабатываемой системе, из которого архитектура и код материализуются. Проектная документация – спецификации, руководства и пр. - лишь частичное, пусть и упорядоченное отражение этих знаний. В отличие от управляющей информации, такие знания более естественно распространяются в горизонтальной плоскости, от одного участника к другому. Так или иначе, стоит подумать о том, как сделать этот обмен эффективнее. Рассмотрим несколько возможных подходов.
  1. Узнавание по факту. Т.е. один сделал – все остальные видят, когда попадается на глаза. Такой подход говорит о том, что взаимодействия между участниками команды нет вообще. Наиболее вероятным следствием станет то, что разрабатываемое приложение будет состоять из разнородных частей, слабо сочетающихся друг с другом.
  2. Неформальная передача информации. Существенно отличается от предыдущего варианта тем, что здесь речь как раз идет о постоянной коммуникации внутри команды. В таких условиях знания передаются легко и быстро, а излишний формализм только препятствует этому процессу. Чтобы добиться такой коммуникации, недостаточно, однако, посадить всех членов команды в одну комнату, хотя это и совершенно необходимо. (Конечно, надо разделить технических и нетехнических специалистов).
  3. Ведение проектной документации. При всем желании, в документацию, в первую очередь, попадают самые важные и наиболее общие решения. Если документация достаточно детализирована, скорее всего, она будет неактуальной – на поддержание ее в актуальном состоянии требуется слишком много времени и сил. Это главная из причин, по которой многие не придают документации большого значения. Надо согласиться, полностью заменить непосредственное общение документация не сможет. В то же время, отдельные документы могут быть чрезвычайно полезны. Кстати, самые нужные и полезные документы часто возникают сами собой, когда люди записывают что-то «для себя», а потом оказывается, что это давно нужно всем. Я бы предпочел создавать документы только по мере необходимости, всегда обсуждая этот вопрос с группой разработки.
  4. Рассылка информации о принятых решениях по электронной почте. Один из наиболее частых вариантов, подходящий для информирования о более-менее крупных решениях. Присущи все недостатки коммуникации по электронной почте. Живого контакта в любом случае не заменяет, в то же время, рассылаемая таким образом информация чаще всего все равно попадает в документацию. В общем, рассылка - она рассылка и есть.
  5. Регулярные встречи. Первый вопрос – насколько регулярные. В команде, которая встречается раз в неделю, накапливается столько нерешенных частных вопросов, что удержаться в рамках регламента может быть очень сложно. Встречи с периодичностью раз в месяц, скорее всего, будут посвящены только самым общим организационным вопросам. Так или иначе, многие, особенно технические вопросы бывает гораздо удобнее обсудить «в процессе», чем на специально назначенном совещании. Почти все технические решения необходимо проверять на реализуемость и эффективность, их часто приходится корректировать и пересматривать много раз.
  6. Неформальная передача информации внутри команды и регулярные встречи представителей отдельных команд. Подход выглядит привлекательно в случае, если разрабатываемое приложение имеет компонентную архитектуру. Внутри команды знания распространяются благодаря ежедневным совместным обсуждениям и коллективному владению кодом, интерфейсы на компоненты разрабатываются и утверждаются на общих встречах представителей команд. Границы компонентов в этом случае могут определяться исходя из естественных ограничений на объем информации, которую каждая команда может охватить одновременно.
Методология разработки
Это один из тех вопросов, по которым людям может быть сложнее всего договориться. Когда для одного наилучшим подходом будет казаться экстремальное программирование, другой будет поднимать вопрос о недостатке документации и протестовать против изменения требований. Здесь у каждого найдется свой личный опыт и собственное видение ситуации. Споры могут продолжаться вечно. Мое мнение – право окончательного решения по таким вопросам должно быть только у одного человека. Авторитет этого человека должен быть достаточно высоким.
Текущая сборка (Build)
Ежедневная сборка – это процесс, дающий команде уверенность, сигнализируя об успешности или не успешности интеграции отдельных модулей. Это - ориентир команды. Очень важно иметь каждый день актуальную версию разрабатываемого приложения и убеждаться, что она работоспособна и проходит тесты. Соответственно, когда проект не собирается из-за ошибки одного из разработчиков, это сказывается на работе всех остальных. Это может пугать, но это позволяет каждому разработчику в полной мере ощутить свое участие в создании конечного продукта, почувствовать командный дух и персональную ответственность за результаты труда.
Проблемные участки

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

Знания о разрабатываемой системе
Как бы ни был организован обмен информацией, всегда есть риск того, что какие-то аспекты разработки останутся в тени, даже при самом тесном взаимодействии внутри команды. Ясно, что новым участникам команды потребуется время на то, чтобы погрузится в процесс. Некоторые части системы могут оказаться слишком сложными, чтобы всем захотелось вникать в детали. Как бы то ни было, последствия неведения могут быть следующими:
  1. Задержки и простои при выяснении необходимых деталей.
  2. Дублирование уже сделанной кем-то работы.
  3. Некорректная реализация функциональности.
  4. Принятие архитектурных решений, не учитывающих всей специфики проекта.
Недоверие
Люди склонны не доверять тем, кого мало знают. Если два человека ни разу не видели друг друга в лицо, они будут стараться оградить каждый свою часть работы от взаимного вмешательства. Банально, отлаживая код, разработчик будет предполагать что ошибка - в коде другого разработчика. А если уж там будет ошибка, реакция будет куда более болезненная, чем если эта ошибка будет сделана человеком, с которым разработчик работает непосредственно. Люди, работающие в одном помещении, в целом мягче относятся к просчетам других людей, и критика их приобретает гораздо более спокойную форму, чем в случае с иногородними или иностранными коллегами. Вот почему еще так важно организовать сотрудникам хотя бы одну личную встречу.
Ощущение, что все лучше, чем на самом деле
Возникает оттого, что проблемы команд часто остаются их внутренними проблемами, а наружу выносятся только положительные стороны и достижения. Чтобы понять действительное состояние дел в команде, необходимо погрузиться в ее ежедневную деятельность, провести внутри нее какое-то время. Другими словами, войти в доверие. Однако когда разработка ведется несколькими удаленными командами, у руководителя не всегда хватает времени на то, чтобы уделить им равное внимание. Если руководитель посещает удаленный офис раз в год и проводит в нем 3 дня – сложно сказать, правильные ли выводы он сделает из своего визита. Проводя в команде хотя бы неделю раз в 3-4 месяца, узнать можно гораздо больше.

Итоги

  1. Стоит подумать о том, какую информацию сделать общедоступной, и обеспечить к этой информации централизованный доступ. В обязательном порядке должна быть доступна информация о требованиях, целях и планах проекта, а также о структуре и ответственности команд. Хорошее место для размещения всей этой информации – внутренний сайт проекта.
  2. Не стоит экономить на средствах коммуникации. Используйте все доступные средства общения, чтобы сделать даже самых удаленных участников команды ближе. Люди наиболее эффективно взаимодействуют между собой, когда непосредственно общаются друг с другом.
  3. Подумайте о приобретении готовых инструментов, повышающих эффективность совместной работы, или разработайте свои собственные инструменты.
  4. Особенное внимание уделите тому, чем участники команд владеют сообща: архитектура, исходный код, знания о системе, методология разработки. Организуйте это владение с учетом всей специфики вашего проекта.
  5. Наконец, собирайте и анализируйте наиболее типичные проблемы, которые будут вам встречаться. Гарантированного решения этих проблем может и не быть, но минимизировать риски и нежелательные последствия – можно.

 

\

Подписка на новости IT-портала citforum.ck.ua
(библиотека, citforum.ck.ua, CitCity)

Новые публикации:

26 декабря

citforum.ck.ua:



IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Информация для рекламодателей Пресс-релизы -
Послать комментарий
Информация для авторов
Rambler's Top100 This Web server launched on February 24, 1997
Copyright © 1997-2000 CIT, © 2001-2007 CIT Forum
Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...