Golang | Вопросы собесов
4.33K subscribers
28 photos
694 links
Download Telegram
🤔 Знакомы ли с концепцией 12FA для проектирования SaaS приложений?

Да, знаком. 12FA (12-Factor App) — это набор принципов, созданных разработчиками Heroku для построения масштабируемых, надежных и удобных в развертывании SaaS-приложений. Эти принципы особенно полезны при разработке облачных сервисов (Cloud-Native).

🚩Основные 12 факторов

🟠Кодовая база (Codebase)
У приложения должна быть единая кодовая база (один репозиторий), независимо от количества развертываний (production, staging, dev).

🟠Зависимости (Dependencies)
Все зависимости должны явно указываться в go.mod / go.sum (для Go). Никаких глобальных зависимостей в системе.

🟠Конфигурация (Config)
Конфигурация должна храниться в переменных окружения, а не в коде.
export DATABASE_URL="postgres://user:pass@host:5432/db"



🟠Бэкенд-сервисы (Backing Services)
Внешние сервисы (БД, кэш, API) должны быть заменяемыми и подключаться через URL (без хардкода).

🟠Постоянная сборка, запуск и запуск (Build, Release, Run)
Сборка, релиз и запуск должны быть разделены. Например, Docker-контейнеры для каждой стадии.

🟠Процессы (Processes)
Приложение должно быть бесстатичным (не хранить файлы локально, использовать БД, S3 и т. д.).

🟠Привязка портов (Port Binding)
Приложение должно быть самодостаточным и слушать порт (например, через http.ListenAndServe).

🟠Параллелизм (Concurrency)
Масштабируемость должна обеспечиваться горизонтальным масштабированием (разделением на процессы).

🟠Отказоустойчивость (Disposability)
Приложение должно быстро запускаться и корректно завершаться (например, ловить SIGTERM).

🟠Совместимость Dev/Prod (Dev/Prod Parity)
Среды разработки и продакшена должны быть максимально похожи.

🟠Логирование (Logs)
Логи должны писаться в стандартный вывод и обрабатываться внешними системами (ELK, Loki, Grafana).

🟠Админ-процессы (Admin Processes)
Скрипты администрирования (миграции, отладка) должны выполняться как отдельные процессы.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Чем отличается интерфейс Go от интерфейсов в других языках?

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


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Как преобразовать строку в int и наоборот?

В Go есть несколько способов преобразования строки в число и числа в строку.

🟠Преобразование строки в `int`
Используется пакет strconv и функция strconv.Atoi() или strconv.ParseInt().
package main

import (
"fmt"
"strconv"
)

func main() {
str := "42"
num, err := strconv.Atoi(str) // Преобразуем строку в int
if err != nil {
fmt.Println("Ошибка:", err)
return
}
fmt.Println("Число:", num) // Выведет: Число: 42
}


Пример 2: strconv.ParseInt() (гибкое преобразование)
num, err := strconv.ParseInt("1234", 10, 64) 
// "1234" → строка, 10 → десятичная система, 64 → int64

if err != nil {
fmt.Println("Ошибка:", err)
} else {
fmt.Println("Число:", num) // 1234
}


🟠Преобразование `int` в строку
Используется strconv.Itoa() или strconv.FormatInt().
num := 42
str := strconv.Itoa(num) // 42 → "42"
fmt.Println("Строка:", str) // "42"


Пример 2: strconv.FormatInt() (int64 → строка)
num := int64(12345)
str := strconv.FormatInt(num, 10) // 10 — десятичная система
fmt.Println("Строка:", str) // "12345"


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какие данные не стоит индексировать?

Индексы занимают память и замедляют вставку/обновление. Не стоит индексировать:
- Часто изменяемые поля.
- Поля с высокой избыточностью (например, булевы значения, где 90% значений — true).
- Очень длинные строки (текст, JSONB без надобности).
- Маленькие таблицы, где поиск и так быстрый.
- Поля, по которым не происходит выборок или фильтраций.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Зачем нужен Prometheus?

Это система мониторинга и оповещения с открытым исходным кодом, разработанная для сбора и анализа метрик с серверов, приложений и других компонентов инфраструктуры.

🚩Почему нужен Prometheus?

🟠Мониторинг состояния системы
позволяет отслеживать производительность, загрузку CPU, память, сетевой трафик и другие параметры.
🟠Автоматический сбор метрик
использует модель pull (запрашивает данные у сервисов), а не push (как, например, Graphite). Это удобнее для управления и отказоустойчивости.
🟠Гибкость в сборе данных
поддерживает кастомные метрики, которые можно интегрировать в своё приложение.
🟠Продвинутая система алертинга
позволяет настроить уведомления при достижении критических значений.
🟠Хранение исторических данных
помогает анализировать тренды и предсказывать возможные сбои.
🟠Масштабируемость
легко развертывается в облаке, Kubernetes, Docker и других средах.

🚩Как работает Prometheus?

🟠Экспортеры метрик
собирают данные.
🟠Prometheus
опрашивает эти экспортеры по HTTP (pull-модель).
🟠PromQL
(язык запросов) используется для анализа данных.
🟠Grafana
или другие инструменты визуализируют метрики.
🟠Alertmanager
отправляет уведомления (Slack, Email, Telegram и др.).

🚩Пример использования в Go-приложении

Для интеграции в Go-приложение можно использовать пакет prometheus/client_golang
package main

import (
"net/http"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)

var httpRequests = prometheus.NewCounter(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests",
},
)

func handler(w http.ResponseWriter, r *http.Request) {
httpRequests.Inc() // Увеличиваем счетчик запросов
w.Write([]byte("Hello, Prometheus!"))
}

func main() {
// Регистрируем метрику
prometheus.MustRegister(httpRequests)

// Эндпоинт для сбора метрик
http.Handle("/metrics", promhttp.Handler())
http.HandleFunc("/", handler)

http.ListenAndServe(":8080", nil)
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какой паттерн использовал в продукте/своем коде?

В реальных проектах часто используются:
- Builder — для конфигурации сложных объектов (например, HTTP-клиентов).
- Factory — для создания сервисов по интерфейсу.
- Singleton — для централизованного логгера или глобальных параметров.
- Также могут применяться Strategy, Adapter, Observer, особенно при построении архитектуры с плагинами, хранилищами, UI-обработкой.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Зачем используется дополнительный блок фигурных скобок в функции?

В Go дополнительный блок фигурных скобок {} в функции используется для ограничения области видимости переменных и улучшения читаемости кода. Это позволяет временно вводить переменные или выполнять локальные операции без влияния на остальную часть функции.

🚩Зачем это нужно

🟠Ограничение области видимости переменных
Переменные, объявленные внутри блока {}, видимы только в пределах этого блока. Это полезно для временных переменных, которые не должны "загрязнять" основную область видимости функции.
func processData() {
{
value := 42
fmt.Println("Внутренний блок, value =", value) // value доступна только здесь
}
// fmt.Println(value) // Ошибка: value не определена за пределами блока
}


🟠Управление временем жизни ресурсов
С помощью блоков можно явно управлять временем жизни переменных, таких как файлы, соединения или буферы. Это особенно важно для ресурсов, которые необходимо закрыть или освободить.
func readFile() {
{
file, err := os.Open("example.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close() // Закроется при выходе из блока

// Работа с файлом
fmt.Println("Чтение файла")
}
// Здесь файл уже закрыт
}


🟠Локализация кода для улучшения читаемости
В больших функциях дополнительный блок позволяет логически разделить код на части, делая его более понятным.
func main() {
{
// Работа с настройками
config := loadConfig()
fmt.Println("Конфигурация загружена:", config)
}

{
// Работа с данными
data := fetchData()
fmt.Println("Данные получены:", data)
}
}


🟠Проверка и тестирование отдельных блоков
Можно использовать дополнительные блоки для изоляции сложных кусков кода и последующего их упрощения.
func calculate() int {
result := 0
{
a := 10
b := 20
result = a + b
}
return result
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 В чём разница шардирования и партиционирования?

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


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Можно ли изменить определенный символ в строке?

Нет, строки (string) в Go неизменяемы (immutable). Это значит, что нельзя просто изменить один символ в строке.

🚩Почему нельзя изменить символ?

Строка в Go — это байтовый срез ([]byte), но неизменяемый. Когда вы создаёте строку
s := "hello"


Ошибка при попытке изменения символа напрямую
s := "hello"
s[0] = 'H' // Ошибка: cannot assign to s[0]


🚩Как изменить символ в строке?

Поскольку строка неизменяема, вам нужно создать новую строку с заменённым символом.
Способ 1: Преобразовать в []byte (для ASCII-строк)
Если строка содержит только английские буквы и символы ASCII, её можно преобразовать в []byte, заменить символ и создать новую строку.
package main

import "fmt"

func main() {
s := "hello"
b := []byte(s) // Преобразуем в изменяемый []byte
b[0] = 'H' // Меняем первый символ
s = string(b) // Преобразуем обратно в строку

fmt.Println(s) // "Hello"
}


Способ 2: Преобразовать в []rune (для Unicode)
Если строка содержит русские буквы, эмодзи или другие многобайтовые символы, используйте []rune.
package main

import "fmt"

func main() {
s := "привет"
r := []rune(s) // Преобразуем в []rune (массив символов)
r[0] = 'П' // Меняем первый символ
s = string(r) // Преобразуем обратно в строку

fmt.Println(s) // "Привет"
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что такое репликация?

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

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Как расшифровывается аббревиатура ACID?

ACID — это набор свойств транзакций в базах данных, обеспечивающий надежность и целостность данных.

🚩ACID

🟠A (Atomicity) — Атомарность
Транзакция выполняется либо полностью, либо не выполняется вовсе.
Пример: Если при переводе денег со счета A на счет B ошибка произошла на полпути, изменения откатываются.

🟠C (Consistency) — Согласованность
Данные остаются в правильном состоянии до и после транзакции.
Пример: Если сумма на всех счетах банка должна оставаться неизменной, транзакция не должна нарушить это правило.

🟠I (Isolation) — Изолированность
Одновременные транзакции не мешают друг другу.
Пример: Два клиента покупают один и тот же товар, но база данных правильно обрабатывает, кто купил первым.

🟠D (Durability) — Долговечность
После завершения транзакции изменения сохраняются, даже если система сломается.
Пример: Если заказ оформлен, он не исчезнет при внезапном отключении электричества.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Как называется механизм сборки мусора?

Go использует трассирующий сборщик мусора с метками (mark-and-sweep).
Он проходит в два этапа:
1. Mark — находят все доступные (живые) объекты.
2. Sweep — освобождают недоступные (мертвые) объекты.
GC работает инкрементально и в параллель с приложением, начиная с Go 1.5.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Можно ли сделать int(string) и string(int) соответственно?

Существуют встроенные функции для преобразования типов, включая преобразование из строки в целое число и наоборот. Для этих целей используются функции из стандартной библиотеки strconv. Рассмотрим, как это делается.

🚩Преобразование строки в целое число

Для этого используется функция strconv.Atoi. Она возвращает два значения: само число и ошибку, если преобразование не удалось.
package main

import (
"fmt"
"strconv"
)

func main() {
str := "123"
num, err := strconv.Atoi(str)
if err != nil {
fmt.Println("Error converting string to int:", err)
} else {
fmt.Println("Converted number:", num)
}
}


🚩Преобразование целого числа в строку

Для этого используется функция strconv.Itoa.
package main

import (
"fmt"
"strconv"
)

func main() {
num := 123
str := strconv.Itoa(num)
fmt.Println("Converted string:", str)
}


🚩Обработка ошибок при преобразовании

Важно обрабатывать ошибки при преобразовании типов, особенно при преобразовании строки в целое число, чтобы избежать неожиданных сбоев в программе.
package main

import (
"fmt"
"strconv"
)

func main() {
str := "abc"
num, err := strconv.Atoi(str)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Converted number:", num)
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что находится перед названием метода?

Перед названием метода указывается ресивер — переменная, к которой метод привязан. Он описывается в круглых скобках перед именем метода и указывает, для какого типа метод определён.
Здесь u *User — это ресивер. Он определяет, что метод PrintName относится к типу User, и при вызове будет доступ к его полям.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Сколько весит число в байтах?

Размер числа в байтах зависит от его типа данных. В Go есть несколько числовых типов, и каждый занимает определённое количество байтов в памяти.

🚩Проверка размера в Go (`unsafe.Sizeof`)

Можно проверить размер типа с помощью unsafe.Sizeof()
package main

import (
"fmt"
"unsafe"
)

func main() {
var a int64
var b float64
var c byte // то же самое, что uint8
fmt.Println("int64:", unsafe.Sizeof(a)) // 8
fmt.Println("float64:", unsafe.Sizeof(b)) // 8
fmt.Println("byte:", unsafe.Sizeof(c)) // 1
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что такое структура (struct) в Go? Зачем они нужны?

struct — это пользовательский тип данных в Go, который группирует поля с разными типами в единый объект. Структуры используются для моделирования сложных сущностей, таких как объекты реального мира, с их свойствами и поведением. Они позволяют организовать данные и методы, что делает код более читаемым и логичным. В отличие от классов в ООП, структуры Go не поддерживают наследование, но позволяют использовать композицию.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Как происходит поиск по ключу в map?

Карты (maps) реализованы на основе хеш-таблиц, что обеспечивает быстрый доступ к значениям по ключам. Давайте рассмотрим, как происходит поиск по ключу в карте, и какие этапы включены в этот процесс.

🚩Основные этапы

🟠Хеширование ключа
Сначала вычисляется хеш-значение ключа. Функция хеширования преобразует ключ в целое число, которое служит индексом в хеш-таблице.

🟠Поиск в хеш-таблице
Хеш-значение используется для доступа к соответствующей "ячейке" или "корзине" (bucket) в хеш-таблице.

🟠Поиск в корзине
Если корзина содержит несколько элементов (из-за коллизий хеширования), Go выполняет линейный поиск среди этих элементов, сравнивая ключи с использованием оператора ==.

🚩Детали

🟠Хеширование ключа
Когда вы пытаетесь получить значение по ключу, Go сначала вычисляет хеш-значение этого ключа. Хеш-функция берет ключ (например, строку или целое число) и преобразует его в индекс хеш-таблицы.

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

🟠Линейный поиск внутри корзины
Если корзина содержит несколько элементов, Go выполняет линейный поиск среди этих элементов. Для каждого элемента в корзине сравнивается ключ с искомым ключом с использованием оператора ==. Если ключи совпадают, возвращается соответствующее значение. Если ключ не найден, возвращается нулевое значение типа (zero value) и флаг, указывающий на отсутствие ключа.

package main

import "fmt"

func main() {
myMap := map[string]int{
"Alice": 25,
"Bob": 30,
}

value, exists := myMap["Alice"]
if exists {
fmt.Println("Alice:", value) // Alice: 25
} else {
fmt.Println("Alice not found")
}

value, exists = myMap["Charlie"]
if exists {
fmt.Println("Charlie:", value)
} else {
fmt.Println("Charlie not found") // Charlie not found
}
}


🚩Подводные камни и особенности

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

🟠Производительность
В среднем, доступ к элементу в карте осуществляется за константное время O(1), что делает карты очень эффективными для поиска по ключу. Однако в худшем случае, при большой нагрузке коллизий, производительность может деградировать до линейного времени O(n).

🟠Изменение карты во время поиска
Карты не являются потокобезопасными. Если одна горутина изменяет карту, в то время как другая горутина читает из нее, это может привести к панике. Для обеспечения потокобезопасности используйте мьютексы или структуру sync.Map.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Чем юнит-тесты отличаются от интеграционных?

- Юнит-тесты проверяют отдельные функции или методы в изоляции от остального кода. Быстрые, лёгкие, часто запускаются.
- Интеграционные тесты проверяют взаимодействие между компонентами, например, работу сервиса с базой или API с внешней системой.
Юнит-тесты дают быструю обратную связь, интеграционные — показывают, как работает система целиком.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM