No implements. No registration. A type satisfies an interface just by having the methods — and that changes where interfaces live.
s.client.ListDApps(...).
That client is an interface. To review PRs you must answer two questions fast: "what does this
interface require?" and "which concrete type is actually plugged in here?" — and in Go the link between them is invisible.
You know two of these already. The third is the one to internalize.
# no declaration at all.
# works if .read() exists
# — discovered when called
def load(src):
return src.read()
class File
implements Reader {
// MUST name Reader
public String read(){...}
}
type File struct{}
// no "implements".
func (f *File) Read() string {...}
// satisfies Reader automatically
Go takes the safety of Java (checked at compile time — a type that's missing a method won't build) and the decoupling of Python (the concrete type never mentions the interface). The result:
Because the implementer doesn't name the interface, the interface can be defined wherever it's used, not where the type is built. Go's strong convention — and a live review rule — is exactly that:
This is the opposite of the Java instinct (interface ships with, or above, the implementation). In Go the consumer says
"I need something that can do X" and declares a tiny interface for X; any type with that method drops in.
The dapp service consumes; the dapp_storage client implements. They never reference each other's interface name.
package dapp
// the service declares ONLY what
// it needs — one method.
type Client interface {
ListDApps(ctx context.Context,
limit, offset int64,
) ([]*dapp_storage.DApp, error)
}
package dapp_storage
type Client struct{ ... }
// has 5 methods, incl. this one.
// never mentions dapp.Client.
func (c *Client) ListDApps(
ctx context.Context, limit, offset int64,
) ([]*dapp_storage.DApp, error) { ... }
// GetDAppByID, GetDAppByProtocol,
// CreateDAppByProtocol, ...
Read it like a reviewer:
*dapp_storage.Client exposes five methods. The dapp service asks for one.
The fat type satisfies the thin interface — the interface is sized to the caller's appetite, not the provider's menu.NewService(...) is called with a *dapp_storage.Client. If
ListDApps were missing or had the wrong signature, it would not compile — caught before review even runs.ListDApps method. No mock framework, no implements.
That's why the interface lives in the consumer.
When a PR uses s.client.Foo() and client is an interface, the concrete type is wherever
NewService / the DI wiring was called. Trace the constructor's argument, not the interface. The interface only
tells you the contract; the wiring tells you the behavior.
Small interfaces are idiomatic; a single-method interface named with an -er suffix (Reader,
Writer, Closer) is the Go ideal. [Effective Go]
A PR that introduces a 12-method interface, or defines an interface in the implementer's package "for mocking", is worth a comment.
*Client where it only calls one method — could take a 1-method interface and become testable.var _ dapp.Client = (*dapp_storage.Client)(nil) — this is a good pattern that documents intent; its absence isn't a bug, but seeing it tells you the author meant the fit.Instant feedback. For the reps, not a grade.
type Client interface { ListDApps(ctx, limit, offset int64) ([]*DApp, error) }, which type satisfies it?PriceFetcher interface so the portfolio service can be tested. The author put it in clients/price_storage/, next to the real client. What's the idiomatic comment?s.client.ListDApps(ctx, 100, 0) where s.client is of interface type dapp.Client. To know what actually runs, where do you look?dapp_storage.Client wraps gRPC errors into errorx namespaces (the
Error.WrapWithNoMessage(err) / ErrNotFound subtypes at the bottom of client.go)? That's
the bridge back to Lesson 1's error chain — ask and I'll walk it. Or ask why method sets differ between *Client
and Client (that's the pointer-vs-value receiver topic, queued next).
Added to your GLOSSARY.md:
implements; the compiler checks the method set at use time.var _ I = (*T)(nil), a documented, free check that *T satisfies I.