пятница, 24 мая 2019 г.

Самый важный процесс

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

суббота, 26 января 2019 г.

"У нас нет ресурсов для ревью кода" (с)


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

Попалась отличная вводная статья про управление качеством продукта https://vsavkin.livejournal.com/6050.html. Без воды,  где все снято как с языка.
Если в кратце, тезисно по ней:

К прописным истинам можно отнести:

  1. Качество процесса разработки определяет качество разрабатываемого продукта!
  2. Чем позднее дефект обнаружен, тем дороже стоит его исправление
  3. «повышение качества системы снижает расходы на её разработку».
    Думаю этот пункту можно добавить несколько примечаний, но в общем случае согласен.
     
Что особо отметил для себя:
  1. Только на тестирование нельзя полагаться не только потому, что это позднее выявление дефектов, но и потому что большинство видов тестирования имеет низкий КПД.
  2. Формальная инспекция кода эффективный инструмент (наш выбор это использование в ревью кода/дизайна практик формальной инспекции).

Интересную статистику подтверждающию Макконела по поводу сравнения эффективности инструментов можно найти в слайдах  автора. Средняя стоимость поиска и исправления дефекта:
  1. Инспекция - 9 чел-часов
  2. Интеграционное тестирование -  21 чел-часов
  3. Системное тестирование - 34 чел-часов

А так же для мнемоники притча
http://losev-al.blogspot.com/2013/12/kpi.html#

Интересно, для кого-то все еще существует проблема "курицы и яйца" применительно к дилемме: в компании выстроена методология и культура, потому что компания успешная и богатая, или все же компания богатая и успешная благодаря выстроенной методологии и культуре?

понедельник, 19 ноября 2018 г.

Список из рекордов на коленке

Честно подсмотрено.

THeader = record
  x,y,z: TMyRecordField;
  NextLabel: packed record end;
end;

На сколько прекрасно, на столько и отвратительно. Но восхищает однозначно хотя бы тем, что это вообще работает. Если что, то NextLabel это ссылка на следующию записью.

воскресенье, 18 ноября 2018 г.

Парное_программирование VS ревью_кода


 Данная заметка - реакция на статью http://alexnesterov.com/code-review/, где практика code review рассматривается как антипаттерн, а парное программирование как способ решить задачи ревью кода правильно. 
Конечно же, у парного программирования и код ревью есть пересекающееся множество целей. Но парное программирование не в силах полностью закрыть потребность в ревью кода.

Минусы парного программирования

Минусы full-тайм парного парного программирования которые сразу бросаются в глаза:
  1. Практика full-тайм парного программирования доступна не всем компаниям:
    • Не каждый кандидат может/согласится работать в такой методологии. 
    • Вопрос организации пар тоже не самый простой.
    • Все остальные процессы в компании должны затачиваться под парное программирование: организация рабочего места, распорядок дня (приход-уход, удаленная работа, больничные, обеды).
    • Т.е. отрезаем себя от части рынка труда и добавляем дополнительные сложности. 
  2. Фул-тайм парное программирование не всегда эффективно. Есть множество достаточно простых  или рутиных задач в которых от создание пары не получить буста в производительности или качестве. Т.е. фул-тайм парное программирование просто получение максимального качества без учета экономической обоснованности. 
  3. Парное программирование не решает всех задач ревью код
    • Как происходит распространение знаний между парами? Ротациями пар? На сколько это эффективно и какой лаг? 
    • Код ревью под парами (инструменты настроены и все знают как ими пользоваться) позволяют в любой момент подключить нужное количество ревьюверов в асинхронном режиме) 

Золотая середина?

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

Мифические проблемы ревью кода

Неправильное использование любой техники может привести к ее негативному восприятию.

Потеря контекста

Это не проблема, а сигнал что в процессе разработки что то не так. Контекст и мотивация автора должна быть понятна из ревью. Про это у меня есть статья http://cemick.blogspot.com/2015/08/svn-git-git.html
Программист  - не археолог разбирающий наскальные записи прошлых поколений. Если контекст и мотивация не понятная на ревью, она будет не понятна через неделю другому программисту который попадет на этот участок кода и автору  уже через месяц.
Ко всему прочему мы к ревью часто прикладываем ссылку на поднятый инстанс или бинарник, что бы ревьювер мог проверить работающее приложение, если есть в этом потребность. 

Это не баг, это фича

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

Отсутствие эмпатии

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

Нарушение потока автора

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


Что же такое ревью кода

  1. Это гибкость
    Можно организовать менторство.
    Можно в любой момент подключить нужное количество людей с известными всем правилами обсуждения и документированием.
    Можно выпонять асинхронно и синхронно. 
  2. Контроль соблюдения стандартов и правил оформления кода 
  3. Распространение знаний о важных изменения в проекте. Об изменения в архитектуре и в сквозной части функционала необходимо извещать все заинтересованные стороны.
  4. Повышение квалификации 
  5. Сохранение дополнительной метаинформации по коммиту: обсуждение в рамках ревью остается задокументированным. 
  6. Распределение нагрузки по контролю за кодовой базой или распространением информации с тимлида или начальника отдела на всех. 


понедельник, 28 марта 2016 г.

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

Сегодня снова об особенностях анонимных функций.
Два предыдущих поста:
  1. Просто баг в коде или особенность счетчика ссылок в замыканиях
  2. Немного про замыкание ( Одно замыкание на несколько анонимных функций в одном контексте)

  У типа анонимный функций есть замечательная возможность. В качестве обработчика можно присваивать не только анонимные функции, но и обычные методы и статические функции. Казалось бы, нет причин почему не использовать тип reference to всегда.
Однако есть ситуации, когда reference to тип может сыграть злую шутку. И я сейчас не про вопрос производительности, о котором я думаю большинство осведомлено. 
Проблема может возникнуть при передаче обычной процедуры или метода в параметр с анонимным типом. Покажем на примере. 
Наиболее распространенный вариант использования процедурных типов в реализации подписки на события.
  Пусть есть какой-нибудь список EventHandlers, и конечно же, как у правильных хипстеров он будет параметризован типом анонимной функций TList<TProc>. 
Заведем два метода для возможности подписаться на событие и отписаться от него:  Subscribe(AHandler: TProc) и Unsubscribe(AHandler: TProc): Boolean соответственно. Однако, крайне вероятно, вас постигнет неудача если в качестве обработчика вы используете неанонимную функцию.  По крайне мере отписаться вы уже не сможете. 

Вот код примера:
program ReferenceToProc;
{$APPTYPE CONSOLE}
{$R *.res}
uses
  System.SysUtils, System.Generics.Collections;
var
  eventHandlers: TList<TProc>;

  procedure Subscribe(AHandler: TProc);
  begin
    eventHandlers.Add(AHandler);
  end;

  function Unsubscribe(AHandler: TProc): Boolean;
  begin
    Writeln(eventHandlers.IndexOf(AHandler));  // 0
    Result := eventHandlers.Remove(AHandler) >= 0;
  end;

  procedure HandlerProc;
  begin
    Writeln('Вызов статической процедуры');
  end;

var
  proc: TProc;
begin
   eventHandlers:= TList<TProc>.Create;

   proc := procedure()
     begin
       Writeln('Вызов анонимной функции')
     end;

   Subscribe(proc); // Подписываемся анонимной функцией
   Writeln(Unsubscribe(proc)); // Метод вернул True, Успех:)

   Subscribe(HandlerProc); // Подписываемся не анонимной функцией
   Writeln(Unsubscribe(HandlerProc)); // Метод вернет False Fail:(
   FreeAndNil(eventHandlers);
   readln;
end.

  Вызов Unsubscribe  в строке  Writeln(Unsubscribe(HandlerProc)); вернет False. Причина впрочем прозаична, вызов метода HandlerProc заворачивается в анонимную функцию и каждый раз новую. Потому функция, которую подписали на событие, будет отличаться от функции, которую пытаемся отписать.

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

  Получается, что хранить обработчики лучше как TMethod, но если все же хотим уметь подписывать универсальные обработчики (анонимные и неанонимные), тогда придется для каждого типа писать свою логику сохранения в список и вызова обработчиков. Это будут либо overload методы, либо будем использовать Rtti  и методы параметризированные типом.
  TEvent = class
    events: TList<TMethod>;
    class procedure Subscribe(AProc: TProc); overload;
    class procedure Subscribe(AProc: TProcedure); overload;
    class procedure Subscribe<T>(AProc: T); overload;
  end;

  А для преобразования анонимного метода в TMethod потребуется сочинять что то вроде того, что написано в Spring4D в Springs.Events.pas:

procedure MethodReferenceToMethodPointer(const AMethodReference; const AMethodPointer);
type
  TVtable = array[0..3] of Pointer;
  PVtable = ^TVtable;
  PPVtable = ^PVtable;
begin
  // 3 is offset of Invoke, after QI, AddRef, Release
  PMethod(@AMethodPointer).Code := PPVtable(AMethodReference)^^[3];
  PMethod(@AMethodPointer).Data := Pointer(AMethodReference);
end;

function TEvent.Cast(const handler): TMethod;
begin
  if fTypeInfo.Kind = tkInterface then
    MethodReferenceToMethodPointer(handler, Result)
  else
    Result := PMethod(@handler)^;
end;


пятница, 23 октября 2015 г.

Просто баг в коде или особенность счетчика ссылок в замыканиях



MyObject.OnEvent := procedure()
  begin
    MyObject.OnEvent := nil;
    MyField.MyProp := 'Value';
  end;
Оказывается на MyObject.OnEvent := nil; счетчик ссылок у класса замыкания скрутится в 0 и замыкание отойдет в мир иной, а на следующей строчке привет AV.