Claude Code ile Go API iskeleti: tek bir temiz handler–servis–hata desenini belirleyin ve yeni uç noktalar oluşturarak Go API'nizde tutarlılığı koruyun.

Go API'leri genellikle temiz başlar: birkaç uç nokta, bir ya da iki kişi ve her şey herkesin kafasında. Sonra API büyür, özellikler aceleyle çıkarılır ve küçük farklılıklar sızar. Her biri zararsız gibi görünür, ama birlikte gelecekteki her değişikliği yavaşlatırlar.
Sık görülen bir örnek: bir handler JSON'u bir struct'a çözer ve yardımcı bir mesajla 400 döner, başka birisi 422 ile farklı bir yapı döndürür, üçüncüsü hataları farklı formatta loglar. Bunların hiçbiri derlemeyi bozmaz. Her seferinde sürekli karar verme ve küçük yeniden yazmalar yaratır.
Sorunu şu yerlerde hissedersiniz:
CreateUser, AddUser, RegisterUser) aramayı zorlaştırır.Burada "iskeletleme" yeni iş için tekrarlanabilir bir şablon anlamına gelir: kod nereye gider, her katman ne yapar ve yanıtlar nasıl görünür. Çok kod üretmekten çok, tutarlı bir şekil sabitlemektir.
Claude gibi araçlar yeni uç noktaları hızlıca iskeletleyebilir, ama kalıp bir kural olarak ele alındığında işe yararlar. Kuralları siz belirlersiniz, her diff'i incelersiniz ve testleri çalıştırırsınız. Model standart parçaları doldurur; mimarinizi yeniden tanımlama hakkı yoktur.
Her istek aynı yolu izlediğinde bir Go API büyümeye kolay adapte olur. Uç noktaları üretmeye başlamadan önce bir katman ayrımı seçin ve ona sadık kalın.
Handler'ın işi yalnızca HTTP olmalı: isteği okumak, servisi çağırmak ve yanıtı yazmak. İş kuralları, SQL veya "sadece bu özel durum" mantığı burada olmamalı.
Servis kullanım senaryosunun sahibi olmalı: iş kuralları, kararlar ve repository'ler veya dış çağrılar arası orkestrasyon. HTTP ile ilgili durum kodları, başlıklar veya hataların nasıl render edildiğini bilmemeli.
Veri erişimi (repository/store) kalıcılık detaylarının sahibi olmalı. Servis niyetini SQL/sorgu/transaction'lara çevirir. Temel veri bütünlüğü dışında iş kurallarını uygulamamalı ve API yanıtlarını şekillendirmemeli.
Uygulanabilir bir ayrım kontrol listesi:
Bir kural seçin ve bükmeyin.
Basit bir yaklaşım:
şekil doğrulaması yapar (zorunlu alanlar, temel format)anlam doğrulaması yapar (izinler, invariantlar, durum)Örnek: handler email alanının var olduğunu ve bir e-posta gibi göründüğünü kontrol eder. Servis, e-postanın izinli olup olmadığını ve zaten kullanımda olup olmadığını kontrol eder.
Servislerin domain tipleri mi yoksa DTO'lar mı döndürdüğüne erken karar verin.
Temiz bir varsayılan: handler'lar istek/yanıt DTO'larını kullanır, servisler domain tiplerini kullanır ve handler domain'i yanıta eşler. Bu, HTTP sözleşmesi değişse bile servisi stabil tutar.
Eşleştirme ağır geliyorsa bile tutarlı kalın: servis bir domain tipi artı tipli hata döndürsün ve JSON şekillendirme handler'da kalsın.
Üretilen uç noktaların aynı kişi tarafından yazılmış gibi hissettirmesini istiyorsanız, hata yanıtlarını erken kilitleyin. Üretim, çıktı formatının pazarlık konusu olmadığı durumlarda en iyi çalışır: tek bir JSON şekli, tek bir durum-kodu haritası ve neyin açığa çıkarılacağı için bir kural.
Tüm başarısızlıklarda döndürülecek tek bir hata zarfı ile başlayın. Küçük ve öngörülebilir tutun:
{
"code": "validation_failed",
"message": "One or more fields are invalid.",
"details": {
"fields": {
"email": "must be a valid email address",
"age": "must be greater than 0"
}
},
"request_id": "req_01HR..."
}
code makineler için (istikrarlı ve öngörülebilir), message insanlar için (kısa ve güvenli) olsun. Yapılandırılmış verileri details içine koyun. Doğrulama için basit bir details.fields haritası üretmesi kolay ve istemcilerin girişlerin yanında göstermesi kolaydır.
Sonra, bir durum kodu haritası yazın ve ona sadık kalın. Her uç noktada daha az tartışma olması iyidir. Eğer hem 400 hem 422 istiyorsanız, ayrımı açık hale getirin:
bad_json -→ 400 Bad Request (bozuk JSON)validation_failed -→ 422 Unprocessable Content (doğru biçimli JSON, geçersiz alanlar)not_found -→ 404 Not Foundconflict -→ 409 Conflict (çakışan anahtar, sürüm uyuşmazlığı)unauthorized -→ 401 Unauthorizedforbidden -→ 403 Forbiddeninternal -→ 500 Internal Server ErrorNe kaydedeceğinize ve neyi döndüreceğinize karar verin. İyi bir kural: istemciler güvenli bir mesaj ve bir request ID alır; loglar tam hata ve dahili bağlamı (SQL, upstream yükleri, kullanıcı ID'leri) içerir ve bunlar asla sızdırılmaz.
Son olarak request_id'yi standardize edin. Gelen bir ID başlığı varsa kabul edin (örneğin bir API gateway'den), yoksa kenarda (middleware) bir tane oluşturun. Bunu context'e ekleyin, loglara iliştirin ve her hata yanıtında döndürün.
Iskeletlemenin tutarlı kalmasını istiyorsanız, klasör düzeniniz sıkıcı ve tekrarlanabilir olmalı. Üreticiler görebildikleri kalıpları takip eder; dosyalar dağınık veya isimler özellik başına değişkense sapma olur.
Bir isimlendirme kuralı seçin ve bükmeyin. Her şey için tek bir kelime seçin ve koruyun: handler, service, repo, request, response. Eğer rota POST /users ise dosya ve tip adlarını users ve create etrafında adlandırın (bazen register, bazen addUser yapmayın).
Katmanlara uyan basit bir düzen:
internal/
httpapi/
handlers/
users_handler.go
services/
users_service.go
data/
users_repo.go
apitypes/
users_types.go
Paylaşılan tiplerin nerede duracağına karar verin; projelerin çoğu burada karışır. Yararlı bir kural:
internal/apitypes içinde (JSON ve doğrulama ihtiyaçlarına göre).Bir tip JSON etiketlerine sahipse ve istemciler için tasarlandıysa, onu API tipi olarak değerlendirin.
Handler bağımlılıklarını minimal tutun ve bunu açık hale getirin:
Depo köküne kısa bir kalıp dokümanı yazın (düz Markdown yeterli). Klasör ağacını, isimlendirme kurallarını ve küçük bir örnek akışı (handler → service → repo ve her parçanın hangi dosyada olduğu) ekleyin. Bu, jeneratöre yapıştırdığınız kesin referanstır, böylece her yeni uç nokta her seferinde aynı yapıyı takip eder.
On endpoint'i üretmeden önce bir endpoint oluşturun veya var olanı refaktörleyin ki elinizde güvenilir bir altın standart olsun. Bu, “Yeni kod bunun gibi olmalı” diyebileceğiniz dosyadır. Sıfırdan yazabilir veya var olanı refaktör ederek eşleştirebilirsiniz.
Handler'ı ince tutun. Çok yardımcı olan bir hareket: handler ile servis arasında bir interface koyun, böylece handler bir somut yapıya değil bir kontrata bağımlı olur.
Referans endpoint'te gelecekte üretilen kodun hata yaşayabileceği yerler için küçük yorumlar ekleyin. Kararları açıklayın (400 ile 422 neden ayrıldı, neden create 201 döndürür, neden dahili hataları genel bir mesajla gizliyorsunuz). Kodu sadece tekrarlayan şekilde açıklayan yorumları atlayın.
Referans endpoint çalıştıktan sonra yardımcıları çıkarın ki her yeni endpoint'in sapma şansı azalır. En yeniden kullanılabilir yardımcılar genellikle şunlardır:
İnce handler + interface pratikte şöyle görünebilir:
type UserService interface {
CreateUser(ctx context.Context, in CreateUserInput) (User, error)
}
func (h *Handler) CreateUser(w http.ResponseWriter, r *http.Request) {
var in CreateUserRequest
if err := BindJSON(r, &in); err != nil {
WriteError(w, ErrBadJSON) // 400: malformed JSON
return
}
if err := Validate(in); err != nil {
WriteError(w, err) // 422: validation details
return
}
user, err := h.svc.CreateUser(r.Context(), in.ToInput())
if err != nil {
WriteError(w, err)
return
}
WriteJSON(w, http.StatusCreated, user)
}
Birkaç testle (hatta hata eşlemesi için küçük bir tablo testiyle) bunu kilitleyin. Üretim, taklit edilecek tek temiz hedef olduğunda en iyi çalışır.
Tutarlılık yapıştırdığınız içerikle ve yasakladığınız şeylerle başlar. Yeni bir endpoint için iki şey verin:
Handler, servis metodu, istek/yanıt tiplerini ve endpoint'in kullandığı yardımcıları dahil edin. Sonra sözleşmeyi açıkça belirtin:
POST /v1/widgets)Paket yolları, isimlendirme ve yardımcı fonksiyonlar (WriteJSON, BindJSON, WriteError, validator) konusunda eşleşmesi gerekenleri açıkça belirtin.
Sıkı bir istem, modelin “yardımcı” refaktörlerini önler. Örnek bir istek:
Using the reference endpoint below and the pattern notes, generate a new endpoint.
Contract:
- Route: POST /v1/widgets
- Request: {"name": string, "color": string}
- Response: {"id": string, "name": string, "color": string, "createdAt": string}
- Errors: invalid JSON -> 400; validation -> 422; duplicate name -> 409; unexpected -> 500
Output ONLY these files:
1) internal/http/handlers/widgets_create.go
2) internal/service/widgets.go (add method only)
3) internal/types/widgets.go (add types only)
Do not change: router setup, existing error format, existing helpers, or unrelated files.
Must use: package paths and helper functions exactly as in the reference.
Test istiyorsanız bunları açıkça isteyin (ve test dosyası adını belirtin). Aksi takdirde model testleri atlayabilir veya hayali bir test kurulumuyla oluşturabilir.
Üretimden sonra hızlı bir diff kontrolü yapın. Eğer paylaşılan yardımcıları, router kaydını veya standart hata yanıtını değiştirdiyse çıktıyı reddedin ve "değiştirme" kurallarını daha sıkı yeniden belirtin.
Çıktı, girdi kadar tutarlı olur. Aynı istem şablonunu her seferinde yeniden kullanmak ve depo bağlamından küçük bir içerik kesitini yapıştırmak "neredeyse doğru" kod üretimini engellemenin en hızlı yoludur.
Aşağıyı kopyalayıp yerleri doldurun:
You are editing an existing Go HTTP API.
CONTEXT
- Folder tree (only the relevant parts):
<paste a small tree: internal/http, internal/service, internal/repo, etc>
- Key types and patterns:
- Handler signature style: <example>
- Service interface style: <example>
- Request/response DTOs live in: <package>
- Standard error response JSON:
{
"error": {
"code": "invalid_argument",
"message": "...",
"details": {"field": "reason"}
}
}
- Status code map:
invalid_json -> 400
invalid_argument -> 422
not_found -> 404
conflict -> 409
internal -> 500
TASK
Add a new endpoint: <METHOD> <PATH>
- Handler name: <Name>
- Service method: <Name>
- Request JSON example:
{"name":"Acme"}
- Success response JSON example:
{"id":"123","name":"Acme"}
CONSTRAINTS
- No new dependencies.
- Keep functions small and single-purpose.
- Match existing naming, folder layout, and error style exactly.
- Do not refactor unrelated files.
ACCEPTANCE CHECKS
- Code builds.
- Existing tests pass (add tests only if the repo already uses them for handlers/services).
- Run gofmt on changed files.
FINAL INSTRUCTION
Before writing code, list any assumptions you must make. If an assumption is risky, ask a short question instead.
Bu, üç şeyi zorunlu kılar: bir bağlam bloğu (ne var), bir kısıt bloğu (ne yapılmamalı) ve somut JSON örnekleri (şekillerin kaymaması için). Son talimat bir güvenlik ağıdır: model emin değilse kod yazmadan önce soru sormalıdır.
Örneğin bir “Create project” endpoint'i eklemek istiyorsunuz. Amaç basit: bir isim kabul et, birkaç kural uygula, kaydet ve yeni bir ID döndür. Zor olan kısım handler–servis–repo ayrımını ve zaten kullandığınız hata JSON'unu korumak.
Tutarlı bir akış şöyle görünür:
Handler'ın kabul ettiği istek örneği:
{ "name": "Roadmap", "owner_id": "u_123" }
Başarılıysa 201 Created döndürün. ID her zaman tek bir yerden gelsin. Örneğin Postgres'in üretmesine izin verin ve repo bunun döndürmesini sağlayın:
{ "id": "p_456", "name": "Roadmap", "owner_id": "u_123", "created_at": "2026-01-09T12:34:56Z" }
İki gerçekçi hata yolu:
Validation başarısızsa (isim eksik veya çok kısa), standart şeklinizi ve seçtiğiniz durum kodunu kullanarak alan düzeyinde hata döndürün:
{ "error": { "code": "VALIDATION_ERROR", "message": "Invalid request", "details": { "name": "must be at least 3 characters" } } }
İsim sahibine göre benzersizse ve servis mevcut bir proje bulursa 409 Conflict döndürün:
{ "error": { "code": "PROJECT_NAME_TAKEN", "message": "Project name already exists", "details": { "name": "Roadmap" } } }
Handler "bu istek doğru şekilde şekillenmiş mi?" kontrolünü yaparken, servis "bu izinli mi?" sorusunun sahibi olsun. Bu ayrım üretilen endpoint'leri öngörülebilir kılar.
Jeneratörün doğaçlama yapmasına izin vermek en hızlı tutarlılığı kaybettirendir.
Yaygın bir sapma yeni bir hata şeklidir. Bir endpoint {error: "..."} döndürür, başka biri {message: "..."}, üçüncüsü nested bir obje ekler. Bunu tek bir hata zarfı ve tek bir durum kodu haritası tutarak ve yeni endpoint'lerin bunları import yoluyla yeniden kullanmasını zorunlu kılarak düzeltin. Eğer jeneratör yeni bir alan öneriyorsa, bunu bir API değişikliği isteği gibi ele alın.
Bir diğer sapma handler şişmesi. Küçük başlar: doğrula, sonra izin kontrolü, sonra DB sorgusu, sonra iş kurallarında dallanma. Kısa süre içinde her handler farklı görünür. Bir kural koyun: handler'lar HTTP'yi tipli girdilere ve çıktılara çevirir; servisler kararların sahibi; veri erişimi sorguların sahibi.
İsim uyumsuzlukları da zamanla birikir. Eğer bir endpoint CreateUserRequest kullanırken diğeri NewUserPayload kullanıyorsa tipleri bulmak ve aracı kod yazmak zaman alır. Bir isimlendirme şeması seçin ve güçlü bir neden olmadıkça yeni adları reddedin.
Ham veritabanı hatalarını istemcilere asla döndürmeyin. İçeriği sızdırmanın yanı sıra, tutarsız mesajlar ve durum kodları yaratır. Dahili hataları sarın, sebebi loglayın ve stabil bir kamu hata kodu döndürün.
Pratik korunma önlemleri:
Aynı şekle sahip iki endpoint'i fark edemiyorsanız, istemi sıkılaştırın ve merge etmeden önce yeniden üretin.
Herhangi bir üretileni merge etmeden önce önce yapı kontrolü yapın. Şekil doğruysa mantık hatalarını bulmak daha kolay olur.
Yapı kontrolleri:
request_id davranışıDavranış kontrolleri:
not found veya conflict) tetikleyin ve HTTP durumu ile JSON şeklini kontrol edinKalıbınızı tercih değil bir paylaşılmış sözleşme olarak ele alın. "Nasıl endpoint yapıyoruz" dokümanını kodun yanına koyun ve bir referans endpoint tutun. İlk olarak küçük partiler halinde üretin: farklı kenar durumlarına dokunan 2–3 endpoint (basit bir okuma, doğrulama içeren create, bulunamadı vakasıyla update). Dur ve inceleyin. İncelemelerde aynı stil sapması bulunuyorsa temel dokümanı ve referans endpoint'i güncelleyin, sonra daha fazlasını üretin.
Yineleyebileceğiniz bir döngü:
Daha sıkı bir build–review döngüsü isterseniz, sohbet destekli iş akışıyla iskeletleyip iterasyon yapmanızı sağlayan bir platform (ör. Koder.ai (koder.ai)) iş akışınızı hızlandırabilir; ancak araç, temel kurallar sizde olduğu sürece ikincildir.
Tekrarlanabilir bir şablonu erken kilitlemek en hızlı yoldur: tutarlı bir katman ayrımı (handler → servis → veri erişimi), tek bir hata zarfı ve durum kodu haritası belirleyin. Ardından her yeni endpoint'in kopyalaması gereken tek bir “referans endpoint” kullanın.
Handler'ları sadece HTTP ile ilgili tutun:
Handler içinde SQL, izin kontrolleri veya iş kuralları görürseniz, bunları servise taşıyın.
Serviste iş kurallarını ve kararları tutun:
Servis, domain sonuçları ve tipli hatalar döndürmeli—HTTP durum kodları veya JSON şekillendirme içermemelidir.
Kalıcılık endişelerini izole edin:
Repo içinde API yanıt formatlarını kodlamaktan veya iş kurallarını uygulamaktan kaçının (temel veri bütünlüğü dışında).
Basit bir varsayılan:
Örnek: handler email alanının var olduğunu ve email gibi göründüğünü kontrol eder; servis bunun izinli olup olmadığını ve zaten kullanımda olup olmadığını kontrol eder.
Her yerde tek bir hata zarfı kullanın ve bunu sabit tutun. Pratik bir şekil:
code makineler için (istikrarlı)message insanlar için (kısa ve güvenli)details yapılandırılmış ek veriler için (alan hataları gibi)request_id izleme içinBu, istemci tarafı özel durumlarını önler ve üretilen endpoint'leri öngörülebilir kılar.
Durum kodu haritanızı yazın ve her seferinde ona uyun. Yaygın bir ayrım:
400 bozuk JSON için (bad_json)422 doğrulama hataları için (validation_failed)404 kaynak bulunamadı için (not_found)409 çatışmalar için (çoğaltma/sürüm uyuşmazlığı)500 beklenmeyen hatalar içinAnahtar nokta tutarlılıktır: her endpoint için tartışma yok.
Kamuya tutarlı ve güvenli hatalar döndürün, gerçek nedeni ise dahili loglarda saklayın.
code, kısa message, ve request_idBu, iç bilgilerin sızmasını önler ve endpoint'ler arasında rastgele mesaj farklılıklarını engeller.
Bir “altın” endpoint oluşturun ve yeni endpoint'lerin ona uymasını zorunlu kılın:
BindJSON, WriteJSON, WriteError, vb.)Ardından birkaç küçük test ekleyin (hata eşlemesi için tablo testleri gibi) ve bu kalıbı sabitleyin.
Modele katı bağlam ve kısıtlar verin:
Oluştuktan sonra, mimariyi “iyileştiren” ama standartı bozan diff'leri reddedin.