Какими принципами разработки вы руководствуетесь? Как они применяются в Go?

Ответ

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

SOLID — это основа объектно-ориентированного дизайна, но его принципы отлично адаптируются для Go:

  • S (Single Responsibility Principle) — Принцип единственной ответственности. В Go это выражается в создании небольших, сфокусированных пакетов, структур и функций, каждая из которых решает одну конкретную задачу.
  • O (Open/Closed Principle) — Принцип открытости/закрытости. В Go достигается через композицию (встраивание структур) и интерфейсы, а не через наследование. Мы можем расширять функциональность, создавая новые типы, удовлетворяющие интерфейсу, не изменяя существующий код.
  • L (Liskov Substitution Principle) — Принцип подстановки Барбары Лисков. Поскольку в Go нет наследования, принцип применяется к интерфейсам. Любая реализация интерфейса должна соответствовать его контракту, не нарушая ожиданий клиента.
  • I (Interface Segregation Principle) — Принцип разделения интерфейса. Это ключевой принцип в Go. Язык поощряет создание маленьких, узкоспециализированных интерфейсов (например, io.Reader). Клиенты должны зависеть только от тех методов, которые им действительно нужны.
  • D (Dependency Inversion Principle) — Принцип инверсии зависимостей. Модули верхних уровней не должны зависеть от модулей нижних уровней. И те, и другие должны зависеть от абстракций (интерфейсов). Это делает код модульным, слабо связанным и легко тестируемым.

    // Модуль зависит от интерфейса Storage, а не от конкретной реализации
    type Storage interface {
        Save(data string) error
    }
    
    type Service struct {
        storage Storage // Зависимость через интерфейс (DIP)
    }

Другие важные принципы:

  • DRY (Don’t Repeat Yourself) — Не повторяйся. Избегание дублирования кода через вынесение общей логики в переиспользуемые функции и пакеты.
  • KISS (Keep It Simple, Stupid) — Делай проще. Философия Go полностью соответствует этому принципу. Предпочтение отдается простому и понятному коду, а не сложным абстракциям.
  • YAGNI (You Aren’t Gonna Need It) — Вам это не понадобится. Не добавлять функциональность "про запас". Это помогает сохранять кодовую базу чистой и сфокусированной на текущих требованиях.

Ответ 18+ 🔞

Так, слушай сюда, про эти ваши принципы. Ну, SOLID, блядь. Все их талдычат, а половина нихуя не понимает, как это в Go прикрутить. Ёпта, сейчас разжую.

Ну, S — это типа одна штука — одна работа. Не делай из своей функции или структуры швейцарский нож, который и бутерброд намажет, и в космос слетает. Сделал SendEmail — пусть только письма и шлет, а не логи пишет и в базу статистику кладет. Просто, как три копейки.

O — открытость/закрытость. В Го нету этого вашего наследования, слава богу. Тут всё через интерфейсы и композицию. Хочешь новую фичу? Напиши новый тип, который впишется в старый интерфейс, и подсунь его. Старый код даже не чихнет. Красота.

L — подстановка Лисков. Короче, если твой тип говорит, что он удовлетворяет интерфейсу Reader, то он должен читать, сука, а не притворяться читателем, а сам втихаря писать в лог. Не обманывай компилятор, а то он тебе потом отомстит.

I — а вот это, блядь, святое в Go! Принцип разделения интерфейса. Не лепи кучу методов в один интерфейс на все случаи жизни. Сделай маленькие, как io.Reader и io.Writer. Хочешь и читать, и писать? Так объедини их, ёпта: type ReadWriter interface { Reader; Writer }. Клиент берет только то, что ему надо, и не тащит за собой овердохуища ненужного кода. Гениально и просто.

D — инверсия зависимостей. Мозг немного сломать можно, но суть простая: высокоуровневый модуль не должен знать, с какой конкретно базой или API он работает. Он должен знать только абстракцию — интерфейс. Смотри:

// Модуль зависит от интерфейса Storage, а не от конкретной реализации
type Storage interface {
    Save(data string) error
}

type Service struct {
    storage Storage // Зависимость через интерфейс (DIP)
}

Вот так. Подсовываешь ему в тестах мок, в продакшене — постгресс, а он и не заметит подмены. Гибко, тестируемо, красиво. Пиздец как правильно.

Ну и остальная хуйня:

  • DRY — не повторяйся, долбоёб. Вынес общую логику в функцию. Нашел копипасту в трёх местах — значит, ты уже облажался.
  • KISS — делай проще, идиот. Это вообще девиз Go. Не выёбывайся с навороченными паттернами, если можно решить задачу парой прямолинейных функций. Компилятор и коллеги скажут спасибо.
  • YAGNI — тебе это не понадобится. Не пиши архитектуру для марсианской колонии, когда делаешь сайт-визитку. Сделал то, что просили, и иди нахуй. Всё остальное — преждевременная оптимизация и вынос мозга самому себе.

Вот и вся философия. Ничего сложного, если голову включать. А если не включать — получишь лапшу, которую через месяц и сам не разберёшь.