пятница, 29 мая 2009 г.

Share the pain : система мотивирования

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

Нечестно, что только программист разделяет боль за некачественное решение. Отдел качества должен быть на передовой взаимодействия с заказчиком. После непродолжительной беседы по чату с Виталием Стаховым, мы решили, что и это не совсем верно. Поэтому предложена схема: клиент разделяет боль с тестером, тестер с программистом. Но стал вопрос, а с кем разделяет боль программист? И поняли, что с аналитиком. А аналитик? С пользователем! Круг замкнулся!

Хитрее всех в данной ситуации находится одна фигура. Эта фигура часто орёт на собраниях, что у него "огромная ответственность перед всеми", и поэтому кстати он получает больше всех денег. Но почему то он выпал из раздачи. Кто это? Конечно же менеджер.

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

И это правильно!


среда, 27 мая 2009 г.

Ретроспектива решает конфликты

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

Пару слов о конфликте. Конфликт - это когда точки зрения не совпадают и мы начинаем подключать эмоции, а по сути никуда не двигаемся. Если посмотрите мои посты про эмоции - то знайте - это вы подключили своё подсознательное. Теперь вы стали сильнее и мудрее, когда включили эмоции. Единственное нужно уметь управлять подсознательным (эмоциями). Это умение управлять эмоциональным интеллектом. Подсознательное через эмоции подсказывает - работаем неэффективно. Теперь понятно, что работаем неэффективно, то есть обсуждения зашли в тупик. И это можно оценить по уровню эмоций.

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

Упрощёная схема:
1. Конфликтная ситуация.
2. Остановится
3. Понять свои эмоции.
4. Найти причину (см. технику)
5. Провести ретроспективу на то КАК вы обсуждаете, а не ЧТО вы обсуждали.

Секретная техника действует! Люди меняются и культура разработки растёт!
Приятной ретроспективы!

понедельник, 25 мая 2009 г.

Эволюция культуры команды - ретроспектива

Как я отличаю аджайл команду от не-аджайл? По наличию одной лишь практики - "Ретроспектива". Мне не достаточно сказать - у нас есть она. Я должен увидеть как происходит это таинство. У меня в загашнике есть ряд критериев по которым определяю качество "ретроспективы". Может быть когда-нибудь напишу :)

Сегодня хотел чуток о другом. Я просто несказанно рад, что Дмитрий у себя на блоге описал эту штучку (ссылка). И этот пост воодушевил закончить находящийся в черновике пост про ретроспективу. Асхат подхватил эстафету про ретро. Рекомендую зайти - ссылка.

Продолжать чтение моего поста нужно после чтения поста Дмитрия.

Я привык использовать облегчённый вариант ретроспективы. Я за упрощение во всем её многообразии. Мои ретроспективы просты по структуре
1. ХОРОШО
2. УЛУЧШИТЬ
3. ИДЕИ

Народ интересуется, почему же есть пункты "Хорошо", "Улучшить" и "Идеи", но нет "Плохо" ) Весь сыр бор вокруг пункта 2. Он показывать отличие в психологии проведения ретроспективы. Готовы ли на свершения (которые оформляются Action-планом, который я забыл упомянуть), или просто "вздыхатели" :)

Для русского менталитета пункт 2 должен быть "ПЛОХО". Для европейского - "УЛУЧШИТЬ".

Разница грандиозна. Во-первых, описать "ПЛОХО" нужно 1 мозго-силу. Констатируем факт и мы в шоколаде. Для описания "УЛУЧШИТЬ" нужно 2 мозго-силы. Первая для описания "плохой" ситуации, а вторая мозго-сила для предложения пути как исправить. Вот и получается пункт "УЛУЧШИТЬ" это посложнее, чем "ПЛОХО.

Во-вторых, "ПЛОХО" характеризует натуру, которая больше любит жаловаться, а не решать проблемы. "УЛУЧШАЮТ" в эффективных коллективах, когда идентифицируют неэффективное поведение, ситуацию и ставят задачи по улучшению. Игра слов. Но все же обращу внимания: жалуются - решают, проблемы - задачи. Можете подумать на досуге к какому поведению приводят две эти стратегии, если ими руководствоваться по жизни.

В общем народ можно разбить на две большие группы по этому критерию. Первая - кто ищет оправдания (для них в ретро нужен пункт ПЛОХО) и другая группа, кто ищет возможности (для этих нужен пункт УЛУЧШИТЬ).

А если получается много "УЛУЧШИТЬ" и улучшения достигаются. Это развивает команду. Команда за счёт "УЛУЧШЕНИЙ" изменяется, изменяется её культура.

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

четверг, 21 мая 2009 г.

Секреты построения команды (Team Building)

Использование практик Agile позволяет эффективно управлять созданием продукта. Но создание команды и динамика построения команды в Agile освещены не очень хорошо. В предлагаемом подкасте я рассказываю о практиках, которые ориентированы на развитие командных отношений и повышение эффективности работы. Весь подход состоит из четырёх этапов:
1. Развитие эмоционального интеллекта
2. Коллективное принятие решений
3. Интеграция личных и командных целей
4. Общее видение

Все секреты одним файлом:

суббота, 16 мая 2009 г.

Null Object и синтаксический сахар C#

public interface Null
{
}

internal class NullWord : Word, Null
{
}

if(word is Null)

Без комментариев. Красотища! :)

Использованы паттерны: NullObject и Marker Interface

Подробнее: http://agile.rpod.ru/109024.html

100% Coverage Driven Design (CDD) - борьба с тухлым кодом

Всем известно, что для качественного ПО покрытие должно быть более 70%. Я не знаю, кто это придумал и причем тут качество, но мне не понятно какая польза от этой цифры. Ещё смешнее слышать, как менеджеры привязывают покрытие к качеству. Если рассуждать в плоскости менеджеров, что и на мелководье и рак рыба, то этого качества конечно же для успокоения самолюбия менеджера достаточно. Но истинные войны света должны понимать, что на этом качество не останавливается... Качество кода - это целая культура разработки, в которую вовлечены клиенты, разработчики, тестировщики и много других людей. Но об этом я уже писал неоднократно, а сегодня остановлюсь на новом подходе. Подходе дающим ещё один мощный инструмент разработчику (ни менеджеру, ни тестировщику) для написания качественного кода.

Я же хочу раскрыть другую замечательную вещь, которая повысит качество кода во много раз. Один нюанс - вам придётся делать 100% покрытие :)

Эту идея пришла во время парного программирования с Кириллом Медведевым в рамках одной из стади-групп.

Эту разработку я называл "100% Coverage Driven Design" (или сокращённо CDD). Суть этого подхода есть сложение TDD, общего подхода написания тестов (ключевая фраза здесь - TestLast) и инструмента покрытия кодом.

Напомню цикл TDD:
1. Пишем тест (формализуем ожидания от будущего кода)
2. Реализуем код
3. Рефакторим

В "100% Coverage Driven Design" цикл такой:
1. Тест
2. Код
3. Рефакторим
4. Добиваемся 100% покрытия

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

Возникает вопрос. А что делать с тестами, которые устаревают. Ведь их с помощью покрытия нельзя выявить, а тем более сказать что код, который покрывается устаревшими тестами уже начинает потухать. А решение простое - смирение. Если тест устарел, то первым делом он начинает из зеленого превращаться в красное. И вы тут же решаете, что с ним делать. В красное он может превратиться по множеству причин. Но если одна из причин - устаревший и ненужный тест. Мы его удаляем. Запускаем Code Сoverage и проверяем, где произошли смещения от точки 100% покрытия.

Покажу на маленьком примере. С помощью TDD цикла создали библиотечку. Проверили её покрытие и выяснилось не 100%. Начинаем разбираться и видим, что появился тухлый код. Сначала комментирую тухляк, а потом лопатой все убираю.


Успешно справившись с продуктами собственной жизнедеятельности (удалив тухлятину), я сталкиваюсь с автогенерённым тухляком:



Мне приходиться напрячь мозги и понять, что components объект никогда и не используется! Такое открытие позволяет мне удалить столь запутанную тухлятину тоже.



Результат не заставляет ждать. Идеально чистый код! Я с уверенностью продолжаю дальнейшую разработку.

Теперь немного GUI-шного, про разработку через TestLast и как техника рефакторинга приводит ухудшению Code Coverage.

1. Конечно же, когда добавляется новый функционал, то используем TestFirst:

public void MainForm()
{
     SetUpClass.PrepareTestFile();

     var form = new MainForm();
     var mocks = new MockRepository();

     var dialog = (OpenFileDialog)mocks.CreateMock(typeof(OpenFileDialog));
     Expect.Call(dialog.FileName).Return("filename.ew");
     Expect.Call(dialog.ShowDialog()).Return(DialogResult.OK);
     mocks.ReplayAll();

     form.openFileDialog = dialog;
     form.MenuItem_Open(null, null);

     mocks.VerifyAll();
     Assert.AreEqual(2, form.dicGridControl.Count);
}

2. Реализуем функцию

public void MenuItem_Open(object sender, EventArgs e)
{
     if (openFileDialog.ShowDialog() == DialogResult.OK)
     {
         Dic dic = new Dic();
         dic.Load(openFileDialog.FileName);
         dicGridControl.Bind(dic.ToList());
         }
}

Покрытие: 100%

3. Проводим рефакторинг Replace Nested Conditional with Guard Clauses (кстати, мой самый любимый)

public void MenuItem_Open(object sender, EventArgs e)
{
     if (openFileDialog.ShowDialog() != DialogResult.OK)
         return;


     Dic dic = new Dic();
     dic.Load(openFileDialog.FileName);
     dicGridControl.Bind(dic.ToList());
}

Покрытие: 97%.

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

В общем снизили покрытие. Что делать. Я останусь верным принципам рефакторинга и оставлю проверку входного условия. Но я должен дотянуть покрытие до 100%. Тут на помощь приходит старая практика Test Last. Если раньше, эта техника была вызвана сообращениями разработки тестов для уже разработанного кода, сейчас эта техника изменит своё предназначение. Используя эту технику я подтяну покрытие до 100%.

После мелких рефакторингов я добавил второй тест, используя технику TestLast:

[Test]
public void MainFormCancel()
{
     Expect.Call(dialog.ShowDialog()).Return(DialogResult.Cancel);
     mocks.ReplayAll();

     form.MenuItem_Open(null, null);

     mocks.VerifyAll();
     Assert.AreEqual(0, form.dicGridControl.Count);
}

Результат: покрытие 100%

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

Другой нюанс, если я пойду дальше и подстрахуюсь от проблем с файловой системой:

public void MenuItem_Open(object sender, EventArgs e)
{
     if (openFileDialog.ShowDialog() != DialogResult.OK)
          return;

     try
     {
          Dic dic = new Dic();
          dic.Load(openFileDialog.FileName);
          dicGridControl.Bind(dic.ToList());
     }
     catch (Exception ex)
     {
          MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
     }

}

Этот кусочек кода пишется даже без теста легко. Я нарушил правило TDD и добавил строчку кода без теста. И кара настигла меня моментально - покрытие упало, плюс протестировать статический метод MessageBox.Show - это целая проблема (ни моки, ни что либо другое здесь не поможет - в морг). А представьте сколько проблем мы делаем, когда разрабатываем более менее серьезное приложение?

Что хотелось бы иметь в инструментах покрытия:
1. Механизм разметки - какие функции все же не стоит покрывать. Например, автогенерацию или какие-то ничего незначащие хитрые кейсы.
2. Историю покрытия, чтобы наблюдать диффы. Это понадобилось бы для работы над легаси-системами. В таких системах, если мы начинаем разрабатывать по TDD большая часть кода будем мешать нашему анализу. А анализ диффов позволил бы использовать этот подход даже там.

Вывод: Данную методику изобрёл буквально вчера. За вчера её опробовал, очумел от эффективности и продолжаю праведно работать по ней. Методика 100% покрытия стала для меня точно так же как запускать юнит тесты. Стала родной и без неё уже свет не мил. Теперь не то чтобы у меня тесты есть согласно TDD, но и код очищается от мертвого кода очень удобно и легко. Теперь я не только уверен в функционировании (этому мне помогает TDD), но и что код чистый (этому помогает 100% покрытие). В общем я доволен как маленький ребёнок от такой простои и в тоже время мощной игрушки. Сейчас нарабатываю опыт от применения, о чем буду делиться и буду ждать последователей, кто готов проверить предложенный метод.