sqlc: как избавиться от ORM с помощью кодогенерации

Я как-то раз писал про то, почему я не люблю SQL (именно язык как концепцию и философию, а не конкретные реализации), зато люблю про это говорить — уже третий пост, где я это упоминаю. Тем не менее я сейчас пишу код для следующего ролика для ютуб-канала, и я сознательно решил там использовать PostgreSQL. Причина простая — это видос про написание проекта. Такие видосы по моей гипотезе полезны прежде всего джунам, чтобы те могли приложить к резюме свой гитхаб, где лежит реализованный проект. И в данном случае джун, умеющий хоть как-то в PostgreSQL будет иметь преимущество, потому что постгря сейчас практически везде. ℹ️ Этот пост взят из моего телеграм-канала. Так вот, я вчера мозг сломал в трёх местах, пока пытался написать запрос с элементарной фильтрацией по колонкам с таймстемпами. Не то что бы это что-то сложное, но способов выразить время и фильтры по времени в PostgreSQL миллион (как и положено в SQL). А к ним добавляется ещё неясность относительно того, как эти даты и интервалы правильно передавать конкретному драйверу — авторы того драйвера, что у меня решили, что не будут делать авто маппинг time.Duration в интервалы. Почему? Потому что не будут. Вообще я думал про то, что в СУБД нужно выкинуть/сделать SQL опциональным и предоставлять бинарный протокол. Почему SQL нужен аналитикам — понятно. Зачем промежуточный язык для продакшен-кода — мне непонятно. Если бы у СУБД существовал бинарный протокол, то язык запросов мог бы быть выражен инструментами языка, на котором ведётся разработка. Да, я знаю про ORM и ActiveRecord. Но они всё равно превращают код на одном языке в код на SQL. Тут уж я скорее солидарен с го-коммьюнити — если и писать SQL-запросы, то на самом SQL. Есть тула, на которую я недавно наткнулся — slqc....

March 15, 2023 · 2 min

Конкурентность в Go простым языком

Этот пост является текстовой версией моего видео про конкурентное программирование: Go — это язык, который не просто обладает хорошими инструментами, позволяющими использовать всю мощь многоядерных процессоров с помощью параллельного программирования. Go — это язык, который проектировался в первую очередь для конкурентных и параллельных вычислений. Поэтому я не могу пройти мимо данной темы. В материале мы разберёмся как с базовыми инструментами для «параллельных» вычислений (почему я взял это слово в скобки, вы узнаете чуть позже), так и с примитивами синхронизации горутин. Хоть далеко не всех из них являются частью языка, они присутствуют в стандартной библиотеке. ⚠️ Это обзорный материал, поэтому я не буду погружаться сильно глубоко в каждую из рассматриваемых тем. Для этого в будущем появятся отдельные подробные материалы. Также материал может содержать неточности при использовании тех или иных терминов для упрощения погружения в описываемые концепции. Что такое параллельные вычисления Википедия гласит: В информатике параллели́зм — это свойство систем, при котором несколько вычислений выполняются одновременно, и при этом, возможно, взаимодействуют друг с другом. Примечание. В русскоязычной литературе нередко путаются термины «параллелизм» и «конкурентность». Оба термина означают одновременность процессов, но первый — на физическом уровне (параллельное исполнение нескольких процессов, нацеленное только на повышение скорости исполнения за счёт использования соответствующей аппаратной поддержки), а второй — на логическом (парадигма проектирования систем, идентифицирующая процессы как независимые, что в том числе позволяет их исполнять физически параллельно, но в первую очередь нацелено на упрощение написания многопоточных программ и повышение их устойчивости). Теперь давайте разберёмся с этим последовательно. Представьте себе очень простой процессор, у которого всего одно ядро. На этом ядре в один момент времени может исполняться только одна программа или процесс. Чтобы компьютер мог исполнять несколько задач одновременно, процессор можно сделать многоядерным. Тогда он сможет исполнять одновременно не более $N$ программ, где $N$ — это количество ядер....

March 9, 2023 · 20 min

golangci-lint и внедрение его в большой проект

Хороший разработчик обычно стремится написать код, который не просто работает, но работает стабильно. Код, который легко читать и сопровождать. Достичь такого высокого качества кода кроме тестов также помогают единый стиль кода, хорошая документация, а также простота и изящность, как на уровне всей системы, так и на уровне отдельных модулей и функций. Считается, что соответствие кода принятым в команде стандартам качества проверяет как сам разработчик, так и его коллеги на код-ревью. Проблема только в том, что все мы люди, и всегда можем что-то упустить. А иногда (на самом деле очень часто), разработчики закрывают глаза на те моменты, которые кажутся им незначительными. В связи с этим придумали статический анализ кода. Если говорить просто, это анализ исходного кода без непосредственного запуска этого кода. Когда вы пробегаете глазами по коду, чтобы убедиться, что с ним всё ок — это тоже статический анализ 🙂. Противоположный подход — динамический анализ — предполагает, что код будет запускаться и анализироваться во время исполнения. Примером динамического анализа кода являются тесты, но о них поговорим в другой раз. Обычно статический анализ строится на основе набора правил, например: Длина строки не должна превышать 120 символов; Нельзя вызывать defer внутри for; и т.д. Программы, которые выполняют статический анализ, называют линтерами. Для Go написано уже очень много линтеров, каждый из которых специализируется на своём наборе проверок. Например, bodyclose проверяет, что разработчик не забыл закрыть тело ответа при отправке HTTP-запросов: internal/httpclient/httpclient.go:13:13: response body must be closed А wsl следит за тем, чтобы в коде были правильно расставлены пустые строки для повышения читаемости. Линтеров для Go настолько много, что управлять всеми ими самостоятельно стало в один момент сложнее, чем проверять программы вручную. Поэтому следующим этапом стало появление металинтеров — программ, которые позволяют настроить и запустить большое количество линтеров из единого места....

July 5, 2022 · 11 min

Интерфейсы в Go

В этом посте речь пойдет об интерфейсах — очень важной и интересной фиче языка Go. Абстрактные типы данных Для того чтобы лучше понять интерфейсы, полезно разобраться с понятием абстрактных типов данных. Упрощённо абстрактные типы данных можно определить как типы, у которых не может существовать собственных значений. Справедливый вопрос: а зачем такие типы тогда нужны? АТД в первую очередь определяет требования к «обычному» типу данных. Если тип соответствует требованиям, то говорят, что он является реализацией для АТД, а значит, может быть использован в переменных, полях и сигнатурах, где в качестве типа был указан реализуемый АТД. Например, над любыми числами можно совершать любые, за некоторыми исключениями, арифметические операции. Следовательно, можно объявить абстрактный тип Число, разными реализациями которого будут натуральные, целые, рациональные, вещественные и комплексные числа. Если не важны специфические свойства какого-либо множества чисел, то гораздо удобнее оперировать более абстрактным понятием числа. Интерфейсы Интерфейс является частным случаем, а в языке Go — по сути единственным представителем семейства АТД. Интерфейсы используются, чтобы описать, каким набором методов должна обладать его реализация. Это полезно, когда нас интересует только поведение некоторой сущности, но совсем не интересуют детали реализации. Это в свою очередь помогает снизить связность компонентов приложения и упрощает сопровождение кода. Давайте взглянем на стандартную библиотеку языка. Она содержит много интерфейсов, которые похожи на то, что авторы языка называют хорошим и идиоматичным интерфейсом. Одни из самых используемых — знаменитая тройка Writer, Reader, Closer, интерфейсы из пакета io: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 type Writer interface { Write(p []byte) (n int, err error) } // ... type Reader interface { Read(p []byte) (n int, err error) } // ... type Closer interface { Close() error } В данных трёх типах можно увидеть идиоматику интерфейсов в Go:...

January 15, 2022 · 7 min

Завершаем правильно программы на Go

Если завершение вашей программы или, если хотите, сервиса вы отдаёте полностью на откуп операционной системе, скорее всего это не самый верный путь. И в этом материале мы разберёмся, почему. Для чего нужен Graceful Shutdown Вы наверняка слышали такое словосочетание как stateless service. Это сервис, который не хранит никакого состояния внутри себя, а делегирует эту задачу внешнему хранилищу. Под состоянием чаще всего имеют ввиду данные. Стремление к тому, чтобы сервис был “stateless”, т.е. свободным от состояния, является хорошей архитектурной практикой, поскольку облегчает жизнь, когда дело доходит до масштабирования. Иногда это «правило» игнорируется для повышения производительности. Например, большинство современных СУБД хранит часть актуальных данных не на диске, а прямо в памяти процесса. Периодически и, самое главное, перед завершением процесса СУБД актуализирует эти данные на диске. Если всё, что делает сервис это принимает a и b, и возвращает a+b, то никаких проблем нет, поскольку у сервиса в данном случае нет состояния. Скорее всего большая часть сервисов, которые вы разрабатываете или будете разрабатывать устроены несколько сложнее. Даже если они свободны от состояния в классическом понимании, но обращаются к СУБД, диску или другим сервисам, они всё ещё обладают состоянием, просто оно выражено не данными, а ресурсами. Почему ресурсы, которые мы запрашиваем у операционной системы необходимо освобождать — тема отдельного разговора. Сейчас давайте примем как аксиому, что за очисткой ресурсов важно следить. Системные ресурсы можно разделить на два вида: С коротким жизненным циклом — например, чтение конфигурационного файла или соединение для REST-запроса. Обычно освобождаются сразу после использования. С неопределённым жизненным циклом — вебсокеты, стриминг, консьюминг из очереди, кэш на диске и т.д. Информация, которую они предоставляют, требуют быстрого доступа, поэтому такие ресурсы занимаются редко, но надолго. Второй тип ресурсов я назвал так именно потому, что скорее всего мы не знаем, когда нам придётся их очистить....

December 31, 2021 · 8 min

Разбираемся с контекстами в Go

Этот пост является конспектом моего видео про контексты: Я заметил, что тема контекстов в языке Go у многих почему-то вызывает сложности с пониманием. Возможно, это связано с тем, что контекст — это очень абстрактная сущность и не встречается в других языках программирования в таком виде, по крайней мере в тех языках, что довелось использовать мне. В общем, я решил написать материал по данной теме с примерами и лучшими практиками. Вам будет легче понять то, о чем я буду рассказывать, если вы уже знакомы с основами языка Go, в частности знаете, что такое горутины и каналы. Что такое контекст? Если мы взглянем на документацию к пакету context, то первый абзац будет таким: Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes. Пакет context определяет тип Context, который позволяет управлять дедлайнами, сигналами отмены и другими значениями области действия запросов между границами API и процессами. Что это, чёрт побери, значит? А значит это примерно следующее: Контекст — это объект, который предназначен в первую очередь для того, чтобы иметь возможность отменить извне выполнение потенциально долгой операции. Кроме того, с помощью контекста можно хранить и передавать информацию между функциями и методами внутри вашей программы. Отменять долгие операции с помощью контекста можно несколькими способами: По явному сигналу отмены (context.WithCancel) По истечению промежутка времени (context.WithTimeout) По наступлению временной отметки или дедлайна (context.WithDeadline) Пример. Столик в ресторане Вы хотите забронировать столик в ресторане. Для этого вы набираете ресторан, и ждете, пока на той стороне возьмут трубку. Далее происходит одно из двух: Сотрудник ресторана берёт трубку. В таком случае вы начинаете диалог — всё хорошо; На той стороне никто не берёт трубку в течение минуты, двух, трёх…...

December 5, 2021 · 9 min

Как начать печатать быстро

Не так давно я задумался о том, что было бы неплохо поднять скорость набора текста. Это не является для меня чем-то необходимым, поскольку IDE придёт на помощь, но меня страшно бесит, что я делаю много опечаток, и длинные сообщения в мессенджерах или по почте приходится набирать неприлично долго. Голосовой ввод в таком случае тоже не спасает — всё-таки его результат требует коррекций. Поэтому летом 2020 года я начал погружаться в тему скоропечатания, и данный пост — концентрация опыта и знаний, которые у меня накопились к моменту написания. 10-пальцевый набор вслепую Главный принцип и идея скоропечатания — в использовании десяти (или меньше, если пальцев меньше) пальцев руки при наборе текста. При этом вы не смотрите на клавиатуру в поисках нужной клавиши — взгляд направлен на экран. Можно игнорировать данный метод, объясняя это тем, что, мол, «я и используя 7 пальцев быстро печатаю». Я именно так и считал, и первое время использование всех пальцев даже снизило мою среднюю скорость. Однако на длинной дистанции это даёт заметный профит, в чём я убедился лично. Основные две метрики, использумые при оценке: Words Per Minute — количество слов в минуту; Точность — доля символов, напечатанных правильно с первого раза. Это очевидно, и наверняка вы уже об этом слышали. Пусть это будет для затравки. Как тренироваться Далее перечислены правила, которые я соблюдаю сам, потому что для меня они работают. Часть из них я вывел сам, другую часть прочитал или увидел в других источниках. Фокусируйтесь на точности, а не на скорости Это звучит контринтуитивно, но в первую очередь эффективнее тренировать точность вместо скорости. Если разобраться, то на деле всё просто. Когда вы делаете опечатку, время уходит не только на исправление ошибки, но и на то, чтобы заметить опечатку. Представим, что вы нажимаете 5 клавиш в секунду, но делаете одну ошибку....

December 21, 2020 · 13 min

Кумиров больше не будет

Можно легко назвать почти всех великих композиторов последних нескольких веков. У каждого десятилетия прошлого века были свои кумиры. И мне кажется, кумиры в привычном смысле всё. Вряд ли с каждым поколением, живущем в наш век, будут прочно ассоциироваться какие-то определенные музыканты. Дело в том, что производство музыки стало настолько доступным во всех смыслах этого слова, что хороших композиторов, саунд-продюсеров, битмейкеров и музыкантов стало слишком много. Да и сама музыка сейчас куда доступнее, а точнее практически бесплатна. Серьёзно, я каждый день обнаруживаю в Спотифае очень талантливых чуваков, и я не могу сказать, что кто-то лучше, а кто-то хуже — они все одинаково круты. Безусловно, у меня всё ещё есть список моих самых любимых авторов и песен, но и он, оказывается, резиновый. На самом деле эта тенденция наблюдается не только в музыке — кроме средств звукоизвлечения доступнее становятся кино, разработка игр, актёрское мастерство, фотография, изобразительное искусство, образование, знания, компьютеры, ремонт, вождение, …, ну вы поняли. Что с этим делать? А вообще, проблема ли это? Чёрт его знает. Лично я просто наслаждаюсь изобилием.

October 30, 2020 · 1 min

Плёночный Петербург (и немного Выборг)

October 29, 2020 · 0 min

Получение опыта из других областей

Я очень долго не мог поладить с Unity. В основном мои проблемы сводились к тому, что связность компонентов любой игры, которую я создавал с использованием этого движка, очень быстро росла. Я знал об этом, но не знал, что с этим делать. В итоге каждое новое изменение в коде игры давалось всё сложнее. Рост сложности кодовой базы выглядел примерно так. На самом деле всё не так плохо, но суть, я надеюсь, ясна. Что интересно, ни с каким из движков, с которыми я работал до этого, таких проблем не возникало. Поэтому я начал полагать, что это с Unity что-то не так, и мне просто нужно сменить движок. Unreal Engine я отмёл сразу, т.к. вся его мощь была для меня избыточной. Я посмотрел в сторону Godot Engine. Где-то на пятом часу его изучения познакомился с местной концепцией сигналов. Немного порефлексировав после, я понял, что это очень похоже на то, что я встречал в других движках и фреймворках и именно то, чего мне не хватало в Unity. После того как я стал использовать паттерны и концепции реактивного программирования в Unity, разработка игр стала идти как никогда плавно и без лишней нервотрёпки. К чему я это всё рассказываю? Даже если вы считаете себя специалистом очень узкого профиля, но планируете повышать свою экспертизу и свои навыки, важно подглядывать в смежные технологии и области и знакомиться с идеями оттуда. Это позволит смотреть по-другому на привычные вещи. Конечно, это не значит, что нужно быть абсолютным фуллстэком (читай мастером на все руки, но руки из одного места). Всё должно быть в меру, и лучше, если приобретаемые навыки положительно влияют на основные компетенции.

October 28, 2020 · 2 min