среда, 25 ноября 2020 г.

Как названия переменных и методов делают сопровождение кода дороже?

    Да легко.

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


  • Несоответствие названий переменной и метода/свойства, из которого получают значение переменной:

    • var renderRequired = this._isInitializingRequired();

      Не понятно как необходимость 'initializing' может превратиться в необходимость 'render', под этими словами я понимаю совершенно разные алгоритмы/события/состояния/действия и при чтении кода с переменной renderRequired не буду связывать это условие с состоянием 'initializing', а при изучении метода _isInitializingRequired не буду предполагать его использование для алгоритма render.


    • this._isUpdateAllowed() && this._updateDOMComponent(renderRequired);

      Из названия флажка "разрешено обновление" я никогда не подумаю о его связи с операцией "обновить DOM компонент", ведь он влияет на 'update' всего объекта.


    •   endUpdate: function endUpdate() {
          var renderRequired = this._isInitializingRequired();
          this.callBase();
          this._isUpdateAllowed() && this._updateDOMComponent(renderRequired);
        },

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


    •   _updateDOMComponent: function _updateDOMComponent(renderRequired) {
          if (renderRequired) {
            this._renderComponent();
          } else if (this._requireRefresh) {
            this._requireRefresh = false;
            this._refresh();
          }
        },

      Из названия функции '_updateDOMComponent' никак не понять что она может выполнить или операцию 'render', или операцию 'refresh', или совсем ничего не сделать. Длят таких 'условных' операций я часто вижу в названии 'tryXXX' и сразу предполагаю вариант "ничего". 


    • <div class="dx-drawer-panel-content">
        <div id="content">content</div></div>

      Под буквами 'content' я понимаю "внутреннее содержимое" и стиль 'dx-drawer-panel-content' я ожидаю найти на внутреннем элементе, но вместо этого этот стиль накладывается на его родительский элемент.

    • const valueFields = descriptions.values;

      'values' - это массив значений. 'valueFields' - это массив имен (наверное свойства какого-то объекта), из которых будут браться значения. Но обе переменные возвращают одно и то же значение. Такое различие в названиях переменных сильно усложняет понимание алгоритма по коду.

    • const expressionArg = new SummaryCell(...);

      Из букв 'expressionArg' я никогда не догадаюсь что переменная ссылается на объект SummaryCell.


    • return {
          direction: drawer.calcTargetPosition(),
          $panel: $(drawer.content()),
          $content: $(drawer.viewContent()),
      };
      Здесь очень художественное передергивание: position становится direction'ом, content превращается в panel, а content'ом становится viewContent. Автор в этом может и не будет путаться, но наследникам кода придется непросто.

  • Присвоение в переменную значения другого типа:

    •     let isEmpty = item.isEmpty;
          if(isEmpty && isEmpty.length) {
              isEmpty = item.isEmpty.filter(function(isEmpty) { return isEmpty; }).length === isEmpty.length;
          }
      Переменная isEmpty возвращала Array, а стала возвращать Boolean

  • Несоответствие имени переменной/функции и возвращаемого значения:

    •     rowItem.isEmpty = [];
      От переменной/свойства с именем 'isEmpty' я ожидаю значение типа Boolean, а не массив.


    •     const cell = expressionArg.cell();
          const value = cell[i] = expression(expressionArg);
      'cell' - это ячейка. Одна ячейка. Ну какой там может быть массив?




    • Метод объекта Drawer называется 'content()' но возвращает элемент с классом 'drawer-panel-content' и в то же время рядом с этим элементом есть другой элемент с классом 'drawer-content', который я и ожидаю получить из метода 'content()'. Прямо наперсточники...




    • Слово 'content' авторы кода используют в двух смыслах: как название для внутренней деталюшки компонента которая содержит клиентский элемент и как название этого клиентского элемента. Такой выбор названия постоянно запутывает, не понятно что именно имеется ввиду в каждом конкретном месте: внутренняя запчасть или клиентский элемент? Обязательно нужно глянуть...

  • Использование '&&' вместо двухстрочных 'if':

    • rowOptions.watch && rowOptions.watch(() => rowOptions.rowIndex)

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

  • Несогласованность названия функции и реального кода внутри нее:

    •     _disposeDataSource: function() {
              const that = this;
              const dataSource = that._dataSource;

              if(dataSource) {
                  dataSource.off('changed', that._changedHandler);
                  that._dataSource = undefined;
              }
          },
      Буквы 'dispose' подразумевают безвозвратное освобождение/закрытие использованных ресурсов. После такого освобождения ресурсов объект надо создавать заново. Но этот код всего лишь убирает подписку на событие changed, хотя у объекта '_dataSource' есть метод 'dispose'. Это неожиданная реализация метода, которая не понятна из его названия.


вторник, 11 августа 2020 г.

Откуда берется "легкое сопровождение"?

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

вторник, 5 мая 2020 г.

ZeroBugs: "Баги в наших компонентах" или "Проблемы у клиентов в создании их приложений на наших компонентах"

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

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

среда, 5 июня 2019 г.

Как писать тесты на поведение с разными конфигурациями настроек?

или "Как писать тесты на расширение поведения контролов?"

Например, у меня есть контрол MyTextEdit и у него сейчас есть свойство "repaintChangesOnly {false, true}". В моем текущем проекте много таких свойств: сначала сделали одну реализацию (и кучу тестов на нее), потом один из клиентов сообщил о ее недостатках и мы придумали другую (и добавили новое свойство). Обычное дело в долгоиграющем проекте на несколько лет.

Внимание!!! Разыкивается Новый Клиент!!!

Как увеличить количество новых клиентов "за неделю"? Не в смысле "как похудеть за неделю", а как увеличить их количество так, что бы через год каждую неделю новых клиентов приходило на 10% больше чем сейчас? Или не через год, а через полгода. А может быть через два года...

На какие цели можно ориентироваться при планировании следующих релизов?

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

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

2. заказчик требует именно Win приложение (или это часть заказа) и в этих прилагах конечные пользователи будут использовать много Win специфичных функций (каких именно?)
 - клиент будет продлевать лицензии ради изменений в новых версиях, которых нет в старых
 - клиент будет покупать новые лицензии что бы начать делать такие заказы на нашем продукте

3. есть явные преимущества Win приложений по сравнению с Web/Mobile/Multy и конечным пользователям они необходимы
 - клиент будет продлевать лицензии ради изменений в новых версиях, которых нет в старых
 - клиент будет продлевать лицензии потому что привык/знает или просто наловчился все делать быстро с этим средством разработки
 - клиент будет покупать новые лицензии что бы начать делать такие заказы на нашем продукте

4. для клиента разработка Win приложения выглядит дешевле/интереснее, он раньше делал прилаги на win и теперь жалко опыт или трудно учиться - выбирают наш продукт потому что можно и дальше делать на win, но в дополнение к нему он дешево получает web/mobile
 - клиент будет продлевать лицензии ради изменений в новых версиях, которых нет в старых
 - клиент будет продлевать лицензии потому что привык к этому средству разработки
 - клиент будет покупать новые лицензии что бы начать делать такие заказы на нашем продукте

+ комбинации факторов

Наши доходы:
- продление лицензий
- новые лицензии

Наши неявные доходы:
- завлечение новых клиентов своими впечатлениями

Наши расходы:
- небольшие исправления в нашем коде для получения нужного поведения (баги)
- помощь в разработке нужного функционала (поддержка)
- реализация сложного функционала (новые фичи)

Много багов!!! Что делать?

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

  1. желания записать именно баг
  2. большого количества кода
  3. код используется в очень разнообразных сценариях/вариантах окружения + команда часто вносит изменения и нет фиксации результатов работы кода во многих сценариях/конфигурациях