Код
#статьи

Все ошибаются: 4 поучительные истории про ошибки в коде

Не ошибается тот, кто ничего не делает.

: solen feyissa / unsplash

Самая известная история про баг, который привёл к катастрофе, — о том, как американский комплекс противоракетной обороны Patriot умудрился пропустить удар по собственной базе в Саудовской Аравии. Он должен был отслеживать объекты, движущиеся по определённой траектории, но из-за ошибки в коде с каждой минутой работы комплекса увеличивалась погрешность расчёта траекторий. На одной из баз система простояла включённой 100 часов и совсем ослепла. В результате курс атакующей ракеты был рассчитан неправильно и погибло 28 человек. Патч появился только на следующий день.

От багов страдали и в NASA — потеряли в космосе спутник Mars Climate Orbiter. Причина банальная: подрядчик забыл преобразовать футы и фунты в единицы метрической системы. Пункт управления задавал двигателю силу в ньютонах, а прошивка думала, что это фунт-силы. В результате аппарат за 125 млн долларов оказался слишком близко к поверхности Марса и вышел из строя. Миссия провалилась. Вот вам и «Лё Биг Мак», if you know, what I mean.

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

История 1


Как усложнить код так, чтобы в нём никто не разобрался

Алексей Некрасов,
лидер направления Python в МТС, программный директор направления Python в Skillbox

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

  1. Инициализировать список объектов (10 шт).
  2. Сформировать из них случайную выборку.
  3. Получить все возможные комбинации.
  4. Для каждой перестановки посчитать определённые свойства.
  5. Отсортировать полученные свойства.
  6. Взять из них топ-10.
  7. Вывести результат в виде словаря, где ключ — это имя свойства, а перестановка — значение.

Код получился такой:

import itertools
import random
from typing import List

class FakeObject(object):
   def __init__(self):
       self.red = random.randint(0, 255)
       self.blue = random.randint(0, 255)
       self.green = random.randint(0, 255)
   def __str__(self):
       return f"r: {self.red}, b: {self.blue}, g: {self.green}"
def fake_feature(fakes: List[FakeObject]) -> int:
   red, blue, green = 0, 0, 0
   for index, i_fake in enumerate(fakes):
       red = (red + index * i_fake.red) % 255
       blue = (blue + index * i_fake.blue) % 255
       green = (green + index * i_fake.green) % 255
   return red + blue + green
print(
   dict(
       sorted(
           map(
               lambda x: (fake_feature(x), list(map(str, x))),
               itertools.permutations(
                   filter(
                       lambda x: random.random() > 0.5,
                       map(lambda x: FakeObject(), range(10))
                   )
               )
           ),
           key=lambda x: -x[0]
       )[0:10]
   )
)

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

Какие советы я мог бы дать:

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

История 2


Как из-за спешки убить два дня на поиски бага

Михаил Корнеев,
тимлид в BestDoctor и автор YouTube-канала «Хитрый питон»

Я делал прототип для одного проекта и в какой-то момент пропустил букву в очень неочевидном месте. Эта маленькая ошибка обернулась двумя днями дебаггинга — моё приложение стало периодически обрывать соединение. Пришлось копаться в «кишках» кода на Python и настройках nginx.

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

Как обезопасить себя от появления таких проблем:

  • Помнить, что ошибки будут всегда, — поэтому надо писать код так, чтобы потом было проще находить проблемные места.
  • Разобраться с тестированием и обязательно писать тесты.
  • Проверять, как работает код: например, запускается или нет. Большая часть ошибок — это опечатки. Если есть тесты, вы их увидите. Но если их нет, а вы всё-таки запустили проверку, то наверняка обнаружите другие проблемы. Я неоднократно видел, как начинающий разработчик закрывает задачу, не проверив код, — в итоге тот просто не работает.
  • Обязательно отдавать проект на код-ревью. Это полезная практика.
  • Если программируете на Python, стоит разобраться с типизацией. И помнить, что явное лучше неявного. Это позволит отловить часть ошибок ещё во время написания кода.
  • Делать резервные копии базы данных и разных версий кода на случай, если всё поломается. А это происходит не так уж редко. Если что-то пошло не так, но у вас есть резервная копия, ничего страшного не случится. План восстановления — обязательный пункт для любого проекта.

История 3


Как молчанием принести компании большие убытки

Алексей Фирсов,
руководитель Python-практики в компании S7 TechLab

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

Редактируя старый модуль, программист ошибся — использовал float. А его нельзя было использовать для подсчёта денег из-за плавающей точки. В результате модель стала начислять бонусы сверх нормы, компания понесла большие убытки. Виноваты оказались двое: разработчик, который никому не сказал о правках в старом коде, и руководитель, который недостаточно внимательно провёл ревью. Чтобы снизить риски появления таких багов, программистам неплохо бы писать, какую именно часть кода они меняют, чтобы QA могли протестировать все новые части программы.

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

  1. В базу попали данные, которые не должны туда попасть. Это происходит из-за плохо проведённого анализа: разработчик не понимает системы, того, как и какие именно данные в ней хранятся. Такие баги время от времени уходят в продакшн.
  2. Плохая коммуникация. Что-то изменилось на уровне одного отдела, а другой отдел этого не знает — и продолжает отрабатывать старые методы, которые утратили актуальность.

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

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

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

История 4


Как потерять инвестора, проигнорировав ревью

Александр Дученчук,
геймификатор и Product Manager

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

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

Мораль простая: отдаёшь код на ревью, сначала проверь его сам.

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

  1. Применять сложные решения, когда есть простые.
  2. Выдавать проект единым полотном, не дробя на логические блоки.
  3. Давать переменным непонятные имена.
  4. Не проводить внутреннее код-ревью.
  5. Использовать нестандартную парадигму.
  6. Не обмениваться информацией со смежными отделами.
  7. Не писать чёткие и понятные комментарии.
  8. Не сообщать об изменениях в коде QA и команде.
  9. Не делать резервных копий.

Хотите прокачать свои навыки в профессии, писать более чистый и красивый код и делать меньше ошибок? Пройдите курс «DevOps-инженер PRO». На этом курсе для джунов и мидлов вы освоите DevOps-практики и научитесь применять Docker и GitLab, чтобы оптимизировать и автоматизировать тестирование, доставку кода и запуск приложений на серверах.

Нейросети для работы и творчества!
Хотите разобраться, как их использовать? Смотрите конференцию: четыре топ-эксперта, кейсы и практика. Онлайн, бесплатно. Кликните для подробностей.
Смотреть программу
Понравилась статья?
Да

Пользуясь нашим сайтом, вы соглашаетесь с тем, что мы используем cookies 🍪

Ссылка скопирована