When writing a custom Bazel ruleset, it is important to carefully
separate its public interface from its private implementation
and be deliberate and careful about changes to the public
interface. Here’s the pattern I use when I’m writing rulesets to
Consider a ruleset named rules_foo. I will define the public
endpoints of the ruleset in a file named @rules_foo//foo:def.bzl,
and put all implementation inside the folder @rules_foo//foo/private.
The public endpoint definition file def.bzl does nothing but
decide which pieces of Bazel code in the private implemtnation
define the ruleset’s public interface.
For example, here’s what the def.bzl might look like:
# @rules_foo//foo:def.bzl## Only load and expose the Bazel rules, providers, etc. that# comprise the public interface of rules_foo. Leave everything# else implicitly hidden in //foo/private/.
load("//foo/private/rules:binary.bzl", _foo_binary ="foo_binary")
load("//foo/private/rules:library.bzl", _foo_library ="foo_library")
load("//foo/private/rules:test.bzl", _foo_test ="foo_test")
foo_binary = _foo_binary
foo_library = _foo_library
foo_test = _foo_test
# Also expose any providers, toolchains, macros, etc. here which# make up the public interface of rules_foo.