Показаны сообщения с ярлыком практика программирования. Показать все сообщения
Показаны сообщения с ярлыком практика программирования. Показать все сообщения

среда, июня 18, 2008

Про офис

Нужен ли офис в программистской компании? Почему бы собственно программистам не сидеть дома, писать себе спокойно программы? Интернет сейчас быстрый поставить недорого. Средства обмена информацией есть в огромном количестве - недорогие и много чего умеющие. Электронная почта, телефон, разнообразные интернет-пейджеры, в том числе и поддерживающие разговоры голосом, телеконференции. Да и показать что происходит можно легко - есть и WebEx и Netmeeting и GoToMeeting и ещё масса подобных программ.

Зачем же нужен офис - за него ведь деньги платить нужно[имеется ввиду за аренду], к тому же в него нужно ездить - тратить время на дорогу.

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

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

понедельник, марта 10, 2008

Отчет об ошибке

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

Поэтому я завязываю с предисловием и перехожу к сути дела.


  1. Перед тем как добавить новый отчёт об ошибке (новый баг, CR типа дефект, называйте как хотите) необходимо убедиться что его ещё нет в системе.

  2. Уточнить о какой версии продукта (и модуля, и если нужно подмодуля) идет речь. До последней цифры, даже если их 18.

    Например: Программа Феликс, версия 4.2.1121.1356b

  3. Описать проблему в примерно таком порядке:
    1. Краткое описание
      Неправильное сложение отрицательных чисел
    2. Последовательность шагов, которую нужно проделать для того чтобы проблему воспроизвести
      • Ввести операцию сложения (ну может Феликс в польской записи работает :-) )
      • Ввести '-2' в качестве первого аргумента
      • Ввести '-3 в качестве второго аргумента
      • Нажать на кнопку получения результата
    3. Ожидаемый результат - вы думали что когда это проделаете, то получится то-то
      Ожидали получить -5
    4. Полученный результат - а на самом деле получилось ....
      Получили -1
    5. Существует ли способ получить ожидаемый результат (способ обойти ошибку)? Если да, то какой?
      Да, если сначала ввести -3, а потом -2 то сложение выполняется верно
  4. Все сообщения об ошибках должны быть обязательно приведены (желательно в виде скриншота)
    Сообщений об ошибках не выдаётся


И все ...

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

четверг, октября 04, 2007

Производительность XSLT

XSLT не очень "быстрый" язык. Разные XSLT процессоры работают по разному, и пользуются разными методами оптимизации.

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

В XSLT существуют потенциально медленные конструкции, при этом разные процессоры могут с разной скоростью исполнять разные инструкции, поскольку по-разному оптимизированы.

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


XSLT процессор и ОС

Иногда может показаться так, что один и тот же XSLT процессор по разному работает на под разными ОС.

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

Про переменные

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

count

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

preceding:: и другие

Использование preceding и других "осевых" функций (preceding-sibling, following, following-sibling) может существенно замедлить XSL преобразование, поэтому необходимо постараться от них избавиться.

Часто preceding и preceding-sibling используются для организации группировки или выборки уникальных элементов. Вместо этого можно пользоваться методом Мюнха(xsl:key + generate-id()).

//

Использование '//' для поиска также может сильно замедлить преобразование, особенно в случае большого файла, с большим количеством узлов. Здесь, также как и в случае группировки, стоит подумать об использовании xsl:key.

Проход по узлам

Старайтесь осуществлять "проход" по коротким спискам.

Использование ключей

Ключи (элемент xsl:key и функция key) могут очень сильно улучшить производительность XSL программы, особенно если в ней производятся "большие" поиски.

В зависимости от реализации использование элемента xsl:key приводит к созданию XSL процессоров внутреннего индекса, за счёт чего достигается очень быстрый доступ к соответствующим узлам.

Использование сокращённого вычисления (short-circuiting)

В XSL, как и во многих других языках программирования вычисление булевых выражений осуществляется при помощи сокращённого вычисления. Это значит что вычисление прекращается, как только ясен результат. Например в выражении [1 OR (всё-что-угодно)] значение выражения 'всё-что-угодно' вычисляться не будет, поскольку ясно, что общий результат от этого не изменится.

А раз так, то стоит в логических вычислениях (xsl:if, xsl:when) ставить в начало те выражения, которые требует меньше времени для вычисления.

Не стоит полагаться на xsl:message

Очень часто мы пользуемся в своей работе "отладочной печатью". В случае XSL ф-ю печати выполняет элемент xsl:message. Этот элемент тем не менее не совсем прост. Дело в том, что он нарушает необходимое в функциональном языке (а XSL - функциональный язык) свойство отсутствия побочных эффектов. На самом деле порядок вывода xsl:message не обязательно будет таким, как нам кажется, он может зависеть от используемого XSL процессора. Поэтому для отладки, на мой взгляд, лучше пользоваться встроенными средствами трассировки, имеющихся практически во всех процессорах.


И напоследок

Каждый XSLT процессор работает немного по-своему. Совершенно необходимо тестировать подготовленное XSLT преобразование именно на том процессоре, который будет работать в реальной версии продукта.

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

Также, помочь определить проблемные места программы на XSLT может профилирование, то есть измерение времени выполнения определенных участков кода. Практически все современные IDE для работы с XML и XSLT (например
Altova XMLSpy, Stylus Studio, oXygen XML) предоставляют также средства профилирования.

Что ещё почитать про производительность XSLT

  1. Предложения разработчиков процессора Xalan
  2. Советы от Microsoft
  3. Список советов по улучшению производительности XSLT из XSLT FAQ (в том числе от Michael Key)
  4. Немного теории: Michael Key: XSLT and XPath Optimization.

воскресенье, августа 19, 2007

Про типы

В Блоге Тру Программиста появилась подборка статей, посвящённых типам данных. Всем интересующимся рекомендуется к прочтению.

Все описанное в статьях (как тех на которые я ссылался, так и первоисточниках имени Chris Smith и Luca Cardelli), написано на довольно высоком уровне. Мне же кажется, что очень важным моментом здесь является "качественное" понимание, объяснение, так сказать "на пальцах". Потому что без подобного понимания знание фактов и определений скорее является следованием "культу карго", чем настоящим знанием.

Итак, что же такое типы и зачем они нужны?

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

Как известно математики часто проводят в своих рассуждениях чёткое различие между элементами, множествами элементов, функциями и так далее. Для новой переменной, используемой в первый раз, математик определяет "тип", например: "Пусть f - это функция действительного переменного". Часто в книгах (обычно во введении и предисловии) также даются подобные определения: "В дальнейшем для множеств будут использоваться прописные буквы". Эти определения являются не более чем "помощниками" для читателя, позволяют ему быстрее ориентироваться в дальнейшем тексте.

Представители логики и теории множеств предпочитали не иметь дело с переменными различных "типов". Однако, такая необходимость появилась весной 1901 года, когда, во время работы над фундаментальным трудом "Principia Mathematica" известный английский математик и философ Бертран Рассел сформулировал так называемый парадокс Рассела:

"Пусть S - это множество всех множеств, которые не содержат себя в качестве элемента. Содержит ли S само себя?"

Любой ответ, данный на этот вопрос практически мгновенно приводит к противоречию. Данный парадокс можно также переформулировать несколькими более "жизненными" способами:

  • Деревенскому брадобрею приказали «брить всякого, кто сам не бреется, и не брить того, кто сам бреется», как он должен поступить с собой?

  • В одной стране вышел указ: «Мэры всех городов должны жить не в своем городе, а в специальном Городе мэров», где должен жить мэр Города мэров?

  • Некая библиотека решила составить библиографический каталог, в который входили бы все те и только те библиографические каталоги, которые не содержат ссылок на самих себя. Должен ли такой каталог включать ссылку на себя?
Решение этого парадокса, предложенное Расселом носит название "теории типов" и заключается в том, что каждой логической или алгебраической переменной приписывается "тип", который определяет, обозначает ли она [переменная] элемент, множество, множество множеств и так далее. Далее, Рассел постулировал, что любое утверждение вида" x является элементом из y" грамматически осмысленно лишьтогда, когда x - переменная типа элемент, а y - переменная типа множество или x - переменная типа множество, а y - переменная типа множество множеств и так далее. Любое утверждение, не удовлетворяющее этому правилу считается бессмысленным - вопрос о его истинности или ложности просто не существует, оно представляет собой просто набор букв.

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

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

Особенностями понятия типа являются:
  1. Тип определяет класс значений, которые могут принимать переменная или выражение
  2. Каждое значение принадлежит одному и только одному типу
  3. Тип значения константы, переменной или выражения можно вывести либо из контекста, либо из вида самого операнда, не обращаясь к значениям, вычисляемым во время работы программы
  4. Какой операции соответствует некоторый фиксированный тип операндов и некоторый фиксированный (обычно тот же самый) тип результата. (операции, обозначаемые одним и тем же символом, но производимые над различными типами операндом считаем разными)
  5. Для каждого типа свойства значений и элементарных операций над значениями задаются при помощи аксиом.
Таким образом знание типа, позволяет обнаруживать в программе бессмысленные конструкции и предотвращает множество ошибок.

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

Использованные материалы:
  1. Russel's Paradox
  2. Парадокс Рассела - Википедия
  3. Luca Cardelli. Type systems.
  4. К. Хоор "О структурной организации данных" из книги У. Дал, Э. Дейкстра, К. Хоор "Структурное программирование", Москва, Мир, 1975

суббота, августа 04, 2007

Реальное рабочее время

Меня продолжает сильно интересовать проблема оценки сроков проекта. В этой связи я наконец добрался до книги Steve McConnel Software Estimation: Demystifying the Black Art, которая в русском переводе имеет совершенно удивительное название "Сколько стоит программный проект". (на самом деле объяснить название конечно можно, поскольку в содержании книги сроки, затраты или рабочее время считаются более или менее взаимозаменяемыми понятиями).

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

В книге описаны различные способы оценки сроков (или затрат) на выполнение проектов, но меня заинтересовало вот что.

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

Насколько это число соответствует действительности - неважно, лучшего у нас все равно нет. Однако важным является то, учитывает ли это число время, проведённое на работе, но при этом потраченное на "нерабочие дела" - интернет, кофе, перекуры и так далее.

В связи с этим какое-то время назад я организовал на RSDN голосование поэтому поводу. Вот вопрос, который я задавал и результаты голосования:

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

Замечания

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

  1. "Подобные подсчеты бессмысленны". Бизнес сторона программирования требует выдачи сроков. Более того, часто невозможно даже давать сроки по Steve McConnel - при помощи диапазонов. И человеку, который должен составить оценку волей или неволей приходится выдавать прогноз. И лучше если у него есть понимание того, что выданная оценка скажем в 5 часов рабочего времени - это в реальности два рабочих дня.
  2. "Настоящий программист может думать о работе и дома и во время отдыха, поэтому непонятно что считаем". Действительно, это так. И действительно это не особенно то и подсчитаешь. Программа подсчёта рабочего времени может подсчитать сколько времени у меня на экране была открыта Visual Studio, но вряд ли догадается, что я в это время думал про то, как здорово понырял в красном море. Тут мой ответ таков - лучше хоть какие-то, пусть даже заниженные оценки, чем никаких.
На самом деле, мне кажется вышеуказанные замечания вызваны некоторой боязнью, что данные о том сколько человек "реально" работает могут быть использованы для оценки работы человека и последующих выводов с точки зрения зарплат, премий, повышений и тому подобного. Ничего такого я не имел ввиду и считаю, что подобное использование таких данных только повредит проекту. Эти данные нужны управленцу исключительно для того, чтобы точнее планировать работу и выдавать более точные сроки, но ни в коем случае для того, чтобы выделять "лучших" и "худших".

17458907.195e50a0db7e7428ccd693c67f635406.1186223110.9626a2d31cf793845f340f27c23ea8b8

пятница, августа 03, 2007

К вопросу о "стандартах кодирования"

На форуме Artima Developer Spotlight появилась тема: "What's the Most Effective Code Style Policy?", посвящённая обсуждения "стандартов кодирования".

Немного о терминологии

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

Рассмотрим возможные смыслы термина:

  1. Стиль и синтаксис форматирования исходного кода программы
    • Расстановка скобок
    • Использование пробелов и пустых строк
    • Правила написания выражений (for, if, while и так далее)
    • Правила именования переменных
    • Правила объявления переменных
    • Правила написания комментариев
    • Правила использование отступов
  2. Иногда к содержанию первого пункта могут быть добавлены элементы "управления конфигурацией"
    • правила именования файлов
    • структура файлов проекта
    • правила использования системы контроля версий
    • стандартизация используемых средств разработки
  3. Иногда к стандартам кодирования могут также быть добавлены "best practices" того, как надо писать программы, в зависимости от определённого языка программирования, среды разработки, компании или специфики разрабатываемого продукта (типичным примером может служить требование "всегда создавать в классе виртуальный деструктор", для C++)
Конечно же, "стандарты кодирования" могут включать в себя содержимое и всех пунктов сразу, и какую-то их смесь, а также что-то что здесь не упомянуто.

Мой взгляд

Здесь я хочу ответить на вопрос, заданный в теме на форму Artima - "What's the Most Effective Code Style Policy?".

Обращаю внимание, что все дальнейшие рассуждения касаются "стандартов кодирования" в смысле (1) и оставляют аспекты (2) и (3) без внимания.

Основной необходимостью существования "стандартов кодирования" обычно называют (некоторые части процитированы по Code Conventions for Java Programming Language, они отмечены '*'):
  1. Необходимость работ над одним и тем же исходным файлом нескольким программистам
  2. Улучшение "понятности" исходного кода, что позволяет программистам более полно и быстро понять новый для них код (*)
  3. Необходимость сопровождения (исправления ошибок, внесения небольших исправлений) кода и тот факт что в большинстве случаев споровождение осуществляется не тем же человеком, который является исходным автором кода. (*)
  4. Программист, работающий над исходным кодом, который написан не в "любимом" или "привычном" стиле будет тратить на его исправление больше времени (аргумент Bill Venners из исходного сообщения в форуме).
Моё мнение состоит в том, что усилия на создание и поддержание "стандартов кодирования" в большинстве случаев существенно выше чем затраты на "понимание" или поддержку кода, написанного "в другом стиле". Я конечно не имею ввиду примеры из "Obfuscated C Code Contest" :-) В большинстве случаев, код написанный вменяемым программистом понятен другому вменяемому программисту, вне зависимости от того, как именно расставлены в нем фигурные скобки или именованы переменные. Единственной неприятностью могут быть различия в настройках отступов и табуляций/пробелов, при просмотре кода различными текстовыми редакторами, просмотрщиками и программами сравнения.

Поэтому в моей голове всегда существовал такой "стандарт кодирования":
  1. Единообразная настройка длины табуляции (tab length)
  2. Использование пробелов вместо табуляций
  3. При правке кода пользоваться тем стилем, который используется в этом коде
Вот собственно и все. А если Вы или Ваш коллега пишете код в стиле "Obfuscated C Code Contest", то подумайте может проблема должна быть решена не при помощи "стандартов кодирования", а как-нибудь по-другому?

17458907.195e50a0db7e7428ccd693c67f635406.1186161181.ba176d26a55281a16d8fd8b02a541ce8
17458907.48319b4763d4aa2700c0fda3363b9fab.1186391267.b8da257c7d1fa1c830df3b9f73d6fb72

четверг, июня 14, 2007

Комментирование и системы контроля версий

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

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

Мне кажется, что такой способ комментирования плох в двух смыслах.

  1. Данный вид комментирования представляет собой подмену (причём недостаточно функциональную) системы контроля версий.
  2. Данный вид комментирования неудобен для тех, кто будет разбираться в коде после вас.
Подмена системы контроля версий

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

Неудобство для "будущих поколений"

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

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

  1. При исследовании кода методом поиска :-) могут вполне попасться в результаты, а значит придётся затратить дополнительное время на их изучение
  2. При интенсивном использовании сильно замусоривают код, что также создаёт дополнительные сложности
  3. Комментарии многими воспринимаются как пояснение относительно работы данного куска кода (и правильно воспринимаются, именно для этого комментарии и нужны). Поэтому закомментированная часть кода, если комментарий был использован вместо системы контроля версий, может вызвать у читающего затруднения с пониманием - зачем здесь комментарий? может быть кто-то что-то тестировал и забыл убрать комментарий? Короче говоря, возникает лишний повод задуматься.

Мой общий вывод из вышеизложенного такой (нагло подделываясь под классика):
"Commenting code out considered harmful". Неиспользуемый код нужно удалять, а не комментировать. Конечно, всегда бывают "ситуации" и мы здесь говорим о каком-то общем, "среднем" случае.


P.S. Да, современные интегрированные среды разработки позволяют достаточно легко бороться с перечисленными выше недостатками (outlining; поиск с использованием регулярных выражений; интеллектуальный поиск, понимающий что комментарии нужно пропускать). Но они же часто содержат функции, позволяющие вообще не заниматься подобным комментированием, например IntelliJ Idea содержит замечательную функцию Local History, являющуюся по сути постоянно включённой системой контроля версий.

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

вторник, июня 05, 2007

Принцип DRY

Про принцип DRY все знают. Ну даже если кто-то и не знает самого сокращения, то уж смысл известен наверняка. Напомню, DRY расшифровывается как Don't Repeat Youself. То есть "Не повторяй самого себя". Первый раз с самим сокращением я столкнулся в книге The Pragmatic Programmer: From Journeyman to Master. На самом деле принцип все мы знаем очень давно, даже если не осознавали его как "принцип" или что-то глубоко умное и философское (в Википедии принцип относится к категории Software Development Philosophies).

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

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

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

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

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

Откуда берутся такие ошибки? Кто в них виноват?

Программисту, которому необходимо добавить новое поле в диалог плюётся "Вот "@#$%^&*! Ну кто так пишет! И диалога два, и поведение в них чуть-чуть разное и по разным принципам они написаны, и сопутствующие классы разные!" После этого он открывает историю в системе контроля версий, смотрит фамилию предшественника и ... Ну сами понимаете. Тут уж предшественнику вполне может достаться по первое число. Зачем скопировал? Почему все то же самое, но чуть-чуть по-другому?

И что потом делает наш гипотетический программист? Вставляет исправления в ОБА места. Почему в оба? Почему он не слил два диалога в один? Ну понятно почему. Потому что у него сроки, потому что у него следующая работа, потому что у него начальство, потому что разбираться где ещё используются эти диалоги нет ни времени ни желания ...

А потом наступает момент когда кому-то нужно добавить в диалог ещё одно поле.

Виноват ли наш гипотетический программист? Или может быть виноват "самый первый" программист - тот кто из одного диалога сделал два? Зачем он так сделал?

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

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

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

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

вторник, мая 22, 2007

Полезный ключ компилятора MS VC++

Я уже довольно давно читаю блог команды разработчиков Visual C++ и должен сказать что это довольно таки занудный блог. Но вот последняя статья в нем - буквально таки жемчужина.

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

/d1reportSingleClassLayoutXXX, где XXX - интересующее нас имя класса.

При компиляции с этим ключом, компилятор показывает, как с его точки зрения "разложился" класс XXX. Вот код из статьи:

// a.h – share class definition (code has been corrected from original post)

#pragma once

class Test_A {
public
:

Test_A(){ c = 'X'; data = 0; }

~Test_A(){ if(data) delete [] data; }

char c;
char* data;

};

void Test_A_Loader(Test_A&);

// loader.cpp - loader defn.
#include
//for strcpy
#include
"a.h"

void Test_A_Loader(Test_A& a) {

a.c = 'p';
a.data = new char[10];
strcpy(a.data, "3.14159");

}

// main.cpp - main program
#include
// for printf

#include "a.h"

int main(){

Test_A a;
Test_A_Loader(a);
printf("%c : %c %c %c %c\n", a.c, a.data[0], a.data[1], a.data[2], a.data[3]);

}

Данный код компилируется вот так (для того чтобы воспроизвести ошибку):

cl main.cpp /Zp2 /c
cl loader.cpp /Zp1 /c
link main.obj loader.obj


При этом конечно возникает ошибка с выравниванием, поскольку класс Test_A выровнен по разному в единице трансляции main.cpp и в единице трансляции loader.cpp.

А вот вывод компилятор при использовании волшебного ключика '/d1reportSingleClassLayoutTest_A' (почему то в статье вывод компилятора целиком не приведён, а мне кажется что это интересно):

1>------ Build started: Project: ClassLayout, Configuration: Debug Win32 ------
1>Compiling...
1>stdafx.cpp
1>Compiling...
1>loader.cpp
1>class Test_A size(5):
1> +---
1> 0 | c
1> 1 | data
1> +---
1>class Test_A size(5):
1> +---
1> 0 | c
1> 1 | data
1> +---
1>Compiling...
1>ClassLayout.cpp
1>class Test_A size(6):
1> +---
1> 0 | c
1> | <alignment member> (size=1)
1> 2 | data
1> +---
1>class Test_A size(6):
1> +---
1> 0 | c
1> | <alignment member> (size=1)
1> 2 | data
1> +---
1>Linking...
1>Embedding manifest...
1>Build log was saved at "file://l:\ClassLayout\Debug\BuildLog.htm"
1>ClassLayout - 0 error(s), 0 warning(s)
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

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


четверг, апреля 19, 2007

Где провести черту: системы контроля версий

Очень часто в повседневной работе возникает вопрос компромисса. Насколько глубоко нужно изучать предмет? Насколько в программе нужно предусматривать возможности расширения? Лучшее действительно враг хорошего? Когда нужно остановиться в улучшениях? Насколько ревностно стоит следовать стандартам форматирования кода? Действительно ли скобка поставленная не на той строке обозначает некоторую критическую проблему в ДНК человека, её поставившего? Все подобные вопросы объединились в моей голове под общим "заголовком" - "Где провести черту?"

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

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

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

Что должно находиться в системе контроля версий?

Ответы на этот вопрос варьируются от "только исходный код" до "всегда используйте систему управления исходными текстами", причём и та и другая стороны вполне категоричны. Где же провести черту? Нужно ли хранить в системе управления исходными текстами все письма, написание которых сопровождало написание программы? А нужно ли хранить там документацию? А различные версии логотипа? А, скажем, внутренние технические документы? А меняющийся, с течением времени, список телефонных номеров разработчиков?

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

Должно ли в системе контроля версий быть что-то ещё? Вопрос вкуса. Мне кажется что на уровне общих "правил" не стоит пытаться засунуть туда все что можно себе представить.

В какой момент вещи должны попадать в систему контроля версий?

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

Заметим, что крайности бывают двух видов - "слишком редко" и "слишком часто".

Проблема "слишком редко", на мой взгляд, достаточно легко решается введением практики "ежедневной сборки". Если такая практика существует (даже если сборка не ежедневная, а просто производится достаточно часто), то трудность сама собой исчезнет.

Проблему слишком частого внесения изменений решить несколько сложнее. Часто вводят строгое правило - "не чекинить не работающий код", которое в результате может вылиться у людей в отторжение и даже боязнь внесения кода в систему контроля версий вообще. Данную проблему можно решить разными способами, но на мой взгляд самым простым является заведение в системе контроля версий "своей" ветки для каждого из разработчиков, в которой он сможет работать так, как ему удобно. В момент "сборки" можно просто переложить готовые файлы из личной ветки в общую (ну и сделать "merge" при необходимости).

Замечание для "администраторов"

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

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

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

Мне кажется, что эти соображения очень опасны. По крайней мере для компании, чьей основной деятельностью и источником заработка является производство программного обеспечения. Представьте себе, что начальник службы безопасности банка скажет - "У нас недостаточно места в главном хранилище, чтобы хранить все ценности, поэтому давайте так - все золото мы оставим в хранилище, а акции и бумажные деньги пусть лежат в кабинетах сотрудников. Здание, в принципе, охраняется, конечно стены и замки в кабинетах не такие надёжные как в хранилище, но ведь и бумажные деньги не столь ценны, как золото". Понимаете, мне кажется что лучше расширить хранилище. Может быть даже купить или построить новое. Для компании, производящей программное обеспечение, нет ничего более ценного чем программисты и исходные тексты написанных ими программ- зачем же на этом экономить?

воскресенье, марта 18, 2007

Про парное программирование - часть 2

Продолжу пост, начатый здесь. Напомню, что я остановился на том, что недавно, а именно в февральском номере журнала IEEE Transactions on Software Engineering была опубликована статья Erik Arisholm, Hans Gallis, Tore Dyba и Dag I.K. Sjøberg посвящённая проведённому ими исследованию в области парного программирования.


Исследование Simula Research Laboratory

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

  • В описываемом эксперименте принимало участие 295 Java-разработчиков - 98 пар и 99 соло программистов из различных европейских компаний (всего 29 компаний).

  • Эксперимент состоял из двух фаз - первая фаза была проведена в 2001 году и в ней участвовали только соло-программисты.
  • Вторая фаза состоялась в конце 2004 начале 2005 годов и в ней участвовали пары

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

  • Задания:
    • Тренировочное задание: сначала каждому участнику была выдана простое задание : прочесть цифры с клавиатуры и вывести их в порядке обратном порядку ввода. Данное задане все участники выполняли для того чтобы ознакомиться с окружением и правилами эксперимента
    • Индивидуальное тестовое задание: каждый из участников должен был внести изменение в программу. Все участники должны были внести одно и тоже изменение в одну и ту же программу. В рассматриваемой программе было 7 классов и 354 строки кода. Необходимо было добавить лог а также возможность распечакти баланса банковского счета. Данная программа не была похожа на программы, использовавшиеся во время самого эксперимента. Тестовое задание использовалось для того, чтобы сравнить относительные уровни программистов, участвующих в тестах.
    • Основные задания эксперимента (4 задания):Основные задания были основаны на программе управления кофе-машиной. При этом существовало два вида такой программы, с одной и той же исходной функциональностью. Одна из программ была устроена более сложно и состояла из 12 классов. Другая программа была устроена проще и состояла из 7 классов. Для каждой из программ существовали UML описания сценариев работы, для того чтобы можно было лучше понять архитектуру программ. Сами задания состояли в добавлении следующих новых возможностей в программу управления кофе-машиной:
      • Реализации кнопки: возврат монеты
      • Добавление нового вида напитка (бульон)
      • Проверка того, имеются ли в наличии все ингредиенты для выбранного вида напитка
      • Добавление возможности содания своего собственного напитка, при помощи разрешения пользователю выбора из всех доступных ингредиентов.
    • Синхронизационное задание: последнее предлагаемое участникам задание имело специальную цель. Результаты его выполнения не учитывались, но участники об этом не знали. Таким образом им приходилось работать над всеми реальными заданиями со всей возможной скоростью.
  • Обстановка эксперимента:
    • Сам эксперимент проводился в течение 27 дней - 10 дней для соло-программистов и 17 дней для пар
    • Все происходило в собственных офисах разработчиков или в офисах Simula Research Laboratory.
    • Всем участникам был предоставлен доступ в интернет, книги и так далее
    • Каждый участник мог также пользльваться любым средством разработки по желанию: Eclipse, IntekkiJ Idea, JBuilder, NetBeans - чем угодно.
    • Все это было сделано для того, чтобы максимально приблизить условия эксперимента к реальным, в которых консультанта нанимает компания-клиент, для того чтобы произвести некоторое изменение в программе.
    • Для того, чтобы более точно учитывать потраченное время, перерывы разрешалось делать только между заданиями, но не во время выполнения одного задания.
  • Что оценивалось:
    • Длительность - время (в минутах), потраченное на то, чтобы справиться с основными заданиями эксперимента
    • "Цена" - человеко-минуты ( ну то есть попросту говоря зарплата), потребовавшиеся на задания. Для пар цена представляла собой длительность, умноженную на 2
    • Правильность - 1, если все 4 задания были сделаны верно и 0, в противном случае. Решения проверялись двумя независимыми экспертами, а также специальными тестами, которые сравнивали результат выполнения программ с заранее изаготовленным правильным результатом.
  • Факторы, влияющие на результат (факторы, которые рассматривались исследователями при анализе)
    • Парное или соло программирование
    • Сложность системы - некоторым программистам выдавалась программа написанная более "сложно", а некоторым "менее сложно"
    • Уровень программистов - Junior, Intermediate, Senior
  • Проверяемые гипотезы
    Изначально экспериментаторы поставили цель проверить следующие гипотезы (замечу, что каждая из гипотез - это некотрое предположение о том как один из факторов влияет на одну из оценок - то есть количество гипотез = кол-во факторов*кол-во оценок = 9):
    • H01 - влияние парного программирования на длительность: парам и соло требуется одинаковое время для выполнения заданий
    • H02 - влияние сложности системы на длительность: разница во времени, нужном для выполнения заданий парам и соло не зависит от сложности системы
    • H03 - влияние опытности программистов на длительность: разница во времени, нужном для выполнения заданий парам и соло не зависит от опытности программистов
    • H04 - влияние парного программирования на "цену": цена одинакова для пар и для соло
    • H05 -влияние сложности системы на "цену": Разница в цене между парами и соло, не зависит от сложности системы
    • H06 -влияние опытности программистов на "цену": Разница в цене между парами и соло не зависит от опытности программистов
    • H07 -влияние парного программирования на правильность: Правильность одинакова для пар и соло
    • H08 -влияние сложности системы на правильность: Разница в правильности между парами и соло не зависит от сложности системы
    • H09 -влияние опытности программистов на правильность: Разница в правильности между парами и соло не зависит от опытности программистов.
  • Результаты
    Подробные результаты в процентах, а также допущения и статистические погрешности эксперимента можно увидеть в статье авторов. Я же здесь приведу лишь сами выводы.
    • H01 - достоверна
    • H02 - не достоверна
    • H03 - достоверна
    • H04 - не достоверна
    • H05 - не достоверна
    • H06 - достоверна
    • H07 - достоверна
    • H08 - не достоверна
    • H09 - достоверна
  • Выводы
    • Основной вывод, который делают авторы состоит в том, что на соновании их эксперимента можно считать, что junior-программистов, при работе со сложными системами выгоднее объединять в пары. При этом можно добиться существенного увеличения качества результатов. Этот вывод, кстати близок к выводам авторов четвёртого исследования из моего предыдущего поста, о том что парное программирование лучше работает в, так сказать, "неизведанных" областях.
Вообще, хочу сказать, что исследование проведено очень аккуратно и вдумчиво. Экспериментаторы вполне отдавали себе отчёт в том, каких аспектов они не рассмотрели и почему (например практически никто из участников раньше на занимался парным программированием, из-за чего эффективность пар могла быть не такая высокая).

Обсуждение

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

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

Собственный опыт

У меня есть и собственный опыт парного программирования, о котором хотелось бы кратко рассказать. Мне приходилось программировать в парах с несколькими людьми и на разных уровнях собственного "развития". То есть я ощущал себя в парах и "новичком", который сидит рядом с "гуру" и "гуру" который сидит рядом с новичком. Было и программирование в парах с равными. Какие впечатления?
  • Знание распространяется существенно лучше и больше людей знают о том что и как внутри работает. Это на мой взгляд очень важный и очень положительный эффект. Меньше становится мест, про которые знает только один человек в команде. Знаете есть такой тест для менеджеров - сколько человек в команде должно уволиться, чтобы проект провалился?( ну то есть чтобы он страшно опоздал или вообще оказался незавершенным). В плохой ситуации количество людей - 1. В случае парного программирования - как минимум два, для тех кусков кода которые "участвовали" в парном програмировании.
  • Для меня-"новичка" парное программирование дало очень много - это совсем неплохой способ обучения, очень быстрый. Приятно чему-то научиться :-)
  • Для меня-"гуру", то есть в тот момент когда рядом был менее опытный в данном вопросе программист - это отличный как нынче модно говорить "мотиватор". Приятно короче говоря кого-то чему-то научить :-)
  • Качество кода: по моему опыту качество кода при парном программировании возрастает. Особенно с точек зрения "понятности" и поддерживаемости".
  • Скорость написания - непонятно. Сильно зависит от того над чем работаешь.
  • Недостатки - как легко догадаться большинство недостатков связаны с "человеческими" сторонами парного программирования:
    • я-"новичок": чувство "какой-же я идиот" вполне может отравить жизнь - у меня такое бывало, особенно, когда рядом сидит настоящий мастер
    • я-"гуру": чувство "какой-же он идиот, нет смысла на него тратить время" тоже далеко не самое приятное
    • в программировании, как и во многих других делах, по завершении какой-то работы возникает чрезвычайно приятное чувство свершения - того что ты чего то добился, и добился этого лично ты. При парном программировании у меня это чувство иногда пропадало, "смазывалось", что-ли.
  • Общий вывод: в целом мой опыт парного программирования положительный. Ну просто попадались очень хорошие "пары" и это конечно их заслуга.

четверг, марта 15, 2007

Про парное программирование - часть 1

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

Поводом для данного поста послужило наверное первое серьёзное исследование посвящённое производительности парного программирования.

Но начнём сначала.

Кратко про парное программирование

Для полноты картины кратенько расскажу про парное программирование. Парное программирование - это одна из практик экстремального программирования, которая состоит в том, что код одновременно пишется двумя программистами, сидящими за одним компьютером. При этом в каждый конкретный момент набивает код один из программистов и он как бы отвечает за "тактику". А другой в это время смотрит на этот код, замечает различные ошибки, если они есть и обдумывает этот код на более высоком, так сказать "стратегическом" уровне. Экстремальное программирование конечно заходит совсем далеко и требует чтобы ВЕСЬ код обязательно писался парами. Ну оно потому и называется экстремальным :-) Утверждается, что использование парного программирования способствует достижению массы положительных факторов, таких как:

  • Написание более качественного кода, за счет того что под постоянным наблюдением партнёра снижается количество ошибок и повышается внимание к деталям
  • Каждый кусок кода известен как минимум двум членам команды, следовательно повышается устойчивость команды к болезням или увольнениям её участников
  • В конечном итоге все члены команды узнают весь код приложения, а не только лишь свой кусок
  • Новые или менее опытные члены команды очень быстро приобретают новые знания, работая в парах со "старожилами" и таким образом существенно лучше и быстрее вливаются в коллектив
  • В архитектурных решениях принимает участие больше людей, что повышает их качество
Что об этом думают люди

На самом деле парное программирование конечно придумал вовсе не Кент Бек (отец "экстремального" программирования). Кент Бек популяризировал идею, которая существовала задолго до него. Ну не говоря про то, что люди частенько советовались и даже писали код совместно с коллегами, не придумывая для этого никаких мудрёных названий, Ларри Константин писал про это ещё в 1995 году и тоже не был первым, поскольку говорят откуда-то списал идею :-).

Но с 2000-го года (когда была опубликована книга Бека:Extreme Programming Explained: Embrace Change) многие стали говорить о парном программировании. Высказывались как положительные отзывы и мысли (подобные приведённым выше), так и отрицательные. К основным недостаткам парного программирования обычно относят:
  • Трудность согласования расписаний программистов - всем нужно быть на работе одновременно, отсюда невозможность работы из дома и по "свободному графику"
  • Традиционно программисты считаются необщительными людьми, которым просто не захочется писать код вдвоём с кем-то
  • Традиционно программисты считаются "одиночками", многие из которых очень высоко оценивают собственную работу и очень низко работу всех остальных, что может приводить к конфликтам при работе в паре
  • Опытный программист может просто не захотеть учить неопытного новичка
  • Зарплату надо платить обоим, а выигрыш в производительности оценить чрезвычайно сложно, если вообще возможно
Так вот к последнему из упомянутых пунктов - оценке выигрыша в производительности многие люди и прицепились. Всем хочется все-таки попытаться оценить и сравнить производительность парного и не-парного, или как его в рассматриваемом контексте называют соло программирования.

Эксперименты

Люди начали ставить эксперименты над людях :-) Некоторое время назад в журнале International Journal of Human Computer Studies была опубликована статья Pair programming productivity: Novice–novice vs. expert–expert. Статья рассказывает о четырёх различных исследованиях, проведённых с 1998 года, в которых учёные пытались сравнить производительность парного и соло программирования.


Исследования были такие.
  1. В первом исследовании, проведенном в 1998 году 15 опытных программистов, из которых сформировали 5 пар, и пять соло программистов должны были написать некоторый UNIX скрипт. Результаты оценивались двумя независимыми проверяющими. Оценивались функциональность скрипта (насколько он решал поставленную задачу) и "читаемость" кода. Оценка за качество выставлялась по шкале от 0 до 6 (0 - скрипт не работает, 6 - делает все что нужно). Оценка за "читаемость" выставлялась по шкале от 0 до 2 (0 - невозможно читать, 2 - отличная читаемость). Результаты таковы:


    соло

    пары

    читаемость

    1.40

    2.00

    функциональность

    4.20

    5.60


  2. В другом исследовании, которое было проведено в 2000 году, участвовал 41 человек,
    из которых было сформировано 14 пар и 13 соло программистов. Участвующие были студентами и пары были сформированы так, чтобы в парах обязательно оказывалиь разные по уровню студенты. Задача была в том чтобы написать некое e-commerce приложение. Задачи были несложные и студентам было выдано по 4 задачи. Оценка качества выполнения проводилась при помощи набора тестов. С выполнением первой задачи пары справились на 40 процентов быстрее чем соло. К третьей задаче задачи пары справилялись на 85 процентов быстрее. С точки зрения качества - у пар проходило примерно 90% тестов, а у соло 74%. Надо также сказать, что данный эксперимент проходил без надлежащего контроля со стороны экпериментаторов - участники работали дома и невозможно точно сказать действительно ли работа велась согласно условиям эксперимента.

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

  4. Все описанные три исследования были проведены разными авторами, и как мы заметили достигли разных и не очень достоверных результатов. Кроме того результаты очень трудно сравнить друг с другом из-за слишком разнеых условий экспериментов и критериев качества результирующих программ. Это же заметили и авторы статьи Pair programming productivity: Novice–novice vs. expert–expert. Они подготовили и провели свой собственный, четвёртый эксперимент. Эксперимент заключался в том, что люди должны были писать одну и ту же программу несколько раз подряд, имитируя таким образом рост профессионального мастерства. Программисты писали одну и ту же программу, каждый раз сначала, 4 раза подряд. Вот график результатов:


    1 итер.

    2 итер.

    3 итер.

    4 итер.

    Соло (потраченное время, мин.)

    635

    407

    334

    285

    Пары (потраченное время, мин.)

    410

    320

    281

    272


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

    Выводы, которые сделали авторы звучат так:
    • Пара существенно более продуктивна, чем соло программист, как с точки зрения качества, так и с точки зрения времени потраченного на разработку, если оба человека, участвующие в паре никогда ранее не решали подобную задачу и им требуется применить усилия для того, чтобы разработать архитектуру, алгоритм и способ решения этой задачи.
    • Продуктивность парного программирования существенно снижается, если у участников уже есть опыт решения подобных задач.
Вот такие были эксперименты. Все они, однако, не идут ни в какое сравнение с экспериментом, который произвели недавно сотрудники Simula Research Laboratory.
В февральском номере журнала IEEE Transactions on Software Engineering опубликована статья Erik Arisholm, Hans Gallis, Tore Dyba и Dag I.K. Sjøberg посвящённая этому исследованию.

О ней, выводах, и моем собственном мнении по всем изложенным и ещё не изложенным поводам - в следующий раз.

пятница, февраля 09, 2007

Quick and Dirty?

Есть такой специальный вид исправления ошибок, название которого (если исключить вопросительный знак) вынесено в заголовок. "Quick and dirty fix" - это такое исправление или решение, которое исправляет проблему в каком-то частном, узком конкретном случае, а не решает её целиком. Примеров подобных решений каждый программист может найти в своей практике множество. Предполагается, что такое вот временное решение будет существовать недолго, а как только появится возможность будет заменено на "правильное". Последнее иногда случается, а иногда нет - как повезёт.

Вот только "quick and dirty" бывают не только исправления проблем в условиях сжатых сроков. Иногда "quick and dirty" бывают сами решения. Ну просто нужно что-то сделать, а времени остаётся мало, и вещи делаются как попало, так, как тому кто их делает кажется в данный момент быстрее. И вот такие-то вещи как раз почти всегда остаются в коде навсегда.

Два подхода

Вообще, есть два подхода к написанию программ.

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

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

  • Код программы написан в "понятном" стиле
    "Написать код, понятный компьютеру, может каждый, но только хорошие программисты пишут код, понятный людям."М.Фаулер

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

  • Код в программе подчиняется принципу DRY (DO NOT REPEAT YOURSELF), то есть каждая часть функциональности существует в единственном экземпляре, нет раскопированных кусков кода, которые делают одно и тоже

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

  • Связи между независимыми компонентами программы минимизированы

  • Любую версию программы (в том числе и предыдущие) можно построить при помощи запуска одного скрипта
Этот список конечно же не полон, и другие, гораздо более знаменитые люди уже давно составили подобные списки. К тому же, как я уже говорил, на самом деле эти списки ни к чему - программист интуитивно понимает "правильно" ли написана программа, на которую он смотрит или нет (это конечно исключая ситуации, когда хочется поспорить и попридираться - разумный человек может придираться к чему угодно, при желании).

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

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

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

А что если посмотреть вперёд? Будут ведь и ещё выпуски? Сколько их будет? Рассмотрим пример. Допустим, что для создания работающей автоматизированной процедуры построения продукта требуется 1 неделя (5 рабочих дней). После этого для сборки продукта будет требоваться 2 часа - время работы процедуры. Допустим также, что для того, чтобы собрать продукт руками опытному разработчику, знакомому с процедурой сборки требуется 1 день. Ясно, что в критериях измерения только "к ближайшему выпуску" предпочтение следует отдавать "ручному варианту". А что если впереди планируется ещё 10 выпусков? Давайте считать. Отлаженная процедура займёт 20 часов ( то есть три дня). "Ручная" же сборка займёт 10 дней. Заметим, при этом, что сборка обычно не происходит один раз - обязательно обнаруживается та или иная ошибка, продукт пересобирается и так далее. В "ручном" режиме это каждый раз трата одного дня.

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

Все зависит от критериев

На самом деле подход один. Понятно что всем хочется написать программу "получше". Просто вопрос в критериях качества. Считаем ли мы возможность удобной поддержки программы в будущем подобным критерием или нет? Думаем ли мы о том, что потом наш код увидят и должны будут в нем разобраться другие программисты (или мы сами через год - что почти тоже самое :-) ) ? Если мы пишем однодневку - то один подход, если мы смотрим в будущее, то другой. "Я так думаю"(с) Мимино.

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

P.P.S. (вольный перевод отсюда, имеет смысл только для программистов): Мы тратим большую часть жизни на разработку программ - стоит ли заниматься этим спустя рукава?

четверг, декабря 21, 2006

Гвидо Ван Россум рассказывает про Code Reviews в Google

Сегодня наткнулся на замечательное видео: http://video.google.com/videoplay?docid=-8502904076440714866

Здесь Гвидо Ван Россум (иозбретатель языка Python) рассказывает о Code Review системе, применяемой в Google. Помимо интересного рассказа, хочу отметить что это редкий случай когда раскрывается информация о том как именно устроен процесс разработки в Google, а не только рассказывается про то какие там хорошие повара и то что 20% рабочего времени каждый разработчик может тратить на собственные проекты.

В частности для меня новой стала информация, что:

  1. В качестве source code control системы в Google используется Perforce (кстати говоря как и в Microsoft, хотя там perforce вроде бы немного доработан напильником)
  2. Branches разработчики практически не используют - все работают в main trunk
  3. Code Review практики используются повсеместно и очень активно
  4. Черт побери у этих ребят почти ВСЕ web-based!!!