Honor and Internal Visibility

I don’t do many crimes. I don’t even do crimes that I could do without getting caught! Every single time my dog poops on the sidewalk, I pick it up. Even if nobody’s watching! I have honor.

With that disclaimer out of the way, here’s instructions on how to do an excellent crime...

First, find a Kotlin API that you want to use, but can’t because it’s marked internal and you’re in a different module.

Next, add this to the top of your file:


Finally, use the internal API! You’ve bribed the compiler.

Pragmatic Hacks

Using non-public APIs is dangerous and fragile. It’s occasionally also a good trade-off! I’ve done hacks like these a few times with mixed results:

  • Moshi’s ClassFactory uses sun.misc.Unsafe to construct types without invoking their constructors. I’ve grown to dislike this behavior.
  • OkHttp’s Platform reflectively accesses the private TrustManager from a SSLSocketFactory to fix a vulnerability in certificate pinning. This hack is carefully constrainted to JDK8, and the OkHttp function that relies on it has been deprecated since 2016.
  • Redwood’s prepareEnvironment() uses the above internal trick to configure a global uncaught exception handler. There’s no public API for that so I asked for one.

I don’t like needing to hack around access controls, but at least two of these crimes are righteous.

Sharing with Friends

I’m asking for trouble when I use somebody else’s non-public API. Upgrading dependencies is riskier. Using different versions in tests vs. runtime is riskier. The reward needs to be substantial to justify these hazards!

But sometimes I just wanna access my stuff from a different module that I also own. In one of Zipline’s tests, I’m using the zipline-cryptography module’s internals from the zipline module’s tests.

Allowing my test code to hack around internal lets me reduce the visibility of my production code. I don’t need to publish public APIs to the world!


Don’t use the hack above. But if you must, do it honorably.