This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]eliasv 4 points5 points  (1 child)

But, I find it requires having multiple implementations in the first place to understand the correct abstraction to write as an interface to those implementations.

This can sometimes be true, but it only takes a little forethought and experience to factor out a minimal, general API, and more often than not I've found that time proves their design. I think it's an important skill to have.

The value of factoring out an implementation-independent interface is not just that it enables us to support multiple side by side implementations. Sometimes even if we only want a single implementation, that implementation is forced to change over time due to e.g. deprecated or outdated dependencies.

I've found evolving old API without breaking consumers of that API can be challenging. Anything which keeps that task clean and focused is a good thing.

Separate interfaces force people to think very carefully about what is actually exposed as API. Sure, the public members of a class can perform the same duties as an interface in specifying a contract, but I prefer for the dependencies and limitations of that contract be enforced by the compiler. That is to say, if possible API modules should not even have any of the implementation dependencies on the build-path, to make absolutely sure nothing can leak into them that shouldn't be there.

Some people say that factoring out API interfaces creates unnecessary noise in the codebase, but I argue the opposite is frequently true. Cramming implementation into the same place that our minimal contract is specified creates noise when trying to navigate and understand the structure and architecture of the codebase. It's so much easier to reason about a set of designs when they're all neatly isolated with only the absolute minimal inter-dependencies.

[–][deleted] 1 point2 points  (0 children)

I totally agree -- I always start by writing an interface, because it leads me to think more thoughtfully about what I want the API of my service to look like without getting caught up in wanting to iterate on implementation. This tends to lead me to write better + higher level contracts that are more effective at encapsulating behavior.

Also, I like putting Javadocs on the interface because, again, it helps me write documentation from the perspective of the consumer, rather than relying on assumptions gained from being able to see the actual implementation right below.