Jauhar's Blog


I Prefix and Impl Suffix

2022/09/22

I often found people write codes with a pair of interface and implementation. This happens usually when working on codes with dependency injection pattern. There is an interface for every implementations. Sometimes, it is hard to find a good name for the interface and implementation type since they describe the same concept but have different role. One of them is the interface whereas the other one is the implementation. For example, suppose you have a tax calculator logic in your codebase, how do you call the interface and the implementation. Both of them is a tax calculator, but sometimes we can’t use the same name since our programming language need to differentiate them. People have different convention to solve this problem, sometimes they use I prefix to describe the interface. So, they will end up having ITaxCalculator and TaxCalculator type in their codebase. Other people use Impl suffix for the implementation type. So, they will end up having TaxCalculator and TaxCalculatorImpl.

However, I don’t really like this approach. This pattern is a little weird to read. When you have an I prefix in your interface, it’s kind of redundant. The I prefix stands for interface, which does nothing to the naming. We already know the type is interface from the type definition. At least for most programming languages such as Go, Rust and Java. You don’t need to have an I prefix to tell that it’s an interface. The same thing with Impl suffix, you don’t need this suffix to tell that this is an implementation. And apart from that, if you change your method definition, you need to change both the implementation and the interface, which means your code is not as loosely coupled as you think it is.

The advantage of this pattern is that by having an interface for everything, you always have the ability to change the implementation with another object. This can be very useful for testing. Sometimes, when writing a unit test, you want to mock some of your objects or want to make a fake implementation of it. If you are using dynamically typed programming language like Python, you don’t have problem. But, if you use statically typed programming language such as Java and Go, you can’t just substitute it easily. Hence, you need an interface to make it pluggable.

Writing testable code is very important. But, people tends to discriminate testing code and see them as a second-class citizen. People tend to think that the test codes are not real codes, it shouldn’t be visible from the actual code, and doesn’t have to be maintanable. People tend to not think about testing during the design phase. Instead of thinking about how to test the code, they think about the implementation and figure out about the test later, after the implementation is already done. When writing the implementation, people rarely think “How am I going to test this”. As a result, they try to make interface for everything, just in case they need to mock it for testing later. I don’t like this practice. It makes the test code looks ugly and unmaintainable. As a result, we can’t get a strong tests and good test coverage.

I personally, don’t like to mock my code too much. I like to mock my code just when I don’t have any other choice such as: when the code is too complex too run and slow down the test, or when working on network or IO because the behavior of the code is hard to reproduce and can be unstable. But, most of the time, mocking makes the test unecessarily complex. The next time a logic in your code changes, you need to change all the mock behavior you have also. Otherwise, your test will be irrelevant to the new requirement. Instead of writing mocks, try to isolate the code that is hard to tests with the one that easier to test. Usually, everything that doesn’t touch any IO like network, file or system call is easier to test because they usually deterministic, or at least predictable in term of the behavior and the performance. If you are doing web-based application, doing this also have another advantages: it is clearer on what are the IO performed by your app. Because you already isolated the IO-related stuff and CPU-related stuff, you can see all the IO you do in your app clerar.

Writing a software is a journey. Having a lot of pair of implementations and interfaces are not ideal. We often don’t need the interface since there is only one implementation of the interface. And sometimes, it is a sign that we don’t think enough about how are we going to test it. But, it doesn’t mean we shouldn’t do it. In the beginning, I think it’s ok to do it since we don’t know what’s the better way to do it, yet. But along the way, we should keep improving our code base. Down the road, we will see that this component should be splitted into two component, these two components should be merge together.

All software engineers write ugly code, but a good software engineer constantly improve their code.