junsenior | Unsorted

Telegram-канал junsenior - dev notes

1385

Subscribe to a channel

dev notes

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

И я давно думал, что было бы очень круто, если бы была возможность объединить простоту и удобство генерации кода через LLM, доступное в Cursor и все фишки neovim, большинство из которых невозможно настроить в Cursor через сторонние плагины.

И как оказалось, такие проекты есть!
Я попробовал два самых популярных:
- https://github.com/yetone/avante.nvim
- https://github.com/olimorris/codecompanion.nvim

avante - самый популярный и имеет наибольшое количество звезд и контрибьюторов, но лично у меня он очень часто и много стрелял багами. Мягко говоря, ему далеко до генерации кода как в Cursor, хотя проект очень интересный и очень надеюсь, что его продолжат развивать.
А вот codecompanion мне понравился. В интеграции с моделями от anthropic качество кода, которое он генерирует - довольно неплохое, и количество багов в процессе работы сильно меньше, чем у avante.

Планирую в ближайшее время взять какую-нибудь более-менее реальную задачу и какой-нибудь проект с небольшим количеством контекста, и сравнить эти плагины на разных моделях (а заодно и chat-плагин для github copilot - https://github.com/CopilotC-Nvim/CopilotChat.nvim, он менее популярен и тоже имеет не мало багов судя по отзывам, но выглядит многообещающе, поэтому его тоже стоит попробовать).

Читать полностью…

dev notes

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

Итак, что было сделано за первые 3 месяца этого года:
1. Прошел курс по system design от balun-cources.
Курс, в целом, неплохой. Очень много теории и практика под конец. Теория полезная, частично пересекающаяся с "Высоконагруженными приложениями"" Клеппмана, но с объяснением на кейсах из реальной жизни. В целом - курс стоит своих денег, хотя лично у меня изначальные ожидания были несколько выше. В любом случае, база получена, осталось попрактиковаться в изученных паттернах как на собесах, так и в реальной работе.

2. Начал проходить курс по алгосам от Влада Тена. С моим темпом прохождения и количеством свободного времени - думаю эта история затянется надолго, но я и не тороплюсь. Зачем вообще нужен курс по алгосам? Лично мне - для дисциплины. Решать задачки проходя курс мне сильно проще, в ином случае я просто забью на середине. А тут за меня уже кто-то продумал структуру задач, объяснил теорию и оформил все это в удобном виде.

3. Английский. Вот этого было действительно много. Зафиналил спринт на 30 занятий на lingoda, и параллельно 3 раза в неделю занимаюсь с нейтивом. Еще для себя обнаружил, что уровень сильно растет вверх от просмотра контента на английском, поэтому каждый день теперь смотрю какие-нибудь сериалы или видосы. В целом - становится лучше, надеюсь в таком же режиме довести его до конца года до того, чтобы проходить/проводить собесы на английском.

Еще из интересного - сходил тут, недавно, на собес. Я почти 2 года работаю в одной компании, пока не планирую ее менять и все эти два года не выходил на рынок. А тут меня позвали и я подумал - почему бы и да, и решил попробовать. Компания - большая, занимается мобильными играми и занимается этим лучше всех в Европе. Но с собесом у ребят, как по мне, большие проблемы.
Мне дали час времени, за который сначала попросили решить задачу, затем провести код ревью строк на 250, а затем написать кусок кода с довольно сложной конкурентной логикой. Задачи - в целом ок, но вот лимит по времени в час - это, конечно, сильно 🙂 За час я успел сделать все 3 задачи, но все 3 - не полностью, особенно код-ревью.
Для себя тут сделал вывод, что буду чаще ходить на собесы, чтобы понимать как сейчас дела на рынке и что там спрашивают.

Читать полностью…

dev notes

Новости из мира Go: состоялся релиз Go 1.24 ❤️

Из интересного:
* Полная поддержка алиасов для дженериков, теперь можно делать такие вещи:


type set[P comparable] = map[P]bool

func NewSet[T comparable]() set[T] {
return make(set[T])
}

func main() {
s := NewSet[string]()
s["hello"] = true
fmt.Println(s["hello"]) // true
}


* map в Go теперь работает на основе Swiss Tables (почитать о том что это можно, например, тут или тут), что дает прирост производительности, в некоторых случаях, до 30-40%;
* Улучшена поддержка WebAssembly: добавилась дирректива go:wasmexport для простого экспорта функций в webassembly и добавилась поддержка сборки программы как WASI reactor/library. Уже прилетела свежая статья от go-тимы, где эти улучшения описаны более подробно - https://go.dev/blog/wasmexport

Анонс релиза: https://go.dev/blog/go1.24
Release notes: https://go.dev/doc/go1.24

Читать полностью…

dev notes

В компании, где я работаю, последний год менеджмент активно продвигает нейросети: сотрудникам сделали подписки на большинство известных инструментов и моделей, и их использование, если ты понимаешь, что это делает тебя продуктивнее - только поощряется.
Я сам буквально каждый день общаюсь с ChatGPT, около двух лет уже юзаю Copilot и последнее время оценил, насколько круто работать в Cursor.
И вот сегодня я узнал, что сейчас в нескольких больших компаниях, где работают мои знакомые, уже начали формализировать и собирать метрики, которые покажут, насколько правильное использование этих инструментов позволит ускорить разработку и процесс доставки задачи на прод.

Я не верю, что условный chatgpt в интервале 4-5 лет сможет заменить разработчика, скорее он станет компаньоном, работать с которым в большинстве компаний станет не то, что желательно, а обязательно. В вакансиях к списку требований добавится еще пункт о том, что требуется опыт работы с ИИ. И помимо того, что изменится рабочий процесс, изменится так же и процесс найма. Скорее всего, если бы мне сейчас сказали "прособеседуй разработчика, но учти, что он все свое время будет работать в Cursor", я бы не стал спрашивать у него алгоритмы или код-ревью. Я бы попросил его открыть Cursor и показать мне, как он за час решит несколько заранее подготовленных мной задач и какие промпты он будет писать.

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

В общем, будем посмотреть. А я пока пошел пробовать новую модель от openai, которую они релизнули вчера - https://techcrunch.com/2025/01/31/openai-launches-o3-mini-its-latest-reasoning-model/

Читать полностью…

dev notes

Как я писал выше - я хочу подготовиться к собесам в FAANG и компаниям около FAANG.

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

Еще в конце прошлого года я купил курс по алгосам, и вот с начала этого начал его проходить. Зачем вообще нужен курс? Вроде бы открыл литкод и поехал, а если что-то не получается - на youtube есть решения буквально всех возможных задач.
Но, в моем случае курс мне нужен не для понимания задач, а для мотивации не забросить это дело. Создается впечатление, будто бы ты не один этим занимаешься, и процесс получается веселей. Плюс, автор очень круто объясняет, набрасываю интересную теорию там, где она нужна.

И вот спустя две недели какие я сделал выводы:
* Как только какая-либо тема начинает получаться, процесс решения задач по этой теме становится интересным. Начал прям кайфовать от того, когда получается написать какой-нибудь алгоритм быстро и завести его с первого раза.
* 🐍Python - очень хорош для алгосов. Когда у тебя есть возможность вызвать str.isalnum(), а не писать свою проверку, что символ в диапазоне нужных значений, это очень упрощает процесс. Плюс, на собесах за это в 99% не спрашивают.
* Подобные задачки развивают умение искать в коде ошибки. Например, у меня в рабочем проекте, из-за его специфики, есть очень много мест где что-то вычисляется, перебирается или проверяется. И частые ошибки, которые пропускают на ревью, это не ошибки бизнес-логики, а ошибки в рассчетах: взяли срез не по нужным элементам или, наприме, забыли добавить что-то к переменной перед тем, как писать ее в метрику. И последнее время замечаю, что становится проще жонглировать такой логикой в голове.

В целом цель на год - отрешать ~150-200 самых частых задач, которые в теории могут быть на собесе.

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

Читать полностью…

dev notes

Все начали писать про итоги и планы, и я тоже решил написать.

Изначально этот канал назывался "1000 дней программирования", и я вел его с целью показать мой путь от джуна до синьора, отсюда и id канала - @junsenior. 1000 дней прошли пару лет назад и лычка синьора у меня уже тоже давно есть. В целом, за 5 лет с момента первого поста я успел поменять PHP на Go, поработать в российском бигтехе, а потом перейти на валютную удаленку в большую и быстро развивающуюся компанию, в которой работаю уже полтора года.
В 2024-ом году я не сменил язык и работу (что для меня удивительно), но решил много интересных и сложных задач. Например, за год я развернул несколько сервисов с нуля, много работал с параллельным программированием, много строил архитектуру, рефачил большие объемы кода и собеседовал людей.

Но главный результат этого года для меня - у меня появилось понимание того, к чему я хочу стремиться дальше. А дальше я хочу попасть в бигтех, но уже американский. В идеале - MAANG, под которым я понимаю пару десятков самых больших американских тех-компаний. Дедлайн, который я сам себе выставил, - 3 года (если в ближайшие пару лет нас не заменят нейросети, конечно). Получается, новый челлендж в 1000 дней 🙂

Горизонт работы, для того, чтобы хотя бы собеседование в такую компанию стало возможным, прочерчивается через 3 основных вещи, на которые я буду делать упор.
1 - английский. Очевидно, собеседование будет на английском, и хотя я активно его учу последние пару лет, в ближайшее время я хочу повысить как эффективность этого обучения, так и интенсивность, о чем тоже планирую тут писать.
2 - алгоритмы. У меня уже было несколько подходов к алгосам, но каждый раз не хватало мотивации и усидчивости, и обычно я забрасывал это дело через месяц-два. Сейчас у меня есть и мотивация, и понимание как правильно решать алгосы так, чтобы запоминать решение, и купленный курс по алгосам от @tenfoundation :), который я планирую пройти в ближайшие полгода.
3 - system design. Тут в планах начать с курса от balun.courses, который я тоже купил и теперь жду старта 11-ого февраля. В общем и целом system design мне всегда очень нравился, так что тут не ожидаю никаких проблем, ожидаю только массив полезной и крутой инфы.

О каждом из этих пунктов я буду тут писать и делиться тем, что мне кажется интересным. Так же в планах возообновить стримы (да, олдфаги помнят, как года 3-4 назад я уже пытался это делать), и вернуться к менторству, в том числе и на бесплатной основе, о чем я тоже планирую тут написать.

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

Желаю нам всем, чтобы 2025-ый год стал лучше, чем год уходящий. Мира вам, и с наступающим!

Читать полностью…

dev notes

Посмотрел доклад "Debugging Go Application" от Matt Boyle из Cloudflare

Сначала коротко. Итак, у нас что-то сломалось, что делаем:
* Сначала проанализируй код глазами и удостоверься, что ты не видишь явных ошибок;
* Затем пишем тест на участок кода, который потенциально выдает ошибки и проходимся по нему отладчиком;
* Затем покрываем код логами, если он еще не покрыт;
* Затем добавляем метрики: успешные/не успешные запросы, время запроса, etc
* И последний шаг - покрываем код трейсами, если они еще не были добавлены.

Теперь более подробный конспект деталей, которые мне показались интересными.
Про логирование. В прошлом году в Go завезли расширенный логгер - slog, и если ты все еще по какой-то причине используешь стороннее решение, например logrus, то можно смело переезжать.

В коде, который Мэт демонстрирует, используется паттерн передачи логгера через контекст:


ctx := context.Background()
// Create a logger that writes to the log file
logger := slog.New(slog.NewJSONHandler(logFile, nil))
ctx = slogctx.With(ctx, logger)


Тут же объясняется, почему он его использует: Мэт заметил, что в нескольких opensource-решениях гугла используют такой подход, и раз компания, разработавшая этот язык так делает, разработчики Cloudflare посчитали эту практику хорошей, и со временем стали использовать ее в своих проектах.

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

Дальше, переходя к секции с трейсингом, Мэт рассказывает про пакет для сбора метрик от VictoriaMetrics - https://github.com/VictoriaMetrics/VictoriaMetrics. В целом выглядит типично для пакетов подобного рода, но в использовании пакет выглядит проще, чем похожий пакет для Datadog, который я использую в своих рабочих проектах.

Если ты ранее никогда не использовал метрики, то Мэт рекомендует для начала мониторить RED-набор метрик:
- Rate
- Error
- Duration

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

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

https://www.youtube.com/watch?v=7YfFBTkGIOI

Читать полностью…

dev notes

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

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

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

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

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

Го читать - https://poltora.dev/neovim-for-developers-2-ru/

Читать полностью…

dev notes

Релиз Go 1.23

Самое большое обновление - появились итераторы. В пакеты slices и maps добавлены новые методы для работы с итераторами, например метод Chunk из пакета slices позволяет перебирать слайс пачками из заданного числа элементов:


people := People{
{"Gopher", 13},
{"Alice", 20},
{"Bob", 5},
{"Vera", 24},
{"Zac", 15},
}

for c := range slices.Chunk(people, 2) {
fmt.Println(c)
}



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

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

Читать полностью…

dev notes

Вчера я писал о первом совете из книги Go with Domains: стоит определять бизнес логику так, как она звучит.

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

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

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


type Hour struct {
hour time.Time
availability Availability
}

// ...

func NewAvailableHour(hour time.Time) (*Hour, error) {
if err := validateTime(hour); err != nil {
return nil, err
}

return &Hour{
hour: hour,
availability: Available,
}, nil
}


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


h := hour.NewAvailableHour("13:00")

if h.HasTrainingScheduled() {
h.SetState(hour.Available)
} else {
return some error
}


и хороший пример:


func (h *Hour) CancelTraining() error {
if !h.HasTrainingScheduled() {
return some error
}

h.availability. Available

return nil
}

h := hour.NewAvailableHour("13:00")
if err := h.CancelTraining(); err != nil {
return err
}


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

Читать полностью…

dev notes

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

С опозданием, но теперь и я узнал о команде, которая позволяет бить в закрытые от внешнего мира поды с локальной машины (единственное условие - находиться в той же vpn сети и иметь права на выполнение команды port-forward в кубере), спешу поделиться.
Сначала находим нужный под в нужном окружении:


➜ ~ kubectl get pods -n prod | grep pod_name_prefix


Затем делаем post-forward с порта на найденном поде на наш локальный:

➜ ~ kubectl port-forward pod_full_name -n=prod 5050:5050
➜ ~ Forwarding from 127.0.0.1:5050 -> 5050
➜ ~ Forwarding from [::1]:5050 -> 5050
➜ ~ Handling connection for 5050

И затем в другой вкладке делаем нужный запрос на перенаправленный порт, но уже на нашу локальную машину:

➜ ~ curl -s http://localhost:5050/mypage

И, вуаля, запрос на под, закрытый для внешнего мира, успешно отправлен 🧙

Читать полностью…

dev notes

Скорее всего у тебя в проекте, на каком бы языке он не был написан, в корне лежит большой Makefile. На команды, определенные там, часто завязывается ci/cd, через make удобно настраивать локальный запуск проекта.
Например, у меня в компании Makefile с определенными командами - обязательное требование, без которого невозможно поднять новый сервис.
Тема и правда удобная, но есть один большой минус - эти файлы очень плохо читаются и еще сложнее что-то туда добавить. В поисках альтернативы для go-проектов нашел mage - тулза, задача которой убрать сложность чтения и записи make-команд. Решается это с помощью определения тех же команд внутри go-файла, что выглядит, пишется и читается сильно удобней:


//go:build mage

package main

import (
"github.com/magefile/mage/sh"
)

// Runs go mod download and then installs the binary.
func Build() error {
if err := sh.Run("go", "mod", "download"); err != nil {
return err
}
return sh.Run("go", "install", "./...")
}


Каждый метод затем можно вызвать из консоли:

mage -h build


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

#tools #note

Читать полностью…

dev notes

PGO-оптимизации или как бесплатно ускорить приложение

Узнал про интересную возможность бесплатно и очень быстро ускорить go-приложение на ~10%.

В Go 1.20, релиз которого был чуть больше года назад, добавили PGO - новые возможности компилятора оптимизировать приложение на основе его поведения.
В чем идея: с помощью профилировщика снимаем профиль процессора, затем запускаем go build с флагом -pgo и нужным профилем: go build -pgo ./pgo/profile.pprof

Компилятор на основе данных профилировщика понимает, где можно заинлайнить какие-либо данные, где можно предсказать ветвление по входным данным или где можно реорганизовать данные так, чтобы тратить меньше времени на переключение контекста. На выходе мы получаем тот же билд нашего приложения, но на 2-7% быстрее, чем его аналог скомпилированный без этого флага. В определенных случаях для специфических паттернов можно ускорить приложение на 10 и более процентов.
Так, например, ребята из cloudflare сумели такой бесплатной оптимизацией сэкономили 97 ядер из 3000, на которых было запущено приложение. Если перевести 97 ядер в стоимость облачной инфры, получается очень приятная цифра, которую получилось сэкономить за пару минут.

Читать полностью…

dev notes

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

Во-первых, SQL. Каждый раз, когда мне нужно написать запрос, будь то запрос на создание таблицы, запрос на select/insert/update, или мне нужно создать индекс - я копирую запрос, копирую структуру таблиц и кидаю в ChatGPT с вопросом "what can be improved in this query". Например, недавно я закинул запрос на создание таблицы в PostgreSQL, где была стандартная история с расширением для UUID:


create extension if not exists "uuid-ossp";
create table if not exists table (
id uuid primary key default uuid_generate_v4(),
...

И помимо всего прочего, ChatGPT посоветовал использовать не uuid-ossp, а pgcrypto, ссылаясь на документацию. Погуглив, я и правда нашел такой совет от разработчиков - https://www.postgresql.org/docs/10/uuid-ossp.html#idm46428633607040. И как правило, таких примеров - очень много. Помимо типов, GPT подсказывает, какие лучше использовать индексы, как лучше назвать поля и как переписать запрос, чтобы он быстрее работал.

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

Третий кейс - прочитать условие. Что я понимаю под прочтением условий: берем любую строчку, которая из-за множества отрицаний или плохого нейминга никак не хочет интерпретироваться в голове (пример синтетический, но отлично показывает, как оно работает):

if !(!(price <= discount) && !((!(!(quantity >= maxLimit)) || !!(minOrderValue > 100)) && !(price*quantity-discount <= 200))) {
...
}

Кидаем ее в ChatGPT и просим кратко описать, что тут происходит. Даже в случае общения на русском, ответ получился таким:

Таким образом, условие !(!(price <= discount) && !((!(!(quantity >= maxLimit)) || !!(minOrderValue > 100)) && !(price*quantity-discount <= 200))) можно переформулировать так:

Условие выполнено, если цена товара больше скидки, и либо количество товара меньше максимального лимита, либо минимальная стоимость заказа больше 100, и при этом общая стоимость товара (цена умноженная на количество) минус скидка больше 200.

Все еще неприятно, но уже сильно-сильно проще.

Четвертый кейс - переформулировать или сформулировать. На текущей работе мне приходится много писать на английском, а английский у меня далеко не advanced. Как GPT с этим помогает:
* Сначала я прошу его исправить ошибки и опечатки - тут GPT заменяет Grammarly и любые его аналоги.
* Затем копирую что получилось, прошу упростить и перефразировать (более официально, менее официально, опционально можно накинуть любого контекста).
* Уже на этом этапе скорее всего получится сильно лучше, чем было.
* Если результат не устраивает - повторяем алгоритм.

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

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

Читать полностью…

dev notes

в очень темное время живем
светлая память Алексею Навальному
https://www.youtube.com/watch?v=wp66e-JRlj0

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

если ты, или твои знакомые переехали из России и у тебя проблемы с работой разрабом - я готов бесплатно посмотреть твое резюме, дать советы и провести мок-собес, если твой стек находится в пределах go/php
я часто вижу подобные вопросы и жалобы от людей в твиттере, так что думаю, что проблема актуальна

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

Читать полностью…

dev notes

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

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

Спойлер - они действительно упростили жизнь и где-то даже позволили оптимизировать потребление ресурсов. Я решил выделить паттерны их использования, которые мне показались удобными, и получилась новая статья - https://poltora.dev/iterators-in-go-ru/

Велкоме.

Читать полностью…

dev notes

В поиске интересных статей на канал с дайджестом, нашел на реддите пост, где автор рассказывает как он создал минималистичную реляционную БД на Go. Реализация поддерживает транзакции, индексы на основе B+ Tree и покрывает весь базовый набор операций по выборке данных.

Вот его проект: https://github.com/Sahilb315/AtomixDB

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

Меня еще со времен чтения Клеппмана очень зацепила тема внутреннего устройства СУБД, но никак не доходили руки поресерчить ее более тщательно. Кажется, настало время сделать это 📆

Читать полностью…

dev notes

Как я уже как-то упоминал, я веду небольшой канал с дайджестом статей, материалов и новостей из мира Go - @digest_golang. И вот в процессе подготовки материалов, наткнулся на интересную заметку, которой хочу поделиться: https://antonz.org/go-map-shrink/

Автор рассказывает о том, как сборщик мусора работает с базовым типом map. Если кратко, то все зависит от того, сколько места занимает тип, который хранится в map.
* Если тип занимает < 128B, и мы храним его без указателя, то Go не освобождает выделенную память даже после того, как мы явно удаляем все элементы в мапе:


type Client struct {
id uint64
body [40]byte
}

...

m := make(map[int]Client)
for i := range 10000 {
m[i] = Client{id: uint64(i)}
}

runtime.GC()
printAlloc("after create")

for i := range 10000 {
delete(m, i)
}

runtime.GC()
printAlloc("after delete")



Вывод:

initial: heap size = 47 KB
after create: heap size = 1073 KB
after delete: heap size = 1073 KB


* в случае, если размер типа, сохраняемого в map больше, чем 128B, Go автоматически сохраняет его через указатели на куче, и после явного удаления память будет освобождена:

type Client struct {
id uint64
body [1024]byte
}

// the same logic


Вывод:

initial: heap size = 42 KB
after create: heap size = 11581 KB
after delete: heap size = 331 KB


В случае, если вес структуры <128B, то можно явно сохранять в мапе все значения через указатели, и тогда место будет освобождаться.
Иначе, в примере выше порядка 1Mb памяти останется не убранной, что в случае сложной многопоточной логики может значительно увеличить количество потребляемых ресурсов и это стоит держать в голове. Плюс, эти знания могут пригодиться на собесе 🙂

Читать полностью…

dev notes

Новость из мира Go: завезли proposal, который предлагает добавить в язык оператор ? для обработки ошибок, что позволит вместо привычной обработки:


r, err := SomeFunction()
if err != nil {
return fmt.Errorf("%v", err)
}


писать такую конструкцию:

r := SomeFunction() ? {
return fmt.Errorf("%v", err)
}

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

Дискуссия получается жаркой, и кажется, что большинству эта идея не нравится, как и мне. Такой сахар - это переусложнения языка, который уже усложнили за последние пару лет дженериками и итераторами (но стоит сказать, что и в первом и во втором есть смысл).
Но менять базовый синтаксис - плохая идея и прямое нарушения философии языка.

Читать полностью…

dev notes

Попробовал на днях Cursor. Если вдруг кто-то еще не сталкивался, то это среда разработки, построенная на основе Visual Studio Code, куда встроена большая языковая модель, которая умеет держать в контексте открытый проект и дописывать его по запросам.

Ощущения - очень крутые ❤️ Открыл рабочий проект, описал на английском, что я хочу и в каких файлах стоит посмотреть пример, и через 10 минут и еще 3 промпта у Cursor написал столько кода, сколько я писал бы часа 2.
Затем он поправил тесты с учетом функционала, который сам же дописал, и суммарно у меня все все, включая освоение интерфейса, ушло минут 30.
В целом - буду использовать, вероятно даже попробую пробную версию и отпишусь потом, стоит ли она того. Как по мне это сильно круче Copilot от github, которым я пользуюсь уже пару лет.

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

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

Единственный минус - это ощущение, что вся эта история с нейросетями через 3-5 лет очень сильно поменяет рынок труда 🙂

Читать полностью…

dev notes

Посмотрел еще один доклад, который долго лежал у меня в закладках, на этот раз про отладку параллельного кода в Go - https://www.youtube.com/watch?v=D_S9qQ7jzkQ

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

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

Пакет https://github.com/xiegeo/coloredgoroutine подсвечивает вывод каждой горутины отдельным цветом, что может быть полезно при отладке.

Использование флага GODEBUG=schedtrace позволит вывести отладочную информацию про то, как планировщик управляет горутинами:


➜ GODEBUG=schedtrace=5000 ./main
SCHED 0ms: gomaxprocs=10 idleprocs=8 threads=3 spinningthreads=1 needspinning=0 idlethreads=0 runqueue=1 [0 0 0 0 0 0 0 0 0 0]


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

func main() {
go func() {
// some work...
}()

go func() {
// some work...
}()

go func() {
// some work...
}()


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


func main() {
go func() {
labels := pprof.Labels("fetcher", "main_goroutine")
pprof.Do(ctx, labels, func(ctx context.Context) {
// some work
})
}()

go func() {
labels := pprof.Labels("worker_1", "main_goroutine")
pprof.Do(ctx, labels, func(ctx context.Context) {
// some work
})
}()

go func() {
labels := pprof.Labels("worker_2", "main_goroutine")
pprof.Do(ctx, labels, func(ctx context.Context) {
// some work
})
}()

// ...
}


И затем, в отладчике можно вывести горутину по конкретному тегу:

(dlv) goroutines -l -with label fetcher
Goroutine 21 - User: ./main.go:20 main.main.func1.1 (0x1003befcc) [chan send]
Labels: "fetcher":"main_goroutine"
[1 goroutines]


Хорошая статья, где описывается как это сделать более подробно и где автор показывает ряд других решений для добавления меток в горутины: https://blog.jetbrains.com/go/2020/03/03/how-to-find-goroutines-during-debugging/#using-a-custom-library-to-enable-debugging-labeling

Вторая часть доклада - про deadlock'и и про то, как с ними бороться.
Из полезного, либа, которая помогает найти дедлоки - https://github.com/sasha-s/go-deadlock
К примеру, автор этой библиотеки с помощью нее же нашел потенциальные дедлоки в коде cockroach db: https://github.com/cockroachdb/cockroach/issues/7972

Читать полностью…

dev notes

Паттерн Circuit Breaker

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

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

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

Нашел хорошую статью с примитивной реализацией с нуля и объяснением, что и как работает: https://rednafi.com/go/circuit_breaker/
Хороший пример, который при желании можно доработать и использовать в своих проектах.

Читать полностью…

dev notes

✉️

Читать полностью…

dev notes

Domain need to be a database agnostic

Продолжаю читать книгу "Go with domains" и учиться строить DDD-приложения.

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

Дальше автор пишет про третье правило - домен не должен работать с базой данных (будь то реляционная, in-memory или любая другая БД) напрямую.

У нас есть как минимум три причины чтобы следовать этому:
• Домены по определению описывают сущности, связанные с бизнес-логикой, а не с DB-слоем, и их нужно различать.
• Вероятно, мы захотим хранить данные в базе в более оптимальном виде. Например, если в домене у нас есть тип []string, то в DB-слое мы, вероятно, захотим использовать pq.StringArray
• В Go нет ORM-решений и магии в виде аннотаций из коробки, что сильно усложняет интеграцию домена с DB-слоем

И пример хорошего кода на основе всех трех правил:


func (g Server) MakeHourAvailable(ctx context.Context, request *trainer.UpdateHourRequest) (*trainer.EmptyResponse, error) {
trainingTime, err := protoTimestampToTime(request.Time)
if err != nil {
return nil, status.Error(codes.InvalidArgument, "unable to parse time")
}

if err := g.hourRepository.UpdateHour(ctx, trainingTime, func(h *hour.Hour) (*hour.Hour, error) {
if err := h.MakeAvailable(); err != nil {
return nil, err
}

return h, nil
}); err != nil {
return nil, status.Error(codes.Internal, err.Error())
}

return &trainer.EmptyResponse{}, nil
}


В 18 строк тут уместилась логика без изменения домена - она вся инкапсулирована в сам домен. У типа, которым мы оперируем - Hour, есть поведение - MakeAvailable(), которое мы используем. И вся логика работы со слоем базы данных скрыта и так же инкапсулирована с помощью паттерна репозиторий - о котором я расскажу в следующем посте.

Читать полностью…

dev notes

Сейчас читаю книгу "Go with domains" про то, правильно приготовить DDD в Go.

Автор пишет:

While implementing your domain, you should stop thinking about structs like dummy data structures or “ORM like” entities with a list of setters and getters. You should instead think about them like types with behavior.


И ниже пример: когда вы разговариваете с кем-то про тренировки в тренажерном зале, вам не скажут: "я настроил состояние аттрибута training schedule на 13:00", вместо этого человек ответит: "я запланировал тренировку на 13:00".
Одна из концепций DDD - сделать логику такой же простой и читаемой, а не усложнять ее атрибутами, состояниями и прочими параметрами, которые в большинстве случаев не нужны.

Например, ответом на вопрос "Я могу записаться на тренировку в это время" может быть или вот такой вот код:


func (s TrainerService) ScheduleTraining(ctx context.Context, request Request) error {
hour, found := s.FindHour(request.Hour)
if request.HasTrainingScheduled && !hour.Available {
//
}
if request.Available && request.HasTrainingScheduled {
//
}
if !request.Available && !request.HasTrainingScheduled {
//
}
}


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


func (h *Hour) ScheduleTraining() error {
if !h.IsAvailable() {
return ErrHourNotAvailable
}

h.availability = TrainingScheduled

return nil
}


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

Читать полностью…

dev notes

Я активно уже несколько месяцев веду канал с дайджестом статей по Go: @digest_golang, пробил там первую тысячу и просмотрел за это время огромное количество статей и материалов.
Хочу продублировать сюда несколько интересных статей за последнее время, думаю что кто-то найдет это полезным:

* Generic Concurrency в Go - автор затрагивает тему дженериков и приводит паттерны, по которым их можно использовать в асинхронной работе вместе с горутинами. Дженерики добавили в Go больше года назад, но многие разработчики, исходя из моих наблюдений, все еще не используются или боятся использовать, хотя по-моему, инструмент очень крутой. У меня было несколько кейсов, когда получилось действительно убрать очень много дублирующего кода.
* No sleep until we build the ideal pub/sub library in Go - очень крутой материал, где автор, кстати с использованием дженериков и горутин, что отлично продолжит первую статью, пишет библиотеку для pub/sub. Отличный материал для понимания работы подобных библиотек и для практики работы с многопоточным программированием.
* Hash-Based Bisect Debugging in Compilers and Runtimes - сильно технический, но очень полезный ман о том, как искать и править баги в коде с помощью поиска по бинарному дереву через инструмент Bisect.

Читать полностью…

dev notes

Открыл для себя google apps script - инструмент, позволяющий работать с инфрой гугла с помощью js-подобного кода и ставить задачи по времени like крон.
Например, задача: вам на почту приходят письма, и некоторые из них - супер важные. Предположим, что как только супер-важное письмо пришло - вам нужно сгенерировать отчет и отправить его заказчику.
И еще предположим, что супер-важное письмо содержит в заголовке строку "weekly report". При этом тот, кто отправляет письма не хочет и не может заинтегрироваться с вашим api. Тут на помощь приходит apps script.
Задача решается в несколько шагов:
* Сначала подготавливаем http-ручку на нашем бэкенде, вызов которой делает нужную нам работу.
* В моем случае я завел ещё одну почту, куда с помощью фильтров отправил только нужные мне письма, добавив к каждому из них определенный лейбл и пометив его звёздочкой.
* Затем идём на script.google.com и под нужным аккаунтом, почту которого нужно проверять, пишем обработку наших писем:


function checkEmails() {
const threads = GmailApp.search('is:starred label:"my_label"');

for (const thread of threads) {
const messages = thread.getMessages()

for (const message of messages) {
if (!message.isStarred()) {
continue;
}
...

let response = UrlFetchApp.fetch('https://myapi.com/api/test', options);
...

message.unstar()
}
}
}

* Затем в меню настраиваем интервал запуска этой функции, и на этом задача решена.

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

#tools #note

Читать полностью…

dev notes

написал большой и подробный ман о том, как с нуля настроить neovim и зачем в 2024 году это нужно
постарался описать все то, чего не хватало мне, когда я погружался в эту тему
https://poltora.dev/neovim-for-developers/
го читать

Читать полностью…

dev notes

реакции есть, так что кто считает, что ему нужна помощь, пишите: @junsenpub

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

я уже давно собеседовал php-разработчиков на разные грейды, и около года как начал собеседовать go-разработчиков на senior и middle позиции

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

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

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

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

какой тут можно сделать вывод: если у тебя совсем нет знаний, и ты решил таким путем быстро получить работу - волшебной кнопки не бывает, и будь готов, что помимо денег за доступ к чату таких же "хакеров", ты еще потратишь очень много нервов, времени и сил на поиск компании, с которой этот номер пройдет
если у тебя уже есть какие-то знания - вероятно, полезней будет посмотреть примеры прохождения собесов на твоем стеке на youtube - это бесплатно, и очень часто совпадает с реальными собесами

Читать полностью…

dev notes

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

unionfs - это слоистая файловая система, 1 из 4-х базовых компонентов любого контейнера
почему система называется слоистой? потому что одно действие, например через директиву RUN в Dockerfile - выполняется на одном слое, затем этому слою выдается hash, который в дальнейшем может быть переиспользован на последующих слоях

так вот докер помечает все ранее выполненные слои как read only, и каждая следующая команда не может изменить данные, оставшиеся на слое выше

иначе говоря, если мы пытаемся скачать архив, затем распаковать его, переместить файлы и удалить, и делаем это 4-мя разными командами:

RUN wget https://releases.hashicorp.com/terraform/0.12.28/terraform_0.12.28_linux_amd64.zip
RUN unzip terraform_0.12.28_linux_amd64.zip
RUN rm terraform_0.12.28_linux_amd64.zip
RUN mv terraform /usr/local/bin/


у нас ничего не получится, в таком случае 3-я команда не сможет удалить данные, а 4-ая не сможет их перенести

чтобы все сработало, нужно сделать это в одну команду:
RUN wget https://releases.hashicorp.com/terraform/0.12.28/terraform_0.12.28_linux_amd64.zip \
&& unzip terraform_0.12.28_linux_amd64.zip \
&& rm terraform_0.12.28_linux_amd64.zip \
&& mv terraform /usr/local/bin/

Читать полностью…
Subscribe to a channel