What is your interface?
For many developers, the term ‘interface’ is a difficult one. Proper understanding of the concept helps to make better informed decisions. You can use it to find out what you can and cannot modify. In discussions, I have experienced that developers sometimes assign a very specific meaning to the word ‘interface’, so this article will explore the interface concept more in-depth. What is it? Why is it important?
What is an interface, actually?
The word ‘interface’ has several meanings. When programming, it is often used to denote the interface
keyword. In this sense, an interface is the bunch of methods that together form a contract. Here, interface
can be used to denote an ability. It can also be used to swap implementations: you can provide objects of different types, as long as they implement the same interface. The consumer of the interface basically says: I require something with these methods that I can call, but I don’t care how they are achieving it.
In a good interface
definition, the methods together encapsulate a concept. Take for example the Command pattern, which is centered around a central Command
interface:
public interface Command {
void Execute();
}
This interface tells that the Command concept is something that can be executed, without telling what will actually be done. It decouples the caller of the command from the actual implementations.
Each interface provides agreements on two levels: how you can interact with it, and what the interaction means. In software terms, there’s syntax and semantics involved. The syntax tells how you must call a method and what arguments you must provide. The semantics tell what the method’s purpose is. For instance, for a Collection
interface, the semantics of the Add
tell that, when you call it, you add a member to the collection so that you can later retrieve it. It is important that all implementations of an interface
obey its semantics, otherwise they lead to inflexible solutions (violation of the Liskov substitution principle).
Interface concept
However, the general interface concept is much broader: it is a set of agreements about how unrelated systems interact. If one side violates the agreement, the other side just won’t work. Note that this tells nothing about programming language constructs. There are actually many sets of interfaces external to the software, for example:
- Each file format is a set of agreements about how data is stored in the file. It says what the data means and where you can find which information.
- The same goes for network protocols: they are a set of agreements about what information to send at which moment and what the data looks like. For example, the FTP protocol tells that you need to supply the
USER
command to provide the user name to the server. Both client and server need to implement the agreements: if the client does not send theUSER
command, then the server won’t accept the connection. If the server does not respond properly to theUSER
command, then the client aborts the operation. - Command line interface (the name says it all): a set of agreements about how you can execute a command in the terminal. Usually a help page describes the interface in detail: what are possible switches and what do they mean.
But also within a programming language, the interface concept is involved in more than the interface
construct. For example, for OO languages, each class’ public fields and methods form its interface in the sense that they can be used to interact with it. And so do all function declarations: they tell how you can call the function.
Breaking changes
Broadly speaking, changes to your software modify its behavior. You might fix a bug or add a feature. These modifications change the agreements that the software works upon. Is every change a breaking change then? Not really. Of course, you have to keep your promises: removing functionality is certainly breaking, as is changing a function’s syntax. After all, clients were depending on that functionality and now they can’t use it anymore.
However, if you add a new feature to a class without altering existing behavior, then you don’t break any agreements, so you have no breaking change.
This is also what semantic versioning is based upon. It specifies a standard so that you can tell from the new software version whether you can upgrade the package without experiencing breaking changes. Has the major number changed? Then you got a breaking change. Has the minor number changed? That constitutes an interface change, but it still backwards compatible. When the patch version has changed, then the interface has not changed at all.
Implicit interface
Keep one thing in mind: your software always has a public interface, because you’re always communicating things. Or to be more precise: you have users with expectations at the input side of the software, and your software is also expected to provide certain outputs. Examples of the input side are the file formats that you accept, or the actual data you request from the user, or the order in which the network service sends messages to you. Examples of the output side might be the files (name and format) that the software writes or the information shown to the user.
Just the fact that you don’t formally write down what your software provides does not mean that your users don’t have expectations. If the software is sufficiently complex, there often are situations where users are using the software in ways that you won’t expect. For example, imagine that your software can export its data as a csv file. Your users might have made their own scripts to process these files. In this case, even adding an extra column of data might break their scripts: you made a breaking interface change.