Prefixes and Suffixes Do Not Belong in Interface Names
Using the I
prefix or Interface
suffix for
interfaces, and Abstract
for abstract classes, is an antipattern.
It doesn't belong in pure code. Distinguishing interface names actually blurs
OOP principles, adds noise to the code, and causes complications while
developing. Here are the reasons.

Type = Class + Interface + Descendants
In the OOP world, both classes and interfaces are considered types. If I use a type when declaring a property or a parameter, from the developer's perspective, there is no difference between whether the type they are relying on is a class or an interface. That's the cool thing that makes interfaces so useful, actually. It's what gives their existence meaning. (Seriously: what would interfaces be for if this principle didn't apply? Try to think about it.)
Take a look at this code:
class FormRenderer
{
public function __construct(
private Form $form,
private Translator $translator,
) {
}
}
The constructor says: “I need a form and a translator.” And it
doesn't care if it gets a GettextTranslator
object or a
DatabaseTranslator
object. And at the same time, I as a user don't
care if Translator
is an interface, an abstract class or a
concrete class.
I don't really care? Actually, no, I admit I'm quite curious, so when I'm exploring someone else's library, I take a peek at what's behind the type and hover over it:

Oh, I see. And that's the end of story. Knowing whether it's a class or an interface would be important if I wanted to create an instance of it, but that's not the case, I'm just talking about the type of the variable now. And this is where I want to stay out of these implementation details. And I certainly don't want them embedded in my code! What's behind a type is part of its definition, not the type itself.
Now look at the other code:
class FormRenderer
{
public function __construct(
private AbstractForm $form,
private TranslatorInterface $translator,
) {
}
}
This definition of constructor literally says: “I need an abstract form and an interface of translator.” But that's silly. It needs a concrete form to render. Not an abstract form. And it needs an object that acts as a translator. It does not need an interface.
You know that the words Interface
and Abstract
are
to be ignored. That the constructor wants the same thing as in the previous
example. But… really? Does it really seem like a good idea to introduce into
naming conventions the use of words to be ignored?
After all, it creates a false idea of OOP principles. A beginner must be
confused: “If the type Translator
means either 1) an object of
class Translator
2) an object implementing the
Translator
interface or 3) an object inheriting from them, then
what is meant by TranslatorInterface
?” There is no reasonable
answer to this.
When we use TranslatorInterface
, even if Translator
can be an interface, we are committing a tautology. The same happens when we
declare interface TranslatorInterface
. Gradually, a programming
joke will happen:
interface TranslatorInterface
{
}
class FormRendererClass
{
/**
* Constructor
*/
public function __construct(
private AbstractForm $privatePropertyForm,
private TranslatorInterface $privatePropertyTranslator,
) {
// 🤷♂️
}
}
Outstanding Implementation
When I see something like TranslatorInterface
, there is likely
to be an implementation called
Translator implements TranslatorInterface
. It makes me wonder: what
makes Translator
so special that it has the unique privilege of
being called Translator? Every other implementation needs a descriptive name,
such as GettextTranslator
or DatabaseTranslator
, but
this one is sort of “default”, as suggested by its preferred status of being
called Translator
without the label.
Even people get confused and don't know whether to typehint for
Translator
or TranslatorInterface
. Then both get mixed
up in the client code, I'm sure you've run into this many times (in Nette for
example in the connection with Nette\Http\Request
vs
IRequest
).
Wouldn't it be better to get rid of the special implementation and keep the
generic name Translator
for the interface? That is, having specific
implementations with a specific name + a generic interface with a generic name.
That makes sense.
The burden of a descriptive name then lies purely on the implementations. If
we rename TranslatorInterface
to Translator
, our
former Translator
class needs a new name. People tend to solve this
problem by calling it DefaultTranslator
, even I am guilty of this.
But again, what makes it so special that it's called Default? Don't be lazy and
think hard about what it does and why it's different from other possible
implementations.
And what if I can't imagine several possible implementations? What if I can only think of one valid way? So just don't create the interface. At least for now.
Eh, There's Another Implementation
And it's here! We need a second implementation. It happens all the time. There has never been a need to store translations in more than one proven way, e.g. in a database, but now there is a new requirement and we need to have more translators in the application.
This is also when you clearly realize what the specifics of the original single translator were. It was a database translator, no default.
What about it?
- Make the name
Translator
the interface - Rename the original class to
DatabaseTranslator
and it will implementTranslator
- And you create new classes
GettextTranslator
and maybeNeonTranslator
All these changes are very convenient and easy to do, especially if the
application is built according to the principles of dependency
injection. No need to change anything in the code, just change
Translator
to DatabaseTranslator
in the DI container
configuration. That's great!
However, a diametrically different situation would arise if we insisted on
prefixing/suffixing. We would have to rename the types from
Translator
to TranslatorInterface
in the code across
the application. Such renaming would be purely for the sake of convention, but
would go against the spirit of OOP, as we have just shown. The interface hasn't
changed, the user code hasn't changed, but the convention requires renaming?
Then it's a flawed convention.
Moreover, if it turned out over time that an abstract class would be better than the interface, we would rename again. Such an action may not be trivial at all, for example when the code is spread over multiple packages or used by third parties.
But Everybody Does It
Not everyone does. It's true that in the PHP world, the Zend Framework, followed by Symfony, the big players, popularized the distinction interface and abstract class names. This approach has been adopted by PSR, which ironically publishes only interfaces, yet includes the word interface in the name of each.
On the other hand, another major framework, Laravel, does not distinguish
interfaces and abstract classes. Even the popular Doctrine database layer
doesn't do this, for example. And neither does the standard library in PHP (so
we have the Throwable
or Iterator
interfaces, the
FilterIterator
abstract class, etc.).
If we look at the world outside of PHP, C# uses the I
prefix for
interfaces, whereas in Java or TypeScript the names are not different.
So not everyone does it, but even if they did, it doesn't mean it's a good thing. It's not wise to mindlessly adopt what others are doing, because you may be adopting mistakes as well. Mistakes that the other would rather get rid of, it's just too big a bite nowadays.
I Don't Know in the Code What the Interface Is
Many programmers will argue that prefixes/suffixes are useful for them because they allow them to know what interfaces are right away in the code. They feel that they would miss such a distinction. So let's see, can you tell what is a class and what is an interface in these examples?
$o = new X;
class X extends X implements Y
{}
interface Y
{}
X::fn();
X::$v;
X
is always a class, Y
is an interface,
it's unambiguous even without prefixes/postfixes. Of course, the IDE knows this
too and will always give you the correct hint in a given context.
But what about here:
function foo(A $param): A
{}
public A $property;
$o instanceof A
A::CONST
try { ... } catch (A $x) { ... }
In these cases, you won't know it. As we said at the very beginning, from a developer's point of view there should be no difference between what is a class and what is an interface. Which is what gives interfaces and abstract classes their sense.
If you were able to distinguish between a class and an interface here, it would deny the basic principle of OOP. And interfaces would become meaningless.
I'm Used to It
Changing habits just hurts 🙂 Often the idea of it hurts. Let's not blame people who are attracted to changes and look forward to them, for most of as the Czech proverb applies that habit is an iron shirt.
But just look at the past, how some habits have been swept away by time.
Perhaps the most famous is the so-called Hungarian notation used since the
eighties and popularized by Microsoft. The notation consisted of starting the
name of each variable with an abbreviation symbolizing its data type. In PHP it
would look like echo $this->strName
or
$this->intCount++
. Hungarian notation started to be abandoned in
the nineties, and today Microsoft's guidelines directly discourage developers
from using it.
It used to be an essential feature and today nobody misses it.
But why go to such a long time ago? You may remember that in PHP, it was customary to distinguish non-public members of classes with an underscore (sample from Zend Framework). This was back when there was PHP 5, which had public/protected/private visibility modifiers. But programmers did it out of habit. They were convinced that without underscores they would stop understanding the code. “How would I distinguish public from private properties in the code, huh?”
Nobody uses underscores today. And nobody misses them. Time has proven perfectly well that the fears were false.
Yet it's exactly the same as the objection, “How would I distinguish an interface from a class in code, huh?”
I stopped using prefixes/postfixes ten years ago. I would never go back, it was a great decision. I don't know any other programmer who wants to go back either. As a friend said, “Try it and in a month you won't understand that you ever did it differently.”
I Want to Maintain Consistency
I can imagine a programmer saying, “Using prefixes and suffixes is really nonsense, I get it, it's just that I already have my code built this way and changing it is very difficult. And if I start writing new code correctly without them, I'll end up with inconsistency, which is perhaps even worse than bad convention.”
In fact, your code is already inconsistent because you're using the PHP system library:
class Collection implements ArrayAccess, Countable, IteratorAggregate
{
public function add(string|Stringable $item): void
{
}
}
And hand on heart, does it matter? Did you ever think that this would be more consistent?
class Collection implements ArrayAccessInterface, CountableInterface, IteratorAggregateInterface
{
public function add(string|StringableInterface $item): void
{
}
}
Or this?
try {
$command = $this->find($name);
} catch (ThrowableInterface $e) {
return $e->getMessage();
}
I don't think so. Consistency doesn't play as big a role as it might seem. On the contrary, the eye prefers less visual noise, the brain prefers clarity of design. So adjusting the convention and start writing new interfaces correctly without prefixes and suffixes makes sense.
They can be purposely removed even from large projects. An example is the Nette Framework, which historically used I prefixes in interface names, which it started to phase out a few years ago, while maintaining full backward compatibility.
Comments (RSS)
Nice article. Same applies to every Exception suffix for exception/throwable classes in PHP and also Trait suffix for traits.
Because removing Trait word from ex. UserTrait will nicely show you, that you missing some proper name.
Sign in to submit a comment