Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
Sichere ProgrammierungThe Model Got Better. Your Judgment Got Worse.(22.09.2026 um 03:02 Uhr)
Sichere ProgrammierungAIFeed - signed content permissions for AI web crawlers(22.09.2026 um 03:10 Uhr)
Sichere ProgrammierungThanks, glad you liked it!(22.09.2026 um 03:15 Uhr)
Sichere ProgrammierungA request for /.env shouldn't render your React app(22.09.2026 um 03:17 Uhr)
Sichere ProgrammierungAI-Agent Marketplaces Need Verifiable Delivery, Not More Listings(22.09.2026 um 03:20 Uhr)
AI & KI NachrichtenBeyond Bigger Models: Toward a Modular Cognitive Architecture(22.09.2026 um 03:21 Uhr)
Sichere ProgrammierungMasa Depan Manajemen Data: Mengenal Konsep Data Mesh yang Revolusioner(22.09.2026 um 03:22 Uhr)
Sichere ProgrammierungHow to Search Your Claude Code Conversation History(22.09.2026 um 03:22 Uhr)
Sichere ProgrammierungThe Model Got Better. Your Judgment Got Worse.(22.09.2026 um 03:02 Uhr)
Sichere ProgrammierungAIFeed - signed content permissions for AI web crawlers(22.09.2026 um 03:10 Uhr)
Sichere ProgrammierungThanks, glad you liked it!(22.09.2026 um 03:15 Uhr)
Sichere ProgrammierungA request for /.env shouldn't render your React app(22.09.2026 um 03:17 Uhr)
Sichere ProgrammierungAI-Agent Marketplaces Need Verifiable Delivery, Not More Listings(22.09.2026 um 03:20 Uhr)
AI & KI NachrichtenBeyond Bigger Models: Toward a Modular Cognitive Architecture(22.09.2026 um 03:21 Uhr)
Sichere ProgrammierungMasa Depan Manajemen Data: Mengenal Konsep Data Mesh yang Revolusioner(22.09.2026 um 03:22 Uhr)
Sichere ProgrammierungHow to Search Your Claude Code Conversation History(22.09.2026 um 03:22 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

Define Interfaces Where You Use Them: The Go Rule That Shrinks Your API

Book: The Complete Guide to Go Programming Also by me: Hexagonal Architecture in Go — the companion book in the Thinking in Go series My project: Hermes IDE | GitHub — an IDE for developers who ship with Claude Code and other AI coding t…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!





You open a Go repo and find a package called interfaces. Inside it,

a UserStore interface with fourteen methods. Every package in the

codebase imports it. The concrete Postgres type implements all

fourteen, even though the email service that depends on it only ever

calls one: GetUser.



That shape comes from Java, C#, and most other object-oriented

languages, where interfaces live next to the class that implements

them. Go inverts it. The idiom, stated in the standard library's own

comment style and repeated across the Go team's talks, is: define the

interface in the package that consumes it, not the package that

produces the concrete type.



This is not a style preference. It changes how big your API is, how

easy your fakes are, and whether your packages can even compile

without import cycles.





The producer-side habit and what it costs



Here is the Java-shaped version in Go. A store package exports both

the interface and the implementation.




package store

type UserStore interface {
GetUser(ctx context.Context, id string) (User, error)
CreateUser(ctx context.Context, u User) error
UpdateUser(ctx context.Context, u User) error
DeleteUser(ctx context.Context, id string) error
ListUsers(ctx context.Context) ([]User, error)
// ...nine more methods
}

type PostgresUserStore struct{ db *sql.DB }

func (s *PostgresUserStore) GetUser(
ctx context.Context, id string,
) (User, error) {
// real query
}
// ...and the other thirteen methods






Now the email package needs one user's address:




package email

import "myapp/store"

type Sender struct {
users store.UserStore // depends on all 14 methods
}

func (s *Sender) SendWelcome(
ctx context.Context, id string,
) error {
u, err := s.users.GetUser(ctx, id)
if err != nil {
return err
}
return s.send(u.Email)
}






Sender uses exactly one method. Its declared dependency is fourteen.

Every test fake for Sender has to implement fourteen methods to

satisfy the type, thirteen of them returning nil and never getting

called. When someone adds a fifteenth method to UserStore, every

fake in the codebase breaks compilation, including the ones in packages

that never touch the new method.





Move the interface to the consumer



The email package cares about one thing. Let it declare exactly that.




package email

type UserGetter interface {
GetUser(ctx context.Context, id string) (User, error)
}

type Sender struct {
users UserGetter
}






The store package no longer exports an interface at all. It exports

the concrete type:




package store

type PostgresUserStore struct{ db *sql.DB }

func NewPostgresUserStore(db *sql.DB) *PostgresUserStore {
return &PostgresUserStore{db: db}
}

func (s *PostgresUserStore) GetUser(
ctx context.Context, id string,
) (User, error) {
// real query
}






Nothing connects the two by name. *PostgresUserStore has a GetUser

method with the right signature, so it satisfies email.UserGetter

automatically. Go interfaces are satisfied structurally, at compile

time, with no implements keyword. The producer does not import the

consumer, and the consumer does not import the producer's interface.

Wiring happens in main, where the concrete type is passed to the

constructor:




package main

func main() {
db := openDB()
users := store.NewPostgresUserStore(db)
sender := email.NewSender(users) // fits UserGetter
_ = sender
}






The dependency Sender declares is now one method wide. That number

is the whole point.





Fakes stop being a chore



With the one-method interface, the test fake is four lines. No mock

framework, no code generation.




package email

type fakeUsers struct {
user User
err error
}

func (f fakeUsers) GetUser(
_ context.Context, _ string,
) (User, error) {
return f.user, f.err
}






The test reads straight through:




func TestSendWelcome(t *testing.T) {
s := NewSender(fakeUsers{
user: User{Email: "[email protected]"},
})
if err := s.SendWelcome(
context.Background(), "u1",
); err != nil {
t.Fatalf("got error: %v", err)
}
}






Compare that to faking a fourteen-method interface. You either

hand-write thirteen stub methods you never call, or you reach for a

generated mock and carry a go:generate line plus a dependency. The

narrow consumer-side interface removes the reason to generate anything.

The surface you depend on is the surface you fake, and it is small

because you only asked for what you use.



There is a second effect. When a test fake is trivial to write, people

write more tests. When it means fourteen stub methods, they skip the

test or reach for the real Postgres in a container. The size of the

interface quietly sets the cost of testing.





Import cycles disappear



This is the argument that turns the pattern from "nice" into

"sometimes the only thing that compiles."



Say store needs to emit an event through email, and email needs

to read a user through store. With producer-side interfaces, both

packages import each other:




package store
import "myapp/email" // store -> email

package email
import "myapp/store" // email -> store, cycle






Go rejects that at build time: import cycle not allowed. There is no

flag to turn it off. The compiler stops.



Consumer-side interfaces break the cycle because the abstraction lives

with the caller. email declares its own UserGetter and imports

nothing from store. store declares its own Notifier and imports

nothing from email. Both depend on local interface types they own.

main imports both concretes and wires them together. The dependency

graph points inward toward main, and a graph that only points one

direction cannot form a cycle.




package store

type Notifier interface {
UserCreated(ctx context.Context, id string) error
}

func (s *PostgresUserStore) CreateUser(
ctx context.Context, u User, n Notifier,
) error {
// insert, then:
return n.UserCreated(ctx, u.ID)
}






The interface is defined where it is called, so the package that calls

it never has to import the package that eventually satisfies it.






When the producer should still export the interface



The rule is a default, not a law. There are cases where the interface

belongs with the producer, and the standard library shows them.



Look at io.Reader and io.Writer. They live in io, the producer

side, and thousands of packages consume them. That is correct: an

interface used by half the ecosystem should be declared once, in a

neutral package, not redefined in every consumer. The signal is

many independent consumers sharing the exact same shape.



database/sql/driver is the other case. It defines interfaces that

third-party drivers must implement so the sql package can drive any

of them. Here the producer is publishing a contract on purpose, for

implementers it will never see.



So the checklist is short. Export the interface from the producer when

it is a stable, widely shared contract or a plugin point for outside

implementers. Otherwise, and this covers most application code, define

it in the consumer, keep it to the methods that consumer calls, and let

the concrete type satisfy it structurally.






The one-line version



Keep interfaces small and keep them next to the code that calls them.

A Go interface is a description of what a caller needs, not a manifest

of what a type provides. When you write it from the caller's side, the

description stays honest: one method, because that is all you used.



Go's own convention says the same thing in fewer words: "The bigger the

interface, the weaker the abstraction." Define it where you use it and

it stays small on its own.









If this was useful



Consumer-side interfaces are one of those Go decisions that look like a

style rule until you trace an import cycle back through three packages

and realize the interface was in the wrong place the whole time. The

Complete Guide to Go Programming
covers how interface satisfaction

actually works in the compiler and runtime, down to the itab and why

structural typing costs you nothing at the call site. Hexagonal

Architecture in Go
takes the same idea up a level, showing where these

ports belong when a service has to survive years of framework churn.



Thinking in Go — the 2-book series on Go programming and hexagonal architecture

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Define Interfaces Where You Use Them: The Go Rule That Shrinks Your API

Thematisch verwandte Begriffe: Define, Interfaces, Where, Them · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-49449 | Joplin is an open source note-taking and to-do application that organise…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick