How Interfaces Shape Everything

A bunch of thoughts about software development, architecture, organizational structures, and myself.

Maciek Czarnik
6 min readNov 23, 2022

For several years, the “iOS Developer, musician, maker. Passionate about building beautiful, robust, useful and user-friendly apps” was my headline on LinkedIn. I still write a lot of better and better code, I’m still a musician, but pursuing it only as a hobby. I still make my things, but it feels that the statement gets more and more outdated over the years. It’s time to make an update.

Software development is still my main job. Developing UIs, logic, and services, and wiring them up still brings me a lot of joy, but more and more, I find myself thinking, deciding, and caring about more important parts of the whole. One such thing is interfaces. And I’m not thinking specifically about a user interface, also not about a type in a programming language. I’m thinking about a more general idea of a “shared boundary across which two or more separate components exchange information”.

Two rectangles symbolize two components. Between them, there is also a line with two arrows symbolizing the exchange of information.
Two components exchange information across the interface. Image by Łukasz Puchała.

Interfaces Are Everywhere

It’s interesting to observe that every abstraction in code (from simple types to entire modules and applications) has its own interface. An interface does not describe exactly how your abstraction looks and behaves instead, it is a programming way of telling what capabilities a given abstraction has to offer to its users. It’s an outer layer of an abstraction.

Some abstractions, like classes and functions, have both the interface and the implementation. Well designed, and good abstractions are deep. We say that given abstraction is deep when the implementation has much more volume than the interface. It’s a characteristic that classifies an interface as a very similar artifact to a real-life contract.

An Interface Is A Basis For Collaboration and Structure

A contract is an artifact that we have a pretty good intuition about. How does it correspond to an interface? You sign a six-page long contract — that corresponds to a proportionally small interface of your abstraction. Then, as a contractor, you provide your service or a product — that corresponds to a proportionally deep implementation hiding a lot of domain, and contract-specific complexity. The contract not only enforces mutual obligations (the what part) but also gives you (as a service provider) a lot of freedom in execution (the how part), as long as you don’t breach the terms, and meet the essential KPIs.

Two people symbolizing two parties. Between them, there is also a contract with two arrows symbolizong the exchange of services.
Interface as a contract between two parties. Image by Łukasz Puchała.

An interface, the same as a real-life contract, is a basis for collaboration and structure. If you can come up with fifty lines of an interface that are specific enough to describe five thousand lines of the implementation, you gain a lot of benefits. Let me focus on a couple of high-level ones.

One of them is the ability to delegate work with clear expectations. Writing an implementation code, and assuring quality with tests (both structured against requirements encoded in the interface) takes a lot more time than writing an interface, and can be treated as one stream of development. There are more streams of work related to a given abstraction:

  • The interface does not dictate the implementation. There are situations where a single interface has to have multiple abstractions that implement it in a different way (polymorphism). Let’s say that you have an interface that obliges the conforming type to store a number in a database. When your system has to support two different databases you can provide two separate implementations, so under a single interface, you can have two independent development streams.
  • The interface always exists between the implementation and the call site (external code, where it gets used), so at least one or more streams of work exist outside of the abstraction that we decided to work on. When you have a UI component that is used in two distinct features, and they do not work on top of a shared code, you have to wire it up with both features separately (two external streams of work).

Another important benefit that comes from the relative brevity of an interface is its understandability. It’s way easier to understand fifty lines of an interface, while it’s not necessarily easy to understand five thousand lines of the implementation. It’s a plausible goal, but you can quickly exhaust your team member’s cognitive load if you expect each of them to know all the implementation details of the code that their team colleagues are working on. It may be within sight of your engineers, but the rest of the team will stay out of the loop creating an engineering silo. Well-defined interfaces can be an inclusive artifact that invites not only your engineers to collaborate and dive deeper into the implementation details. To a certain degree, it invites everybody to speak the same language, discuss, understand, and come to an agreement about the big picture (a mythical shared goal).

It’s a good idea to think about interfaces early in the software development process. They allow you to better structure your work and define more clearly the expectations by setting out the specific roles and responsibilities of each party involved. They also enable you to achieve your goals faster and more responsibly thanks to their collaboration and communication-enabling nature. It comes as no surprise, that programming to an interface as a software development practice has been used effectively for decades.

Some Interfaces Are More Important Than Others

The importance of an interface is a function of its depth, availability (access level), and the number of its users. The deeper, the more available (less restrictive access level), and the more users of a given interface — the more important it is. Why? Because suboptimal decisions about the interface can affect hundreds or thousands of lines of code, engineers, features, teams, and even companies (when your product is a publicly available API or an SDK) that depend on it. If you ever have to change any important interface it’s going to be expensive. That's why interfaces, especially the important ones, have to be carefully designed and as essential as possible.

A set of the most important and expensive to change design decisions that shape the system are considered its architecture. Important interfaces fall into that category. The most important interfaces are used to draw boundaries between software subsystems, and here we are getting to a very interesting problem space.

Important Interfaces Transcend the Software Boundary

There is enough scientific proof as well as good literature, that mentions the importance of Conway’s Law and more importantly — the inverse Conway maneuver. The inverse Conway maneuver is a recommendation to treat the shape of your subsystems and architecture as a force that drives the structure and responsibilities of your teams. Following it empowers interfaces to transcend the software boundary and impact the organizational structure, shape the communication channels and relationships between people, their autonomy (thus motivation), and ultimately keep their cognitive load in check, and that’s very important.

Two sets symbolizing two teams. Between then, there is also a line with two arrows symbolizong the exchange of information.
Interface as a boundary between teams. Image by Łukasz Puchała.

It’s a fascinating premise that makes interfaces a truly powerful thing.

Conclusion

Interfaces are important. Sometimes, more important than you think. If you give them the attention they deserve they can become strategic artifacts of your organization. They can be used to enable even broader cross-functional collaboration, drive parallel streams of high-volume implementation with clear expectations, and ultimately shape processes and the structure of your teams.

That's a rough outline of a part of my professional credo. Initially, I planned a brief post on my LinkedIn profile but ended up with this. I still didn’t exhaust the topic, but let me put a semicolon here. If you’re interested in the next part, stay tuned. I’ll dot the i‘s and cross the t‘s in a separate story soon.

Thank you for your attention.

P.S.

I’m keen to start working with organizations that value structure, collaboration, and tight feedback loops. If you look any further than the end of the current project, and the start of the next one straight after. If you agree that the only way to go far is to go together. If you believe that it doesn’t have to be crazy at work and you still will be able to achieve great results, then we may be a good match.

Let’s talk, hello@mczarnik.com 👋

--

--