 Понимание как основа устойчивости к ошибкам
            Понимание как основа устойчивости к ошибкам
          
          на примерах из жизни
             Красной Шапочки
            Красной Шапочки
          
            и пирожочков
             
             
          
             Красной Шапочки
            Красной Шапочки
          
            и пирожочков
             
             
          
 
             
             
             
             
               
               
             
             
             
             
               
                 
                 
                 
               
                 
                 
                 
               
             
               
               
               
             
               
               
               
               
             
           
          Оба представления содержат ошибки, что не мешает коду работать
субъективное представление о том, как что-то в реальном мире работает
код — формальное описание такой модели
Изменения
Изменения
Изменения
Изменения
Иcкажение
Исправление ошибок
Добавление ошибок
Иcкажение
Сложность
Количество мысленных усилий, требуемых для воссоздания исходной когнитивной модели
                  function packPies(redRidingHood, packA, packB, pies) {
                    const parcel = [];/*1*/
                    for (/*2*/let i = 0; /*3*/i < pies.length;/*4*/i++) {
                      let pie = null;/*5*/
                      switch (pies[i].type) {/*6*/
                        case "HINKAL"/*7*/: pie = packA(pies[i]); break;/*8*/
                        case "CHEBUREK"/*9*/:
                        case "PIE"/*10*/: pie = packB(pies[i]); break;/*11*/
                      }
                      if (pie /*12*/) { parcel.push(pie);/*13*/ }
                    }
                    if (parcel.length /*14*/) {
                      redRidingHood.carry(parcel);/*15*/
                    }
                  }
                
          
            function packPies(redRidingHood, packA, packB, pies) { // 1
              const parcel = [];                                   // 0
              for (let i = 0; i < pies.length; i++) {              // 1
                let pie = null;                                    // 0
                switch (pies[i].type) {                            // 0
                  case "HINKAL": pie = packA(pies[i]); break;        // 1
                  case "CHEBUREK":                                     // 1
                  case "PIE": pie = packB(pies[i]); break;        // 1
                }                                                  // 0
                if (pie) { parcel.push(pie); }                     // 1
              }                                                    // 0
              if (parcel.length) {                                 // 1
                redRidingHood.carry(parcel);                       // 0
              }                                                    // 0
            }                                                      // 0
          
        
                      a || b || c
                      a && b && c
                      switch + case + default
                      try + catch + finally
                    
                  
                    if for while switch ? && || catch
                  
                
                  function packPies(redRidingHood, packA, packB, pies) { // 0
                    const parcel = [];                                   // 0
                    for (let i = 0; i < pies.length; i++) {              // 1
                      let pie = null;                                    // 0
                      switch (pies[i].type) {                            // 1 + 1
                        case "HINKAL": pie = packA(pies[i]); break;        // 0
                        case "CHEBUREK":                                     // 0
                        case "PIE": pie = packB(pies[i]); break;        // 0
                      }                                                  // 0
                      if (pie) { parcel.push(pie); }                     // 1 + 1
                    }                                                    // 0
                    if (parcel.length) {                                 // 1
                      redRidingHood.carry(parcel);                       // 0
                    }                                                    // 0
                  }                                                      // 0
                
          служебные слова и операторы, например
              ! != === !=== && || * *= 
              + += - -= / /= % 
              () {} [] let const var
              for if while catch case
            
          идентификаторы, константы, типы, свойства объектов
              1 'hello' true testName length
              null undefined
            
          
              function packPies(redRidingHood, packA, packB, pies) {
                const parcel = [];
                for (let i = 0; i < pies.length; i++) {
                  let pie = null;
                  switch (pies[i].type) {
                    case "HINKAL": pie = packA(pies[i]); break;
                    case "CHEBUREK":
                    case "PIE": pie = packB(pies[i]); break;
                  }
                  if (pie) { parcel.push(pie); }
                }
                if (parcel.length) {
                  redRidingHood.carry(parcel);
                }
              }
            
          | оператор | N | оператор | N | операнд | N | операнд | N | 
|---|---|---|---|---|---|---|---|
| const | 1 | . | 5 | parcel | 4 |   | 1 | 
| = | 5 | ++ | 1 | i | 6 |   | 1 | 
| [] | 3 | switch | 1 | pies | 4 |   | 1 | 
| ; | 10 | case | 3 | length | 2 | packA | 1 | 
| for | 1 | break | 2 | null | 1 | packB | 1 | 
| {} | 4 | () | 6 | type | 1 | push | 1 | 
| let | 2 | if | 1 | pie | 5 | redRidingHood | 1 | 
| < | 1 | 0 | 1 | carry | 1 | ||
| n1=15 N1=46 | n2=16 N2=32 | ||||||
| Длина программы | N = N1 + N2 = 46 + 32 = 78 | 
| Размер словаря | n = n1 + n2 = 15 + 16 = 31 | 
| Объем программы | V = N * log2n = 386,43 | 
| Сложность | D = (n1 * N2) / (2 * n2) = 15 | 
| Усилия программиста | E = V * D = 5796,43 | 
| Число багов | B = E2/3/3000 = 0,107 | 
Объединяет предыдущие метрики в один показатель относительной надежности кода
                    "complexity": ["error", { "max": 10 }]
                  
                
                    "max-lines-per-function": [
                      "error", {"max": 2, "skipBlankLines": true}
                    ]
                  
                
                    "max-depth": ["error", 3]
                  
                "cyclomatic-complexity": ["error", { "max": 10 }] 
               
               
               
               
               
             
               
               
               
               
               
               
               
               
               
               
               
             
               
               
               
             
               
               
               
            Если волк съел пирожок, попытаться еще N раз
                phone
                 .addEventListener("callFromGrandma", makePie);
                function makePie(event, counter = 0) {
                  bakePie(event)
                   .then(sendPie)
                   .then(grandmaGetsPie)
                   .catch(() => eatenByWolf(event, counter++));
                }
                function eatenByWolf(event, counter) {
                  if (counter < N) {
                    return makePie(event, counter);
                  } else {
                    return excuse();
                  }
                }
            
          Если волк съел пирожок, попытаться еще N раз
Работа с потоками событий в декларативном стиле.
                fromEvent("callFromGrandma", phone)
                .pipe(
                  concatMap(event => of(event).pipe(
                    concatMap(bakePies),
                    concatMap(sendPies),
                    retry(N)
                  )),
                  catchError(excuse)
                )
                .subscribe(grandma);
              
            
              function eatenByWolf(event, counter) {
                if (counter < N) {
                  return makePie(event, counter);
                } else {
                  return excuse();
                }
              }
          
           
             
          
                function eatenByWolf(event, counter) {
                  if (counter > N) {
                    return excuse();
                  } 
                  return makePie(event, counter);
                }
            
          
                class PieFactory {
                  constructor() {
                   this.maker = new RedRidingHood();
                  }
                  bakePie(pie) { this.maker.bakePie(pie); }
                  sendPie(pie) { this.maker.sendPie(pie); }
                }
                const factory = new PieFactory();
                fromEvent("callFromGrandma", phone)
                  .pipe(
                    concatMap(factory.bakePie),
                    concatMap(factory.sendPie)
                  ).subscribe(grandmaGetsPie);
            
          
                class PieFactory {
                    constructor() {
                     this.maker = new RedRidingHood();
                     this.friend = new Friend();
                    }
                    bakePie(pie) { return pie.type === 'PIE'
                       ? this.maker.bakePie(pie)
                       : this.friend.buyPie(pie)
                    }
                  }
            
            
              const factory = new RedRidingHood();
            
           
               
               
             
            
                function basePresentPack(item) {
                  const box = putInBox(item);
                  const pack = addCard(box);
                  return item.type && item.type === 'HINKAL'
                   ? addFork(pack)
                   : pack;
                }
                /*где-то в коде*/
                const pie = bakePie(pieType);
                const box = basePresentPack(pie);
            
          
                function basePresentPack(item) {
                  const box = putInBox(item);
                  const pack = addCard(box);
                  return pack;
                }
                function piePack(pie) {
                  const pack = basePresentPack(item);
                  return pie.type === 'HINKAL'
                    ? addFork(pack)
                    : pack;
                }
            
          
                function cook(recipe, ingredients) {
                  const mixture = mix(recipe, ingredients);
                  const cookedItem = bake(mixture);
                  const result = someCookingMagic(cookedItem);
                  return result;
                }
                function someCookingMagic(item) {
                  const result = decorate(item);
                  return sayAbracadabra(result);
                }
            
          
                function cook(recipe, ingredients) {
                    const mixture = mix(recipe, ingredients);
                    const cookedItem = bake(mixture);
                    const result = decorate(cookedItem);
                    return sayAbracadabra(result);
                }
            
          
                fromEvent("callFromGrandma", phone).pipe(
                  .pipe(
                    concatMap(event => {
                      const needPie = !!event.pie;
                      const pie = needPie ? event.pie : null;
                      const type = needPie ? pie.type : null;
                      const pieType = type || 'DEFAULT_TYPE';
                      return type ? bakePie(pieType) : null;
                    }),
                    concatMap(sendPie)
                  ).subscribe(grandmaGetsPie);
            
          
                fromEvent("callFromGrandma", phone).pipe(
                    concatMap(event => event.pie
                      ? bakePie(event.pie.type || 'DEFAULT_TYPE')
                      : null
                    ),
                    concatMap(sendPie)
                  ).subscribe(grandmaGetsPie);
            
          Тратят время и засоряют код?
                  // спечь пирожок по просьбе бабушки
                  function bakePie(grandmaRequest, resources) {
                    let pie = grandmaRequest.pie;
                    if (pie.ingredients.some(x => !resources[x])) {
                      pie = X.availablePie(resourses) /*1*/;
                    }
                    switch (pie.type /*2*/) {
                      case "PIE": return makeA();
                      case "HINKAL": return makeB();
                      case "CHEBUREK": return makeC();
                      ..../*3*/
                    }
                  }
              
            Два золотых правила
Два золотых правила
                // 1: используем X для оптимизации (Y работало плохо)
                // 2: не проверяем на null, потому что Z
                // 3: список из JIRA-XXX
              
            
                  // bakePie(
                  //   { type: 'PIE', ingredients: ['flour','oil','meat'] },
                  //   { 'flour': true, 'oil': true, 'meat' true }
                  // )
              
             
        Слишком много условий в коде
                function cookForGrandma(recipeNames) {
                  getRecipesFromServer(recipeNames)
                    .then(cookByRecipe)
                    .catch(handleErr);
                }
                function cookByRecipe(recipes) {
                   if (!recipes) return; 
                   recipes.forEach(recipe => {
                     if (recipe.ingredients) {
                         /* mixIngedients */
                     }
                     if (recipe.type === 'soup') {
                       /* Cook soup */
                     } else { /* Bake pie */ }
                   });
                }
            
          Удалите лишнее
Слишком много условий, и они нужны
              function packPies(courier, packA, packB, pies) {
                const parcel = [];
                for (let i = 0; i < pies.length;i++) {
                  let pie = null;
                  switch (pies[i].type) {
                    case "HINKAL": pie = packA(pies[i]); break;
                    case "CHEBUREK":
                    case "PIE": pie = packB(pies[i]); break;
                  }
                  if (pie ) { parcel.push(pie); }
                }
                if (parcel.length ) {
                  courier.send(parcel);
                }
              }
          
          Слишком много условий, и они нужны
Измените поведение/архитектуру, чтобы проверки больше не требовались
            const pack = {'HINKAL': packA,'CHEBUREK': packB,'PIE': packB };
            function packPies(courier, pies, packMap = pack) {
              courier.send(pies
                .filter(pie => packMap[pie.type])
                .map(pie => packMap[pie.type](pie)));
            }
          
        Слишком много условий, и они точно нужны!!!
                function getRecipe(pieType) {
                  return getHttpRecipe(pieType).then(extract);
                )
                /*где-то в коде*/
                getRecipe('CHEBUREK')
                  .catch(error => {
                    if (error && error.message) {
                      notify(error);
                    }
                    return DEFAULT_RECIPE;
                  });
                /*где-то еще в коде*/
                getRecipe('HINKAL')
                  .catch(error => {
                    if (error) { console.log(error.message); }
                    return DEFAULT_RECIPE;
                  });
              
          Слишком много условий, и они точно нужны!!!
Перенесите условие туда, где оно меньше всего мешает (на более базовый уровень).
            function getRecipe(pieType) {
              return getHttpRecipe(pieType).then(extract).catch(error => {
                if (error.message) { notify(error.message); }
                return DEFAULT_RECIPE;
              });
            )
          
          Или объедините и обрабатывайте разом.
            function bake(pieType) {
              return getRecipe(pieType).then(mix).then(bake).catch(handleBakeError);
            )
          
        Задача не сложная и не очень важная, сделаем потом
              // добавить loader
              fromEvent("callFromGrandma", phone)
              .pipe(
                concatMap(event => of(event).pipe(
                    concatMap(bakePies),
                    concatMap(sendPies),
                    retry(N)
                  )),
                catchError(excuse)
              )
              .subscribe(grandma);
            
          Задача не сложная и не очень важная, сделаем потом
Стандартные вещи делайте сразу, пока они еще не требуют значительных усилий, например:
                concatMap(event => of(event).pipe(
                  tap(loaderIncrement),
                  concatMap(bakePies),
                  concatMap(sendPies),
                  retry(N),
                  finally(loaderDecrement),
                )),
              
            Не надо обсуждать код, который работает
 
               
              Не надо обсуждать код, который работает
Постоянная рекалибровка когнитивных моделей повышает устойчивость.
 
        И тогда пирожочки будут реже теряться на пути к бабушке!
 
               
              