Як досвід роботи з Go змінив моє бачення PHP

Автономія волі полягає в тому, що воля є сама для себе законом.

(Іммануїл Кант)

За час роботи бекенд-інженером я кілька разів пробував писати на Go. Частково через цікавість, частково через те, що все частіше PHP-інструменти стикаються з Go. Але код здавався чужим і незручним.

В одній з аутсорс-компаній мені пощастило отримати та попрацювати з ментором, який пише на Go і починав саме з цієї мови. Він дивився на неї зовсім інакше, без домішок знань з PHP/Java/C#. Ми домовилися, що цього разу я не кину все на півдорозі.

На першій зустрічі він сказав мені: «Забудь, що ти вчив у PHP. Почни програмувати з Go спочатку». Минув час і, здається, починаю розуміти, що він тоді мав на увазі. Спочатку Go здається незручним і таким, де потрібно купу коду написати, щоб зробити мінімальні речі, звичні в PHP, але з кожним кроком розумієш, чому все зроблено саме так. І тоді потрошки починаєш ловити кайф. А ще по-новому дивитися на сам PHP. І, як не дивно, ця історія, мабуть, більше про нього, ніж про Go.

🐘 І з цього моменту я почав дивитися на мови не як на інструменти, а як на системи мислення. PHP зараз для мене як добре облаштована кухня. Усе поруч: фреймворки, бібліотеки, готові рецепти. Ти просто береш інгредієнти й готуєш, додаючи спецій вкінці. Головне не спалити вечерю. Система працює, навіть якщо ти не заглядаєш під кришку.

🐹 Go зовсім інша історія. Тут немає зайвого. Ну як мінімум, поки не бачив. Кожна дія свідома. І набір інструментів вже дає мова і тобі лишається лише користуватись цими інструментами. Горутіни, канали, проста модель типів, композиція замість класів — усе це змушує трохи спинитись і подумати, що ти взагалі робиш, а не лише про результат. Go не ховає складність, він показує її прямо. І це б’є по голові тим, хто звик до фреймворкової магії.

І можу сказати, що після Go програмування на PHP сприймається інакше. Не як щось старе чи обмежене, просто як інший спосіб мислити.

Подавай заявку на премію DOU!

Інженерний контроль

Підхід Go одразу показує, наскільки важливо тримати технічні деталі під контролем. Коли проектуєш, наприклад, API, важливо думати не тільки про те, як система працює в нормі, а й про те, що станеться, коли все піде не за планом.

Сучасні PHP-фреймворки дають комфорт: DI-контейнери, autowire, middleware, глобальні обробники помилок, статичний аналіз — усе це знімає рутину й дозволяє більше думати про бізнес-логіку. Go не дає сховатися за фреймворком — змушує побачити, що зазвичай відбувається «під капотом».

У PHP це теж можливо, просто більшість із нас давно делегували цей контроль контейнеру чи middleware, а отже, варто усвідомлено визначати, де саме проходить межа між гнучкістю та прозорістю. Це момент, коли з’являється архітектурна зрілість — не лише зручно, а й контрольовано.

Контракти як договір

🐘 У великих PHP-проєктах інтерфейси часто перетворюються на громіздкі декларації, які важко підтримувати. Інтерфейси дозволяють визначити, як компоненти взаємодіють між собою, допомагаючи досягати низького звʼязування (low coupling) і високої когезії (high cohesion). Клас імплементує контракт, а PHP гарантує, що всі методи реалізовані. Це забезпечує сумісність компонентів і дозволяє легко замінювати реалізації, не торкаючись бізнес-логіки.

Схема 1. Контракт окремо від споживача

Контракти можна розміщувати поруч зі споживачем або в загальному каталозі, якщо їх використовують кілька компонентів. Важливо уникати «надутого» інтерфейсу: методи, які не потрібні всім реалізаціям, ускладнюють підтримку, тестування і заміну компонентів.

<?php
declare(strict_types=1);

interface Notifier {
    public function notify(string $message): void;
}

class EmailNotifier implements Notifier {
    public function notify(string $message): void { /* надсилаємо лист */ }
}

class OrderProcessor {
    public function __construct(private Notifier $notifier) {}
    public function process(string $orderId): void {
        $this->notifier->notify("Order {$orderId} processed");
    }
}

class PaymentProcessor {
    public function __construct(private Notifier $notifier) {}
    public function process(string $paymentId): void {
        $this->notifier->notify("Payment {$paymentId} processed");
    }
}

Цей приклад показує, як споживачі взаємодіють із контрактами. Подібний підхід часто використовується у репозиторіях для роботи з даними, де інтерфейс може містити методи, які не потрібні всім реалізаціям. Надлишкові методи ускладнюють тестування та заміну реалізацій, тому важливо тримати контракт мінімальним і сфокусованим на реальних потребах споживача.

src/
├── Interfaces/
│   └── Notifier.php
├── Infrastructure/
│   └── EmailNotifier.php
├── Application/
│   ├── OrderProcessor.php
│   └── PaymentProcessor.php
└── index.php

🐹 У Go інтерфейс народжується біля споживача, а не в загальному каталозі. Він формулює очікування «мені потрібен хтось, хто вміє це зробити».

Схема 2. Контракт це угода між споживачем та постачальником

У Go контрактом фактично є інтерфейс, але визначений споживачем, а не розробником бібліотеки.

Достатньо, щоб методи імплементації збігалися з очікуваними:

// consumer/printer.go
package consumer

type Printer interface {
    Print(s string)
}

func Render(p Printer) {
    p.Print("Hello from consumer!")
}

// provider/myprinter.go
package provider

import "fmt"

type MyPrinter struct{}

func (p MyPrinter) Print(s string) {
    fmt.Println(s)
}

// main.go
package main

import (
    "consumer"
    "provider"
)

func main() {
    var p provider.MyPrinter
    consumer.Render(p) // MyPrinter задовольняє інтерфейс Printer без явного оголошення
}

Цей підхід кардинально відрізняється від звичного для PHP способу роботи з інтерфейсами. Інтерфейс — це не наказ реалізувати все, а домовленість між споживачем і постачальником про очікувану поведінку.

<?php
declare(strict_types=1);

interface OrderNotifier {
    public function notifyOrderCreated(string $orderId): void;
}

interface PaymentNotifier {
    public function notifyPaymentReceived(string $paymentId, float $amount): void;
}

class EmailNotifier implements OrderNotifier, PaymentNotifier {
    public function notifyOrderCreated(string $orderId): void { /* ... */ }
    public function notifyPaymentReceived(string $paymentId, float $amount): void { /* ... */ }
}

class OrderProcessor {
    public function __construct(private OrderNotifier $notifier) {}
    public function process(string $orderId): void {
        $this->notifier->notifyOrderCreated($orderId);
    }
}

class PaymentProcessor {
    public function __construct(private PaymentNotifier $notifier) {}
    public function process(string $paymentId, float $amount): void {
        $this->notifier->notifyPaymentReceived($paymentId, $amount);
    }
}

👉 І саме це змінює те, як дивишся на PHP. Це, по суті, те саме, про що говорить Interface Segregation із SOLID, але в Go цей принцип не декларується, а вимушено проживається. Від універсальних контрактів із десятком методів «про всяк випадок», тепер виникає бажання робити контракти мінімальними рівно настільки, наскільки потрібно конкретному споживачу. І розташовувати їх ближче до місця використання, а не в єдиному «контрактному» каталозі.

src/
├── Application/
│   ├── Order/
│   │   ├── OrderNotifier.php
│   │   └── OrderProcessor.php
│   ├── Payment/
│   │   ├── PaymentNotifier.php
│   │   └── PaymentProcessor.php
├── Infrastructure/
│   └── EmailNotifier.php
└── index.php

У PHP це означає:

  • інтерфейси ближче до місця використання;
  • менше універсальності, більше конкретики;
  • тести стають природним споживачем контракту, перевіряючи не структуру, а поведінку, як Go-компілятор.

Go допомагає усвідомити, що контракт це не просто декларація методів, а вираження наміру. І коли повертаєшся до PHP, будуєш API від взаємодії між частинами системи, а не від класів. Контракт стає живою домовленістю.

Типи як контракт

🐹 Звучить банально — компілятор ловить помилки раніше. Але коли звикаєш до цього в Go, відчуваєш типи не як формальність, а як частину дизайну API.

Go компілятор не дасть зібрати програму:

package main

func Half(n int) float64 {
    return float64(n) / 2
}

func main() {
    var s string = "10"
    // compile-time error
    _ = Half(s)
}

🐘 У PHP із union-типами, strict_types та сучасними інструментами контроль став набагато сильнішим, але перевірка відбувається у рантаймі.

PHP-контроль у рантаймі:

<?php
declare(strict_types=1);

function half(int|float $n): float {
    return $n / 2;
}

echo half(10);   // 5.0

echo half("10"); // Fatal error: Uncaught TypeError

Сьогодні звичним стало:

  • declare(strict_types=1);
  • обов’язковий Psalm та PHPStan (або Larastan) на strict-рівні;
  • тайпгінти для всіх параметрів, повернень і властивостей;
  • readonly, enum, атрибути, Rector/PHPCS для автоматизації стилю.

👉 Після досвіду з Go змінюється майндсет:

  • не покладаєшся лише на аналізатор;
  • ретельніше відслідковуєш, що і куди передаєш, а статичний аналізатор стає страховкою;
  • менше покладаєшся на «магію» фреймворку.
<?php
declare(strict_types=1);

function halfSafe(int|float|string $n): float {
    if (is_string($n)) {
        if (!ctype_digit($n)) {
            throw new InvalidArgumentException('Expected numeric string');
        }
        $n = (int)$n;
    }

    return $n / 2;
}

Тут ти не відмовляєшся від Psalm, а робиш його страховкою.

Помилки завжди явні

🐹 Те, що в інших мовах називається «явна обробка помилок», у Go< просто звичка. І після кількох тижнів ловиш себе на тому, що хочеш такого ж у PHP (не помилку, як значення, а явності =)).

Go змушує думати про типи ще на етапі компіляції, що зменшує несподівані баги. Go без err далі не піде:

package main

import (
    "errors"
    "fmt"
)

func risky() (string, error) {
    return "", errors.New("something went wrong")
}

func main() {
    res, err := risky()
    if err != nil {
        fmt.Println("Error: ", err) // треба обробити тут і зараз
        return
    }
    fmt.Println(res)
}

🐘 У PHP винятки можна перехопити глобально через middleware або Subscriber (як у Symfony). Це виглядає «чисто», адже немає необхідності перевіряти кожен виклик через if. Водночас поведінка помилки часто залишається прихованою від споживача функції.

PHP-винятки дозволяють делегувати обробку:

<?php
declare(strict_types=1);

class ApiExceptionSubscriber
{
    public function onKernelException(ExceptionEvent $event): void
    {
        $exception = $event->getThrowable();

        $response = new JsonResponse(['error' => $exception->getMessage()]);

        $event->setResponse($response);
    }
}

// Приклад функції, яка кидає помилку
function risky(): string {
    throw new RuntimeException('Something went wrong');
}

// Десь у контролері:
echo risky();

// Помилка «пролетить» догори і перехопиться глобальним subscriber

👉 Практика з Go привчає обробляти помилки ближче до джерела, щоб логіка виконання була очевидною. У PHP це означає поєднання локальної обробки та глобального Subscriber:

<?php
declare(strict_types=1);

try {
    echo risky();
} catch (RuntimeException $e) {
    // локальна обробка помилки: логування, альтернативна поведінка
    error_log($e->getMessage());
    echo 'Sorry, something went wrong';
}

// Глобальний Subscriber або middleware відповідає за транспорт:
// HTTP статус, формат повідомлення, без розкриття внутрішніх деталей

Таким чином Go вчить PHP-розробника обробляти помилки локально, навіть якщо глобальний обробник існує. Це підвищує надійність коду, робить його більш послідовним і передбачуваним.

Явна ініціалізація (DI)

🐹 У Go немає контейнера або autowire. Кожна залежність передається явно через конструктор або builder.

  • Одразу зрозуміло, від чого залежить поведінка сервісу.
  • Легко підставити mock-реалізації для тестів.
  • Розробник контролює граф залежностей, і він не росте непомітно.

Go залежності передаються явно:

package main

type Service struct {
    repo Repository
    log  Logger
}

func NewService(r Repository, l Logger) *Service {
    return &Service{repo: r, log: l}
}

Це не нова ідея, просто в Go у тебе немає вибору, і ти відчуваєш на власній шкірі, що таке справжня явність залежностей.

🐘 У сучасному PHP контейнер і autowire дають швидкість розробки, автоматичну ін’єкцію залежностей та менше шаблонного коду. Автowire безпечний, особливо у добре структурованих проектах.

PHP-контейнер і autowire-стандарт:

<?php
declare(strict_types=1);

class Service
{
    public function __construct(
        private Repository $repo,
        private Logger $log
    ) {}
}

// У Symfony/Laravel контейнер сам підставить потрібні сервіси

Go допомагає зрозуміти, чому явна ін’єкція корисна:

  • у критичних сервісах і складних залежностях бажано бачити, що саме підставляється, щоб уникнути невизначених реалізацій;
  • прозорий граф залежностей допомагає підтримувати масштабованість системи;
  • бачиш граф залежностей не як технічну деталь, а як нервову систему застосунку.

У PHP це можна реалізувати, поєднуючи autowire і явну ін’єкцію там, де важливо:

# services.yaml
App\Service\CriticalService:
    arguments:
        $repo: '@App\Repository\CustomRepo'
        $log: '@App\Logger\CustomLogger'
    autowire: false

Такі дрібниці формують культуру явності і це головний урок Go.

👉 Так ти отримуєш баланс: швидкість і зручність autowire там, де це безпечно, і явний контроль там, де це важливо. Це і є той урок, який дає Go: цінувати прозорість і передбачуваність.

Закриття ресурсів

🐹 У Go завжди потрібно явно закривати ресурси (файли, сокети, з’єднання) через defer. Це вбудована практика: ресурс відкрив та ресурс закрий. І ти точно знаєш, коли це станеться. Це знову про інженерний контроль: передбачити й оформити життєвий цикл залежності, а не сподіватися, що «система сама» закриє.

Go-ресурси закриваються явно:

package main

import (
    "fmt"
    "os"
)
func main() {
    f, err := os.Open("file.txt")
    if err != nil {
        // panic використано лише для прикладу
        panic(err)
    }
    defer f.Close() // явне закриття ресурсу після використання
    // працюємо з файлом
    fmt.Println("File opened")
}

🐘 У PHP збирач сміття та обгортки (наприклад, ‘‘PDO’’) закривають більшість ресурсів автоматично, коли об’єкт знищується.

PHP автоматичне закриття є, але контроль важливий:

<?php
declare(strict_types=1);

$fp = fopen('file.txt', 'r');
try {
    // працюємо з файлом
    echo "File opened";
} finally {
    fclose($fp); // явне закриття ресурсу після використання
}

Досвід з Go формує звичку свідомо керувати ресурсами у PHP. Особливо важливо у критичних місцях, де ресурсів багато або вони дорогі. Це забезпечує передбачуваність і контроль:

  • використовувати finally для явного закриття файлів або сокетів;
  • використовувати SplFileObject або генератори із yield для автоматичного, але контрольованого обмеження ресурсів.

👉 Go навчає цінувати явне управління ресурсами. У PHP це означає не покладатися лише на автоматичне закриття об’єктів, а документувати життєвий цикл ресурсів у коді. Коли відкриваєш ресурс, обов’язково плануй його закриття: це робить код більш надійним і зрозумілим для колег і для самого себе.

Архітектурне мислення

У PHP кожен запит — короткий спалах: запустився -> виконався -> зник. Go змусив повністю змінити цю оптику. І найцікавіше, що цей досвід повернув мене до PHP із новим поглядом на архітектуру.

Shared nothing vs long-running

🐘 У PHP запит живе кілька мілісекунд і зникає. Це розслаблює: пам’ять сама «очищається», підключення можна відкрити й забути, транзакція і все знову чисте.

🐹 Go ж навчив мене думати про процес, який ніколи не завершується. Тут уже важливо, щоб нічого не текло: пам’ять, з’єднання, горутини. Щоб процес міг закритися акуратно (graceful shutdown), передати роботу, не втратити дані.

👉 Повернувшись у PHP, я вже інакше дивлюся на код:

  • пул підключень не «магія фреймворку», а відповідальність;
  • ресурси треба контролювати, навіть якщо середовище перезапускає процеси за мене;
  • глобальні сервіси — ворог тестованості та підтримки.

Concurrency як фундамент

У PHP конкурентність це надбудова. У Go це частина мови. Після кількох місяців із goroutines ти вже мислиш інакше: не «як послідовно виконати кроки», а «як організувати потоки роботи».

Це змінює і PHP-код:

  • роблю функції ідемпотентними, бо розумію, що виклик може повторитися;
  • ставлю таймаути на зовнішні сервіси, навіть якщо вони «завжди стабільні»;
  • використовую черги не як «оптимізацію», а як нормальний інструмент масштабування.

👉 Навіть якщо твій код синхронний — думати асинхронно це теж частина інженерної зрілості.

Системний дизайн

Коли довго працюєш із PHP, легко звикнути думати на рівні застосунка: контролери, ORM, події. Після Go приходить усвідомлення іншого рівня архітектури сервісу.

Go змушує враховувати життєвий цикл сервісу, обмеження ресурсів і конкурентність. Цей досвід змінив мій підхід до PHP: навіть у звичайному Symfony-сервісі я починаю дивитися на систему ширше.

  • Не просто «чи працює цей endpoint», а «як система поводиться під навантаженням і при повторних запитах».
  • Не тільки «як обробити запит», а «що трапиться, якщо щось піде не так, і чи видно це у логах та метриках».
  • Не лише «архітектура коду», а «архітектура сервісу», як частина великої системи, де кожен компонент може падати або сповільнюватися.

👉 Тобто Go не дає чарівної палички, але змушує мислити більш фундаментально. Цей погляд наділяє PHP-код відчуттям стійкості та передбачуваності, навіть коли він живе в stateless-середовищі.

Кодова економіка

Баланс між явністю коду та вартістю підтримки

Кодова економіка є балансом між явністю коду й ціною його підтримки.

👉 Зручність писати ≠ зручність підтримувати.

  • У Go ти пишеш більше явного коду на старті: DI руками, явна обробка помилок, явне закриття ресурсів. Немає магічних фасадів, прихованих слухачів подій чи багаторівневих структур.
  • У PHP фреймворки дають швидкий старт менше коду, але за це платиш більшою кількістю прихованої магії. Це зручно сьогодні, але з часом часто перетворюється на техборг.

Швидкий старт

Розглянемо приклад із Laravel, який добре ілюструє, як можна швидко накидати MVP і запустити працюючий продукт.

<?php
declare(strict_types=1);

// routes/web.php
Route::post('/register', [AuthController::class, 'register']);

// AuthController.php
public function register(Request $request)
{
    $user = User::create($request->all());
    event(new Registered($user));
    return response()->json(['ok' => true]);
}

На старті краса: мінімум коду, все працює.

Але:

  • валідація відбувається «десь» у middleware;
  • надсилання листа «десь» у слухачі;
  • транзакції «десь» у базовому репозиторії.

👉 Через рік, коли кількість фіч і слухачів зростає, стає складно зібрати в голові повний ланцюжок того, що відбувається при простій дії, наприклад, під час реєстрації.

Більше явності

Альтернативний підхід писати більше явного коду. Наприклад, у тому ж PHP це може виглядати так:

<?php
declare(strict_types=1);

final readonly class RegistrationData
{
    public function __construct(
        public string $email,
        public string $name,
        public string $password,
    ) {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException('Invalid email: ' . $email);
        }
        if (empty($name)) {
            throw new InvalidArgumentException('Name cannot be empty');
        }
        if (mb_strlen($password) < 6) {
            throw new InvalidArgumentException('Password must be at least 6 characters');
        }
    }
}

final readonly class RegistrationService
{
    public function __construct(
        private Mailer $mailer,
        private UserRepository $repo,
    ) {}

    public function register(RegistrationData $data): User
    {
        $user = $this->repo->create($data);

        $this->mailer->sendWelcome($user);

        return $user;
    }
}

Так, «рядків більше», але це має відчутні плюси:

  • усі залежності видно відразу;
  • зрозуміло, де відбувається валідація і коли надсилається лист;
  • тестування та заміна компонентів стають простими і контрольованими.

Це саме той підхід, якого вчить Go:

  • показуй залежності;
  • не ховай побічні ефекти;
  • контролюй життєвий цикл об’єктів.

Замість висновків

👉 Go не робить магії у PHP, але змушує по-новому цінувати дисципліну: ти бачиш, як мова може диктувати процеси і стандарти, і тепер у PHP усвідомлено обираєш інструменти і інтегруєш їх, а не просто домовляєшся «про свій стиль».

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті

👍ПодобаєтьсяСподобалось17
До обраногоВ обраному5
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Спробуйте ще Rust :)

Спробуйте ще по-писати на Rust :)

Останній раз писав на PHP, десь у 2017 році. Потім перейшов на Node.js, написав кілька REST API з тестами і т.п. Потім війна, звільнення, блекаути. Починав з цікавості знайомства з мовою, зараз пишу свій власний проект на Go.
Але добре коли є ментор, або робота яку пишуть інші, бо трохи складно орієнтуватись із накопиченими знаннями, оскільки функційність та філософія Go справді побудована інакше. Все набуте з інших мов справді не допоможе.
Але мені до вподоби ця мова, хоч я ще повністю не збагнув що саме мене в ній чіпляє.

До речі Go дуже чудовий вибір для створення WEB застосунків, оскільки він чудово для цього орієнтований. А ще він компільюється, має статичну типізацію, безпечний щодо використання оперативної пам’яті, дуже швидкий, має можливість будування асинхронно-орієнтованої архітектури (канали, горутини) та інший функціонал з конкурентністю (race condition), підтримка генерації сертифікатів, високопродуктивний в математичних обчисленнях і ще багато чого цікавого :)

Тож це дуже гарний конкурент Python та Node.js у створенні WEB застосунків.

Будьте обережним, горутини текуть, а слайси і мапи мають цікаву специфіку (ємність). Також враховуйте go не сама швидка мова (java по cpu я бачив по тестам обгоняла go) також gurbarge collector не самий кращий (але вони цого робили під суто свої потреби). У останьому оновлеі вони його покращили, але саме утілітах і мікросервисах його сила. І з чистим web варто бути обережним.

Так, мабуть не варто завантажувати величезні дані в слайси та map, а ще певно не варто їх робити глобальними та інколи слід використовувати copy.

Горутини я не часто використовую. Принаймні поки що, оскільки ще не досить гарно підкований. Але бачу їх ефективність в якості функціональної парадигми.
Було конкретне завдання з графами, потрібно було віднайти в БД дублікати, їх було дуже багато. Тупий перебір налічував сотні мільйонів ітерацій, кластеризація на графах з використанням горутин дуже швидко виконала це завдання, не враховуючи допідготовку інформації та специфічне індексування БД.
Але ваша правда, це специфічне завдання більш кваліфікується до утиліт. Хоча подібна функціональність може бути використана в якості адміністрування.

От щодо застереження «чистого web» я не зрозумів... Розкриєте цю тему?

«чистий web» CRUD маю на увазі. Стосовно «Так, мабуть не варто завантажувати величезні дані в слайси та map, а ще певно не варто їх робити глобальними та інколи слід використовувати copy.» — я сам ще не перевіряв повністью споживання пам’яті, та мав на увазі що навіть 50 записів для slice, і дуже інтесивно створювати таку структуру (віддавати список записів для різних користувачів), може стати тягарем для garbage collector. garbage collector описувався у GO як дуже спрощений, та не самий ефективний (хоча у 1.25 його наче прокачали). Але тільки метрики це можуть показати, може колись потестую. Я бачив навіть як людина щоб на валідатор форм не створювати кожного разу об’єкт ліби валідації, створив pool таких об’єктів. Вже в цей пул передавав самі значення форми. Чесно у php or node js я такого не бачив раніше, так частіше кожного разу новий інстанс ліби створюють.

А розбаратись потім в коді де кожна друга строчка це

if err != nil {

мабуть пекло. Читаю зараз Роберта Мартіна, Go влаштована наче спеціально навпаки. Замість того щоб розділити логіку і обробку помилок вони навалені на купу. Замість того щоб використовувати наслідування і композицію в залежності від потреб наслідування просто немає. Функції які повертають кілька результатів це взагалі жах. Імпорт пакетів через лінк на гітхаб це взагалі як костиль виглядає. Що як розробник захоче репозиторій перейменувати?
Ну і у мене завжди було питання чому в го на символах в назвах змінних економлять?

Що саме з Роберта Мартіна ви читаєте? Бо є підозра на чисту архітектуру, яка, до речі, була подана досить давно, суто актуальна для того часу зоопарку мов та технологій. До того ж Роберт інколи приводить приклади деяких застарілих мов, та й взагалі не варто сприймати все викладене в книзі буквально та одразу ж інтерпретувати всюди де не ліньки. Оскільки, як і сам Роберт згадує — багато чого залежить від інфраструктури мови, яка накладає певні обмеження. Якщо вже розглядати в контексті Go, звісно що подібне слід розглядати враховуючи інфраструктуру, функціонал мови та дотримуватись філософії яку регламентує спільнота, де явна обробка помилок одна з філософії будування надійного коду, що з часом просто перетворюється у звичку.

Імпорт пакетів через лінк на гітхаб це взагалі як костиль виглядає. Що як розробник захоче репозиторій перейменувати?

Розробник модуля може перейменувати репозиторій. Але це вже буде інший модуль.

Ну і у мене завжди було питання чому в го на символах в назвах змінних економлять?

Russ Cox пояснив це у своєму блозі: «A name’s length should not exceed its information content». Він один з інженерів, які будували Go. Або в цій відповіді на Reddit є посилання на ресурси, чому саме так називаються змінні.

Ганра стаття і цікаві порівняння. Дякую

Непогана стаття. Якщо б ви з самого початку писали код на PHP, так само як на GO зараз, то у вас не було б питань.
Я так розумію, ви пішли по загальному шляху — спочатку вивчення фрейморків, потім програмування.

Досвіду в мене поки не багато. Почав я з написання сайту з 0 на базі власного фреймворку. Виклав код на github та запустив live preview. Зробив це задля того, щоб зрозуміти як все працює. Зараз, вивчаючи Laravel, маю своє бачення реалізації того чи іншого.

Коли мене питають в середовищі PHP, навіщо ти все це робиш, це зайва витрата часу. Відповідь їм — ваша стаття.

Радію, що відгукується на ваш досвід

Базовий go як php на початку 2000-х, коли не було сталих фреймворків, кожен вигадував своє колесо та була купа boilerplate коду.
В Uber і свій DI на go є, та і взагалі коли коду багато, то хочеться більше стандартизованих підходів, більше абстракцій та меньше рядків коду.

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

Можливо, не в курсі деталей. Якщо це невеликий мікросервіс, чи вузькоспеціалізоване рішення: web/proxy/media server, high performance щось з мінімумом алокацій, то там такого не треба. Але коли business layer, то більш цікаво зосередитись на високо-рівневих задачах, ніж обробити помилку тут і зараз. Хоча навіщо тоді go, але ж порівняння і на таких задачах.

Більше коду — більше когнітивне навантаження на мозок розробника.

Тому ІМХО, оці всі явні обробки помилок, відсутність автовайрингу, ітд — просто засмічують код до неймовірності, без особливого профіту 😀

Можливо, якщо писати ендпоінти на 3 рядочки, то воно дає відчуття «ото я крутий інженер, кручу тут всякими ресурсами, горутинами, контрактами». Але я бачив проекти з такою бізнес логікою на php, що навіть з трьома рівнями абстракцій треба витратити декілька днів аби зрозуміти як працює той, чи інший функціонал.

Мабуть, тому ніхто в здоровому розумі і не пише сайти на C++ 🤷‍♂️

Ну і до речі

У Go немає контейнера або autowire. Кожна залежність передається явно через конструктор або builder.
Одразу зрозуміло, від чого залежить поведінка сервісу.
Легко підставити mock-реалізації для тестів.

А далі приклад коду на PHP

class Service
{
public function __construct(
private Repository $repo,
private Logger $log
) {}
}

Так це ж якраз GO-way, ні?) Замість реалізації — контракти (інтерфейси). Зробити мок — супер легко. Від чого залежить поведінка? Та від цих інтерфейсів!

Короче, так і не зрозумів, чому це відсутність нормального DI це плюс, а не величезний мінус)

Для себе все ще роблю висновок, що писати web на golang — це щось з розряду «тролейбус з буханки хлібу». Якийсь низкорівневий код, мікроконтроллери, щось performance-critical — так, погоджуюсь, Go має сенс. Але типовий проект на PHP, який крутить туди-сюди JSON-и і реалізує якусь бухгалтерську логіку умовної Карен із Каліфорнії — навіщо? PHP якраз вигадали для того, щоб підняти рівень абстракції. І дивлячись на те, скільки років воно живе, і яку частку інтернету відтяпало — це має сенс.

Більше коду — більше когнітивне навантаження на мозок розробника.

Ну... проблема не в кількості, а в складності та у звʼязності. Так, коду можу бути небагато, але коли під кожною операцією сховано щось, що може впливати... то навантаження стає лише більше, бо треба розкручувати.

Майже те саме можу сказати про python/go

дія перша: PHP народжується, славімо його
дія друга: на сцену виходить J2EE — строга типізація, громіздка іерархічна структура класів, абсурдна місцями абстракція. Натовп шаленіє. PHP — холд май бір!
протоходить 15 років
дія третя: на сцену виходить Go — лаконічні контракти по місцю використання, PHP — холд май бір...

чекаємо умовний PHP 10.

ПС

Сьогодні звичним стало:

declare(strict_types=1);

та ого, та де?

Я б тут не шукав циклічну еволюцію. Мови рухаються не одна за одною, а від різних вимог

Але історія саме пиха, поки що, показує інше

Класна стаття 👍
Дуже підтримую те, що в довгострокових проектах краще пожертвувати «магією» для того, щоб проект був підтримуваний в майбутньому 🙌

Що зробили слонику що в нього з дупи стирчить рука?

Теж про це подумав, але потім придивився — а воно наче на ракету схоже, яка влетіла слонику в оте саме місце :D

Дякую за статтю!

Однозначно з PHP треба переходити на Go, маючи хоча б 3–5 років досвіду: і проєкти цікавіші, і винагороди вищі (виняток — компанії з екосистеми Genesis, бо там оплата «зірочками»).

навіть у звичайному Symfony-сервісі я починаю дивитися на систему ширше.
Не просто «чи працює цей endpoint», а «як система поводиться під навантаженням і при повторних запитах».

Не думав як система поводиться під навантаженнями ?))

Підписатись на коментарі