Go öğrenmeye karar verdiniz. Yıllardır Laravel veya Symfony ile çalışıyorsunuz. Gözlerinizi kapatıp Eloquent yazabilirsiniz, hangi konteyner bağlama işleminin ne zaman gerçekleştiğini biliyorsunuz ve PHP-FPM ile Octane talep yaşam döngüsü arasındaki fark sizin için alışkanlık haline gelmiş.
Ancak bunların hiçbiri, ilk üç gün size beklediğiniz şekilde yardımcı olmayacak.
Bunu gelecek dört hafta için bir harita olarak düşünün. Haftalara sıralanmış, dilin sizinle savaşmayı bıraktığı özel anlardan oluşuyor.
1. Hafta — Hayal Kırıklığı
1. Hafta — Hayal Kırıklığı
İlk hafta, ekranınıza bakıp her şeyin nereye gittiğini merak etmekle geçiyor.
1. Gün: Django nerede
1. Gün: Django nerede
Go’yu kurdunuz. go run main.go yazdınız. Çalışıyor. Burada bir composer install, bir .env dosyası, bir php artisan serve komutu, mürekkep dolu bir vendor/ dizini yok. Bir dosyanız var. O dosya çalışıyor.
Bu bir rahatlama gibi görünse de, değil. Günün sonunda böyle bir şey yazmış olacaksınız:
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("https://dev.to/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "hello")
})
http.ListenAndServe(":8080", nil)
}
Ve orada, yönlendirme konfigürasyonunun nerede olduğunu, ara katman listesine nereden ulaşacağınızı ve kontrolcü çözümleme işleminin nerede olduğunu merak ediyorsunuz. Dürüst cevap: henüz hiçbirinde. Bu katmanları kendiniz yazmalısınız veya ihtiyaç duyduğunuzda küçük kütüphaneler olarak getirirsiniz. On yıl boyunca “çerçeve kodunuzu çağırır,” Go ise kodunuzu kendiniz çağırmanızı istiyor.
3. Gün: Tinker’a Ulaşmak
3. Gün: Tinker’a Ulaşmak
Üçüncü gün, bir şeyleri incelemek istiyorsunuz. Bir yapılandırma ayrıştırıcı yazdınız ve sonucu görmek istiyorsunuz. Laravel’de php artisan tinker yazar, kodunuzu yapıştırır ve çıktıyı okurdunuz.
Go’da REPL yok. “Go REPL” için Google araması yapacak ve gore, yaegi ve birkaç yarı bakımda olan aracı bulacaksınız. Hiçbiri doğru gelmeyecek. Kazanan model, üçüncü günde inanmayacağınız, bir test yazmak.
func TestParseConfig(t *testing.T) {
cfg, err := ParseConfig("./testdata/sample.toml")
if err != nil {
t.Fatal(err)
}
t.Logf("%+v", cfg)
}
go test -run TestParseConfig -v ./... ile incelemenizi gerçekleştirin. Üçüncü günde zorlayıcı olan şey, yirmi gün içinde bunu düşünmeden yazmaya başladığınız ve bir Tinker oturumu kaybolmak yerine, neyle uğraştığınızın kalıcı bir kaydını bıraktığınızdır.
5. Gün: Hatalar Değerdir ve Bu Acıtır
5. Gün: Hatalar Değerdir ve Bu Acıtır
Başarısız olabilecek her fonksiyon iki şey döndürür: değeri ve bir error. Beşinci gün boyunca if err != nil { return err } yazmak için hayatınızda yazdığınızdan daha fazla zaman harcayacaksınız.
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
data, err := io.ReadAll(file)
if err != nil {
return nil, err
}
var cfg Config
if err := json.Unmarshal(data, &cfg); err != nil {
return nil, err
}
PHP’de üç satır yazar ve bir istisnanın havada kalmasına izin verirsiniz. Burada on iki satır yazıyorsunuz ve derleyici, hata atlamanıza izin verecek; bunu _ ile atayarak yapmayın. Atlanan hatalar, Go’da sessiz hataların iyi bilinen bir kaynağıdır. Bu yüzden errcheck ve staticcheck standart lint olarak var.
Birinci haftanın sonunda bu stili bir özellik olarak okuyacaksınız. Hata yolunuz belirgindir. Arama yapabilirsiniz. Dört dosya ileride, RuntimeException‘ı yakalayacak bir try/catch yok. Beşinci günde sadece acı verir.
2. Hafta — Küçük Kazanımlar
2. Hafta — Küçük Kazanımlar
İkinci hafta, dil size bir şeyler vermeye başlıyor.
8. Gün: Yapılar ve Etiketler Tam Oturdu
8. Gün: Yapılar ve Etiketler Tam Oturdu
Yıllardır Laravel’de JSON ayrıştırma yapıyorsunuz. json_decode($body, true) ve ardından $data['user']['email'] diyerek umuyorsunuz ki hiç kimse bir alanı yeniden isimlendirmeyecek. Go’da bir yapı yazıyorsunuz.
type CreateUserRequest struct {
Email string `json:"email"`
Password string `json:"password"`
Role string `json:"role,omitempty"`
}
func handleCreate(w http.ResponseWriter, r *http.Request) {
var req CreateUserRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "bad json", http.StatusBadRequest)
return
}
// req.Email, req.Password, req.Role ile tip güvenliğiniz var
}
Her alanın ardındaki backtick dizgeleri yapı etiketleridir. Derleyici bunları göz ardı eder; encoding/json paketi, bunları çalışma zamanında okur. Aynı etiketler sqlx, doğrulayıcılar, ORM’ler tarafından kullanılır; yani yapının meta verilerine ihtiyaç duyan her şey tarafında.
Sekizinci gün, bu tek model, Laravel’de bir araya getirdiğiniz yığınları değiştirmektedir: form istekleri, doğrulayıcılar, ORM sütun eşlemeleri, API kaynakları. Tek bir yapı, dört etiket, her katman aynı bilgi kaynağını okur.
10. Gün: Tinker’ı Özlemeyi Bırakıyorsunuz
10. Gün: Tinker’ı Özlemeyi Bırakıyorsunuz
Şu ana kadar yirmi kadar test dosyası yazdınız. Bir fonksiyonu incelemek istediğinizde bir test yazıyorsunuz. Yeni bir paketi denemek istediğinizde test yazıyorsunuz. Bir HTTP yanıtının neye benzediğini görmek istediğinizde, httptest.NewRecorder ve t.Logf kullanarak yanıt gövdesini yazdıran bir test yazıyorsunuz.
func TestUserHandler_Create(t *testing.T) {
body := strings.NewReader(`{"email":"[email protected]","password":"x"}`)
req := httptest.NewRequest(http.MethodPost, "/users", body)
w := httptest.NewRecorder()
<span class="n">handleCreate</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="n">req</span><span class="p">)</span>
<span class="n">t</span><span class="o">.</span><span class="n">Logf</span><span class="p">(</span><span class="s">"status=%d body=%s"</span><span class="p">,</span> <span class="n">w</span><span class="o">.</span><span class="n">Code</span><span class="p">,</span> <span class="n">w</span><span class="o">.</span><span class="n">Body</span><span class="o">.</span><span class="n">String</span><span class="p">())</span>
}
go test -run TestUserHandler_Create -v ile çalıştırın ve sonucu milisaniyeler içinde görün. Hiçbir web sunucusu yok. Hiçbir Postman yok. Hiçbir tinker yok. Üçüncü günde ağır gelen şey, şimdiye kadar yaşadığınız en hızlı geri bildirim döngüsünü sağlıyor ve geriye CI’de tekrarlanabileceğiniz bir test bırakıyor.
12. Gün: Sızmayan Gorutinler
12. Gün: Sızmayan Gorutinler
İlk gorutininizi yazıyorsunuz. Paralel olarak on HTTP isteği gönderiyorsunuz. Geri dönüyorlar. Kendinizi sihirbaz gibi hissediyorsunuz.
var wg sync.WaitGroup
results := make([]string, len(urls))
for i, u := range urls {
wg.Add(1)
go func(i int, u string) {
defer wg.Done()
results[i] = fetch(u)
}(i, u)
}
wg.Wait()
Sonra ilk goroutine sızıntısı ile ilgili bir yazı okumaya başlıyorsunuz ve şapka düşündüğünüzden daha kötü oturuyor. Zaman aşımı olan context.Context hakkında, on isteğin birinin başarısız olduğu durumlar için errgroup hakkında öğreniyorsunuz. Döngü-değişken tuzağı (Go 1.22 ile daha az bir tuzak, fakat eski kodlar için bilmekte fayda var).
On ikinci gün, o fan-out’ı üç kez yeniden yazdınız ve hangi gorutinlerin bitme yolu olduğunu biliyorsunuz. Ders şöyle yerleşiyor: İptal yoluna sahip olmadan başlattığınız bir goroutine, zaman ayarlı bir bellek sızıntısıdır.
3. Hafta — Oturuyor
3. Hafta — Oturuyor
Üçüncü hafta, dilin çevirisini yaptığınız bir dil olmaktan çıkıp düşündüğünüz dil haline geldiği zamandır.
16. Gün: Bileşim Kalıtımı Değiştirir
16. Gün: Bileşim Kalıtımı Değiştirir
Laravel’de kontrolcüleriniz bir temel kontrolcüyü genişletiyor. Modeller Eloquent‘i genişletiyor. İşlemler ShouldQueue‘i uyguluyor. Kalıtım alıyorsunuz, geçersiz kılıyorsunuz ve parent::method()‘u çağırıyorsunuz.
Go’da kalıtım yok. Hiç. Gövdeleme ile birleşirsiniz.
type Logger struct{}
func (l Logger) Info(msg string) { / ... / }
type UserService struct {
Logger
db *sql.DB
}
// kullanım
svc := UserService{db: db}
svc.Info<span{p}>({"created user")
Adı olmayan Logger alanı gömülü bir türdür. Logger üzerindeki yöntemler UserService üzerindeki yöntemler haline gelir. Hiçbir üst sınıf yok — derleyici alan taşıması yapar ve her adım yapının tanımında görülür.
16. gün, kalıtımı yeniden yaratmaya çalışmayı bırakırsınız. “Temel kontrolcü”nüz, bir günlük ve bir veritabanı havuzuna sahip bir yapı haline gelir ve “onu genişleten” sekiz kontrolcü sadece bunu gömülü hale getirir.
18. Gün: Ara Yüzler, Küçük Olanlar
18. Gün: Ara Yüzler, Küçük Olanlar
Java, C#, veya PHP’de, ara yüzler genellikle uygulamaya yakın yaşar ve türün isteyebileceği her yöntemi listeler. Go’da ara yüzler küçüktür. Genellikle tek bir yöntem. Bunlar, üretici değil, tüketene yakındır.
type Storer interface {
Save(ctx context.Context, u User) error
}
func RegisterUser(ctx context.Context, s Storer, u User) error {
return s.Save(ctx, u)
}
RegisterUser, Save yöntemine sahip herhangi bir şeyi kabul eder. Gerçek bir Postgres deposu onu karşılar. Bir test sahteciliği de onu karşılar. Hiçbiri implements Storer olarak tanımlamaz. Derleyici şekli kontrol eder.
İşte burada “ara yüzleri kabul et, yapıları geri döndür” mantığı mantıklı hale gelir. Tek bir işlevi test etmek için 30 yöntemlik bir UserRepository ara yüzüne ihtiyacınız yok. İhtiyacınız olan tek yöntemi, ihityaç duyduğunuz yerde belirliyorsunuz. Testleriniz daha küçük hale geliyor. Bağımlılıklarınız görünür hale geliyor.
20. Gün: Eloquent’a Neden İhtiyacınız Olduğunu Unutuyorsunuz
20. Gün: Eloquent’a Neden İhtiyacınız Olduğunu Unutuyorsunuz
Belki de sekiz sorgu yazdınız database/sql ya da pgx ile ve bir ya da iki sqlc ile üzerinde sorgu fonksiyonları oluşturduk. Sorgular SQL’dir. .sql dosyalarında yer alır. Onları arayan Go kodu üretilmiştir.
-- name: GetActiveUsers :many
SELECT id, email, created_at
FROM users
WHERE active = true
ORDER BY created_at DESC
LIMIT $1;
users, err := q.GetActiveUsers(ctx, 20)
if err != nil {
return err
}
users tipli bir yapının dilimidir. IDE, her alanı otomatik tamamlama açar. Hiçbir ->where, yok ->with, hiçbir gizli SQL dizesini üreten yöntem çağrısı yok. Yirmi günün sonunda, N+1 hakkında birkaç gündür düşünmeyi unuttunuz. whereHas‘ın başka bir beklenmeyen join üretmesini sağladığınız olmadı. ddd($query->toSql()) komutunu çalıştırarak Eloquent’un çevirdiğini anlamadınız. Çeviri gerçekleşmez – SQL yazdınız.
Bu noktada birçok PHP geliştiricisi Eloquent’u özlemekten vazgeçer. Bazıları CRUD yazmanın hızını özlemeyi sürdürür – bu kısımlar adil. Hiç kimse sürprizleri özlemez.
4. Hafta — PHP’yi Eleştirmeye Başlıyorsunuz
4. Hafta — PHP’yi Eleştirmeye Başlıyorsunuz
Dördüncü hafta tuhaf bir haftadır. Hala bir PHP geliştiricisiniz. Hala faturalarınızı ödeyen bir Laravel uygulamanız var. Ama artık kabul ettiğiniz bazı şeyleri fark etmeye başlıyorsunuz.
23. Gün: Başlatma Maliyetini Fark Ediyorsunuz
23. Gün: Başlatma Maliyetini Fark Ediyorsunuz
time ./myapp çalıştırıyorsunuz ve Go “hazır” yazdığında on milisaniye içinde. php artisan serve çalıştırıyorsunuz ve bu, çoğunlukla bir saniye alıyor. (Sayılar makineye bağlı olarak değişecektir; ölçek sırasına göre düşünülecek.) Her istekte bir süreç yeniden başlatılır, aksi takdirde Octane veya RoadRunner kullanmıyorsanız. Bunu yıllardır biliyorsunuz. Yirmi üç günün sonunda sizleri rahatsız etmeye başlıyor.
Laravel iş kuyruğunuzun var olmasının nedeninin kısmen PHP’nin tek bir süreçte gerçek eşzamanlılık yapamaması olduğunu fark ediyorsunuz. Redis, bellek içi önbelleğiniz çünkü PHP-FPM istekler arasında ölüyor. Arka plan çalışması, süreç içinde yeterince uzun olmayan talep döngüsünde yer alıyor, bu nedenle onu ayrı bir işçide gerçekleştiriyorsunuz.
Go’da, “kullanıcı oluşturulduktan sonra hoş geldin e-postası gönder” işlemi için bir kuyruğa ihtiyacınız yok. Bir goroutine başlatırsınız, 30 saniyelik bir bağlam ile geçersiniz ve işiniz tamamdır.
func (s *UserService) Create(ctx context.Context, u User) error {
if err := s.repo.Save(ctx, u); err != nil {
return err
}
go func() {
ctx, cancel := context.WithTimeout(
context.Background(), 30*time.Second)
defer cancel()
s.mailer.SendWelcome(ctx, u.Email)
}()
return nil
}
Yine de, yeniden denemeye değer, kalıcı, gözlemlenebilir işler için gerçek bir kuyruğa ihtiyacınız olacak. Bu değişmedi. Fakat “bir kuyruk işçisini başlatmak” için gereken seviye, artık bir ay önce olduğundan daha yüksektir, çünkü çalışma zamanı kendi başına arka planda çalışabilmektedir.
25. Gün: İkili Dosya Dağıtımdır
25. Gün: İkili Dosya Dağıtımdır
go build -o myapp ./cmd/myapp yazıyorsunuz. Tek bir dosya alıyorsunuz. Bunu bir sunucuya scp ile yurtiçine gönderiyorsunuz. Çalıştırıyorsunuz. Trafiği karşılar.
PHP sürümü hizalaması yok. Hiçbir uzantı yok. Sunucuda composer install yok. Hiçbir php-fpm.conf yok. RAM’inizde boyutlandırmanız gereken hiçbir pm.max_children yok. Bir ikili dosya. Bir systemd birimi. Tamam.
Laravel Dockerfile’ınıza geri döneceksiniz ve buna farklı bir şekilde bakacaksınız. Uzun kurulum zincirine sahip olan: PHP 8.4, opcache, belirli bir uzantı seti, composer install --no-dev, php artisan optimize, FPM ile uyumlu işçi sayısı. Tüm bunlar PHP’nin her istekte bir çalışma süresine ihtiyaç duyması içindir. Go, her süreçte bir çalışma süresine sahiptir.
Bu, Laravel uygulamanızı atmanız gerektiği anlamına gelmez. Bunun nedeni, Go hizmetinin genellikle onlu megabaytlarda yer alırken, PHP-FPM havuzunun yüzlerce megabayta ulaşabilmesi konusunda anlamanızı sağlamaktır, pm.max_children açısından. (İllüstratif aralıklar, benchmarklar değil.)
28. Gün: PHP 8.4, Hatırladığınızdan Daha Yakın Görünüyor
28. Gün: PHP 8.4, Hatırladığınızdan Daha Yakın Görünüyor
Ayın sonunda PHP’yi, başlangıcınıza kıyasla daha adil bir yönden göreceksiniz. Modern PHP, 8.4’e kadar, tipli özellikler, salt okunur sınıflar, asimetrik görünürlük, gerçek enumlar, iplikler, isimli argümanlar ve declare(strict_types=1) içerir. Octane size uzun ömürlü bir süreç verir. Modern PHP, Go’ya, itiraf etmek istemediğinizden daha çok benzemektedir.
Size vermediği şey, araç kiti. gofmt müzakere edilemez. go vet düzenleyicinizde çalışır. go test -race veri yarışmalarını bulur. go build bir ikili dosya üretir. Her Go hizmeti aynı biçimlendiriciyi, aynı test çalıştırıcıyı, aynı içe aktarma sistemini kullanır. Pest ile PHPUnit, PSR-12 ile özel stil, Rector ile Psalm üzerine tartışmayı bırakıyorsunuz. Go, yığını ile geldi.
30. Gün: İkinci Go Hizmetinizi Yazıyorsunuz
30. Gün: İkinci Go Hizmetinizi Yazıyorsunuz
Otuzuncu günde, bir oyuncak hizmeti ve yarı gerçek bir hizmet göndermiş olacaksınız. Üçüncüsünü başlatıyorsunuz; bu, “Go HTTP sunucusu öğreticisini” bir kez bile Google’layamayacağınız bir hizmet olacaktır. main yazıyorsunuz ve slog, gerçek bir ReadHeaderTimeout ile http.Server, ListenAndServe için bir goroutine ve SIGTERM’yi yakalayan bir signal.Notify bloğu,
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
sig
ctx, cancel := context.WithTimeout(
context.Background(), 10*time.Second)
defer cancel()
srv.Shutdown(ctx)
Bunu kopyalamadınız. Bunu yazdınız çünkü parçalar artık aklınızda. Bir ay önce aklınızda olmayan hiçbir şey burada yok.
İşte, bir aydır Go’da geçen bu süre, bir PHP geliştiricisi için böyledir. Go uzmanı değilsiniz — GC, kaçış analizi, kanal kalıpları, generikler hala önünüzde. Ama dil, yabancı bir nesne olmayı bırakmıştır. Ayrıca, izni olmadan bir hizmet yazmak için ikinci bir dile sahipsiniz.
Bu Harita ile Ne Yapmalısınız
Bu Harita ile Ne Yapmalısınız
Seyahate çıkmaya hazırlanıyorsanız, elinizde bulundurmanız gereken üç şey:
- Test yazın, Tinker’a ulaşmak yerine. Sürtünme, beklediğinizden daha hızlı kaybolur ve testler hala gelecek ay oradadır.
- Her hatayı ele alın.
_ =bir koku.if err != nil { return err }ifadesi, dilin, açık olmanızı istediğini gösterir. - Sonlandırmak için bir yolu olmayan hiçbir goroutine başlatmayın. Bağlam, kanal veya WaitGroup. Birini seçin ve kullanın.
İlk ay, en dik kısımdır. Otuz birinci günde eğri düzleşir. Altmışıncı günde, PHP’de kısıtlı hissetmeye başlarsınız.
Bu Faydalı İse
Bu Faydalı İse
Go’da Düşünmek, bir çerçeve yoğun geçmişe sahip geliştiriciler için yazılmış 2 kitaplık seridir. Go Programlamaya Tam Kılavuz, ilk ayınızda ortaya çıkan ancak açıklanmayan dili ve stdlib’i dolduruyor. Go’da Altıgen Mimari, proje düzeninin, hizmet numaranızda kendi başına bir sorun haline geldiği zaman için izlediğiniz bir takip kitaplığıdır.
Bir PHP geliştiricisinin, Laravel kalıplarını çevirmeyi bırakmasını ve Go gibi okuyacak şekilde Go yazmasını sağlamak için ihtiyaç duyduğu iki kitap bunlardır.
Kaynak: Orijinal Makale
- 1. Hafta — Hayal Kırıklığı
- 2. Hafta — Küçük Kazanımlar
- 8. Gün: Yapılar ve Etiketler Tam Oturdu
- 10. Gün: Tinker’ı Özlemeyi Bırakıyorsunuz
- 12. Gün: Sızmayan Gorutinler
- 3. Hafta — Oturuyor
- 16. Gün: Bileşim Kalıtımı Değiştirir
- 18. Gün: Ara Yüzler, Küçük Olanlar
- 20. Gün: Eloquent’a Neden İhtiyacınız Olduğunu Unutuyorsunuz
- 4. Hafta — PHP’yi Eleştirmeye Başlıyorsunuz
- 23. Gün: Başlatma Maliyetini Fark Ediyorsunuz
- 25. Gün: İkili Dosya Dağıtımdır
- 28. Gün: PHP 8.4, Hatırladığınızdan Daha Yakın Görünüyor
- 30. Gün: İkinci Go Hizmetinizi Yazıyorsunuz
- Bu Harita ile Ne Yapmalısınız
- Bu Faydalı İse


