Home Interfaces are Everything
Post
Cancel

Interfaces are Everything

Interfaces are Everything

If you work with me, you’ll have heard me say this a lot. It’s a phrase I stole from my dad - who, like any good programmer, I’m certain stole it from somewhere else.

This is how I develop almost everything. Why do I say this so often (it’s at least once a day), you may ask? Allow me to show you.

Why?

Let’s talk about the Internet. When you search for something - what do you do? You type something into your search bar, and it takes you off to a magical realm that occasionally provides you with relevant results. That, in itself, is an interface. We as humans fundamentally rely on interfaces to live our daily lives. When you want to search for something, you don’t go and build a database from scratch containing all data accessible over the internet, then program a search algorithm specific to your one query. In fact - that example in itself is an interface. You have to start out with data in some form, which you have to access and compile. You have to build the data into a regular format (an interface) which you can then search through. In fact - all the data you access over the Internet uses interfaces already! HTTP is an interface, TCP is an interface, Ethernet is an interface, RJ45 connectors are interfaces, and so is PCIe, the CPU installed in your motherboard, and the entire rest of your system.

We use interfaces in our daily lives, without ever thinking about it. We drive our cars with a steering wheel and two (or three, and sometimes extremely rarely, one) pedals. We use a touchscreen to interact with many of our devices, we use doorhandles to open and close rooms, we use our hands to interact with the outside world. Our eyes are interfaces through which we interpret data - information exchanged through patterns projected onto our retinas at specific wavelengths of light.

So, why should I care?

I think I’ve successfully argued that we use interfaces. So, why is it important when we program an application.

I have a very simple answer: you are never the only engineer.

Single-Engineer Fallacy

As an aside: I’m not sure if that’s an official term, but if it isn’t, I’m coining it now.

You are never the sole engineer working on a project. Even if you are the only person working on it, I guarantee, you will have to return to your code later down the line, where you do not remember every nuance of every decision as to why you wrote it the way you did.

Bad Code

Source: XKCD 1926

This is a common rhetoric repeated by programmers. There have been many essays on the topic, so I don’t think I need to elaborate - after all, if you’re reading this, there’s a nonzero chance you’ve sworn at your former self after reading some of your older code.

Major Projects of a Single Person

We, as humans, are not very good at holding an entire problem in our head. It’s the reason we are able to have our program’s source code written across multiple files, nevermind using libraries.

It is always good to break up a problem into multiple sub-problems. “A journey of a thousand miles begins with a single step” and all that jazz. Even if you are the only person working on the project, isolating a project into multiple sub-problems allows you to focus on a single topic at a time, and solve for individual issues. Not to mention, nobody likes code where database calls are made from the frontend classes.

Working on code as isolated “modules” will allow separation of concerns, and make the lives significantly better for those involved in its eventual rewrite & maintenance. Because if you think code is never rewritten, you’re either lying to yourself or currently working in the finance industry.

Major Projects of a Team

So, let’s talk about teams, then. One of the things that personally drives me up a wall is merge conflicts. There is always a game of pushing your changes before your peer, so they have to resolve the conflicts, instead of you.

Borat knows how to code

Source: u/2jesusisbetterthan1 on Reddit

I propose a simple solution: don’t work on the same file.

Now, I know what I’m saying is nearly idiotically oversimplified. However, I’m leading to a point: isolated portions of a codebase that follow the same API change procedures as, for example, a publicly consumed REST API developed by a major provider, can allow ease of onboarding and ease of expansion (both of the codebase and of the team) without tension from “who owns this code?” and “what’s their home address?” and “where’s the nearest axe?”

Where do interfaces not make sense?

Like all good things, interfaces should come in moderation. I’m sure we’ve all heard of code like this before:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int is_odd(int *value) {
    if (*value % 2 == 1)
    {
        return 1;
    } 
    else if (*value % 2 == 0)
    {
        return 0;
    }
    else
    {
        // Undefined behavior?
        return 1; // 50% chance it's odd, send it!
    }
}

This is an… interesting function. Not just because of the curly brace style guaranteed to annoy someone (should I be writing } else { instead?), but also because it has explicitly no purpose. You can perform the same check in code in at least two different (marginally) efficient ways. Ignoring the cost of the stack frame, branch instructions, jump, etc., you can just not have the function. Interfaces are great, but they aren’t for everything.

And yes, Daniel, I’m aware that the function is almost definitely optimized out by modern compilers.

IsOddFunctionBuilderFactory

Java is waving hello. Not everything needs to be written as a factory that is able to output builders for generating procedures that accomplish a task.

My entire argument with the above is that extremely often, programmers don’t write code to be modular enough. I cannot even count how many times in the past month I’ve had to argue this, simply because the immediate cost of a band-aid fix is lower than a rewrite. And, that is often a correct decision. It rarely makes sense to rewrite entire codebases. Less so to do so every week.

To summarize the point, follow the “principle of least surprise.” Don’t be clever, be predictable. Wizardry only gets you so far, and trust me: you don’t want to be passed up for a promotion or new project because you’re too important to a legacy codebase.

When does it make sense?

Allow me to compile a non-exhaustive list with terms & conditions:

  1. During the initial design and implementation of a codebase
  2. During large rewrites of sections or entire programs
  3. When adding new major functionality
  4. When changing backing for a given service (database, HTTP library, upstream API, etc.)
  5. If the source code causes physical pain (or lasting trauma) to engineers who have to modify it

All of these reasons are subject to the usual cost-benefit analysis (with the exception of #1, please for the love of whatever you choose to believe in or swear at, write modular code).

An Anecdote

Programs take three writes to be written correctly. The first time is a disaster. The second write will work better, but be a stepping stone where you test out new ideas and organizational patterns. The third one is the one that lasts.

Until a new engineer takes over the project. Then all bets are off, and they’re going to go through the above “three writes” process. This rule may have some caveats.

Standards

Source: XKCD 927

Please don’t be the guy above.

–E

This post is licensed under CC BY 4.0 by the author.