How To (And Not To) Use Interfaces

If you’re writing code in a language that supports the concept of interfaces – or variants on the theme of pure abstract types with no implementation – then I can think of several good reasons for using them.

Polymorphism

There are often times when our software needs the ability to perform the same task in a variety of ways. Take, for example, calculating the area of a room. This code generates quotes for fitted carpets based on room area.

double quote(double pricePerSqMtr, Room room) {
double area = room.area();
return pricePerSqMtr * Math.ceil(area);
}
view raw Carpet.java hosted with ❤ by GitHub

Rooms can have different shapes. Some are rectangular, so the area is the width multiplied by the length. Some are even circular, where the area is π r².

We could have a big switch statement that does a different calculation for each room shape, but every time we want to add new shapes to the software, we have to go back and modify it. That’s not very extensible. Ideally, we’d like to be able to add new room shapes without changing our lovely tested existing code.

If we define an interface for calculating the area, then we can easily have multiple implementations that our client code binds to dynamically.

public interface Room {
double area();
}
public class RectangularRoom implements Room {
private final double width;
private final double length;
public RectangularRoom(double width, double length) {
this.width = width;
this.length = length;
}
@Override
public double area() {
return width * length;
}
}
public class CircularRoom implements Room {
private final double radius;
public CircularRoom(double radius) {
this.radius = radius;
}
@Override
public double area() {
return PI * Math.pow(radius, 2);
}
}
view raw Room.java hosted with ❤ by GitHub

Hiding Things

Consider a class that has multiple features for various purposes (e.g., for testing, or for display).

public class Movie {
private final String title;
private int availableCopies = 1;
private List<Member> onLoanTo = new ArrayList<>();
public Movie(String title){
this.title = title;
}
public void borrowCopy(Member member){
availableCopies -= 1;
onLoanTo.add(member);
}
public void returnCopy(Member member){
availableCopies++;
onLoanTo.remove(member);
}
public String getTitle() {
return title;
}
public int getAvailableCopies() {
return availableCopies;
}
public Boolean isOnLoanTo(Member member) {
return onLoanTo.contains(member);
}
}
view raw Movie.java hosted with ❤ by GitHub

Then consider a client that only needs a subset of those features.

public class LoansView {
private Member member;
private Movie selectedMovie;
public LoansView(Member member, Movie selectedMovie){
this.member = member;
this.selectedMovie = selectedMovie;
}
public void borrowMovie(){
selectedMovie.borrowCopy(member);
}
public void returnMovie(){
selectedMovie.returnCopy(member);
}
}
view raw LoansView.java hosted with ❤ by GitHub

We can use client-specific interfaces to hide features for clients who don’t need to (or shouldn’t) use them, simplifying the interface and protecting clients from changes to features they never use.

public interface Loanable {
void borrowCopy(Member member);
void returnCopy(Member member);
}
public class Movie implements Loanable {
private final String title;
private int availableCopies = 1;
private List<Member> onLoanTo = new ArrayList<>();
public Movie(String title) {
this.title = title;
}
@Override
public void borrowCopy(Member member) {
availableCopies -= 1;
onLoanTo.add(member);
}
@Override
public void returnCopy(Member member) {
availableCopies++;
onLoanTo.remove(member);
}
public String getTitle() {
return title;
}
public int getAvailableCopies() {
return availableCopies;
}
public Boolean isOnLoanTo(Member member) {
return onLoanTo.contains(member);
}
}
public class LoansView {
private Member member;
private Loanable selectedMovie;
public LoansView(Member member, Loanable selectedMovie) {
this.member = member;
this.selectedMovie = selectedMovie;
}
public void borrowMovie(){
selectedMovie.borrowCopy(member);
}
public void returnMovie(){
selectedMovie.returnCopy(member);
}
}
view raw Loanable.java hosted with ❤ by GitHub

In languages with poor support for encapsulation, like Visual Basic 6.0, we can use interfaces to hide what we don’t want client code to be exposed to instead.

Many languages support classes or modules implementing multiple interfaces, enabling us to present multiple client-specific views of them.

Faking It ‘Til You Make It

There are often times when we’re working on code that requires some other problem to be solved. For example, when processing the sale of a CD album, we might need to take the customer’s credit card payment.

Instead of getting caught in a situation where we have to solve every problem to deliver or test a feature, we can instead use interfaces as placeholders for those parts of the solution, defining explicitly what we expect that class or module to do without the need to write the code to make it do it.

public interface Payments {
Boolean process(double amount, CreditCard card);
}
public class BuyCdTest {
private Payments payments;
private CompactDisc cd;
private CreditCard card;
@Before
public void setUp() {
payments = mock(Payments.class);
when(payments.process(any(), any())).thenReturn(true); // payment accepted
cd = new CompactDisc(10, 9.99);
card = new CreditCard(
"MR P SQUIRE",
"1234234534564567",
"10/24",
567);
}
@Test
public void saleIsSuccessful(){
cd.buy(1, card);
assertEquals(9, cd.getStock());
}
@Test
public void cardIsChargedCorrectAmount(){
cd.buy(2, card);
verify(payments).process(19.98, card);
}
}
view raw Payments.java hosted with ❤ by GitHub

Using interfaces as placeholders for parts of the design we’re eventually going to get to – including external dependencies – is a powerful technique that allows us to scale our approach. It also tends to lead to inherently more modular designs, with cleaner separation of concerns. CompactDisc need not concern itself with how payments are actually being handled.

Describing Protocols

In statically-typed languages like Java and C#, if we say that an implementation must implement a certain interface, then there’s no way of getting around that.

But in dynamic languages like Python and JavaScript, things are different. Duck typing allows us to present client code with any implementation of a method or function that matches the signature of what the client invokes at runtime. This can be very freeing, and can cut out a lot of code clutter, as there’s no need to have lots of interfaces defined explicitly.

It can also be dangerous. With great power comes great responsibility (and hours of debugging!) Sometimes it’s useful to document that fact that, say, a parameter needs to look a certain way.

In those instances, experienced programmers might define a class – since Python, for example, doesn’t have interfaces – that has no implementation, that developers are instructed to extend and override when they create their implementations. Think of an interface in Python as a class that only defines methods that you must only override.

A class that processes sales of CD albums might need a way to handle payments through multiple different payment processors (e.g., Apple Pay, PayPal etc). The code that invokes payments defines a contract that any payment processor must fulfil, but we might find it helpful to document exactly what that interface looks like with a base class.

class Payments(object):
def pay(self, credit_card, amount):
raise Exception("This an an abstract class")
view raw payments.py hosted with ❤ by GitHub

Type hinting in Python enables us to make it clear that any object passed in as the payments constructor parameter should extend this class and override its method.

class CompactDisc(object):
def __init__(self, stock, price, payments: Payments):
self.payments = payments
self.price = price
self.stock = stock
def buy(self, quantity, credit_card):
self.stock -= quantity
self.payments.pay(credit_card, self.price)
view raw compact_disc.py hosted with ❤ by GitHub

You can do this in most dynamic languages, but the usefulness of explicitly defining abstractions in Python is acknowledged by the widely-used Abstract Base Class (ABC) Python library that enforces their rules.

from abc import ABC, abstractmethod
class Payments(ABC):
@abstractmethod
def pay(self, credit_card, amount):
pass
view raw payments.java hosted with ❤ by GitHub

So, from a design point of view, interfaces are really jolly useful. They can make our lives easier in a variety of ways, and are very much the key to achieving clean separation of concerns on modular systems, and to scaling our approach to software developer.

But they can also have their downsides.

How Not To Use Interfaces

Like all useful things, interfaces can be overused and abused. For every code base I see where there are few if any interfaces, I see one where everything has an interface, regardless of motive.

When is separation of concerns not separation of concerns?

If an interface does not provide polymorphism (i.e., there’s only ever one implementation), and does not hide features, and is not a placeholder for something you’re Faking Until You’re Making, and describes no protocol that isn’t already explicitly defined by the class that implements it, then you can clutter up your code base with large amounts of useless indirection.

In real code bases of the order of tens or hundreds of thousands, or even millions, of lines of code, classes tend to cluster. As our code grows, we may split out multiple helper classes that are intimately tied together – if one changes, they all change – by the job they collaborate to do.

A better design acknowledges these clusters and packages them together behind a simple public interface. Think of each of these packages as being like an internal microservice. (They may literally be microservices, of course. But even if they’re all released in the same component, we can treat them as internal microservices.)

Hide clusters of classes that change together behind simple interfaces

In practising outside-in Test-Driven Development, I will use interfaces to stub or mock solutions to other problems to separate those concerns from the problem I’m currently solving. So I naturally introduce interfaces within an architecture.

But I also refactor quite mercilessly, and many problems require more than one class or module to solve them. These will emerge through the refactoring process, and they tend to stay hidden behind their placeholder interfaces.

(Occasionally I’ll introduce an interface as part of a refactoring because it solves one of the problems described above and adds value to the design.)

So, interfaces – useful and powerful. But don’t overdo it.

What Is ‘Leadership’ Anyway?

If you spend any time on LinkedIn you’re likely to bump into content about this thing called “leadership”. Many posters fancy themselves as experts in this mysterious quality. Many promote themselves as professional “leaders”.

I’m sure you won’t be surprised to learn that I think this is nonsense. And now I’m going to tell you why.

Leading Is Not What You Think It Is?

Let’s think of what that word means: “Lead the way”, “Follow my lead”, “Tonight’s leading news story”, “Mo Farah is in the lead”.

When you lead, it usually means that you go first.

Leading is distinctly different from commanding or inspiring, but that’s what many professional “leaders” mistake it for.

Leaders don’t tell people where to go. They show people the way by going first.

I don’t tell people to write their tests first. I write my tests first and show them how. I lead by example.

‘Leader’ Is Not A Job Title

Organisations appoint what they believe to be good leaders into roles where leading by example is difficult, if not impossible. They give them titles like “Head of” and “Director of” and “Chief” and then promote them away from any activity where they would have the time to show rather than tell.

The real leaders are still on the shop floor. It’s the only place they can lead from.

And, as we’ve probably all experienced, promoting the people who could set the best example into roles where they can’t show instead of tell is a very common anti-pattern.

We Are Not Your Flock

Another common mistake is to see leadership as some kind of pastoral care. Now, I’m not going to suggest that organisations shouldn’t take an interest in the welfare of their people. Not just because happy workers make better workers, but because they are people, and therefore it’s the right thing to do.

And executives could set examples – like work-life balance, like the way they treat people at all levels of the corporate ladder, and like how much they pay people (yeah, I’m looking at you, gig economy) – but that’s different to the way many of them perceive that role.

Often, they’re more like religious leaders, espousing principles for their followers to live by, while indulging in drug-fuelled orgies and embezzling the church’s coffers.

And the care that most people need at work is simply to not make their lives worse. If you let them, grown-ups will grown-up. They can buy their own massage chair if they want one. Nothing more disheartening than watching managers impose their ideas about well-being on to actual adults who are allowed to drink and drive and vote.

If people are having problems, and need help and understanding, then be there for that. Don’t make me go to paintball. I don’t need it, thanks.

The Big Bucks

Most developers I know who moved into those “leadership” roles knew it was a mistake at the time – for the organisation and for themselves – but they took the promotion anyway. Because “leadership” is where the big bucks are.

The average UK salary for a CTO is £85,000. For a senior developer, it’s £60,000 (source: itjobswatch.co.uk). But how senior is “senior”? I’m quite a senior developer. Most CTOs are junior by comparison.

And in most cases, CTO is a strategic command – not a leadership – role (something I freely admit I suck at). A CTO cannot lead in the way I can, because I set an example for a living. For all I know, there are teams out there I’ve never even met who’ve been influenced more by me than by their CTO.

‘Leader’ Is A Relative Term

When I’ve been put in charge of development teams, I make a point of not asking developers to do anything I’m not prepared to at least try myself, and this means I’m having to learn new things all the time. Often I’m out of my comfort zone, and in those instances I need leadership. I need someone to show me the way.

Leadership is a relationship, not a role. It’s relative. When I follow you, and do as you do, then you are the leader. When you do as I do, I’m the leader.

In the course of our working day, we may lead, and we may follow. When we’re inexperienced, we may follow more than we lead. But every time you’ve shown someone how you do something and they’ve started to do it too, you’re a leader.

Yes, I know. That sounds like teaching. Funny, that.

But it doesn’t have to be an explicit teacher-student relationship. Every time you read someone’s code and think “Oh, that’s cool. I’m going to try that”, you have been led.

It’s lonely at the top

For sure, there are many ways a CxO could lead by example – by working reasonable hours, by not answering emails or taking calls on holidays, by putting their trust in their people, or by treating everyone with respect. That’s a rare (and beautiful) thing. But it’s the nature of heirarchies that those kinds of people tend not to get ahead. And it’s very difficult to lead by example from a higher strata. If a CTO leaves the office at 5:30pm, but none of her 5,000 employees actually sees it, does it make a sound?

Show, Don’t Tell

So, leadership is a very distinct thing from command. When you tell someone to do something, you’re commanding. When you show them how you do it – when you go first – that’s leading.

“Show, don’t tell” would be – if it had one – Codemanship’s mission statement. Right from the start, I’ve made a point of demonstrating – and not presenting – ideas. The PowerPoint content of Codemanship training courses has diminished to the point of almost non-existent over the last 12 years.

And in that sense, I started Codemanship to provide a kind of leadership: the kind a CTO or VP of Engineering can’t.

Set Your Leaders Free

I come across so many organisations who lack technical leadership. Usually this happens because of the first mistake – the original sin, if you like – of promoting the people who could be setting a good example into roles where they no longer can, and then compounding that mistake by stripping authority and autonomy from people below that pay grade – because “Well, that’s leadership taken care of”.

I provide a surrogate technical leadership service that shouldn’t need to exist. I’m the CTO who never took that promotion and has time – and up-to-date skills – to show you how to refactor a switch statement. I know people who market themselves as an “Interim CTO”. Well, I’m the Interim Old Programmer Who’s Been Around Forever.

I set myself free by taking an alternative career path – starting my own company. I provide the workshops and the brown bag sessions and the mobbing sessions and the screencasts and the blog posts that you could be creating and sharing within your organisation, if only they’d let you.

If only they’d trust you: trust you to manage your own time and organise things the way you think will work best – not just for getting things done, but for learning how to do them better.

People working in silos, keeping their heads down, is antithetical to effective leadership. Good ideas tend to stay in their silos. And my long experience has taught me that broadcasting these ideas from on-high simply changes nothing.

Oh, The Irony

I believe this is a pretty fundamental dysfunction in organisational life. We don’t just have this problem in tech: we see it repeated in pretty much every industry.

Is there a cure? I believe so, and I’ve seen and been involved with companies who’ve managed to open up the idea of leadership and give their people the trust and the autonomy (and the resources) to eventually provide their own internal technical leadership that is self-sustaining.

But they are – if I’m being honest – in the minority. Training and mentoring from someone like me is more likely to lead to your newly inspired, more highly skilled people moving on to a company where they do get trust and autonomy.

This is why I warn clients that “If you water the plant, eventually it’ll need a bigger pot”. And if pushed to describe what I do, I tell them “I train developers for their next job”. Would that it were not so, but I have no control over that.

Because I’m not in charge.