Фёдор Борщёв

Заметки с тегом «Программирование»

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

Код, который не работает

У меня недавно случилось озарение — я понял почему мне так знакомо чувство, которое возникает, когда я работаю с модулями nuxt.js. Вот взять модуль gtm, к примеру. Среди кучи других issues у него есть issue, которая так честно и называется — «модуль не работает». И что интересно — модуль и правда не работает.

Так вот, когда-то давно, лет 15 назад, у меня была Нива — это такие жигули-кроссовер. И меняли мне на ней как-то стартер. Там эта операция проводится довольно сложно — стартер расположен крайне неудобно, приходится даже разбирать выхлопную систему. Так вот, купил я стартер, поставили его мне, собрали всё, а машина не заводится: стартер не крутит. Полезли разбираться, и выяснили, что проблема в том, что я купил рязанский стартер (не уверен насчёт города, может белгородский, или тамбовский).

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

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

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

Как выучить любой язык программирования

Если вы только собираетесь войти в ИТ, и выбираете куда пойти учиться и какой курс купить, вот вам краткая инструкция:

  1. Покупаете хорошую книгу по выбранному языку программирования. «Хорошая» означает, что её кто-нибудь кому-нибудь советовал в группах/форумах больше 5 раз. К примеру по питону это Марк Лутц — «Изучаем питон».
  2. Изучаете её всю, от корки до корки, делая все домашние задания.
  3. Выбираете самый популярный фреймворк. «Самый популярный» означает «чаще всего упоминается на тематических сайтах». В питоне под веб это Django.
  4. Изучаете всю официальную документацию к фреймворку, от корки до корки. Проходите официальные обучалки. Гугите <framework> best practices, изучаете их.
  5. Делаете свой пет-проект. Блог, сайт, исследование аномалий в парковке под окном, что угодно — зависит от выбранного языка.
  6. Ищете команду с высокой инженерной культурой, которая возьмёт вас джуниором. Дальше учитесь у них.
  7. На сэкономленные на курсе деньги езжайте на недельку или две в Питер (если живёте в Питере — в Калининград).

И не благодарите.

Как переименовать приложение в Django

Как-то в бекенде школы меня перестало устраивать название приложения в Django. Всё просто — когда я начинал проект, я продавал только курсы, и приложение с моделями курсов называлось courses. Потом появились записи этих курсов (record) и наборы (bundles). Конечно мне захотелось переименовать приложение courses в products, чтобы уменьшить когнитивную нагрузку при чтении кода.

К сожалению, я не нашёл ни одного решения, кроме стрёмного django-rename-app, который предлагал мне выполнять management-команды в консольке на проде. Кроме того, что это ломает любые пайплайны CI\CD, там ещё и код был не очень понятный. Так что в итоге я решил сделать это сам. Оказалось сложно, поэтому я написал эту инструкцию.

Исходные условия:

  • CI\CD пайплайн, который запускает миграции при каждом деплое.
  • Данные терять нельзя.
  • Мы не стесняемся править старые миграции.

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

  • Подменяем имя приложения в истории миграций: ./manage.py makemigrations <some-other-app> -n RenameOldApp --empty. Коммит.
  • Деплоим миграцию на прод. После этого шага ничего нельзя деплоить до конца.
  • Делаем новый пулл-реквест.
  • Переименовываем папку с приложением: git mv courses products
  • Заменяем все упоминания старого приложения в миграциях. Коммит.
  • У всех моделей в новом приложения в Meta прописываем db_table с именем старого приложения. В моём случае, если модель называется bundle и лежала в приложении courses, нужно прописать db_table = 'courses_bundle'. Коммит.
  • В старых миграциях, которые используют операцию CreateTable, добавляем новую db_table в options. Коммит.
  • Меняем упоминания приложения в коде, можно автозаменой. Коммит.
  • Проверяем, ./manage.py makemigrations --check. Новых автоматических миграцией был не должно, ошибок тоже.
  • Деплоим на прод.

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

Органы чувств в инфраструктуре

Вот упал у вас прод, вы заходите по ssh и видите, что Load Average в 10 раз больше, чем количество ядер. Люди не могут воспользоваться сервисом, партнёры задают вопросы, а всё, что вы знаете, — это то, что нагрузка выше номинальной. Где именно, из-за чего всё это случилось — непонятно.

Вы залезаете в консоль, ищете по логам (медленно, сервер-то перегружен), запускаете ps и strace, совершаете другие шаманские действия. Чувствуете, что двигаетесь по тёмному лесу с закрытыми глазами, как бегуны из старого клипа Pendulum.

Четыре золотых сигнала — время ответа, количество запросов, рейт ошибок и запас нагрузки

Выход простой — сделайте себе нормальные органы чувств. Заведите  дашборды, которые покажут 4 золотых сигнала (время ответа, количество запросов, рейт ошибок и запас нагрузки) и ТОП-10 самых нагруженных эндпоинтов (отдельно по количетву запросов, отдельно — по затраченному машинному времени). 4 сигнала заменяют кучу производных метрик и позволяют однозначно сказать, лежит ли прод:

Чётко видна авария с 16:00 до 18:00

ТОП-10 запросов декомпозирует проблему из «упал прод» в «у нас тормозит эндпоинт деталей товара». Теперь легко локализовать источник — если увеличилось количество запросов, то ищем, того, кто их делает. Если запросов столько же, а нагрузка выросла — ищем, что поменялось в коде, или какие проблемы у нас могут быть на внешних ресурсах: базе данных, кеше или партнёрских API. Во втором случае здорово помогает разобрать поток выполнения — у каждого запроса, который обработал сервер, мы записываем трейс, в котором видно, сколько времени приложение потратило на каждую активность, а затем визуализируем это примерно вот так:

Flame graph показывает, что PostgreSQL отвечал чуть меньше, чем 30мс: отличный результат

Такая работа с цифрами и статистикой называется APM — App Performance Management. Мой любимый сервис для APM — Datadog. Я выбрал его когда-то за хороший дизайн: Datadog не просто даёт инструменты, которые собирают цифры, но и подсвечивает и объясняет самые важные из них.

Дефолтный борд Датадога для PostgreSQL

Если у вас есть хоть какой-то продакшн, который до сих пор не обложен цифрами — прикрутите к нему APM. Не хотите Датадог —  есть куча конкурентов: New Relic, AppDynamics, AppOptics, Elastic APM.