I-prefixes disappear from interface names

8 months ago by David Grudl  

In Nette, interfaces were named with the letter I at the beginning (e.g. IRequest). A couple of years ago, an extremely slow and long term process began, which quietly while maintaining backwards compatibility is getting rid of these prefixes. How exactly is this happening and why?

The Conventions Around Us

Nette has used the convention of prefixing interfaces with the letter I since its inception. It has never made any distinction in abstract class names. In other frameworks (e.g. Symfony, CakePHP, Zend or PSR) you may see a different convention, using the Interface suffix (e.g. Psr\Cache\CacheItemInterface) together with the Abstract prefix for abstract classes (e.g. Psr\Log\AbstractLogger). Finally, there are libraries (e.g. Laravel or the PHP system library) that do not distinguish interfaces or abstract classes in any way (e.g. the Throwable or Iterator interfaces, or abstract class FilterIterator).

If we were to look outside the PHP world, the I prefix is used for example by C#, whereas in Java or TypeScript the names are not distinguished.

About a decade ago, when the Nette DI, the heart of all modern and cleanly designed applications was created, I fully realized that distinguishing interfaces and abstract classes, whether by prefix or suffix, was a huge mistake. But at that time, the framework was already celebrating 5 years of existence and it was impossible to change the interface names without causing a colossal BC break. To maintain consistency, I continued to use prefixes for Nette, but abandoned them for all other projects. (Nette was still a monolith at that time, but e.g. Nette Tester was a separate project and you won't find the prefix for interfaces there).

However, I was still looking for a way to painlessly drop the I prefix. I see the differentiation of interface names as such a significant mistake that it was worth putting a lot of effort into correcting it. Especially since the framework is taken by programmers as a example of clean design.

The reasons are the subject of a separate article Prefixes and Suffixes Do Not Belong in Interface Names.

How to Say Goodbye to the I-names?

After a few years I found a way to give I away. Quietly and with full compatibility. It was a process planned many years in advance, which started with the release of Nette 3.1.

Usually, it was enough to simply rename the original interface (e.g. Nette\Mail\IMailer to Mailer) while creating an alias (IMailer) to make both versions work. Along with this, I added “hidden” code to the source file that only the IDE and Composer can see. It makes autoloading work for the alias as well, and forces the editor to strike it as deprecated in the code with a note that the non-prefixed version should be used. So all existing code works unchanged, while the programmer is led to prefer the new version.

In some cases, I did not rename the interface because the plan was to change it (e.g. the new Nette\Security\UserStorage is different from the old Nette\Security\IUserStorage and both can be used at the same time as part of the transition), or because I am planning a different solution (e.g. Tracy\IBarPanel).

In some cases, removing the prefix was not possible because the name was already taken. This is for example the case of Nette\Security\IIdentity and Identity. Here, SimpleIdentity needs to be taken first as a successor to Identity and the name will be freed. Such a thing has to be spread over several major versions and thus many years to be truly painless. But there is no hurry.

So in Nette 4 only Nette\Http\IRequest and IResponse will remain prefixed, as a reminder of a former time. And also Nette\ComponentModel\IComponent and IContainer, but I'm not sure if their existence is useful at all. Aliases will continue to work, of course.