dev notes – Telegram
dev notes
1.42K subscribers
24 photos
3 videos
170 links
Пишу про Go, Vim, и про то, как я медленно ползу в сторону FAANG.
Веду @digest_golang

С предложениями: @junsenpub
Download Telegram
написал большой и подробный ман о том, как с нуля настроить neovim и зачем в 2024 году это нужно
постарался описать все то, чего не хватало мне, когда я погружался в эту тему

го читать

#vim #nvim #article
👍19😢53👎1🔥1
PGO-оптимизации или как бесплатно ускорить приложение

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

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

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

#go #note
👍9🔥64
Открыл для себя google apps noscript - инструмент, позволяющий работать с инфрой гугла с помощью js-подобного кода и ставить задачи по времени like крон.

Например, задача: вам на почту приходят письма, и некоторые из них - супер важные. Предположим, что как только супер-важное письмо пришло - вам нужно сгенерировать отчет и отправить его заказчику.
При этом тот, кто отправляет письма не хочет и не может заинтегрироваться с вашим api. Тут на помощь приходит apps noscript.

Задача решается в несколько шагов:
* Сначала подготавливаем http-ручку на нашем бэкенде, вызов которой делает нужную нам работу.
* В моем случае я завел ещё одну почту, куда с помощью фильтров отправил только нужные мне письма, добавив к каждому из них определенный лейбл и пометив его звёздочкой.
* Затем идём на noscript.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
👍8🔥3👎1👾1
Скорее всего у тебя в проекте, на каком бы языке он не был написан, в корне лежит большой 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
👍8🔥4👾4👎2
Я активно уже несколько месяцев веду канал с дайджестом статей по 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.
👍6🔥43👾3
Большинство компаний, особенно относительно больших, сейчас имеют в своей инфраструктуре кубер или его аналоги, внутри которого множество подов обмениваются друг с другом запросами.
Часто под не светит свои порты во внешний мир и сервис в целом не имеет внешнего домена, доступного снаружи.

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

➜ ~ kubectl get pods -n prod | grep pod_name_prefix


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

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

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

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

И, вуаля, запрос на под, закрытый для внешнего мира, успешно отправлен 🧙
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6👾3
Сейчас читаю книгу "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
}


Таким образом внешняя логика, вызывающая этот метод, очень сильно упрощается и становится более читаемой. Более того, такую логику сильно проще покрывать тестами.
👍11🔥4👾3
Вчера я писал о первом совете из книги 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
}


Вся работа с доменом - только в пакете с доменом, никакая логика по установке каких-либо состояний не выносится наружу.
1🔥5👾5👍21
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(), которое мы используем. И вся логика работы со слоем базы данных скрыта и так же инкапсулирована с помощью паттерна репозиторий - о котором я расскажу в следующем посте.
1👍4🔥3👾2😢1
Релиз 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++.
1🔥5👍42👾1
✉️
Please open Telegram to view this post
VIEW IN TELEGRAM
8
Некоторое время назад я написал большой гайд о том, как настроить neovim с нуля и привести его к виду, пригодному для разработки: с подсветкой синтаксиса, пониманием кода, автокомплитом, и десятком других возможностей, к которым мы привыкли в IDE.

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

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

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

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

Го читать - https://poltora.dev/neovim-for-developers-2-ru/
3🔥6👍44
Паттерн Circuit Breaker

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

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

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

Нашел хорошую статью с примитивной реализацией с нуля и объяснением, что и как работает: https://rednafi.com/go/circuit_breaker/
Хороший пример, который при желании можно доработать и использовать в своих проектах.
2🔥6👍421
Посмотрел доклад "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
1👍8🔥31
Посмотрел еще один доклад, который долго лежал у меня в закладках, на этот раз про отладку параллельного кода в 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
1🔥53👍3
Все начали писать про итоги и планы, и я тоже решил написать.

Изначально этот канал назывался "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-ый год стал лучше, чем год уходящий. Мира вам, и с наступающим!
🔥16👍94🎉1
Попробовал на днях Cursor. Если вдруг кто-то еще не сталкивался, то это среда разработки, построенная на основе Visual Studio Code, куда встроена большая языковая модель, которая умеет держать в контексте открытый проект и дописывать его по запросам.

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

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

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

Единственный минус - это ощущение, что вся эта история с нейросетями через 3-5 лет очень сильно поменяет рынок труда 🙂
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12🔥6👾2
Как я писал выше - я хочу подготовиться к собесам в FAANG и компаниям около FAANG.

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

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

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

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

В заключении скажу, что алгосы - абсолютно точно не панацея. Их стоит отрешивать или если ты хочешь подготовиться к собесу, или если тебе это нравится. В моем случае оба этих фактора совпали 🙂
Please open Telegram to view this post
VIEW IN TELEGRAM
👍92🔥2
Новость из мира Go: завезли proposal, который предлагает добавить в язык оператор ? для обработки ошибок, что позволит вместо привычной обработки:

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


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

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

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

Дискуссия получается жаркой, и кажется, что большинству эта идея не нравится, как и мне. Такой сахар - это переусложнения языка, который уже усложнили за последние пару лет дженериками и итераторами (но стоит сказать, что и в первом и во втором есть смысл).
Но менять базовый синтаксис - плохая идея и прямое нарушения философии языка.
👾7🔥5👍3
Как я уже как-то упоминал, я веду небольшой канал с дайджестом статей, материалов и новостей из мира 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 памяти останется не убранной, что в случае сложной многопоточной логики может значительно увеличить количество потребляемых ресурсов и это стоит держать в голове. Плюс, эти знания могут пригодиться на собесе 🙂
2🔥6👍31
Новости из мира 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
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍42🔥2😢1