Основные ошибки программирования

Научитесь выявлять их. Выработайте привычки избегать их.

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

От переводчика

Иногда бывает трудно объяснить простыми словами казалось бы банальные вещи: зачем использовать гит, в чем фишка инкапсуляции, зачем писать тесты, как планировать свой код, рефакторить чужой и т.д. Мне показалось, что в этой статье компактно собраны важные «гуманитарные» аспекты программирования. Что-то вроде морального кодекса, ориентира и мотиватора в принятии решений, связанных с написанием кода.

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

1) Программирование без планирования
2) Чрезмерное планирование
3) Недооценивание важности качества кода
4) Хвататься за первое решение
5) Не отступать
6) Не гуглить
7) Не использовать инкапсуляцию
8) Планирование неизвестного
9) Использование неподходящих структур данных
10) Ухудшать код
11) Комментирование очевидных вещей
12) Не писать тесты
13) Думать, если что-то работает, то это правильно сделано
14) Не подвергать сомнению существующий код
15) Одержимость лучшими практиками
16) Одержимость производительностью
17) Не ориентироваться на конечного пользователя
18) Не подбирать правильные инструменты
19) Непонимание, что проблемы с кодом вызывают проблемы с данными
20) Изобретение колеса
21) Неправильное отношение к инспекции кода (code review)
22) Не использование систем контроля версий
23) Злоупотребление общим состоянием (shared state)
24) Неправильное отношение к ошибкам
25) Не отдыхать

1) Программирование без планирования

Качественный контент не создаётся “на коленке”, а требует основательной работы. Программный код – не исключение.

Хороший код должен проходить через следующие стадии:

Замысел. Исследование. Планирование. Написание. Проверка. Изменение.

Каждому из этих пунктов надо уделить достаточно усилий.

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

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

“Если ты зол, сосчитай до 10, прежде чем говорить. Если очень зол — то до 100”. (Томас Джефферсон)

Для нашего случая это можно перефразировать так:

“Когда проверяешь код, сосчитай до 10 прежде чем переписать 1 строчку. Если для этого кода нет тестов — то до 100”.

Программирование – это на 90% изучение существующего кода и его изменение посредством маленьких, легко тестируемых порций, вписывающихся в общую систему. Само написание кода это всего лишь 10% работы программиста.

Программирование – это не просто написание строк кода, а творчество, основанное на логике, которое надо развивать в себе.

2) Чрезмерное планирование

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

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

Линейное планирование всей программы “от А до Я” (водопадным методом) – не годится для большинства программных продуктов. Разработка подразумевает обратную связь и вы постоянно будете удалять и добавлять функционал, что никак нельзя учесть в «водопадном планировании». Планировать следует несколько следующих элементов. И каждый новый надо включать в план лишь после гибкой адаптации к реальности (Agile).

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

3) Недооценивание важности качества кода

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

Смотрите на программу как на составные части, общающиеся посредством кода. Плохой код — это плохая коммуникация.

“Пишите свой код так, будто его будет сопровождать агрессивный психопат, знающий, где вы живете”. (Джон Вудс)

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

tHIS is
  WAY MORE important
than
         you think

Не используйте длинные строки. Строку длиннее 80 символов очень трудно читать. Используйте специальные инструменты для приведения кода в порядок (ESLint, Prettier для js).

Следите за количеством строк в функциях и файлах. Разделяйте код на мелкие части, понятные и тестируемые. Функция больше 10 строк – слишком длинная.

Не используйте двойное отрицание. Не не не делайте так. Это очень не не плохо.

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

“В компьютерных науках есть только две по настоящему сложные вещи: инвалидация кэша и именование переменных”. (Фил Карлтон)

Используйте константы с содержательным названием для хранения примитивов. Если вам где-то нужно использовать число 12, сделайте сначала так:

const monthsInYear = 12; 

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

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

“Измерять программирование строками кода, это то же самое, что измерять авиастроительство тоннажем произведенных самолетов”. (Билл Гейтс)

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

4) Хвататься за первое решение

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

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

“Есть два пути написания программы: 1) сделать её настолько простой, что в ней, очевидно, не будет недостатков; 2) сделать её настолько сложной, что в ней никакие недостатки не будут очевидными”. (Тони Хоар)

5) Не отступать

Другая частая ошибка новичков – не отступать. Даже когда они поняли, что выбранное решение не самое лучшее. Подход “не сдаваться” хорош во многих сферах, но не в программировании. Программистам полезно признавать ошибки раньше и чаще. Как только вы засомневались в решении – отбросьте его и переосмыслите проблему. Не важно, сколько вы уже вложили в этот путь. Системы контроля версий типа Git позволяют создавать ветки и экспериментировать с разными решениями, активно пользуйтесь этим.

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

6) Не гуглить

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

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

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

Не будьте креативным в понятиях Брета Виктора, который сказал:

“Думать, что ты знаешь, что делаешь – самая опасная мысль креативного человека”.

7) Не использовать инкапсуляцию

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

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

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

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

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

Стремитесь к тому, чтобы ваш код имел высокое зацепление и низкую связанность (High Cohesion and Low Coupling). Этот загадочный термин означает, что внутри класса должно быть максимально связей, а между классами – минимально зависимостей.

8) Планирование неизвестного

Когда вы пишете новую строчку кода, порой в голову лезут мысли: “а что если…” И вы начинаете фантазировать о разных новых фичах, которыми можно украсить программу. В большинстве случаев такие мысли вредны и не стоит на них ориентироваться.

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

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

“Рост ради роста — это идеология раковой клетки”. (Эдвард Эбби)

9) Использование неподходящих структур данных

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

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

Неуместно использованная структура данных это кричащее предупреждение: “код новичка!”. Вот несколько примеров.

Обычный массив или ассоциативный?

Самая распространённая ошибка – это использование обычных массивов вместо ассоциативных для хранения списка записей.

Обычный массив:

[{id: 1, title: "entry1"}, {id: 2, title:"entry2"}, .... ]

Ассоциативный массив:

{ 1: {id: 1, title: "entry1"}, 2: {id: 2, title:"entry2"}, ....}

Да, вы не ослышались, для хранения списка записей нужно использовать ассоциативные массивы. Под списком записей я подразумеваю такие записи, где присутствует идентификатор. Массивы оправданы для хранения скалярных величин и если планируется активное использование методов, вроде push, pop, shift, unshift, которые обращаются к записям не через ключ, а через порядок в списке.

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

Стек или рекурсия?

Оптимизировать рекурсии довольно сложная задача, особенно в однопоточной среде. Это усложняется, если рекурсивная функция вызывает себя 2 и более раз.

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

10) Ухудшать код

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

Вот несколько распространенных ошибок, приводящих к беспорядку в коде:

Дублирование

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

Не использование файла конфигурации

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

Лишние условные операторы и временные переменные

Любой if разветвляет логику вашей программы, поэтому их наличие должно быть сведено к минимуму, насколько это возможно без вреда для читабельности. Самое сложное – определить правильный уровень для изменений: будете вы расширять текущий код или вынесете его во внешнюю функцию и вызовете её?

Вот яркий пример ненужного if:

function isOdd(number) {
 if (number % 2 === 1) {
   return true;
 } else {
   return false;
 }
}

Его можно переписать без единого if:

function isOdd(number) {
 return (number % 2 === 1);
};

11) Комментирование очевидных вещей

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

Например, такой код:

// This function sums only odd numbers in an array
const sum = (val) => {
  return val.reduce((a, b) => {
    if (b % 2 === 1) { // If the current number is odd
      a+=b;            // Add current number to accumulator
    }
    return a;          // The accumulator
  }, 0);
};

Можно заменить таким:

const sumOddValues = (array) => {
  return array.reduce((accumulator, currentNumber) => {
    if (isOdd(currentNumber)) { 
      return accumulator + currentNumber;
    }
    return accumulator;
  }, 0);
};

Но иногда прояснить код можно только комментарием. В таком случае сосредоточьтесь на вопросе: “ЗАЧЕМ нужен этот код”, а не “ЧТО делает этот код”. Вот пример кода, где комментарии только засоряют код:

// create a variable and initialize it to 0
let sum = 0;
// Loop over array
array.forEach(
  // For each number in the array
  (number) => {
    // Add the current number to the sum variable
    sum += number;
  }
);

Не делайте так, если вы программист. А если вы работодатель таких программистов — увольте их прямо сейчас.

12) Не писать тесты

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

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

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

Если можете, создавайте проверки даже прежде самого кода. Разработка через тестирование (test-driven development, TDD) создана не для прикола и хайпа. Она благотворно влияет на то, как вы продумываете, проектируете и реализуете программные элементы.

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

13) Думать, если что-то работает, то это правильно сделано

Взгляните на эту функцию, которая суммирует нечетные числа. Всё ли там правильно?

const sumOddValues = (array) => {
  return array.reduce((accumulator, currentNumber) => {
    if (currentNumber % 2 === 1) { 
      return accumulator + currentNumber;
    }
    return accumulator;
  });
};
 
 
console.assert(
  sumOddValues([1, 2, 3, 4, 5]) === 9
);

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

Проблема 1

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

TypeError: Cannot read property 'reduce' of undefined.

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

Для пользователя функция просто не работает и он не поймет, что делать для исправления ситуации. Было бы намного лучше, если бы ошибка выдавалась в таком виде:

TypeError: Cannot execute function for empty list.

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

Проблема 2

Нет валидации. Что если в функцию передадут вместо массива строку, число или объект? Вот что произойдет:

sumOddValues(42);
TypeError: array.reduce is not a function

Подвох в этой ситуации в том, что array.reduce — это как раз функция. Но так как вы назвали аргумент функции array (массив), то что бы вы не передали ей (в данном примере это 42), будет названо массивом внутри функции. На самом деле ошибка говорит о том, что 42.reduce — это не функция. Не лучше ли сделать вывод ошибки в виде:

ОшибкаТипа: 42 - это не массив, чувак. 

Проблемы 1 и 2 описывают стандартные исключения, которые легко предусмотреть. Но бывают и менее очевидные исключения, с которыми надо быть внимательнее. Например, что произойдёт, если в массиве будут отрицательные числа?

sumOddValues([1, 2, 3, 4, 5, -13]) // => still 9

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

“Это не баг. Это задуманный функционал” — удобная отмазка тех, кто не пишет тестов.

Проблема 3

Не все валидные случаи правильно работают. Забудьте разные каверзные исключения. Эта функция неправильно работает и с вполне обыденным набором переменных.

sumOddValues([2, 1, 3, 4, 5]) // => 11

В данном примере 2 попадёт в сумму, хотя не должна. Это произойдёт потому, что в функцию reduce не передано initialValue, и поэтому в качестве исходного значения будет взят первый элемент массива. Поэтому важно написать тест и для такого случая. Если такого нет, то это ещё один признак кода новичка.

14) Не подвергать сомнению существующий код

Если вы не супер-пупер программист, который всегда работает в одиночку, то рано или поздно в своей жизни вы столкнетесь с тупым кодом. Новички склонны не замечать этого, тем более, если он исправно работает и давно написан.

Самое страшное в этой ситуации, что новичок может подумать, что это хороший код, и будет повторять существующие в нём неправильные подходы.

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

Новичкам можно посоветовать такое правило: любой недокументированный код, который вы не понимаете — возможно, плохой. Изучайте его. Спрашивайте о нём. Пользуйтесь командой git blame, выдающей автора каждой строки кода.

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

15) Одержимость лучшими практиками

Термин “лучшие практики” вредный, он ограничивает вас в исследовании, «ведь уже есть лучшая практика».

“Лучших практик” не бывает. Бывают хорошие практики на сегодняшний день и для этого языка программирования.

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

Не делайте что-то, потому что где-то прочитали цитату об этом, или увидели как кто-то делает это, или кто-то сказал про это “лучшая практика”. Ставьте всё под сомнение, бросайте вызов всем теориям, знайте все возможные варианты, и принимайте только обоснованные решения.

16) Одержимость производительностью

“Преждевременная оптимизация – это корень всех зол в программировании (или почти всех)”. Дональд Кнут, 1974

Хотя программирование существенно изменилось со времен Дональда Кнута, его совет актуален и в наши дни.

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

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

В погоне за вымышленной производительностью вы можете наделать реальные баги в самых неожиданных местах.

17) Не ориентироваться на конечного пользователя

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

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

18) Не подбирать правильные инструменты

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

Ориентироваться на популярность инструмента, а не пригодность для конкретной задачи – признак новичка.

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

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

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

19) Непонимание, что проблемы с кодом вызывают проблемы с данными

Один из важнейших аспектов программирования — это управление данными. Программа – это интерфейс для добавления, редактирования и удаления записей.

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

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

Как защитить себя от такого? Можно использовать несколько уровней валидации: на фронтенде, бекенде, при передаче и в базе данных (БД). Как минимум, используйте встроенные ограничения в БД.

Хорошо знайте все типы ограничений в БД и используйте их все при создании новых столбцов и таблиц.

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

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

CHECK проверяет произвольное выражение, например, для процентов нужно проверять вхождение в интервал от 0 до 100.

PRIMARY KEY подразумевает одновременно уникальность и не пустое значение. Каждая таблица базы данных должна иметь такое поле для идентификации записей.

FOREIGN KEY говорит о том, что величины этого столбца содержатся в другой таблице.

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

20) Изобретение колеса

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

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

Иногда бывает сложно выбрать нужное колесо из-за многообразия. Проводите исследование. Пробуйте перед покупкой. Большинство “программных колёс” бесплатны и с открытым кодом. По возможности используйте заготовки с открытым исходным кодом (open source), их легко отлаживать, улучшать, заменять и поддерживать.

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

21) Неправильное отношение к инспекции кода (code review)

Один из признаков новичков, это восприятие инспекций кода как критицизма. Они не любят их, не ценят и даже боятся.

Это в корне неправильное отношение надо как можно быстрее изменить. Смотрите на каждую инспекцию кода как на ценную возможность обучения. Любите и цените их. Учитесь посредством них. И благодарите делающих замечания.

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

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

22) Не использование систем контроля версий (Git)

Новички склонны недооценивать пользу хорошей системы контроля версий/кода, вроде Git.

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

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

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

Ещё одно предназначение системы контроля версий – понятность предназначения той или иной вещи. Допустим, вы столкнулись с функцией и вам надо понять её назначение и устройство. Вы можете найти коммит, в котором она появилась, и перед вами возникнет контекст её создания, что прольёт свет на всё остальное, связанное с ней.

Система контроля версий может даже помочь обнаружить баг, а именно, ту строку кода, с добавления которой программа дала сбой. В Гите есть бинарный поиск bisect, обнаруживающий коммит, внёсший баг.

Систему контроля можно использовать в разных целях, даже до того, как изменения кода превратились в официальные коммиты:

  • отслеживание изменений (staging changes)
  • выборочный патч (patching selectively)
  • сброс (resetting)
  • прятание (stashing)
  • перезапись истории (amending)
  • применение (applying)
  • просмотр изменений (diffing)
  • отмена коммитов (reversing)

Изучите все эти возможности, поймите, используйте и цените их. Чем меньше возможностей Git вы знаете, тем больше вы новичок.

23) Злоупотребление общим состоянием (shared state)

И снова это не про сравнение парадигмы функционального программирования с остальными.

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

Большая проблема общего состояния начинается тогда, когда несколько ресурсов меняют его в рамках одной итерации цикла событий (в событийно ориентированных средах). Возникает состояние гонки (Race conditions). И новички склонны решать эту проблему посредством таймера, особенно при блокировке данных. Это большой красный флаг. Избегайте этого. Ни в коем случае нельзя писать такой код или принимать его.

24) Неправильное отношение к ошибкам

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

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

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

25) Не отдыхать

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

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

Определение

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

Баги обнаруживаются чаще всего в момент отладки или бета-тестирования. Реже – после итогового релиза готовой программы. Вот несколько вариантов багов:

  1. Появляется сообщение об ошибке, но приложение продолжает функционировать.
  2. ПО вылетает или зависает. Никаких предупреждений или предпосылок этому не было. Процедура осуществляется неожиданно для пользователя. Возможен вариант, при котором контент перезапускается самостоятельно и непредсказуемо.
  3. Одно из событий, описанных ранее, сопровождается отправкой отчетов разработчикам.

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

История происхождения термина

Баг – слово, которое используется разработчиками в качестве сленга. Оно произошло от слова «bug» – «жук». Точно неизвестно, откуда в программировании и IT возник соответствующий термин. Существуют две теории:

  1. 9 сентября 1945 года ученые из Гарварда тестировали очередную вычислительную машину. Она называлась Mark II Aiken Relay Calculator. Устройство начало работать с ошибками. Когда его разобрали, то ученые заметили мотылька, застрявшего между реле. Тогда некая Грейс Хоппер назвала произошедший сбой упомянутым термином.
  2. Слово «баг» появилось задолго до появления Mark II. Термин использовался Томасом Эдисоном и указывал на мелкие недочеты и трудности. Во время Второй Мировой войны «bugs» называли проблемы с радарной электроникой.

Второй вариант кажется более реалистичным. Это факт, который подтвержден документально. Со временем научились различать различные типы багов в IT. Далее они будут рассмотрены более подробно.

Как классифицируют

Ошибки работы программ разделяются по разным факторам. Классификация у рядовых пользователей и разработчиков различается. То, что для первых – «просто программа вылетела» или «глючит», для вторых – огромная головная боль. Но существует и общепринятая классификация ошибок. Пример – по критичности:

  1. Серьезные неполадки. Это нарушения работоспособности приложения, которые могут приводить к непредвиденным крупным изменениям.
  2. Незначительные ошибки в программах. Чаще всего не оказывают серьезного воздействия на функциональность ПО.
  3. Showstopper. Критические проблемы в приложении или аппаратном обеспечении. Приводят к выходу программы из строя почти всегда. Для примера можно взять любое клиент-серверное приложение, в котором не получается авторизоваться через логин и пароль.

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

Также существуют различные виды сбоев в плане частоты проявления: постоянные и «разовые». Вторые встречаются редко, чаще – при определенных настройках и действиях со стороны пользователя. Первые появляются независимо от используемой платформы и выполненных клиентом манипуляций.

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

Виды

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

Разработчики выделяют следующие типы ошибок по уровню сложности:

  1. «Борбаг» – «стабильная» неполадка. Она легко обнаруживается на этапе разработки и компилирования. Иногда – во время тестирования наработкой исходной программы.
  2. «Гейзенбаг» – баги с поддержкой изменения свойств, включая зависимость от среды, в которой было запущено приложение. Сюда относят периодические неполадки в программах. Они могут исчезать на некоторое время, но через какой-то промежуток вновь дают о себе знать.
  3. «Мандельбаг» – непредвиденные ошибки. Обладают энтропийным поведением. Предсказать, к чему они приведут, практически невозможно.
  4. «Шрединбаг» – критические неполадки. Приводят к тому, что злоумышленники могут взломать программу. Данный тип ошибок обнаружить достаточно трудно, потому что они никак себя не проявляют.

Также есть классификация «по критичности». Тут всего два варианта – warning («варнинги») и критические весомые сбои. Первые сопровождаются характерными сообщениями и отчетами для разработчиков. Они не представляют серьезной опасности для работоспособности приложения. При компилировании такие сбои легко исправляются. В отдельных случаях компилятор справляется с этой задачей самостоятельно. А вот критические весомые сбои говорят сами за себя. Они приводят к серьезным нарушениям ПО. Исправляются обычно путем проработки логики и значительных изменений программного кода.

Типы багов

Ошибки в программах бывают:

  • логическими;
  • синтаксическими;
  • взаимодействия;
  • компиляционные;
  • ресурсные;
  • арифметические;
  • среды выполнения.

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

Ошибки синтаксиса

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

Синтаксические ошибки – ошибки синтаксиса, правил языка. Вот пример в Паскале:

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

Логические

Тут стоит выделить обычные и арифметические типы. Вторые возникают, когда программе при работе необходимо вычислить много переменных, но на каком-то этапе расчетов возникают неполадки или нечто непредвиденное. Пример – получение в результатах «бесконечности».

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

Выше – пример логической ошибки в программе. Тут:

  1. Происходит сравнение значения i с 15.
  2. На экран выводится сообщение, если I = 15.
  3. В заданном цикле i не будет равно 15. Связано это с диапазоном значений – от 1 до 10.

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

Время выполнения

Run-time сбои – это ошибка времени выполнения программы. Встречается даже когда исходный код лишен логических и синтаксических ошибок. Связаны такие неполадки с ходом выполнения программного продукта. Пример – в процессе функционирования ПО был удален файл, считываемый программой. Если игнорировать подобные неполадки, можно столкнуться с аварийным завершением работы контента.

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

Компиляционный тип

Встречается при разработке на языках высокого уровня. Во время преобразований в машинный тип «что-то идет не так». Причиной служат синтаксические ошибки или сбои непосредственно в компиляторе.

Наличие подобных неполадок делает бета-тестирование невозможным. Компиляционные ошибки устраняются при разработке-отладке.

Ресурсные

Ресурсный тип ошибок – это сбои вроде «переполнение буфера» или «нехватка памяти». Тесно связаны с «железом» устройства. Могут быть вызваны действиями пользователя. Пример – запуск «свежих» игр на стареньких компьютерах.

Исправить ситуацию помогают основательные работы над исходным кодом. А именно – полное переписывание программы или «проблемного» фрагмента.

Взаимодействие

Подразумевается взаимодействие с аппаратным или программным окружением. Пример – ошибка при использовании веб-протоколов. Это приведет к тому, что облачный сервис не будет нормально функционировать. При постоянном возникновении соответствующей неполадки остается один путь – полностью переписывать «проблемный» участок кода, ответственный за соответствующий баг.

Исключения и как избежать багов

Исключение – событие, при возникновении которых начинается «неправильное» поведение программы. Механизм, необходимый для стабилизации обработки неполадок независимо от типа ПО, платформ и иных условий. Помогают разрабатывать единые концепции ответа на баги со стороны операционной системы или контента.

Исключения бывают:

  1. Программными. Они генерируются приложением или ОС.
  2. Аппаратными. Создаются процессором. Пример – обращение к невыделенной памяти.

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

P. S. Большой выбор курсов по тестированию есть и в Otus. Присутствуют варианты как для продвинутых, так и для начинающих пользователей.

Кто-то из великих мыслителей когда-то сказал: «Покажите мне человека, который не ошибся ни разу в жизни, и я покажу вам человека, который ничего не достиг». Сами по себе ошибки — это не зло, они помогают нам расти и развиваться. Совершить их может специалист любого уровня. Однако учиться лучше на чужих неудачах, нежели на своих. «Библиотека программиста» детально разобралась в этой теме и составила список распространенных ошибок кодеров, которых вы можете избежать, если дочитаете статью до конца. Поехали!

1. Приступать к программированию, не до конца проработав схему проекта

Реализацию любого проекта можно разделить на три простых этапа:

  1. Анализ требований проектируемого продукта
  2. Создание схемы или прототипа самого проекта
  3. Реализация или непосредственное написание кода

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

2. Отсутствие единообразия и формата написания кода

⚠️ 10 самых распространенных ошибок, ежедневно допускаемых каждым программистом

Это ошибка, которую чаще всего совершают неопытные разработчики и новички.

Разрозненный неоднообразный код затруднит чтение и расстроит не только членов вашей команды, но и всех, кому доведется читать ваши угловатые конструкции. Старайтесь оставлять после себя чистоту и не заставляйте потомков извергать проклятья, глядя на ваш свободный стиль написания кода. В современном программировании большинство ИТ-специалистов используют определенные методологии написания кода, которые варьируются в зависимости от области разработки и языка. Также многие из них используют «плагины для форматирования кода», помогающие мгновенно избавиться от этой ошибки.

3. Написание глупых или заумных комментариев

⚠️ 10 самых распространенных ошибок, ежедневно допускаемых каждым программистом

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

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

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

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

Однако при комментировании нужно знать меру. Не стоит писать философское эссе размером в страницу, все должно быть четко, лаконично и по существу.

4. Не тестировать написанное

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

5. Написание универсальных супер-функций

⚠️ 10 самых распространенных ошибок, ежедневно допускаемых каждым программистом

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

6. Плохие сообщения коммитов

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

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

Фиксации с сообщениями типа: «Исправлена ​​ошибка» или «Обновлена ​​функция» — так себе названия. Хороший коммит содержит информацию о проблеме и ее решении, предшествующий уникальному идентификатору токена (если он доступен).

7. Чрезмерная инженерия или усложнение простых вещей

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

  1. Что использовать?
  2. Где и когда использовать?
  3. Как использовать?

Не стоит городить огород из ненужных плагинов и библиотек — старайтесь делать все проще и не усложняйте себе и без того нелегкий труд.

8. Не анализировать готовые решения в сети

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

9. Консерватизм и проповедование только одного стека

⚠️ 10 самых распространенных ошибок, ежедневно допускаемых каждым программистом

Поговорка, про кулика, расхваливающего свое болото актуальна и для мира ИТ. Наверняка, вам также доводилось слышать, как некоторые специалисты превозносят одну конкретную технологию, говоря, что все остальные ужасны. И заголовки кликбейтных статей, увещевающих о войнах фреймворков и языков («Vue vs React» или «Python vs Java») рождаются именно по их вине. Нельзя говорить, что один популярный инструмент хуже другого только лишь потому, что вы к нему привыкли. В разных ситуациях каждый будет хорош по-своему. Здесь главное уметь с ним работать. Грешно ругаться на молоток, утверждая, что он плохо забивает гвозди. А поскольку технологии совершенствуются довольно быстро, крайне важно быть открытым для новых идей и способов реализации. Консерватизм в ИТ — это ошибочный путь.

Последняя, наиболее важная проблема и главная ошибка начинающих разработчиков — это пренебрежение собственным здоровьем. Бесспорно, к работе нужно относиться ответственно и стараться делать поставленные задачи в срок, но не в ущерб себе. Факторов, влияющих на самый ценный ваш ресурс может быть много, начиная с вредного начальника и ежедневных мозговых штурмов и заканчивая решением срочных задач с горящими дедлайнами и работой допоздна. Однако несмотря ни на что, одно должно оставаться неизменным — ваша забота о собственном самочувствии. Помните, жизнь — это дар и ее качество гораздо важнее, чем сверхурочная работа, за которую в большинстве случаев вам даже «спасибо» никто не скажет.

***

Учитесь на чужих ошибках! Такой подход поможет вам сэкономить время, энергию и деньги. Удачи!

Материалы по теме

  • 🐛 Типовые ошибки в разработке UI: найти и обезвредить
  • 8 самых распространенных ошибок веб-разработчика
  • ⚠️ Как не нужно учить TypeScript: 5 распространенных ошибок
  • 👶 10 ошибок начинающего разработчика
  • ТОП-13 ошибок начинающего программиста
Самые распространенные ошибки программистов

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

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

Работа с необъявленными переменными

Суть ошибки проста. Вы начинаете пользоваться переменной, которая не была указана в блоке объявления переменных и не получила собственный тип. Как на это отреагирует программа, зависит от выбранного языка:

  1. Если синтаксис ЯП требует жесткого объявления и типизации переменных, интерпретатор «вылетит» из процесса компиляции с указанием ошибки. Это хороший случай, так как вы, скорей всего, быстро поймете, в чем дело, ведь необъявленный объект будет присутствовать в указанной строке ошибки.
  2. При использовании языков с менее жесткой структурой, переменная «объявится» автоматически с момента ее появления в коде. Казалось бы, это удобно. На самом деле, отладка в случае подобной ошибки значительно усложняется. В вашу переменную может быть записано любое значение, в том числе, не предусмотренного программой типа. В результате код будет «вылетать» в строке с попыткой выполнить какие-то вычисления или другой вид обработки с участием этой переменной, что усложнит поиск проблемы. В худшем случае, ошибка будет неявной, логической, т.е. программа будет работать, но выдаст неверный результат.

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

Инициализация переменных без начального значения

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

Приведем пример такого кода:

[code]int num1, num2;
int sum = num1 + num2;
cout << «Введите два числа для суммирования: «;
cin >> num1;
cin >> num2;
cout << «Сумма = » << sum;[/code]

В результате его исполнения вы можете ввести, например, числа 2 и 5, а в качестве результата получить 2384.

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

Ошибки начинающих разработчиков

Необъявленные функции

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

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

Переполнение типа

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

Приведем пример на C++:

[code]A = B + C;
char* G = new char[A];[/code]

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

Но если значения B и C будут большими, их сумма «не поместится» в объем памяти, который занимает A. В результате такого переполнения вместо ожидаемого положительного значения, в переменной A окажется отрицательное число. И на строке выделения памяти для G программа покажет ошибку.

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

Переполнение буфера

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

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

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

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

Ошибки в программировании

Ошибки в оценке границ массива

Бытует мнение, что такую ошибку можно допустить только в C или C++. На самом деле, обращение к несуществующему элементу массива возможно в Python, Java и многих других языках. Суть проблемы заключается в том, что программист по причине невнимательности или из-за ошибки в расчетах обращается к элементу массива с несуществующим номером.

Самый просто пример:

  1. Вы определили массив из 10 элементов.
  2. Нумерация в массиве начинается с нуля, т.е. существуют номера с 0 до 9.
  3. Вы забываете об этой особенности массива и обращаетесь к элементу с индексом 10.

При этом программа обращается к неиспользуемой области памяти, присваивает это случайное значение элементу с индексом 10. Результат обработки «мусорной» информации, само собой, непредсказуем.

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

«Забытые» ограничения ресурсов

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

  1. Программа игнорирует ячейки памяти, занятые полезной информацией. В результате в работе возникают критические ошибки.
  2. Приложение «съедает» практически всю оперативную память, «виснет» само и «подвешивает» систему.

Аккуратно относитесь к ограничителям массивов, проверяйте базы данные при слиянии, а при прямом обращении к ОЗУ обязательно указывайте граничные значения.

Обращение к освобожденной памяти

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

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

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

Баги в программах

Инъекции SQL и команд ОС

Эти два типа инъекций на самом деле являются хакерскими атаками, которые оканчиваются доступом злоумышленника либо к базам данных (SQL), либо к root-доступу на компьютере пользователя.

Причины SQL-инъекций – низкий уровень защиты сайта. Чаще всего, их проводят через отправку сообщений от пользователей (форма обратной связи, добавление записи на форум, обращение в чат и т.д.). Если «дыра» в безопасности не закрыта, злоумышленник отправляет через эти формы вредоносный код, сервер начинает его исполнять. И хакер получает доступ ко всем базам данных.

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

Рискованные алгоритмы

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

С первым случаем все понятно. Защита данных – не та область, где стоит полагаться только на свои, довольно скромные возможности. Пример второго случая – использование алгоритма хэштегирования SHA-1. Если вы воспользуетесь поиском, то очень быстро узнаете, что этот алгоритм уже устарел, в нем найдено множество уязвимостей, под которые написан не один вирус. А потому лучше пользоваться SHA-2 или SHA-3.

Как видите, в большинстве случаев причины багов и уязвимостей – обычная невнимательность к деталям и нежелание лишний раз протестировать код. Будьте внимательны, не забывайте о своих «любимых ошибках» и примерах из нашего рейтинга. При таком подходе ваш код будет работать, как задумано!

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

Программная ошибка: что это и почему возникает

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

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

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

Ошибки часто называют багами, но подразумевают под ними разное, например:

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

Исключения. Это не ошибки, а особые ситуации, которые нужно обработать.

Синтаксические ошибки. Это ошибка в программе, связанная с написанием кода. Пример: программист забыл поставить точку или неверно написал название оператора. Если не исправить, код программы не запустится, а останется просто текстом.

Классификация багов

У багов есть два атрибута — серьезности (Severity) и приоритета (Priority). Серьезность касается технической стороны, а приоритет — организационной.

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

По серьезности баги классифицируют так:

  • Blocker — блокирующий баг. Программа запускается, но спустя время баг останавливает ее выполнение. Чтобы снова пользоваться программой, блокирующую ошибку в коде устраняют.
  • Critical — критический баг. Нарушает функциональность программы. Появляется в разных частях кода, из-за этого основные функции не выполняются.
  • Major — существенный баг. Не нарушает, но затрудняет работу основного функционала программы либо не дает функциям выполняться так, как задумано.
  • Minor — незначительный баг. Слабо влияет на функционал программы, но может нарушать работу некоторых дополнительных функций.
  • Trivial — тривиальный баг. На работу программы не влияет, но ухудшает общее впечатление. Например, на экране появляются посторонние символы или всё рябит.

🚦 По приоритету. Атрибут показывает, как быстро баг необходимо исправить, пока он не нанес программе приличный ущерб. Бывает таким:

  • Top — наивысший. Такой баг — суперсерьезный, потому что может обвалить всю программу. Его устраняют в первую очередь.
  • High — высокий. Может затруднить работу программы или ее функций, устраняют как можно скорее.
  • Normal — обычный. Баг программу не ломает, просто где-то что-то будет работать не совсем верно. Устраняют в штатном порядке.
  • Low — низкий. Баг не влияет на программу. Исправляют, только если у команды есть на это время.

Типы ошибок в программе

🧨 Логические. Приводят к тому, что программа зависает, работает не так, как надо, или выдает неожиданные результаты — например, не записывает файл, а стирает.
Логические ошибки коварны: их трудно обнаружить. Программа выглядит так, будто в ней всё правильно, но при этом работает некорректно. Чтобы победить логические ошибки, специалист должен хорошо ориентироваться в коде программы.

🧨 Синтаксические. Это опечатки в названиях операторов, пропущенные запятые или кавычки. Безобидные ошибки: их обнаруживают и подсвечивают в коде компиляторы, а программисту остается исправить.

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

🧨 Компиляционные. Любая программа — это текст. Чтобы он заработал как программа, используют компилятор. Он преобразует программный код в машинный, но одновременно может вызывать ошибки.

Компиляционные баги появляются, если что-то не так с компилятором или в коде есть синтаксические ошибки. Компилятор будто ругается: «Не понимаю, что тут написано. Не знаю, как обработать».

🧨 Ошибки среды выполнения. Возникают, когда программа скомпилирована и уже выглядит как файл — жми и работай. Юзер запускает файл, а программа тормозит и виснет. Причина — нехватка ресурсов, например памяти или буфера.

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

🧨 Арифметические. Бывает, в коде есть числовые переменные и математические формулы. Если где-то проблема — не указаны константы или округление сработало не так, возникает баг. Надо лезть в код и проверять математику.

Инженер-тестировщик: новая работа через 9 месяцев

Получится, даже если у вас нет опыта в IT

Узнать больше

Что такое исключения в программах

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

Как это происходит:

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

Исключения бывают программными и аппаратными:

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

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

🔧 Следите за компилятором. Когда компилятор преобразует текст программы в машинный код, то подсвечивает в нём сомнительные участки, которые способны вызывать баги. Некоторые предупреждения не обозначают баг как таковой, а только говорят: «Тут что-то подозрительное». Всё подозрительное надо изучать и прорабатывать, чтобы не было проблемы в будущем.

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

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

Ключевое: что такое ошибки в программировании

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

Понравилась статья? Поделить с друзьями:

Не пропустите эти материалы по теме:

  • Яндекс еда ошибка привязки карты
  • Основными факторами способствующими возникновению ошибок являются
  • Основными причинами аварий и катастроф являются найдите ошибку
  • Основные угрозы доступности информации непреднамеренные ошибки пользователей
  • Основные типы ошибок машинного перевода

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии