In most languages, there is the concept of public, protected, internal, fileprivate, private, and other access control keywords. The intention is to restrict the usage of methods and access to data. In a way, it is the most basic form of encapsulation. After some recent work on a few apps, I’ve come to the conclusion that there are only two forms of access control that should be used in the world: public and internal.

Most of the effort in maintaining these different forms of access control is wasted. If you are in the position to change the code, you can modify the access control from private to public with just a few keystrokes. You may have initially wanted something to be private because you don’t want to accidentially leak the implementation details (even to yourself). Or you don’t trust other code developers working on your project to use the data or methods. However, anyone who has write access to the code can change the access control or make other code changes which break implementation preconditions and invariants. Using private is a small speed bump to preventing bad code.

Instead of maintaining such detailed access control, for libraries, you should use only public and the equivalent of internal if available. internal means any other code in the same module (e.g. package/library) has access to the internal data/function. In the end, libraries have only two forms of access control that anyone cares about. public is for all the consumers of the library. internal is for implementation details that only the library authors should have access to.

Users of your app do not care what the access control is, so let everything be internal. The idea is that while it does not matter today, you may extract code into a re-usable module later. So make things internal in the app, and then go back and expose the required types/methods as public if the code is extracted to a module.

protected, fileprivate, private, and other access control should hardly be used. There have been too many times where I’ve seen people expose the implementation details through leaky abstractions already. Or someone either changes the access control protection to be less restrictive or copies the private code for their uses (which can be even worse). Instead of relying on private or similiar access control levels, it seems to be better to just rely on code reviews and discipline instead. For larger code bases with more than a handful of developers, break the code base into separate modules. Encapsulation should be done at the module/library level versus in every code file across a monolith application.

Most of the time, I do believe that actually codifying the intent behind data/methods into the codebase is a best practice, but restrictive access control is not one of them. The next time that someone (maybe even yourself) changes the access control level of code you work on, try to imagine a world with only two levels of access control.