Free — no signup required

Interfaces: Implicit Implementation

2 min read

Behavior Without Inheritance

An interface in Go defines a set of method signatures — a contract describing what a type can do, not what it is. Unlike Java or C#, you never write implements InterfaceName. If a type has all the methods an interface requires, it automatically satisfies that interface. This is called structural typing or, informally, duck typing.

Why does this matter for Ops tooling? Because it lets you write code that works against an abstraction, then swap the concrete implementation without changing the calling code. This is how you write testable infrastructure tools.

package main

import "fmt"

// Notifier defines the behavior we need: send an alert
type Notifier interface {
    Notify(message string) error
}

// SlackNotifier sends alerts to Slack
type SlackNotifier struct {
    WebhookURL string
}

func (s SlackNotifier) Notify(message string) error {
    fmt.Printf("[Slack -> %s] %s\n", s.WebhookURL, message)
    // In real code: HTTP POST to webhook
    return nil
}

// PagerDutyNotifier sends alerts to PagerDuty
type PagerDutyNotifier struct {
    ServiceKey string
}

func (p PagerDutyNotifier) Notify(message string) error {
    fmt.Printf("[PagerDuty -> %s] %s\n", p.ServiceKey, message)
    // In real code: PagerDuty Events API call
    return nil
}

// sendAlert works with ANY type that satisfies Notifier
// It doesn't know or care whether it's Slack, PagerDuty, or something else
func sendAlert(n Notifier, msg string) {
    if err := n.Notify(msg); err != nil {
        fmt.Println("Alert failed:", err)
    }
}

func main() {
    slack := SlackNotifier{WebhookURL: "https://hooks.slack.com/..."}
    pd := PagerDutyNotifier{ServiceKey: "abc123"}

    sendAlert(slack, "CPU above 95% on web-01")
    sendAlert(pd, "CPU above 95% on web-01")
}

Neither SlackNotifier nor PagerDutyNotifier declares that it implements Notifier. The Go compiler checks this automatically. If you add a new notification backend (email, OpsGenie, webhook), you just write the struct and its Notify method — no registration, no base class, no modification to existing code.

The io.Reader and io.Writer interfaces are the most important examples of this pattern in the standard library. Anything that can be read from implements io.Reader; anything that can be written to implements io.Writer. This means the same function can read from a file, an HTTP response body, a gzip stream, or an in-memory buffer — without knowing which one it is. This composability is why Go's standard library is so powerful.

This is one of 18 chapters

Every chapter is free — Kubernetes, Terraform, SRE, distributed systems, and more — with fast daily review built in.

Sign up free