<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Interfaces on</title><link>/tags/interfaces/</link><description>Recent content in Interfaces on</description><generator>Hugo</generator><language>en</language><lastBuildDate>Thu, 08 May 2025 00:00:00 +0000</lastBuildDate><atom:link href="/tags/interfaces/index.xml" rel="self" type="application/rss+xml"/><item><title>Lesson 8: Designing Interfaces for Libraries — Libraries export interfaces, apps consume them</title><link>/post/go/go-iface-library-design/</link><pubDate>Thu, 08 May 2025 00:00:00 +0000</pubDate><guid>/post/go/go-iface-library-design/</guid><description>&lt;p&gt;Writing a library for other Go developers is a fundamentally different design problem than writing application code. In an application, you control every call site. If an interface needs to change, you update all the callers. In a library, your callers are other people, other teams, other binaries you have never seen — and when you change a public interface, you break them all in ways you cannot fix yourself. This constraint forces a discipline that makes library-authored Go interfaces among the most carefully designed in the ecosystem.&lt;/p&gt;</description></item><item><title>Lesson 7: The io.Reader/Writer Ecosystem — The most powerful 2-method interfaces in Go</title><link>/post/go/go-iface-io-ecosystem/</link><pubDate>Sat, 08 Mar 2025 00:00:00 +0000</pubDate><guid>/post/go/go-iface-io-ecosystem/</guid><description>&lt;p&gt;If you want to understand what makes Go interfaces powerful, do not start with your own code. Start with &lt;code&gt;io.Reader&lt;/code&gt;. It is one method — &lt;code&gt;Read(p []byte) (n int, err error)&lt;/code&gt; — and it is the foundation of an entire ecosystem that spans files, network connections, HTTP bodies, compressed streams, encrypted data, buffered reads, pipes, test helpers, and dozens of third-party libraries. Everything that produces bytes implements &lt;code&gt;io.Reader&lt;/code&gt;. Everything that consumes bytes accepts one.&lt;/p&gt;</description></item><item><title>Lesson 6: Testability Without Over-Mocking — Fakes beat mocks every time</title><link>/post/go/go-iface-testability/</link><pubDate>Mon, 06 Jan 2025 00:00:00 +0000</pubDate><guid>/post/go/go-iface-testability/</guid><description>&lt;p&gt;Mock frameworks are a seductive solution to a real problem. You have a dependency — a database, an email service, an HTTP client — and you need your tests to run without it. A mock framework promises to solve this with generated code and assertion APIs. In my experience, what actually happens is that the tests become tightly coupled to implementation rather than behavior, they break whenever you refactor internals, and maintaining the mock library becomes a part-time job.&lt;/p&gt;</description></item><item><title>Lesson 5: Composition with Embedding — Small interfaces compose into powerful contracts</title><link>/post/go/go-iface-composition/</link><pubDate>Tue, 12 Nov 2024 00:00:00 +0000</pubDate><guid>/post/go/go-iface-composition/</guid><description>&lt;p&gt;One of the things that surprised me most about Go when I came from Python was that Go has no inheritance. No base classes, no method overriding, no type hierarchies. What it has instead is embedding — the ability to include one type inside another so that the outer type promotes the inner type&amp;rsquo;s methods. Combined with interface composition, this gives you something more flexible than inheritance: you can build complex behaviors from simple, testable pieces without the fragility that comes from deep class trees.&lt;/p&gt;</description></item><item><title>Lesson 4: Returning Concrete Types — Give callers the real thing</title><link>/post/go/go-iface-return-concrete/</link><pubDate>Tue, 01 Oct 2024 00:00:00 +0000</pubDate><guid>/post/go/go-iface-return-concrete/</guid><description>&lt;p&gt;There is a piece of Go advice that sounds almost too obvious to state, and yet I see it violated in production codebases every week: return concrete types from your functions. Not interfaces. Not &lt;code&gt;interface{}&lt;/code&gt;. The actual, named, exported struct that your function produces.&lt;/p&gt;
&lt;p&gt;The reason this feels controversial is that it conflicts with object-oriented instincts. In Java and C#, returning an interface from a factory is considered good practice — it hides the implementation detail and &amp;ldquo;programs to an interface.&amp;rdquo; In Go, that instinct leads to information loss, surprise type assertions, and constructors that promise less than they deliver.&lt;/p&gt;</description></item><item><title>Lesson 3: Interface Pollution — More interfaces means more indirection</title><link>/post/go/go-iface-pollution/</link><pubDate>Thu, 22 Aug 2024 00:00:00 +0000</pubDate><guid>/post/go/go-iface-pollution/</guid><description>&lt;p&gt;Interface pollution is not a hypothetical risk. I have walked into codebases where more than half the types in the package are interfaces, where every function parameter is an interface even when the concrete type is never substituted, and where adding a new feature means navigating six files just to trace what a single method call actually does. The code is technically &amp;ldquo;flexible&amp;rdquo; in the sense that every seam is abstract. In practice it is a maze with no map.&lt;/p&gt;</description></item><item><title>Lesson 2: Consumer-Side Interfaces — Define where you use, not where you build</title><link>/post/go/go-iface-consumer-side/</link><pubDate>Mon, 15 Jul 2024 00:00:00 +0000</pubDate><guid>/post/go/go-iface-consumer-side/</guid><description>&lt;p&gt;There is a convention I spent a long time following without questioning it: put the interface in the same package as the type that implements it. If I had a &lt;code&gt;payment&lt;/code&gt; package with a &lt;code&gt;Stripe&lt;/code&gt; struct, I would also define &lt;code&gt;PaymentProcessor&lt;/code&gt; right there. Callers would import &lt;code&gt;payment.PaymentProcessor&lt;/code&gt;. It felt natural — the interface lives next to the thing it describes.&lt;/p&gt;
&lt;p&gt;Go&amp;rsquo;s designers had a different idea in mind, and it took me several refactoring sessions on a real codebase before I understood why it matters: interfaces should be defined by the package that uses them, not the package that implements them. The Go standard library does this consistently and for good reason.&lt;/p&gt;</description></item><item><title>Lesson 1: When NOT to Use Interfaces — Abstractions cost clarity</title><link>/post/go/go-iface-when-not/</link><pubDate>Sat, 08 Jun 2024 00:00:00 +0000</pubDate><guid>/post/go/go-iface-when-not/</guid><description>&lt;p&gt;The first time I saw Go interfaces click in a codebase, I made the same mistake every developer makes: I started reaching for them everywhere. If I had a struct, I made an interface for it. If I had two types that shared even one method name, I defined an interface over both. It felt disciplined. It felt like proper software engineering. It was making my code worse.&lt;/p&gt;
&lt;p&gt;The uncomfortable truth about Go interfaces is that they are most powerful when you use them least. Unlike Java, where every class implementing an interface is an explicit declaration, Go interfaces are satisfied implicitly. That means there is almost zero syntax cost to adding one. And that near-zero cost hides the real cost: cognitive overhead, indirection, and the loss of explicit signal about what a piece of code actually does.&lt;/p&gt;</description></item></channel></rss>