воскресенье, 18 декабря 2011 г.

Идеальный программист


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


Подобная закономерность обусловлена следующими базовыми для аутсорсинга вещами:

  1. Заказчик (особенно, если он представляет небольшую фирму) хочет заплатить за разработку программы как можно меньше. Один из самых важных критериев (особенно, для неопытного Заказчика) – это цена проекта. Мысль о том, что дешево написанная программа может «глючить», и ее сопровождение может превратиться в серьезные муки, ему не приходит в голову.
  2. Заказчик, как правило, имеет слабое представление о том, как разрабатывается программа и из чего состоит программа. То, что он видит – это лишь видимая часть программы, интерфейс. Но помимо интерфейса есть еще и бизнес-часть, которая не видна, но на разработку которой нужны существенные затраты времени.

Фактически, разработчик оказывается поставленным перед двумя противоречиями:

  1. Цена – Качество. Если потратить больше денег (времени), то можно получить более высокое качество, но тогда Заказчик не подпишет контракт, т.к. «индусы обещали сделать дешевле».
  2. Внешний вид – Функциональность. Если потратить время на технический дизайн, то можно создать программу, которую будет легко сопровождать, и она будет содержать минимальное количество ошибок, но, при этом, компания потеряет Заказчика, т.к. Заказчик начнет беспокоиться, если ему не демонстрировать прогресс. Периодически Заказчику нужно показывать «почти работающий» прототип, который выглядит «прельстиво и любовно».

Для «решения» подобных задач разработчики прибегают к следующим тактикам:

1. Прототипирование.
Быстренько создается прототип, который можно показать Заказчику. Далее он «обрастает мясом».

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

ПРИМЕР. Два года назад, анализируя ход одного из проектов, заметил следующее. Во-первых, в программе было очень много ошибок, связанных с корректной работой wizard’а. Это было Internet-based приложение, и wizard пришлось реализовывать вручную. Он «глючил» по страшному. Например, некорректно работали кнопки Internet Explorer’а «Назад» и «Вперед». Или при пролистывании страниц wizard’а назад, а затем – вперед, не сохранялись уже выбранные опции. Эти «глюки» несомненно были обусловлены подходом к созданию wizard’а. Скорее всего сначала быстро-быстро был создан wizard, который показали Заказчику. И лишь затем программист заметил баги, на которые ему указал тестер. Причем каждый баг фиксился индивидуально, без всякой системы. Это видно по тому, что была масса подобных ошибок, и после исправления одной появлялась какая-нибудь другая. Если бы сначала программист немного подумал и создал бы модель работы wizard’а, то ошибок можно было бы избежать.

Во-вторых, интерфейс программы был ужасно неудобен. В качестве примера сошлюсь на страницу, которая была предназначена для задания значений для переменных. Дело в том, что программа предоставляла пользователю возможность на основе шаблона сгенерировать свой PDF-документ. Шаблон содержал множество различных переменных, вместо которых можно было подставить графику и текст. Для задания значения каждой переменной на странице имелась отдельная кнопка. Такое решение было приемлемым для случая, когда в шаблоне было всего 5 переменных. Но если количество переменных равнялось 20, то страница превращалась в «портянку» из кнопок.

Опять-таки проблему породил подсистемный подход. Если б программист думал не о быстрейшем создании прототипа, а о модели функционирования использования приложения, он бы неминуемо задался вопросом: «А сколько переменных может быть в одном шаблоне?» Соответственно, и продумал бы интерфейс таким образом, чтобы было удобно и при 5, и при 20 переменных.

2. Использование стороннего кода.
Сторонний код позволяет существенно сократить время и стоимость разработки. Успешный в аутсорсинге программист, по моим наблюдениям, имеет нечто вроде картотеки кода. Как правило, это либо код программ, написанных им ранее, либо примеры, скаченные с различных программерских сайтов.

Новую программу разработчик конструирует из набора почти готовых блоков – фрагментов кода из других программ.

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

ПРИМЕР. Разработчику необходимо создать Календарь (на самом деле – планировщик событий) на подобии Календаря из MS Outlook. Как известно, события, которые можно наметить в Календаре, могут быть двух типов:

  • события, которые случаются всего 1 (один!) раз;
  • события, которые происходят многократно.

Это обуславливает возникновение двух серьезных задач, которые нужно решить при проектировании системы:

  1. Интерфейс работы с базой данных событий (назовем ее ядром) должен быть одинаковым и не различаться для единичных и рекуррентных событий. Если интерфейс не универсален, он породит ветвление в GUI-коде (диалогах и контролах для работы с Календарем). А это, в свою очередь, приведет к разрастанию и дублированию кода.
  2. Интерфейс для «распаковки» рекуррентного события на временной интервал должен быть одинаков вне зависимости от того, какой период используется: дни, недели, месяцы. Паттерны рекурренции – в зависимости от периода – могут быть разные. Например: «Повторять событие каждые три дня», «Повторять событие каждый понедельник через 2 недели», «Повторять событие 3-го числа каждых трех месяцев» и т.д. Если интерфейс и алгоритм «распаковки» рекуррентного события будет для каждого паттерна свой, то мы опять придем к ветвлению и дублированию кода.

Вместо решения этих задач, разработчик предпочел найти готовый контрол для Календаря. Этот контрол лишь демонстрирует один из видов Календаря, которые есть в MS Outlook. Отсутствует база данных для единичных и рекуррентных событий. Контрол демонстрирует только один view Календаря, а по условиям задачи их – как в MS Outlook – должно быть четыре.

Тем не менее, менеджер проекта предпочитает это решение, т.к. оно позволяет ему быстро продемонстрировать Заказчику прототип программы.

«Нарастить мясом» Календарь предполагается постепенно. Это, к сожалению, приведет к проектированию от подсистемы. Ведь сначала будут реализованы наиболее простые и, следовательно, наиболее частные случаи. Соответственно, код будет содержать много операторов if.

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

ПРИМЕР. Одному Клиенту понадобилась почтовая программа, которая хранит все данные и запускается с Compact Flash. Суть идеи проста: пользователь садится за любой компьютер, подключает к нему флэшку с почтовым клиентом, запускает программу и получает работает с электронной почтой. Поскольку все настройки и вся почта хранится на флэшке, программу можно запустить на любом компьютере.

Идея Заказчика – распространять подобные программы вместе с Compact Flash в рекламных целях, например, при покупке пользователем автомобиля. Предполагается, что на e-mail пользователя (покупателя) впоследствии будут рассылаться письма рекламно-информационного характера, например, напоминания о необходимости пройти техосмотр.

Программист, ведущий этот проект, по согласованию с Заказчиком выбрал за прототип для e-mail клиента MS Outlook. Создаваемый почтовый клиент внешне (по интерфейсу) должен иметь аналогичный вид. Кроме того, в нем должен быть Календарь, Контакты, правила сортировки почты и т.д.

Мне кажется, что подобный подход приведет к созданию красивой вещи, которой нельзя будет пользоваться. На текущий момент у потенциальных пользователей программы уже есть почтовые клиенты. Скорее всего, на работе – это MS Outlook. Дома – это либо тот же Outlook, либо Outlook Express, либо The Bat. Есть и другие программы. Полученную почту люди хранят в базах данных тех программ, которыми пользуются. Если же на этот рынок вывести еще один e-mail клиент без возможности импорта писем из других программ и без возможности экспорта писем в другие программы, то, вероятнее всего, этой новой программой не будут пользоваться. Ибо для пользователя важно не потерять информацию.

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

Другой пример из того же проекта. Программист создал контакты, которые выглядят так же или почти так же, как в MS Outlook. Красиво отображаются. На их основе можно создать письмо. Но у контактов две функции. Первая – отображаться в списке контактов и позволять создавать сообщение контакту. Вторая – заменять в заголовке письма e-mail адрес именем контакта. О первой функции программист подумал, потому что она была видна, а о второй – нет, т.к. с первого взгляда ее было не разглядеть.

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

Настрой на прототипирование приводит к тому, что программисты думают постоянно в исходном коде и производительность своей работы измеряют либо исходным кодом, либо каким-нибудь интерфейсным элементом, который можно показать. Например, один C++ программист, который считается самым опытным в нашей компании, не в состоянии изложить свои мысли на бумаге, т.е. не может написать проектную документацию. Ему проще взять какой-нибудь готовый фрагмент кода за основу и далее «нарастить его мясом», чем подумать и написать сначала дизайн-документ. Он считает это бесполезной тратой времени.

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

Например, у одного заокеанского Заказчика, который применял формальный подход, была такая технология работы:

  1. Разработка и написание ERD (Engineering Requirements Document). Этот документ описывает требования к создаваемой системе, а также содержит элементы пользовательского интерфейса и алгоритмы работы с ними.
  2. Разработка и написание SDD (Software Design Document). Этот документ содержит описание структур данных, их интерфейсов, а также – описание наиболее сложных алгоритмов.
  3. Написание кода.
  4. Тестирование.
  5. Интеграция в основной бранч.

При этом наибольшее время уходило на шаг 2 (от двух до четырех недель), т.к. нужно было продумать структуры данных и описать алгоритмы. Зато написание кода занимало 2 – 3 дня. Написанный таким образом код содержал очень малое количество ошибок.

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

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

Написано в 2006-ом году.