Колеоптерология в IT, или Как избавиться от багов в своем коде

Kate

Administrator
Команда форума
Всем привет, я Максим Усатенко, .NET разработчик в компании TELEMART.UA, и за несколько лет работы я стал автором десятков багов, которые в большей или меньшей степени негативно сказались на развитии компании.

Не хочу давать советы из серии: «Покройте 150% кода unit тестами, и будет вам счастье». Unit тесты это вообще тема для отдельной книги. Разные компании имеют о них абсолютно противоречивые мнения, начиная от «Без них никуда» и заканчивая «Зачем тратить время на такую ерунду». Предпочитаю оставить этот вопрос открытым, с учетом бюджета, конкретного проекта или определенных бизнес-требований. Я же сделаю упор на человеческий фактор и определенные взгляды на процесс разработки это именно то, что может пересмотреть каждый разработчик, не увеличивая бюджет проекта и не привязываясь к конкретным требованиям заказчика.

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

Программная ошибка​

Для начала давайте разберемся, что такое баг, или, как его правильно называть, «программная ошибка».

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

Обращаю ваше внимание на то, что большинство ошибок возникает именно по вине разработчиков, а значит, в первую очередь это наша зона ответственности. К сожалению, баги от нас никогда не денутся, как бы мы ни старались. Писать код без багов невозможно, и человеческий фактор никто не отменял. В наших силах только уменьшить их количество и снизить критичность. Важно понимать, что к ошибкам надо относиться серьезно, а не как к чему-то обыденному: «Кинь в unsprint tasks, и через полгодика исправим».

Достаточно вспомнить историю с ракетоносителем Ариан-5 в 1996, когда всего одна строчка кода уничтожила 1 ракету и 4 спутника по исследованию магнитосферы земли, с суммарным убытком в полмиллиарда долларов.

Не торопись​

image_62056800651632208153949.jpg


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

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

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

Вот фразы, которые являются всадниками апокалипсиса в разработке ПО:

  • «Сейчас быстренько пофикшу, и сразу на прод зальем».
  • «До конца рабочего дня осталось совсем немного, правка маленькая, не будем терять времени и сразу выкатим».
  • «Нам надо успеть доделать 4 задачи и 7 багов до завтрашнего релиза, без регрессии обойдемся, вроде ничего сломаться не должно».
Этот список можно продолжать до бесконечности, но главную идею вы поняли: спешка приводит только к багам, и ничему больше. Лучше вообще ничего не релизить, чем выкатить фичу, реализованную в максимальной суете и без должного тестирования. Как итог, вы получите вместо работающего функционала баги, которые еще непонятно во что могут вылиться для компании.

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

Null Reference​

image_56597701131632208153848.jpg


Около 30% всех багов — это Null Reference или Null Pointer (в разных языках разное название, но суть одна). Кто-то где-то когда-то не предусмотрел значение NULL для поля, и теперь это вылилось в баг, возможно, очень серьезный и критичный. Вывод? Старайся всегда предусмотреть значение NULL настолько скрупулезно, насколько это возможно. Даже если сейчас кажется, что значение NULL невозможно, через год после тебя на проект придет другой разработчик, изменятся бизнес-правила, и уже никто не вспомнит про место в коде, где ты не предусмотрел NULL.

Также хочу отметить важность валидации данных перед вставкой в БД. Любая хорошо спроектированная реляционная база должна иметь определенный уровень нормализации (например, стандартный третий). Все эти уровни нормализации подразумевают наличие FOREIGN KEY, UNIQUE KEY, PRIMARY KEY и других элементов, поддерживающих целостность данных. Вставка несуществующего внешнего ключа приведет к ошибке, а это именно то, чего мы хотим избежать. Рекомендую перед любой CRUD операцией стараться проверять целостность данных.

Проверяй выполненную задачу сам​

image_82094452921632208153827.jpg


Хочешь мало багов? Проверяй код, чтоб их не было! Звучит, конечно, очень логично и понятно, но это факт, и это работает. Особый плюс этого подхода в том, что тебе со своей стороны разработки могут быть виднее некоторые нюансы. Проверять нужно хотя бы минимальный положительный тест-кейс. Раньше я, как и многие, думал, что для этого и нужны QA, пусть отрабатывают свой хлеб. Теперь же мое мнение кардинально изменилось. Во-первых, это уменьшает количество багов, которые прилетают на QA, а во-вторых, это правило хорошего тона: отдать на тестирование то, что, как минимум, работает.

Минимально работающий функционал для меня — это:

UI — все открывается, нажимается, визуально все соответствует описанию задачи.

Новый API метод — дергается через Postman, отрабатывает основной положительный тест-кейс.

Новый SQL скрипт — скрипт отработал на тестовой базе корректно.

Также не стоит забывать просматривать все изменения по файлам перед каждым коммитом. Возможно, свежим взглядом вы увидите ошибку в своем коде либо же случайное исправление соседнего метода класса. Хочу привести пример: я случайно поставил точку с запятой в код, который не должен был менять. Тогда я не проверял изменения и не обратил внимания на лишнюю правку перед коммитом. Этот баг особо примечателен тем, что не ломает сервис, что можно легко отловить на этапе CI/CD, а попросту меняет поведение метода:

image0_uB8uJ8V.jpeg


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

Не делай задачи одновременно​

image_2507056761632208155180.png


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

Раньше мне казалось: делать несколько задач одновременно это быстро и практично. Экономлю время и при этом быстрее закрываю большой объем поставленных задач. На одном мониторе фикшу баг, на втором, в этом же сервисе, в другой ветке реализовываю фичу. Все классно до тех пор, пока тебя не отвлекут на пару минут, и в итоге в ветке бага я пилю фичу, а в ветке фичи исправляю баг. Как итог: потраченное время и нервы. Из плюсов разве что могу отметить новые скилы работы с гитом, ведь когда путаешь ветки, волей-неволей осваиваешь cherry pick и commit reverse.

Не доверяй сторонним API​

image_68763446141632208153883.jpg


API бывают разные: хорошие, плохие, интуитивные и не очень, документированные и абсолютно не соответствующие заявленному. Всех их объединяет одно: в любой момент времени они могут начать работать не так, как ожидаешь, и это надо предусмотреть. Простой интеграции с сервисом и успешных Unit-тестов недостаточно, чтобы считать его надежным. Я вообще полагаю, что рассчитывать на сторонний сервис рискованно, даже на сервис мирового масштаба. Достаточно вспомнить Google Indentity Service, который лег на полчаса в этом году, повалив за собой половину мировых компаний, завязанных на аутентификации гугла.

В моей практике было очень много багов, связанных с поломкой стороннего сервиса. Я интегрировался с «Новой Почтой», Portmone, LiqPay, MeestExpress, Monobank и другими украинскими компаниями, и все они рано или поздно выходили из строя на небольшой промежуток времени. Особо хочу выделить один сервис (не хочу тыкать пальцем), который отправлял в ответе вместо «OK» — «0K» (во втором случае это ноль), и это еще нужно спарсить и обработать. Рекомендую относиться к внешнему сервису как врач к пациенту в клинике для душевнобольных: общаться с уважением, но всегда входить к нему в палату с двумя санитарами и смирительной рубашкой наготове.

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

Помогай тестировщику​

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

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

Отдыхай во время работы​

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

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

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

Итоги​

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

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

 
Сверху