I was pairing with a developer to walk through some code. They had a question regarding the documentation.
In the documentation I had the following documentation # @api public
and the sibling # @api private
. Both methods were scoped as public methods.
Let’s setup a concrete example:
class Tesseract
# @api public
def self.fetch(path:, remote: default_remote)
new(path: path, remote: remote)
end
# @api private
def self.default_remote
end
def initialize(path:, remote:)
end
end
In the above example, both Tesseract.fetch
and Tesseract.default_remote
are public class methods. However I have documented Tesseract.fetch
as being part of the public Application Programming Interface (API 📖) and Tesseract.default_remote
as being part of the private API.
What Do I Mean by Interface?
An interface is how you interact with something. A great example is a toaster. There are two common interfaces:
- The Slot for the Bread
- The Lever to Start the Toaster
Both of these are “public” interfaces. This is how you may and should interact with the toaster. Most toaster’s I know have a lever that has a plastic cover; to improve asthetics and not expose the more “primative” metal lever.
In way, that metal lever is the “private” interface. You can use it directly and it’s most certainly “exposed” in a way that you could. But the designers wanted you to interact with the nicely covered lever.
You may use that lever directly but probably should not because that was not quite it’s intention.
To draw this back to programming; a public method that is part of the public interface is intended for you to use. A public method that is part of the private interface is also something you can consider using, but the maintainers of that interface are not committing to maintaining that private interface.
A Quick Explanation of Public and Private Methods
Most programming languages I know have consideration for public
and private
methods. A public
method is one that can be called on the receiver. A private
method is one that may only be called within the context of the receiver.
What’s a receiver? The object from which you’re invoking/calling a function.
receiver.public_method
Let’s create an example to illustrate:
module Receiver
def self.public_method
:public_method
end
def self.private_method
:private_method
end
private_class_method :private_method
def self.public_wrapper_for_private_method
private_method
end
end
When I call Receiver.public_method
it returns :public_method
. When I call Receiver.private_method
it raise the following exception with message
private method `private_method' called for Receiver:Module (NoMethodError)
When I call Receiver.public_wrapper_for_private_method
it returns :private_method
.
In other words, outside of the Receiver
module, we cannot directly call Receiver.private_method
.
Why Not Privatize Everything?
In an ideal state, perhaps the Venn Diagram of public methods and public interface declarations would be a single circle. However those concepts are not quite overlapping.
The goal of private
method declaration is to programmatically enforce strict rules. The goal of @api private
declaration is to narratively describe how humans can and should interact and implement against it.
Also, in an ideal state, an interface would be very narrow; however to understand the scope/breadth of an interface often requires some degree of using that interface. Only then does it become clearer what should be public versus private.
In other words, and leaping forward in logic, start from a position of private methods and private APIs and gradually move “promote” them for public consumption.
Other Means of “Telegraphing” Privacy
One idiom I have seen is to declare private Application Programming Interface methods with a double underscore (e.g. __
) prefix.
Another example, from my current Emacs 📖 fixation, is Denote’s “For Developers and Advanced Users” documentation stating:
By contradistinction, a “private” form is declared with two hyphens in its symbol such as denote–file-extension. Do not use those as we might change them without further notice.
So, keep an eye out for methods that are named contrary to the standard idiom; their naming may indicate an assumption of the maintainers.
Conclusion
This quick foray into public/private
methods versus public/private
API is to help folks understand the differences in interface and function/method visibility.