I wrote an article a while ago titled What's the case for data hiding? where I argued that the concept of private fields and attributes is a misguided one. Today I had an experience with the Go standard library that illustrates exactly why.
Background: our app needs to be able to act as an email client (beacuse our customers don't believe in the Unix philosophy sadly). That means we need to negotiate with different SMTP servers and use an authentication method they support. Here's the hard part: Microsoft Office does not support AUTH PLAIN.
Aside: I'm fucking pissed at Microsoft for this. Here's their article explaining that they dropped support for AUTH PLAIN in 2017. Far as I can tell, AUTH PLAIN superseded AUTH LOGIN 14 years before that. Despite all the evidence being in agreement, I am struggling to believe what happened. How could Microsoft have removed existing support for a new replacement in favor of a standard that was already deprecated 14 years ago? I just have no words 🤬
So anyway, we self-implement AUTH LOGIN as an alternative to AUTH PLAIN, but that leaves an obstacle: our SMTP client library, mailyak, requires passing in an
smtp.Auth struct to create a mail struct which means we have to decide which method to use before connecting to the server and finding out which ones it supports.
So off I went to use the building blocks in Go's stdlib net/smtp to implement a function that would connect to a server before we make our mail, and determine what AUTH methods it supports. Here's where the point of the story comes in: almost everything in there is unexported so I can't use it. A Client.Hello method is exported, but doesn't return the results; it stores them in Client.auth (unexported). The underlying Client.cmd is also unexported. In the end I had to basically copy the text of the cmd method instead of using it.
That's what enforced data hiding does. It doesn't stop implementation details from leaving the package, it just duplicates the implementation details.
More general lesson: enforcing things is almost never correct because when you limit someone else's options you don't see what the alternative is. It's true that it's usually a bad idea to access implementation details of a package, but enforcing privateness takes the decision out of the hands of the end-developer, the only one actually qualified to decide as they are the only one aware of the context of the situation.