Нияз Хадимуллин | Ментор по GO – Telegram
Нияз Хадимуллин | Ментор по GO
1.17K subscribers
123 photos
1 video
35 links
Авторский канал ментора Нияза про Go, базы данных и разработку

Если хочешь записаться на моё менторство и начать получать офферы, не стесняйся писать мне https://mentor-niyaz.ru
Download Telegram
🎨 Декоратор (Decorator): динамическое добавление поведения объектам

Что такое паттерн Декоратор?
Декоратор (Decorator) — это структурный паттерн, который позволяет динамически добавлять объектам новое поведение, оборачивая их в объекты-декораторы.

💡 Основные концепции:
- Оборачивание объектов:
1. Декораторы реализуют тот же интерфейс, что и декорируемые объекты.
2. Декораторы добавляют новое поведение, не изменяя код объектов.

❗️Ключевые особенности:
- Гибкость:
1. Возможность динамического добавления и удаления поведения.
2. Упрощение расширения функциональности объектов.

- Модульность:
1. Разделение ответственности между объектами и декораторами.
2. Улучшение поддержки и тестируемости кода.

🔍 Типичные сценарии использования:
1. Добавление нового поведения объектам без изменения их кода.
2. Реализация дополнительных функций, таких как логирование или кэширование.
3. Управление графическими объектами с дополнительными свойствами.

🎯 Преимущества использования Decorator:
- Упрощение добавления нового поведения объектам.
- Улучшение модульности и поддержки кода.
- Повышение гибкости и расширяемости системы.
🔗 Заместитель (Proxy): контроль доступа к объекту

Что такое паттерн Заместитель?
Заместитель (Proxy) — это структурный паттерн проектирования, который предоставляет заместитель объекта для контроля доступа к нему.

💡 Основные концепции:
- Контроль доступа:
1. Заместитель управляет доступом к основному объекту.
2. Возможность добавления дополнительной логики при обращении к объекту.

❗️Ключевые особенности:
- Ленивая инициализация:
1. Отложенная загрузка ресурсов до момента их фактического использования.
2. Улучшение производительности за счёт экономии ресурсов.

- Защита и безопасность:
1. Контроль доступа и проверка прав.
2. Логирование и мониторинг обращений к объекту.

🔍 Типичные сценарии использования:
1. Управление доступом к ресурсам (файлы, базы данных).
2. Реализация ленивой инициализации объектов.
3. Добавление уровня безопасности при работе с объектами.

🎯 Преимущества использования Proxy:
- Улучшение производительности за счёт ленивой инициализации.
- Повышение безопасности и контроля доступа.
- Упрощение добавления дополнительной логики при обращении к объекту.
🔌 Адаптер (Adapter): совместимость интерфейсов

Что такое паттерн Адаптер?
Адаптер (Adapter) — это структурный паттерн, который позволяет объектам с несовместимыми интерфейсами работать вместе, преобразуя интерфейс одного класса в интерфейс, ожидаемый клиентом.

💡 Основные концепции:
- Совместимость интерфейсов:
1. Адаптер преобразует вызовы одного интерфейса в вызовы другого.
2. Обеспечение совместимости между несовместимыми классами.

❗️Ключевые особенности:
- Гибкость:
1. Возможность интеграции сторонних библиотек и компонентов.
2. Упрощение изменения и расширения системы.

- Повторное использование:
1. Возможность использования существующих классов без их изменения.
2. Улучшение модульности и поддержки кода.

🔍 Типичные сценарии использования:
1. Интеграция сторонних библиотек с несовместимыми интерфейсами.
2. Использование старых классов с новыми интерфейсами.
3. Обеспечение совместимости между различными версиями системы.

🎯 Преимущества использования Adapter:
- Упрощение интеграции несовместимых компонентов.
- Улучшение модульности и поддержки кода.
- Повышение гибкости и расширяемости системы.
🌊 Object Pool: повторное использование объектов

Что такое паттерн Object Pool?
Object Pool — это поведенческий паттерн, который управляет пулом объектов, позволяя их повторное использование вместо создания новых, что улучшает производительность приложений.

💡 Основные концепции:
- Пул объектов:
1. Хранение и управление объектами для их повторного использования.
2. Уменьшение накладных расходов на создание и уничтожение объектов.

❗️Ключевые особенности:
- Повышение производительности:
1. Уменьшение времени на создание объектов.
2. Снижение нагрузки на систему сборки мусора.

- Экономия ресурсов:
1. Уменьшение использования памяти и других ресурсов.
2. Улучшение масштабируемости приложений.

🔍 Типичные сценарии использования:
1. Управление соединениями с базой данных.
2. Работа с потоками и сетевыми ресурсами.
3. Оптимизация производительности высоконагруженных приложений.

🎯 Преимущества использования Object Pool:
- Улучшение производительности за счёт повторного использования объектов.
- Экономия ресурсов и улучшение масштабируемости.
- Уменьшение нагрузки на систему сборки мусора.
🔍 Указатели на элементы map в Go

Можно ли взять указатель на элемент map в Go и почему?
В Go нельзя взять указатель на элемент map. Это связано с тем, что map — это сложная структура данных, которая может изменять свою внутреннюю организацию (например, при перераспределении памяти), и указатель на элемент может стать недействительным.

💡 Почему нельзя:

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

2. Безопасность:
- Описание: Разрешение указателей на элементы map может привести к неопределённому поведению, если структура map изменится.
- Преимущества: Защита от ошибок, связанных с изменением структуры данных.
- Недостатки: Ограничение возможностей работы с элементами map.

🔍 Как работать с элементами map:
1. Используйте ключи для доступа к элементам map.
2. Если нужно сохранить ссылку на элемент, рассмотрите возможность использования других структур данных, таких как слайсы, если это подходит для вашей задачи.
🔍 Режим транзакции по умолчанию в PostgreSQL

Какой режим транзакции по умолчанию в PostgreSQL?
В PostgreSQL режим транзакции по умолчанию — это READ COMMITTED. Это означает, что каждая транзакция видит только те данные, которые были зафиксированы (committed) до её начала.

💡 Основные характеристики READ COMMITTED:

1. Изоляция:
- Описание: Транзакции изолированы друг от друга. Изменения, сделанные одной транзакцией, не видны другим транзакциям до тех пор, пока они не будут зафиксированы.
- Преимущества: Обеспечение целостности данных.
- Недостатки: Возможны конфликты при одновременном доступе к данным.

2. Производительность:
- Описание: Этот режим обеспечивает хороший баланс между производительностью и изоляцией, так как не требует блокировки чтения.
- Преимущества: Высокая производительность при работе с большим количеством транзакций.
- Недостатки: Меньшая изоляция по сравнению с другими режимами.
🔍 PgBouncer

Что такое PgBouncer?
PgBouncer — это лёгкий и быстрый пул соединений для PostgreSQL. Он управляет соединениями с базой данных, позволяя повторно использовать их и уменьшая нагрузку на сервер базы данных.

💡 Основные функции PgBouncer:

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

2. Управление нагрузкой:
- Описание: Ограничение количества одновременных соединений с базой данных.
- Преимущества: Предотвращение перегрузки сервера базы данных.
- Недостатки: Возможные задержки при большом количестве запросов.

3. Поддержка транзакций:
- Описание: Возможность работать в режиме транзакций, сессий или выполнения отдельных запросов.
- Преимущества: Гибкость в управлении соединениями.
- Недостатки: Сложность настройки для различных режимов работы.

🎯 Преимущества использования PgBouncer:
- Уменьшение нагрузки на сервер базы данных.
- Повышение производительности приложений за счёт повторного использования соединений.
- Упрощение управления соединениями и нагрузкой.
🔧 Императивное программирование и Go

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

💡 Основные концепции:
- Управление потоком:
1. Использование циклов, условий и функций для управления выполнением программы.
2. Изменение состояния переменных и структур данных.

❗️Ключевые особенности:
- Простота и ясность:
1. Чёткое описание последовательности действий.
2. Легкость отладки и тестирования.

- Гибкость:
1. Возможность точного контроля над выполнением программы.
2. Поддержка сложных алгоритмов и логики.

🔍 Типичные сценарии использования:
1. Реализация алгоритмов и бизнес-логики.
2. Работа с данными и их преобразование.
3. Управление ресурсами и состоянием приложения.

🎯 Преимущества использования императивного программирования в Go:
- Чёткость и предсказуемость выполнения программы.
- Высокая производительность и эффективность.
- Поддержка сложных вычислений и алгоритмов.
👍2019🔥15
📜 Декларативное программирование и SQL

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

💡 Основные концепции:
- Описание результата:
1. Фокус на том, какие данные нужно получить, а не на том, как их получить.
2. Использование запросов для описания операций над данными.

❗️Ключевые особенности:
- Удобство и лаконичность:
1. Простота написания и чтения запросов.
2. Снижение вероятности ошибок за счёт абстракции.

- Оптимизация:
1. Возможность автоматической оптимизации запросов базой данных.
2. Улучшение производительности и эффективности работы с данными.

🔍 Типичные сценарии использования:
1. Выполнение запросов к базам данных для получения и обработки данных.
2. Определение правил и ограничений для данных.
3. Анализ и отчётность на основе данных.

🎯 Преимущества использования декларативного программирования в SQL:
- Удобство и лаконичность написания запросов.
- Автоматическая оптимизация и повышение производительности.
- Снижение вероятности ошибок за счёт абстракции.
18👍18🔥17
🔍 Линтеры и Go: обеспечение качества кода

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

💡 Основные концепции:
- Анализ кода:
1. Проверка синтаксиса и стиля кода.
2. Обнаружение потенциальных ошибок и уязвимостей.

❗️Ключевые особенности:
- Повышение качества кода:
1. Раннее выявление ошибок и проблем.
2. Обеспечение соблюдения стандартов и лучших практик.

- Интеграция в процесс разработки:
1. Возможность использования в CI/CD пайплайнах.
2. Поддержка различных IDE и редакторов кода.

🔍 Типичные сценарии использования:
1. Проверка кода перед коммитом в систему контроля версий.
2. Обеспечение соблюдения стандартов кодирования в команде.
3. Автоматизация проверок кода в процессе разработки.

🎯 Преимущества использования линтеров в Go:
- Повышение качества и надёжности кода.
- Раннее выявление ошибок и проблем.
- Обеспечение соблюдения стандартов и лучших практик.
🔥24👍1910
🔍 Виды валидации кэша

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

💡 Основные виды валидации кэша:

1. Cache-Aside (Lazy Loading):
- Описание: Данные загружаются в кэш только при первом запросе.
- Преимущества: Уменьшение нагрузки на источник данных, так как данные кэшируются только по запросу.
- Недостатки: Возможны задержки при первом запросе.

2. Write-Through:
- Описание: Данные записываются в кэш и основное хранилище одновременно.
- Преимущества: Гарантирует, что данные в кэше всегда актуальны.
- Недостатки: Увеличение времени записи из-за необходимости обновления двух источников.

3. Write-Behind (Write-Back):
- Описание: Данные сначала записываются в кэш, а затем асинхронно обновляются в основном хранилище.
- Преимущества: Ускорение записи за счёт асинхронного обновления.
- Недостатки: Риск потери данных в случае сбоя.

4. Cache-Aside with TTL (Time-To-Live):
- Описание: Данные хранятся в кэше в течение определённого времени, после чего считаются недействительными.
- Преимущества: Простота реализации и управления.
- Недостатки: Возможность получения устаревших данных, если TTL слишком велик.

🔍 Типичные сценарии использования:
1. Ускорение доступа к часто запрашиваемым данным.
2. Снижение нагрузки на основное хранилище.
3. Обеспечение актуальности данных в распределённых системах.

🎯 Преимущества использования валидации кэша:
- Улучшение производительности приложений.
- Снижение нагрузки на основное хранилище.
- Обеспечение актуальности данных.
20🔥19👍16
🧪 Юнит-тесты: основа надежного кода

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

💡 Основные концепции:
- Изоляция:
1. Тестирование отдельных компонентов без зависимостей.
2. Использование моков и стабов для симуляции внешних зависимостей.

- Повторяемость:
1. Возможность многократного выполнения тестов с одинаковым результатом.
2. Автоматизация тестирования в процессе разработки.

❗️Ключевые особенности:
- Раннее выявление ошибок:
1. Обнаружение проблем на этапе написания кода.
2. Уменьшение времени на отладку и исправление ошибок.

- Поддержка рефакторинга:
1. Уверенность в том, что изменения не нарушают существующую функциональность.
2. Упрощение улучшения и оптимизации кода.

🔍 Типичные сценарии использования:
1. Проверка корректности работы отдельных функций и методов.
2. Обеспечение надёжности кода при внесении изменений.
3. Автоматизация тестирования в CI/CD пайплайнах.

🎯 Преимущества использования юнит-тестов:
- Раннее выявление ошибок и улучшение качества кода.
- Поддержка рефакторинга и улучшения кода.
- Автоматизация тестирования и повышение надёжности приложения.
🔥2116👍16
🤖 Авто-тесты: обеспечение качества на каждом этапе

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

💡 Основные концепции:
- Автоматизация:
1. Выполнение тестов без участия человека.
2. Интеграция в процессы разработки и сборки.

- Покрытие кода:
1. Проверка различных сценариев использования приложения.
2. Обеспечение высокого уровня покрытия тестами.

❗️Ключевые особенности:
- Быстрота и эффективность:
1. Выполнение тестов быстрее, чем вручную.
2. Возможность проверки большого объёма функциональности.

- Повторяемость и надёжность:
1. Одинаковые результаты при каждом запуске.
2. Уменьшение вероятности человеческих ошибок.

🔍 Типичные сценарии использования:
1. Проверка функциональности приложения на различных этапах разработки.
2. Регрессионное тестирование для выявления новых ошибок.
3. Интеграция в CI/CD пайплайны для автоматического выполнения тестов.

🎯 Преимущества использования авто-тестов:
- Ускорение процесса тестирования и повышение его эффективности.
- Обеспечение высокого уровня качества и надёжности кода.
- Уменьшение ручного труда и вероятности ошибок.
25👍17🔥12
Как не бояться технических собесов и проходить их на изи 😎

Одна из самых распространённых проблем на технических интервью — паника. Ученики пишут мне: «Я всё знаю, но на собесе туплю, краснею и забываю элементарные вещи». Вот несколько простых шагов, которые помогут тебе пройти техничку максимально уверенно и эффективно:

1️⃣ Не молчи, даже если ничего не знаешь 🤔
На вопрос «Как реализовать Rate Limiter в GO?» у тебя в голове образовалась пустота? Не замыкайся в себе, начни говорить вслух: «Давайте порассуждаем...» и дальше выстраивай свою логику. HR и технический специалист оценят твою способность думать, а не просто зазубренные ответы. 😘

2️⃣ Пиши на собесе в IDE или доске 💻
Многие ребята думают, что писать код на интервью надо как в финале чемпионата по программированию — молча и быстро. Это не так! Комментируй каждый свой шаг: «Вот тут проверю ошибку, вот тут сделаю интерфейс, а здесь нужна горутина». Когда рекрутер или техлид видят ход твоих мыслей, они намного охотнее дают положительный фидбек и продвигают тебя дальше. 👨‍🦰

3️⃣ Задавай уточняющие вопросы 🧠
Услышав задачу, не бойся уточнять. Например: «Нужно ли учитывать нагрузку?», «А какие ограничения по памяти?». Покажи свою компетентность и вовлечённость. Правильно заданный вопрос даст тебе +20 баллов к лояльности со стороны интервьюера. 🗣

4️⃣ Используй шпаргалки, пока тебя не видят 😏
Если собес удалённый, и ты часто забываешь какие-то тонкости, открой на втором мониторе заранее подготовленную табличку с часто забываемыми моментами. Поверь, даже топы иногда подсматривают базовые вещи, не стоит этого стесняться. 💃

5️⃣ Не бойся ошибаться и признавать ошибки 🫡
Технический интервьюер не ждёт, что ты будешь знать всё на 100%. Ошибся? Сразу же скажи: «Да, тут я поторопился, сейчас исправлю», — покажи, что ты умеешь быстро и адекватно реагировать на свои ошибки. За это тебе дадут намного больше баллов, чем за скрытые баги, о которых ты молчишь. 💀

Вывод:
Технический собес — не экзамен в универе, это живой диалог с таким же инженером, как и ты. Показывай, что ты умеешь мыслить, задавать правильные вопросы и спокойно реагировать на ошибки. Тогда любой техинтервью превратится в простой и приятный разговор, после которого тебе прилетит долгожданный оффер 🚀
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2517👍17
Интерфейсы в GO: как отвечать на собесе, чтобы техлид сразу понял, что ты шаришь 🚀

Мои ученики почти всегда легко проходят вопросы про интерфейсы, потому что чётко знают, как грамотно отвечать и что именно хотят услышать интервьюеры. Давай разберём пошагово, как звучит идеальный ответ на вопрос: «Что такое интерфейсы в GO?»

1️⃣ Что такое интерфейс и зачем он нужен? 🤓
Интерфейс в GO — это абстракция, которая описывает поведение объекта (набор методов), но не говорит о реализации. Простыми словами: «Если оно крякает, значит это утка». Интерфейс позволяет писать универсальный код, не привязываясь к конкретной реализации.

2️⃣ Как в GO реализуются интерфейсы? 💻
Реализация интерфейсов в GO происходит неявно. Если структура реализует все методы интерфейса, то она автоматически ему удовлетворяет.
Простой пример реализации:

type Worker interface {
Work() string
}

type Programmer struct{}

func (p Programmer) Work() string {
return "Пишу крутой код"
}

Тут структура Programmer автоматически удовлетворяет интерфейсу Worker.

3️⃣ Когда использовать пустой интерфейс (interface{})? 🔍
Пустой интерфейс (interface{}) — это особенный интерфейс, у которого нет ни одного метода, поэтому ему удовлетворяет любой тип. Он часто используется в GO, когда заранее неизвестен тип данных.


func PrintAny(val interface{}) {
fmt.Println(val)
}

PrintAny("привет") // строка
PrintAny(42) // число


4️⃣ Что происходит под капотом интерфейсов? 🔥
Под капотом интерфейс — это пара значений:
🟣Указатель на тип значения (таблица методов).
🟡Само значение (или указатель на него, если большое).

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

var x interface{}
var y *int = nil
x = y

fmt.Println(x == nil) // false (!)

Почему так? Потому что интерфейс — это тип (не значение!), а тут тип не nil (*int), хотя значение и nil

5️⃣ Когда интерфейсы замедляют работу программы? ⏱️
Использование интерфейсов в GO — это не бесплатно. Из-за косвенности вызовов (indirect calls) интерфейсы могут слегка снижать производительность. Но не спеши паниковать — замедление минимальное и на практике редко критично. Главное, что ты должен знать: интерфейсы используем по делу, а не просто «потому что красиво».

Вывод:
Интерфейсы в GO — мощная штука, которую важно не просто выучить, а реально понимать на уровне механики языка. Если ты будешь на собесе рассказывать именно так, с конкретными примерами и тонкостями, то впечатлишь любого техлида и гарантированно повысишь свой шанс на оффер с большим количеством нулей 💪
Please open Telegram to view this post
VIEW IN TELEGRAM
23🔥21👍19
Каналы в GO: что нужно понимать, чтобы не облажаться на собеседовании 🔥

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

Вот пошаговая шпаргалка, как отвечать про каналы так, чтобы сразу стало понятно: ты шаришь.

1️⃣ Что такое каналы и зачем они нужны? 🤔
Канал в GO — это типизированный механизм коммуникации и синхронизации между горутинами. Каналы позволяют безопасно передавать данные между горутинами, избегая гонок (race condition) и необходимости вручную блокировать доступ

Простой пример:

ch := make(chan int)

go func() {
ch <- 42
}()

val := <-ch
fmt.Println(val) // 42


2️⃣ Буферизованные и небуферизованные каналы — в чём разница? 📬
🔘Небуферизованный канал (make(chan T)):
«Передача данных блокирует отправителя, пока не появится получатель, и наоборот. Используется для синхронизации».

🔘 Буферизованный канал (make(chan T, N)):
«Позволяет отправить N значений без блокировки, если буфер не полон. Используются для оптимизации и развязки горутин».

Пример буферизованного канала:

ch := make(chan int, 2)

ch <- 1
ch <- 2
// ch <- 3 уже заблокирует, пока не прочитаем хотя бы один элемент


3️⃣ Закрытие каналов и что значит «закрытый канал»? 🔒
Закрытие канала (close(ch)) сигнализирует, что данных больше не будет. Чтение из закрытого канала всегда вернёт нулевое значение для типа канала и второй параметр, который показывает, открыт канал или закрыт

Правило:
🟣Писать в закрытый канал нельзя (panic!).

🟡Читать из закрытого канала можно (получаем ноль + false).

Пример проверки закрытого канала:

val, ok := <- ch
if !ok {
fmt.Println("Канал закрыт")
}



4️⃣ Цикл for-range для чтения из каналов 🔄
Cамый удобный способ читать из канала — это for range. Он автоматически завершится, как только канал закроется, и обработает все оставшиеся значения


for val := range ch {
fmt.Println(val)
}


5️⃣ Select для работы с несколькими каналами 🎛️
Оператор select позволяет работать одновременно с несколькими каналами. Это мощный инструмент для организации параллельной обработки и graceful shutdown

Классический пример select:

select {
case val := <-ch1:
fmt.Println("Получено из ch1:", val)
case val := <-ch2:
fmt.Println("Получено из ch2:", val)
default:
fmt.Println("Нет данных")
}


Вывод:
Отвечая про каналы именно так — детально, с примерами, и упоминая подводные камни — ты сразу демонстрируешь высокий уровень знаний. После такого ответа ты просто обречён на оффер с жирной зарплатой! 💪
Please open Telegram to view this post
VIEW IN TELEGRAM
👍24🔥2016
Как грамотно отвечать про срезы (slices) в GO, чтобы сразу получить плюс к офферу 🔥

Срезы (slices) — одна из любимых тем на собеседованиях. Часто кандидаты отвечают на поверхностном уровне, и теряют баллы. Вот чёткий пошаговый план, как отвечать про срезы так, чтобы интервьюер сразу понял — ты знаешь и глубоко понимаешь, о чём говоришь.

1️⃣ Что такое срез и как он устроен под капотом? 🤔
Срез — это структура данных в Go, которая представляет собой ссылку на участок массива. Внутри срез состоит из трёх элементов:
🟣Указатель на первый элемент массива
🟡Длина среза (len) — сколько элементов доступно
🟢Ёмкость (cap) — сколько элементов можно вместить без выделения новой памяти

Пример среза:

arr := [5]int{1, 2, 3, 4, 5}
slice := arr[1:3] // {2, 3}


2️⃣ Как правильно использовать append() и что происходит при превышении ёмкости? 🛠️
Когда мы используем append, если ёмкость (cap) превышена, Go автоматически выделяет новый массив большей ёмкости и копирует данные в него. При этом оригинальный срез больше не указывает на старый массив

Простой пример реализации:

nums := []int{1, 2, 3} // len=3, cap=3
nums = append(nums, 4) // выделяется новая память (len=4, cap может быть больше)


3️⃣ Как избежать типичных ошибок при работе со срезами? 🚧
На собесах любят спрашивать про распространённые ошибки:

🔘 Ошибка модификации базового массива через срезы:

arr := [3]int{1,2,3}
slice := arr[:]
slice[0] = 10
fmt.Println(arr[0]) // 10 (меняется исходный массив)


🔘 Утечки памяти при использовании больших срезов:

bigSlice := make([]int, 1<<20) // большой срез
smallSlice := bigSlice[:10]
// bigSlice остаётся в памяти, пока smallSlice на него ссылается

Чтобы избежать утечек, можно скопировать нужную часть в новый срез:

smallSliceCopy := make([]int, 10)
copy(smallSliceCopy, smallSlice)



4️⃣ Как устроен срез при передаче в функции? 📦
Срезы передаются по значению, то есть копируется структура из трёх полей (указатель, len, cap). Но при этом данные, на которые указывает срез, не копируются, и модификация элементов среза внутри функции повлияет на исходные данные

func modify(slice []int) {
slice[0] = 100
}

nums := []int{1,2,3}
modify(nums)
fmt.Println(nums) // [100 2 3]


5️⃣ Когда использовать срезы, а когда массивы? 📌
Массивы фиксированного размера редко используются в Go, чаще их заменяют срезами из-за гибкости и удобства. Но массивы нужны тогда, когда нужен именно фиксированный размер (например, ключи в map или строгая гарантия размера структуры в памяти)

Вывод:
Если на собеседовании ты объяснишь устройство срезов именно так — с примерами и тонкостями реализации, то интервьюер сразу поймёт, что ты уверенно владеешь Go, и твоему офферу уже можно начинать завидовать! 💪
Please open Telegram to view this post
VIEW IN TELEGRAM
👍24🔥1817
Как правильно объяснить работу defer в GO, чтобы собеседование прошло идеально 🔥

На собесах по Go часто спрашивают про оператор defer. Почему? Потому что он показывает, насколько ты глубоко понимаешь порядок выполнения кода и обработку исключительных ситуаций.

1️⃣ Что такое defer и зачем он нужен? 🤔
defer в Go — это оператор, который откладывает выполнение функции до момента выхода из текущей функции. Чаще всего используется для освобождения ресурсов, закрытия файлов или соединений с БД, чтобы гарантировать, что ресурс всегда будет освобождён.

Пример:

func readFile() {
file, _ := os.Open("data.txt")
defer file.Close() // файл закроется гарантированно в конце функции
}


2️⃣ Порядок выполнения нескольких defer 🔄
Если в одной функции несколько вызовов defer, то они выполняются в порядке LIFO (последний зашёл — первый вышел). Важно помнить это, чтобы не запутаться и избежать неожиданного поведения

Пример:

func example() {
defer fmt.Println("Первый defer")
defer fmt.Println("Второй defer")
defer fmt.Println("Третий defer")
}

// вывод будет:
// Третий defer
// Второй defer
// Первый defer


3️⃣ Как defer влияет на возвращаемые значения? 🔥
На собеседованиях любят спрашивать tricky вопрос про взаимодействие defer и именованных возвращаемых значений. defer может изменить возвращаемое значение перед выходом из функции

Частый пример с собесов:

func tricky() (x int) {
defer func() {
x = 100 // меняем значение перед возвратом
}()
return 10 // сначала вернёт 10, но defer изменит это на 100
}

fmt.Println(tricky()) // 100 (!)


4️⃣ Как defer работает с panic/recover? 🚨
Если внутри функции произошла паника, то все вызовы defer выполнятся перед тем, как паника продолжит распространяться наверх. Это свойство активно используется вместе с recover(), чтобы восстановить приложение после паники

Классический пример обработки паники:

func safeDivide(a, b int) (result int) {
defer func() {
if r := recover(); r != nil {
fmt.Println("Произошла паника:", r)
result = 0 // возвращаем безопасное значение
}
}()
result = a / b // возможна паника (деление на 0)
return
}

fmt.Println(safeDivide(10, 0)) // "Произошла паника" и вывод 0


Вывод:
Если ты расскажешь про defer на собесе именно так — понятно, чётко и с примерами — то вопросы отпадут сами собой, и твой путь к жирному офферу станет короче и легче! 💪
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥31👍2313
Горутины и WaitGroup в GO: как ответить, чтобы рекрутер понял - ты приручил параллелизм 🏎️

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

1️⃣ Что такое горутина и чем она лучше OS-потока? 🤔
💞 goroutine - лёгкий кооперативный поток, стартующий c 8 KB стека
💓Планируется рантаймом, а не ОС → дешевле переключается, можно запускать тысячи


go func() { fmt.Println("Hello from goroutine") }()


2️⃣ WaitGroup: корректное ожидание завершения 🔄
💞Создаём sync.WaitGroup, добавляем счётчик перед стартом, в конце горутины вызываем Done().
💓Никогда не делай Add() после запуска - поймаешь race!


var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
work()
}()
wg.Wait()


Вывод:
Показываешь знание стоимости горутин, WaitGroup - и техлид уже mentally пишет «offer approved»! 💸
Please open Telegram to view this post
VIEW IN TELEGRAM
23👍18🔥16
Ошибки, panic и recover: отвечаем так, чтобы интервьюер улыбнулся 😎

Error-handling в Go - лакмусовая бумажка инженерной культуры. Расскажи по пунктам - и не оплошаешь.

1️⃣ Ошибка — это значение, а не исключение
💞 Интерфейс error + idiома if err != nil { … } = явный контроль.


val, err := strconv.Atoi("42")
if err != nil { return 0, fmt.Errorf("parse: %w", err) }


2️⃣ Когда panic допустима, а когда — нет 🚨
💞panic - для truly unrecoverable: индекс за границей, nil-map.
💓Логика приложения должна возвращать error, а не паниковать.

3️⃣ recover для безопасного сервера 🛡️


func safeHandler(h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
defer func() {
if rec := recover(); rec != nil {
log.Printf("panic: %v", rec)
http.Error(w, "internal error", http.StatusInternalServerError)
}
}()
h(w, r)
}
}


Вывод:
Четко разделяешь error vs panic, умеешь оборачивать и восстанавливать - +100 к доверию и к предложению! 💰
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥27👍1817
RWMutex и atomic: отвечаем про синхронизацию так, чтобы тимлид сказал «берём!» 🌟

1️⃣ Почему одного Mutex-а мало?
⚪️sync.Mutex блокирует и чтение и запись - плохо, когда 99 % операций - чтение.
🟡sync.RWMutex разделяет RLock/Lock: несколько читателей vs один писатель.

var mu sync.RWMutex
func (c *cache) Get(k string) (v int, ok bool) {
mu.RLock(); defer mu.RUnlock()
v, ok = c.data[k]
return
}
func (c *cache) Set(k string, v int) {
mu.Lock(); defer mu.Unlock()
c.data[k] = v
}


2️⃣ atomic: микролок для счётчиков

Для простых чисел используем atomic.AddInt64, atomic.LoadUint32 - дешевле, чем мьютекс

var hits int64
func hit() { atomic.AddInt64(&hits, 1) }
func readHits() int64 { return atomic.LoadInt64(&hits) }


Вывод:
Различаешь случаи Mutex/RWMutex/atomic, умеешь выбирать по нагрузке - +100 очков к уровню senior и к жирному офферу! 💰
Please open Telegram to view this post
VIEW IN TELEGRAM
👍25🔥1812