Good code is behavioural code



A few years ago, as a junior php dev, I discovered traits. Something felt remarkable about them. It was more than just a workaround for inheritance problems. It introduced a notion of 'behaviours' in code. This was always something which I liked about Laravel. Laravel made use of traits to augment classes with certain behaviours. You'd see classes like...

class UserService extends AuthService  
{
    use Emailable, Authable;
}

These use statements ending with 'able', are indicating that you were adding a capability, or a 'behaviour' to a class. Instead of inheriting entire classes, you were simply just pulling in a behaviour that you could now call and re-use.

When I started learning Go a couple of years back, I discovered the power an beauty of interfaces. They're not quite like interfaces in other languages. To satisfy an interface in Go, you just need to create a struct, which contains some or all of the methods defined within an interface. For example...

type Authable interface {  
  Auth(User) bool
}

type AuthService struct {}

func (service *AuthService) Auth(user User) bool {  
  ...
  return isValid
}

Now AuthService satisfies the Authable interface. Even if you add more methods to this struct, it will still satisfy this interface, but you will only be able to call the methods defined on that interface, unless of course you convert it back to the concrete type using authService.(AuthService). But in most circumstances you're missing the point if you're doing this.

I realised that interfaces and traits etc, are about defining behaviours, rather than implementations. Something clicked when I started thinking about code in terms of behaviours, rather than classes and objects, etc. I found I started to write better, cleaner code, because of this shift in perspective.

type Mailer interface {  
  Send(Message) bool
}

func SignUp(mailer Mailer) bool {  
  ...
  mailer.Send(message)
}

What you're saying is, instead of 'I want X'. You're saying, 'I want anything which can do Y and Z'. That ambiguity gives you room to switch things out, or alter these behaviours with minimal disruption.

It also makes testing a thousand times easier, because it's easy to fake behaviours.

This is pretty basic, it's just writing good quality, de-coupled code. But that got me thinking about what makes code de-coupled, and what makes interfaces in Go so powerful, or traits in PHP for example. I came to realise that the distinction is that you create, and share re-usable behaviours not objects or classes themselves.