Public and Private APIs versus Public and Private Methods

Using API Declaration to State Intention

I was pairing with a developer to walk through some code. They had a question regarding the documentation.

I wrote the inline documentation using Yardoc syntax. I previously wrote about The Why and How of Yardoc, which isn’t necessary to read but does highlight how documentation can help the development process.

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:

  1. The Slot for the Bread
  2. 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.