DEV Community

perez odiyo
perez odiyo

Posted on

# Golang Beginner Mistakes #4: Learning Interfaces Changed How I Write Go

For the longest time, I avoided interfaces.

Not because I thought they were useless.

But because every explanation I came across felt like a computer science lecture.

People kept saying things like:

"Program to interfaces."

"Accept interfaces, return structs."

I understood the words.

I just didn't understand why.

Why would I need an interface when my structs and functions were working just fine?

Then I ran into a problem that structs alone couldn't solve.

That's when interfaces stopped feeling like theory and started feeling like one of the most practical features in Go.


The Story: Dog, Cat, and a Better Way

I started with a dog.

type Dog struct {
    Name string
}

func (d Dog) MakeSound() string {
    return d.Name + " says Woof!"
}
Enter fullscreen mode Exit fullscreen mode

Then came the cat.

type Cat struct {
    Name string
}

func (c Cat) MakeSound() string {
    return c.Name + " says Meow!"
}
Enter fullscreen mode Exit fullscreen mode

Everything looked fine.

Until I wanted one function that could make any animal speak.

My first thought wasn't "interface."

It was...

"There has to be a better way than writing one function for every animal."

I almost started writing

PrintDogSound()
PrintCatSound()
PrintCowSound()
PrintBirdSound()
Enter fullscreen mode Exit fullscreen mode

That didn't feel right.

There had to be a cleaner solution.

There was.


The Beginner Mistake

Many Go beginners postpone learning interfaces.

I certainly did.

The word sounds like something from a computer science lecture.

In reality, it's one of the simplest ideas in Go.

The trick is realizing that interfaces aren't about data.

They're about behavior.


What Is an Interface?

A struct answers one question.

What is this thing?

type Dog struct {
    Name string
}
Enter fullscreen mode Exit fullscreen mode

A dog has a name.

That's data.

An interface answers a different question.

What can this thing do?

type Speaker interface {
    MakeSound() string
}
Enter fullscreen mode Exit fullscreen mode

Notice something.

The interface doesn't mention dogs.

Or cats.

Or humans.

It only says:

"If you can make a sound, you're welcome here."

That's the magic.


No implements Keyword?

If you're coming from Java or C#, this part feels strange.

Normally you'd write something like

class Dog implements Speaker
Enter fullscreen mode Exit fullscreen mode

Go doesn't do that.

Instead, it quietly checks:

"Does this type have the required method?"

If yes...

Congratulations.

It already satisfies the interface.

No keyword.

No inheritance.

No registration.

Just behavior.


The "Aha!" Moment

Let's add more types.

type Speaker interface {
    MakeSound() string
}

type Dog struct {
    Name string
}

func (d Dog) MakeSound() string {
    return d.Name + " says Woof!"
}

type Cat struct {
    Name string
}

func (c Cat) MakeSound() string {
    return c.Name + " says Meow!"
}

type Human struct {
    Name string
}

func (h Human) MakeSound() string {
    return h.Name + " says Hello!"
}
Enter fullscreen mode Exit fullscreen mode

Now watch this.

func Announce(s Speaker) {
    fmt.Println(s.MakeSound())
}

func main() {
    Announce(Dog{Name: "Rex"})
    Announce(Cat{Name: "Milo"})
    Announce(Human{Name: "Perez"})
}
Enter fullscreen mode Exit fullscreen mode

The beautiful part is that Announce never asks:

  • Are you a Dog?
  • Are you a Cat?
  • Are you a Human?

It only asks:

Can you make a sound?

If the answer is yes...

The function is happy.

That's the moment interfaces finally clicked for me.


⚠️ Beginner Mistake #1: Creating Interfaces Too Early

Once interfaces make sense, beginners often swing too far in the other direction.

Suddenly everything gets an interface.

Even when there's only one implementation.

type UserRepository interface {
    GetUser(id string) User
}
Enter fullscreen mode Exit fullscreen mode

If you only have one repository...

You probably don't need the interface yet.

A popular Go saying is:

Accept interfaces. Return structs.

Another one you'll hear often is:

Define interfaces where they're used, not where they're implemented.

Start simple.

Add interfaces when they solve a real problem.

Not before.


⚠️ Beginner Mistake #2: Using any for Everything

Some beginners discover

any
Enter fullscreen mode Exit fullscreen mode

and think they've found the ultimate shortcut.

func Print(v any) {
    fmt.Println(v)
}
Enter fullscreen mode Exit fullscreen mode

Sure...

It accepts everything.

But it also throws away type safety.

Instead of saying

"I accept anything."

Ask yourself:

"What behavior do I actually need?"

That's usually an interface.


Real Examples You'll See Every Day

Once interfaces click, you'll notice they're everywhere.

  • io.Reader
  • io.Writer
  • http.Handler
  • Database repositories
  • Payment gateways
  • Logging libraries

They all follow the same idea.

Describe behavior.

Not implementation.


Struct vs Interface

Whenever I'm unsure, I ask myself one question:

Am I describing data... or behavior?

If it's data...

Use a struct.

If it's behavior...

Use an interface.

Simple.

Situation Interface?
Multiple types share behavior
Only one implementation
Modeling data
Want loose coupling
Future implementations expected

The Mental Model That Finally Stuck

Forget complicated definitions.

Just remember this.

Struct = What is it?

Interface = What can it do?

Structs describe things.

Interfaces describe capabilities.

That distinction is what makes Go code so flexible without becoming complicated.


Final Thoughts

One thing I love about Go is that it encourages simple designs.

You don't start with giant inheritance trees.

You don't spend hours designing interfaces "just in case."

You build concrete types.

Then, when multiple types share behavior...

You introduce an interface.

Go doesn't care what something is.

It cares what something can do.

That tiny mindset shift completely changed how I write Go.


Over to You

What Go concept took you the longest to understand?

  • Interfaces?
  • Pointers?
  • Slices?
  • Goroutines?

I'd love to hear your story in the comments.

If you enjoyed this article, consider following the Golang Beginner Mistakes series, where we break down the kinds of mistakes almost every new Go developer makes—and how to avoid them.

Top comments (0)