Опыт работы не имеет значения

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

В 1968 году было проведено исследование продуктивности работы программистов (источник), которое показало что соотношение лучших и худших программистов составило:

  • 20:1 по времени написания кода
  • 25:1 по времени отладки кода
  • 10:1 по скорости работы программы
  • 5:1 по объему кода

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

Но это было давно и в исследовании куча ошибок...

Аналогичные исследования повторялись другими группами ученых на протяжении 30 лет:

  • Curtis, Bill. 1981. "Substantiating Programmer Variability." Proceedings of the IEEE 69, no. 7: 846.
  • Mills, Harlan D. 1983. Software Productivity. Boston, Mass.: Little, Brown.
  • DeMarco, Tom, and Timothy Lister. 1985. "Programmer Performance and the Effects of the Workplace." Proceedings of the 8th International Conference on Software Engineering. Washington, D.C.: IEEE Computer Society Press, 268-72.
  • Curtis, Bill, et al. 1986. "Software Psychology: The Need for an Interdisciplinary Program." Proceedings of the IEEE 74, no. 8: 1092-1106.
  • Card, David N. 1987. "A Software Technology Evaluation Program." Information and Software Technology 29, no. 6 (July/August): 291-300.
  • Boehm, Barry W., and Philip N. Papaccio. 1988. "Understanding and Controlling Software Costs." IEEE Transactions on Software Engineering SE-14, no. 10 (October): 1462-77.
  • Valett, J., and F. E. McGarry. 1989. "A Summary of Software Measurement Experiences in the Software Engineering Laboratory." Journal of Systems and Software 9, no. 2 (February): 137-48.
  • Boehm, Barry, et al, 2000. Software Cost Estimation with Cocomo II, Boston, Mass.: Addison Wesley, 2000.

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

Что это значит?

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

Это наводит на мысль, что есть некоторый икс-фактор, не связанный с опытом, который отличает хорошего программиста от плохого. На сегодняшний момент нет единого мнения что это за фактор. Одни говорят что это умение планировать свою работу и “видеть” структуру кода еще до того, как код написан. Другие говорят, что самые продуктивные программисты те, кто умеет быстро выдавать видимый результат. Мои личные наблюдения говорят о том, что лучше всего пишут код те, кто умеет читать код.

И что теперь делать?

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

Еще один вывод, который немного противоречит логике, но часто встречается на практике: программист не станет более продуктивным, получив годы опыта. Единственный способ улучшить продуктивность и качество кода программистов – проводить целенаправленное обучение и тренировку .

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

Заключение

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



Сообщество по SharePoint на facebook

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

Были и есть форумы на GotDotNet.ru (сейчас лежит) и MSDN\Technet, но оба сфокусированы на вопрос-ответ и населены новичками, которые пытаются сделать что-то нехорошее.

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

Присоединяйтесь к https://www.facebook.com/groups/sharepointrussian/



Улучшаем экспорт в Excel для SharePoint

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

Преимущество такого подхода в том, что данные в Excel можно обновить, так как есть соединение. Но недостатков больше:

  1. Нужен установленный Excel на компьютер, чтобы получить данные.
  2. В полученном документе для названий колонок используется InternalName полей.
  3. В полученном документе используется “сырой” формат данных, что далеко не всегда подходит.

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

Подмена кнопок экспорта

Чтобы заменить существующий элемент в Ribbon надо добавить новый CommandUIDefinition с параметром Location равным Id существующего элемента. Все стандартные элементы находятся в файле C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\GLOBAL\XML\CMDUI.XML для SharePoint 2010 (или 15 для SharePoint 2013).

Для того, чтобы подменить кнопки экспорта в Excel надо найти кнопки с Id равными Ribbon.List.Actions.ExportToSpreadsheet и Ribbon.Library.Actions.ExportToSpreadsheet и полностью скопировать в свой проект. В качестве Location для новых элементов надо указать эти Id.

<CommandUIDefinitions>
  <CommandUIDefinition
    Location="Ribbon.Library.Actions.ExportToSpreadsheet">
    <Button Id="Ribbon.Library.Actions.ExportToSpreadsheet-Replacement"
      Sequence="40"
      Command="ExportToSpreadsheet-Replacement"
      Image16by16="/_layouts/$Resources:core,Language;/images/formatmap16x16.png" Image16by16Top="-152" Image16by16Left="-32"
      Image32by32="/_layouts/$Resources:core,Language;/images/formatmap32x32.png" Image32by32Top="-352" Image32by32Left="0"
      LabelText="$Resources:core,cui_ButExportToSpreadsheet;"
      ToolTipTitle="$Resources:core,cui_ButExportToSpreadsheet;"
      ToolTipDescription="$Resources:core,cui_STT_ButExportListToSpreadsheet;"
      TemplateAlias="o2"
        />
  </CommandUIDefinition>
  <CommandUIDefinition
    Location="Ribbon.List.Actions.ExportToSpreadsheet">
    <Button Id="Ribbon.List.Actions.ExportToSpreadsheet-Replacement"
      Sequence="40"
      Command="ExportToSpreadsheet-Replacement"
      Image16by16="/_layouts/$Resources:core,Language;/images/formatmap16x16.png" Image16by16Top="-152" Image16by16Left="-32"
      Image32by32="/_layouts/$Resources:core,Language;/images/formatmap32x32.png" Image32by32Top="-352" Image32by32Left="0"
      LabelText="$Resources:core,cui_ButExportToSpreadsheet;"
      ToolTipTitle="$Resources:core,cui_ButExportToSpreadsheet;"
      ToolTipDescription="$Resources:core,cui_STT_ButExportListToSpreadsheet;"
      TemplateAlias="o1"
        />
  </CommandUIDefinition>
</CommandUIDefinitions>

Есть еще отдельная кнопка для календаря с Id="Ribbon.Calendar.Calendar.Actions.ExportToSpreadsheet", с ней можно сделать тоже самое.

Создание контрола

Кнопка должна вызывать некоторый серверный код. Чтобы поведение было аналогично стандартной кнопке лучше всего сделать DelegateControl, который будет размещаться на каждой странице. Для этого есть контейнер с Id="AdditionalPageHead"

<Control 
  Id="AdditionalPageHead"
  Sequence="1000"
  ControlAssembly="$SharePoint.Project.AssemblyFullName$" 
  ControlClass="$SharePoint.Type.7fd7c6f0-4eda-48ce-ac8f-aa9f9d2666ac.FullName$"/>

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

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

<CommandUIHandlers>
  <CommandUIHandler
    Command="ExportToSpreadsheet-Replacement"
    CommandAction="javascript:(function(){var x=SP.ListOperation.Selection.getSelectedList(); if (x) {__doPostBack('ExportToSpreadsheet-Replacement', x);}})();" />
</CommandUIHandlers>

Далее на сервере нужно проверить нажата ли кнопка на странице представления списка или на обычной странице:

protected override void OnLoad(EventArgs e) { 
    if (this.Page.Request["__EVENTTARGET"] == "ExportToSpreadsheet-Replacement") 
    {
        var spContext  = SPContext.Current;
        SPList list;
        SPView view;

        if (spContext.ViewContext.View != null)
        {
            list = spContext.List;
            view = spContext.ViewContext.View;
        }
        else
        {
            var listId = new Guid(this.Page.Request["__EVENTARGUMENT"]);
            var web = spContext.Web;
            list = web.Lists[listId];
            view = list.DefaultView;
        }

        ExportData(list.Title + " - " + view.Title, GetDataTable(list, view));
    } 
}

 

Получение таблицы данных по представлению очень просто:

private static System.Data.DataTable GetDataTable(SPList list, SPView view)
{
    var query = new SPQuery(view);
    SPListItemCollectionPosition position;
    var flags = SPListGetDataTableOptions.UseBooleanDataType | SPListGetDataTableOptions.UseCalculatedDataType;
    var result = list.GetDataTable(query, flags, out position);
    while (position != null)
    {
        list.AppendDataTable(query, flags, result, out position);
    }
    return result;
}

Генерация Excel файла

Последним шагом требуется сформировать Excel файл и отдать его клиенту. Один из самых простых способов сгенерировать Excel – использовать библиотеку ClosedXml (http://closedxml.codeplex.com/).

private void ExportData(string title, System.Data.DataTable table)
{
    var wb = new XLWorkbook();
    var ws = wb.Worksheets.Add(title);
    ws.Cell(1, 1).InsertTable(table);

    var response = this.Page.Response;
    response.Clear();

    response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
    var filename = title+".xlsx";
    response.AddHeader("content-disposition", GetContentDisposition(filename));

    // Flush the workbook to the Response.OutputStream
    using (var memoryStream = new MemoryStream())
    {
        wb.SaveAs(memoryStream);
        memoryStream.WriteTo(response.OutputStream);
    }
    response.End();
}

 

Первые три строки метода собственно формируют Excel документ (спасибо ClosedXml), а все остальное – код для отдачи файла клиенту.

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

Код для формирования корректного заголовка content-disposition я нашел на StackOverflow http://stackoverflow.com/questions/93551/how-to-encode-the-filename-parameter-of-content-disposition-header-in-http

private string GetContentDisposition(string filename)
{
    var request = this.Page.Request;
    string contentDisposition;
    if (request.Browser.Browser == "IE" && (request.Browser.Version == "7.0" || request.Browser.Version == "8.0"))
        contentDisposition = "attachment; filename=" + Uri.EscapeDataString(filename);
    else if (request.UserAgent != null && request.UserAgent.ToLowerInvariant().Contains("android")) // android built-in download manager (all browsers on android)
        contentDisposition = "attachment; filename=\"" + MakeAndroidSafeFileName(filename) + "\"";
    else
        contentDisposition = "attachment; filename=\"" + filename + "\"; filename*=UTF-8''" + Uri.EscapeDataString(filename);
    return contentDisposition;
}

private static readonly Dictionary<char, char> AndroidAllowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._-+,@£$€!½§~'=()[]{}0123456789".ToDictionary(c => c);
private string MakeAndroidSafeFileName(string fileName)
{
    char[] newFileName = fileName.ToCharArray();
    for (int i = 0; i < newFileName.Length; i++)
    {
        if (!AndroidAllowedChars.ContainsKey(newFileName[i]))
            newFileName[i] = '_';
    }
    return new string(newFileName);
}

Заключение

Чтобы все заработало на Production надо добавить файлы ClosedXml.dll и DocumentFormat.OpenXml.dll в ваш пакет. Кнопки Ribbon и контрол поместить в одну фичу уровня Site или Web.

Весь проект можно посмотреть по ссылке - https://spsamples.codeplex.com/SourceControl/latest#ExportToExcel/

Готовый WSP файл тут - https://spsamples.codeplex.com/releases/view/117220



Как обычно делают корпоративный портал на SharePoint

Недавно на facebook был опубликован (не мной) просто эпичный пост про корпоративный портал на SharePoint. Поиска по постам на facebook нет, поэтому приведу содержание здесь:

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

В итоге, в крайнюю пятницу старого года, взяв 30е и 31е декабря в счёт отработанных выхов, иду в бар, пью пиво и вообще всячески начинаю радоваться наступившим наконец каникулам. Но радость моя длилась ровно 2 дня, пока не закончились выходные.

В понедельник меня будит звонок руководителя, который сообщает, что портал не работает. При чем весь и полностью. До этого у нас были проблемы с главной страницей, но мы их успешно решили. А тут умерло вообще все. Ни один сайт не открывается. Исследования показали, что серваки просто не справляются с нагрузкой: очередь в IIS как при СССР за докторской колбасой, а памяти она жрет больше обычного раза в три. Кое-как продравшись в layouts, меняю мастер-страницу на дефолтную и наследую её для всех узлов. Портал моментально взлетает. Почти. Весь, кроме главной страницы. Выяснилось, что на главной странице masterpage жестко прописан в коде. Так как подрядчиков было уже не достать, оставил всё как есть и почти счастливо пошел отмечать новый год.

Сегодня танцы с бубном продолжили уже подрядчики. Код их, деньги уже уплочены, так что пусть свои косяки и исправляют. И что в итоге? А всё очень просто. В masterpage оказалась вшита веб-часть, которая подгружала курсы валют с сайта cbr.ru, но подгружала она не на серверной стороне, а на клиентской без какого-либо кеширования. В итоге, админы cbr получив с одного IP 100 000+ запросов за менее чем рабочий день, банально нас забанили. Веб-часть же начала ругаться на отсутствие аутентификации. А так как написана она была левой пяткой, то ругалась до бесконечности, не позволяя подгрузить о стальной контент страницы. Баг, который может проявиться только при опытной эксплуатации и на тестах его выявить практически нереально.

Автор поста – админ, не программист, но хорошо разбирается в SharePoint. Поэтому оставим технические детали вроде серверной\клиентской стороны для веб части (судя по симптомам загрузка курсов была именно на сервере).

Первоначально все накинулись на подрядчиков с рекомендациями писать try\catch. Но ошибка, вероятнее всего, была вызвана как раз использованием try с пустым catch (без throw) в цикле. Ошибка довольно типичная для среднестатистического разработчика SharePoint. Но самое важное в этой истории как раз то, что не написано и не касается подрядчиков вообще.

Какая необходимость вообще возникла пихать информер курсов валют на все страницы всех сайтов портала? От этого есть реальный value? Даже в финансовых организациях от силы 30% сотрудников будут получать преимущество от такого функционала, но у них, скорее всего, будет специальный софт, где будут и курсы валют и многое другое. А в среднестатистической организации курсы валют на каждой странице не нужны никому, а если вдруг кому-то понадобятся, то они быстрее зайдут на сам cbr.ru.

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

Курсы валют все равно есть и в 1С и на всяких яндексах и т.д. Но партия (руководство) сказала надо, значит надо.

Это прекрасно! Полтора года назад у меня уже был пост на тему создания решений, где тоже все свелось к мифическому “руководству”. Значит и подходы, описанные  в моем посте, остаются такими же верными.

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

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

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

Ну и корень всей этой ситуации:

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

Без комментариев...

Резюме

Вот так обычно делают порталы на SharePoint:

  1. Выбирают по красивой картинке (проблема заказчика).
  2. Не работают с требованиями (проблема заказчика).
  3. Полное отсутствие менеджмента проекта на стороне заказчика (понятно чья проблема).
  4. Полное отсутствие контроля качества на стороне заказчика (аналогично) .
  5. Подрядчики не обладают достаточной квалификацией по созданию порталов (по сути единственная проблема подрядчиков).
  6. У заказчика вообще нет стратегии внедрения и развития портала.

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

Если же нет специалистов у заказчика, то обязательно нужно нанимать консультантов ДО начала проекта. Это позволит сэкономить гораздо больше денег.



Как сделать хороший корпоративный портал

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

Взгляд ИТшника

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

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

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

Взгляд дизайнера

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

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

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

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

Взгляд архитектора

Здесь речь пойдет об Information Architect, но на адекватного перевода на русский я не нашел.

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

Архитекторы стремятся сделать портал удобным и понятным, но без помощи дизайнеров не могут сделать его привлекательным для пользователей, а без помощи ИТ – полезным.

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

Взгляд бизнесмена

Сочетание всех трех предыдущих взглядов необходимо для создания хорошего корпоративного портала. Но не достаточно.

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

Естественно лояльность и продуктивность для всех сотрудников разом увеличить не получится. Для начала нужно четко определить цели, желательно по методике SMART. Для каждой цели должны быть метрики достижения, а также целевая аудитория пользователей, релевантная данной цели. Целевая аудитория (aka сегмент пользователей) далеко не всегда будет соответствовать организационным единицам или должностям.

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

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

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

Заключение

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