среда, 21 марта 2012 г.

О качестве


Точнее об управлении качеством в IT проектах

Наивное представление о качестве

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

Чуть более продвинутое представление о качестве

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

Качество программной системы.  Две группы качеств.

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

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

Здесь часто смешивают эти два набора качеств и делают обобщенный ошибочный вывод. Вот такой

Если для достижения целей проекта достаточно иметь качество, скажем 80% то именно такое качество и надо обеспечивать с самого начала.

Чтобы понять, почему это не всегда так, рассмотрим

Жизненный цикл программной системы

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

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

А значит маленькая и огромная программы с одинаковым уровнем качества (здесь говорим о группе показателей качества, важных для развития системы) 80% - абсолютно не одинаковы с точки зрения стоимости их развития. В первой будет скажем 50 недочетов, а во второй, если она в 100 раз больше их будет уже 5000. Все недочеты влияют друг на друга, создавая множество "степеней свободы". Поэтому, считая сложность системы, нужно не складывать недочеты, а перемножать - рост функции "сложность(количество недочетов)" - экспоненциален. А чем выше сложность - тем больше будет новых недочетов.

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

Можно сказать и проще - программист Вася Пупкин может осмыслить систему если в ней есть не более 50 недочетов. В системе из 100 строк кода 50 недочетов будет соответсовать 50% му качеству. В системе из миилиона строк - 99.995%

Вывод - с ростом сложности и размеров системы планка качества должна повышаться, для достижения тех же показателей затрат.

Но это еще не все. Есть еще

Закон стоимости повышения качества

Который, звучит так "повышать качество намного дороже, чем его поддерживать"

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

Управление качеством

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

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

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

Ступенчатое повышение качества

Обычно мы поступаем так:

Имеем систему из 10000 строк кода. В ней 100 недочетов. Вася Пупкин перестает осознавать эту систему целиком из-за возросшей сложности. Требуется понизить сложность до приемлемого уровня. Вопрос - как это сделать?

Плохой ответ - исправить 50 недочетов.

Хороший ответ - разделить систему на две слабосвязанные подсистемы.
Тогда в каждой будет 50 недочетов и условный Вася Пупкин справится со сложностью в обоих случаях.

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

Планируя качество надо представлять
  • Срок жизни системы
  • Ее предполагаемый объем
  • Целевую функцию затрат на ее создание (какие затраты мы хотим минимизировать)
Исходя из этого можно планировать
  • Несколько этапов развития системы
  • Рефакторинг - пересмотр архитектуры системы с дроблением ее на модули в конце каждого этапа
  • Планку качества, оптимальную для каждого этапа и для каждого модуля
Зачем нужен план

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

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


P.S. Очевидно, если вы пишете систему управления для атомной станции, реактивного истребителя или чего-то в том же духе, рассуждения будут "несколько" другими :-)

Читать полностью...

вторник, 19 июля 2011 г.

Разработка больших систем. Часть 5. Про хаки и рефакторинг.

Хаком назовём кусок кода, который не вкладывается в общую концепцию, не соответствует архитектуре проекта и решает ту или иную специфическую проблему.



Например, при создании программы, где есть пользователи и есть механизм их удаления не вовремя вспомнили, об опасности удаления аккаунта главного администратора системы. А поскольку сроки поджимали, то в программе появился код:

if(“admin”.equals(username)) {

showError(“Can't delete admin”);

return;

}


Как известно, в СССР секса не было.

Аналогично, в идеальной системе нет хаков.

Потому, что в идеальном мире:

· Есть неограниченное количество времени, для проведения любого количества рефакторингов

· Не существует сроков, при нарушении которых, пропадает всякий смысл в дальнейшей разработке

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

За последние 15 лет, мне такие проекты, к сожалению, не попадались.

Поэтому, скажу крамольную вещь: в реальных проектах хаки имеют право быть.

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

Плохие и приемлемые хаки

Любой код, в своей работе опирается на те или иные предположения. Хороший код, опирается только на факты, которые невозможно изменить случайно или незаметно.

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

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

Так вот приемлемым будет только такой хак, который ЯВНО СЛОМАЕТСЯ при выполнении любого из таких условий. «Явно сломается» - значит жестко упадет один из основных use-кейсов, и это не сможет пройти незамеченным для тестировщиков.

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

if(!accountExists(“admin”)) {

throw new RuntimeException(“No admin exists”);

}


или лучше


assert(accountExists(“admin”));


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

Энтропия и рефакторинг


Два факта:

1. Попытки немедленно уничтожать каждый приемлемый хак приводят к неприемлемому удорожанию проекта

2. Чрезмерное увеличение количества приемлемых хаков приводят к переусложнению кода и резкому снижению его понятности

Количество хаков в коде - это величина, которую необходимо постоянно отслеживать. Для каждого проекта опытным путём находится максимально допустимое количество хаков. При превышении этого количества необходимо делать рефакторинг.

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

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


Читать полностью...

Разработка больших систем. Часть 4. Принципы ООП.

Принципы ООП очень хорошо описаны тут: http://www.oodesign.com/design-principles.html (правда по английски).

Их всего 5.

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

Принцип open-close

Класс должен быть закрыт для модификаций, но открыт для расширений.

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

Принцип инверсии зависимостей

Состоит из двух утверждений:
1. Высокоуровневые модули не должны зависеть от низкоуровневых модулей. И те и другие должны зависеть от абстракций.
2. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций (конкретизируя их)

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

Принцип сегрегации интерфейсов

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

Пример плохого интерфейса
interface HrenovaTucha {
void sentToPrinter(String text);
Image scan();
void burnCD(Image cd)
}


Что мне делать, если у меня нет сканера?

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

Принцип единственной зоны ответственности

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

Принцип подменяемости Лискова

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

Другими словами — класс наследник обязан соблюдать контракт класса-родителя. Однако наследник имеет право расширить контракт, введя новые методы. И полное право изменить способ выполнения контракта, перегрузив существующие методы.

Читать полностью...

понедельник, 18 июля 2011 г.

Разработка больших систем. Часть 3. О хорошем дизайне.

Как было упомянуто, одной из причин «плохих» багов является неверная архитектура приложения. И, хотя построение архитектуры является обязанностью архитектора, а не разработчика, разработчику полезно бывает уметь видеть стандартные ошибки в архитектуре и отличать правильную архитектуру от неправильной. Хотя бы для того, чтобы не испортить правильную архитектуру своими изменениями.

Здесь очень коротко изложу основные принципы дизайна.



Принцип отсутствия скрытых зависимостей

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

Смысл: если этот принцип не соблюдается, то любое «неполное» изменение автоматически приводит к плохому багу.

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

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

Во-вторых, каждый класс должен также относиться к тем классам и интерфейсам, которые он использует: он не должен ничего предполагать об их внутреннем строении. Он пользуется их публичным API от которого ожидает разумного поведения (исполнения их контракта). Если поведение неразумно — это повод исправить «неразумный» класс, в соответствии с контрактом, а не подстроиться под это неразумное поведение.

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

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

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

Упражнение

Назовите плохие методы в данном классе. Скажите, почему они плохие.
class AlertManager {
public List searchAlerts(String query);
public List searchAlerts2(String query);
public void processAlerts(List alerts);
public List doSearchAndThenDeleteAlertsIfBad(String query);
public Map getInternalAlertMap();
public Alert getAlertById(String id);
public void putAlert(String id,Alert alert);
}


Принцип одного контракта

В любой паре взаимодействующих классов один класс определяет весь контракт взаимодействия, а другой управляет этим взаимодействием, согласно этому контракту.
То есть если класс А вызывает публичное API класса Б, то класс Б не только не имеет права вызывать публичное API класса А, но и вообще не должен иметь прямую ссылка на класс А. Если классу А необходимо получать информацию о каких-то событиях от класса Б, то класс Б должен иметь в своем API методы для подписывания на данный вид информации, а класс А — использовать эти методы, согласно контракту класса Б.

Другими словами, из двух взаимодействующих классов ОДИН (в данном случае класс Б) определяет ВЕСЬ контракт взаимодействия. А другой управляет, оставаясь в рамках этого контракта.

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

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

Принцип 80-20

Любая программная компонента, предназначенная для использования другими компонентами, должна решать 80% проблем. При попытке решить 100%, сложность компоненты превзойдет сложность решения проблем без ее использования.
Ключевым признаком нарушения этого принципа является появление методов, которые имеют среди своих параметров управляющие, влияющие на ход выполнения метода. Причем в случае, если ранее в этих методах таких параметров не было.

Например, был метод

doSearch(String query);

А вам хочется сделать из него метод

doSearch(String query,boolean useNewAdvacedApproach, double weightForDescriptionField,SpecialSearchParameters params);

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

Принцип незавоевания

Ваша библиотека, API , компонент (и вообще что угодно) НЕ ДОЛЖНО конфликтовать с использованием стандартных способов разработки, существовавших до вас. Другими словами - дополняйте, но не подменяйте. Должно допускаться "смешанное" кодирование: с использованием вашего API и стандартного вместе.

Этот принцип проще показать на примере.

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

Читать полностью...

пятница, 15 июля 2011 г.

Разработка больших систем. Часть 2. Работа с багами.

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



Хорошие баги

К хорошим багам относятся разного рода опечатки, сделанные по причине того, что люди не являются роботами. (Например, разного рода NPE и т д). Другой вид хорошего бага, локальная логическая ошибка в определенном месте кода, которая влияет только на это место. (Например, вместо “>” поставили “<” или немного неверно предсказали поведение внешнего компонента, и, поэтому, неверно интерпретировали его результат).

Общими признаками хороших багов является:

• Повторяемость (они всегда возникают при одних и тех же условиях)
• Локальность (они относятся только к одному объекту и никак не затрагивают другие объекты)

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

Плохие баги

Признаками плохого бага являются

• Трудноуловимость (он возникает иногда и никто не знает, как его повторить)
• Не локальность. (Если его нашли, способ его исправления не очевиден, поскольку он влияет на множество компонентов системы)

Мне известно три причины плохих багов:

• Неверный дизайн (архитектура) приложения
• Неправильно вносимые изменения (см. главу про изменения)
• Гены разработчика

(Последняя причина не является устранимой на современной стадии развития науки. )

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

Работа с разными типами багов

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

Правка хорошего бага

Сводится к исправлению тупой ошибки и проверке, что баг более не появляется.

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

Правка плохого бага

Проводится в точности также, как и внесение изменения! (см главу про внесение изменений).

Читать полностью...

четверг, 14 июля 2011 г.

Разработка больших систем. Часть 1. Внесение изменений.

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

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

Начну с первой (исторически) части - как делать (и как НЕ делать) изменения в большой программной системе.



Внесение изменений

Как вносить изменения (или исправления) в сложную систему.

Неверный способ

1) Найти место для внесения изменения
2) Внести изменение
3) Проверить работает ли
4) Если нет, перейти к пункту 2

Почему этот способ не работает

Нет анализа исходной точки

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

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

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

Нет анализа конечных целей

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

Пример из жизни: Заказчиком была поставлена задача добавить к системе полнотекстовый поиск. Закзачик сделал акцент на том, что этот поиск позволит искать внутри описания объектов. При этом он не отметил, что поиск , работает в несколько раз быстрее, чем ранее применявшееся фильтрование в памяти, а также , что поиск может быть использован ВМЕСТО фильтрования, что повысит скорость работы системы в несколько раз. Пропуск фазы анализа конечной цели привел к тому, что полнотектовый поиск был приделан нарядус фильтрованием. И, хотя и позволил искать внутри описания объектов, никак не увеличил скорость работы. Именно это неверное решение привело к возникновению в будущем задачи про 6000 медленных объектов (см выше).

Нет гарантии, что изменение будет работать всегда

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

На самом деле, этот процесс гарантирует в точности обратное.

Образный пример – вы приносите в сервисный центр сломанный аудиоплэйер. Который включается через раз и иногда зависает. Ждете неделю. Потом вам его возвращают с диагнозом «неисправность не обнаружена». И все потому, что девочка-приемщик включила плэйер, подержала его включенным 2 минуты, и он за это время не завис.

Правильный способ внесения изменений

Вероятно, уже понятно, что неверный способ внесения изменений (также известный, как «запинывание») приводит к очень плохим результатам. Как минимум, все изменения , внесенные таким образом, потом вычищает и переписывает более опытный специалист, как максимум, проект убивается и становится не управляемым.
Теперь опишу правильный способ внесения изменений.

ДАО изменений

Вкратце, чтобы сделать изменение, вы должны точно знать что вы имеете сейчас (точка А), что хотите иметь в результате(точка Б) и как убедиться, что ваше изменение делает переход из точки А в точку Б и не делает ничего больше.

Процесс выглядит так:

1) Понять цели вносимого изменения
2) Описать, как должна работать система после внесения изменения
3) Понять, как система работает сейчас, какие аспекты она учитывает
4) Предложить метод перехода из состояния «сейчас» в состояние «надо»
5) Встать в позицию критика и объяснить, почему предложенный способ – полное говно
6) Вернуться в конструктивную позицию и перейти к пункту 4

На третьей-четвертой итерации вам не удастся выполнить пункт (5). Значит, пора переходить к реализации:

7) Реализовать выбранный метод, снабдив его комментариями, описывающими ваш ход мысли – почему такое изменение приведет к нужным результатам
8) Проверить реализацию с учетом всех аспектов, которые могут влиять на изменение (разные входные данные, секьюрити и т п). Здесь иногда надо написать юнит тест (если напрямую проверить не получается). А иногда, если требуется много проверок, можно написать инструкцию для тестера – включить в нее все действия, которые тестер должен выполнить.
9) Важно: если при проверках все идет не так, как было вами предсказано, НЕОБХОДИМО понять, почему это так. Ни в коем случае не списывать на случайность.
10) Задача считается сделанной, если вы видите, что переход в нужную точку состоялся и прекрасно понимаете как все работает после этого изменения.

Читать полностью...

вторник, 15 сентября 2009 г.

Процессы, проекты и российские компании

Введение

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





Определения

Задача – однократно выполняемая за короткое время работа одного исполнителя (то есть, каждое новое задание выполняется одним сотрудником по каждый раз новым или модифицированным правилам);

Функция – регулярная работа одного работника, выполняемая по известным ему правилам (то есть, каждое новое задание выполняется одним сотрудником, но всегда по установленным, повторяющимся, известным ему правилам);

Проект – однократно выполняемая работа многих исполнителей за длительное время (то есть, каждое новое задание выполняется многими сотрудниками, по каждый раз заново сформулированным правилам и алгоритмам);

Процесс – это регулярно выполняемая работа многих исполнителей по четко зафиксированным правилам и алгоритмам.

Отличия процессов от проектов

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

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

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

Процесс «планируется» однократно, на стадии внедрения системы автоматизации и становится частью этой системы. Далее, он периодически подправляется (оптимизация процесса) или может быть полностью заменен (реинжениринг процесса).

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

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

Далее стоит отметить разницу в подходах к контролю деятельности при процессном и проектном управлении:

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

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

Таким образом, автоматизация управления процессами отличается от автоматизации управления проектами подходами к планированию и контролю деятельности. Причем разница существенна и применение систем управления процессами для управления проектами, либо наоборот — неэффективно.

Автоматизация проектов и автоматизация процессов

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

Классические системы управления процессами характеризуются наличием нескольких типов задач и настраиваемого жизненного цикла для задач каждого типа. Таким образом реализуется описанный выше подход к планированию процессов — планируем один раз — выполняем много раз.

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

Классические системы управления проектами строятся вокруг стандартного цикла: план-реализация-контроль результата”. Они включают компоненту для создания плана проекта, компоненту для сбора информации о фактическом ходе проекта и компоненту для сравнения плана и факта.

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

Но часто ли в реальной жизни мы сталкиваемся с чистыми процессами и чистыми проектами?

Организация деятельности в средней российской компании

Рассмотрим классический пример, взятый из статьи, посвященной процессам и проектам:

Пример:

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

Но представьте, что в какой-то момент времени, Вам необходимо закупить нечто, что Вы никогда не закупали, и при этом, не ясно как это нечто закупить. Например, Вам потребовалось сырье, которое производится лишь в одной стране мира, представительства и поставщика данного производителя в РФ нет, и не ясно как его можно вообще купить? Что Вы будете делать в этой нестандартной ситуации?

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

Андрей Борисов, старший консультант компании TOPs, г. Москва.

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

Пример является характерным, по крайней мере для российских средних компаний.

Что получается при применения классической процессной автоматизированной системы для управления такой деятельностью? Одно из двух:

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

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

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

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

Заманчивой, выглядит идея примененить для управления такой деятельностью проектную систему. Однако и здесь есть свои недостатки. Если процессная система не учитывает нестандартные активности, то проектная не удобна при работе со стандартными. При ее применении, вам придется каждый раз заново планировать повторяющуюся деятельность. Это надоедает, занимает лишнее время, приводит к ошибкам. Кроме того, в системе теряется информация о том, что какие-то части являются повторяющимися активностями, что не помогает при анализе деятельности.

Тогда какая нужна система?

В идеале, было бы иметь систему, способную обрабатывать оба вида активностей — периодические и нестандартные.

Читать полностью...