I-prefixes disappear from interface names

3 years 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?

Conventions Around Us

Nette has used the convention of prefixing interfaces with the letter I since its inception. Abstract class names were never distinguished in any way. In other frameworks (e.g., Symfony, CakePHP, Zend, or PSR), you may encounter a different convention – using the Interface suffix (e.g., Psr\Cache\CacheItemInterface) along with the Abstract prefix for abstract classes (e.g., Psr\Log\AbstractLogger). And finally, we have libraries (e.g., Laravel or the PHP system library) that do not distinguish interfaces or abstract classes at all (e.g., the Throwable or Iterator interfaces, the FilterIterator abstract class).

If we looked beyond the PHP world, the I prefix is used for example in C#, while Java or TypeScript do not distinguish names.

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

Nevertheless, I was still looking for a way to painlessly abandon the I prefix. I consider distinguishing interface names to be such a significant misstep that it was worth putting great 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 several years, I managed to find a way to remove the I. Quietly and with full backward compatibility. It was a process planned many years ahead, 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) and create an alias (IMailer) at the same time, so that both versions would work. Along with that, I added “hidden” code to the source file, which is only recognized by the IDE and Composer. Thanks to this, autoloading works for the alias, and it forces the editor to strike through it as deprecated with a note to use the version without the prefix. Thus, all existing code works without change, and the programmer is guided to prefer the new variant.

In some cases, I did not rename the interface because it was planned to be changed (e.g., the new Nette\Security\UserStorage differs from the old Nette\Security\IUserStorage, and both can be used simultaneously during the transition), or because I am planning a different solution (e.g., Tracy\IBarPanel will be replaced by new Tracy extensions).

In some cases, removing the prefix was not possible because the name was already taken. This is the case with Nette\Security\IIdentity and Identity. Here, it is necessary for SimpleIdentity to be used first as a successor to Identity and the name to be released. Such a thing has to be spread over several major versions and therefore many years to go really painlessly. But there is no hurry.

So in Nette 4, only Nette\Http\IRequest and IResponse will remain prefixed, as a reminder of a bygone era. And also Nette\ComponentModel\IComponent and IContainer, for which I am not sure if their existence is useful at all. The aliases will, of course, continue to work.