josevalim

josevalim

Creator of Elixir

Hi everyone,

With the v1.7 release, we have received reports of some dependencies no longer working properly on the new release.

Developers should not expect breaking changes from Elixir on minor and patch releases. We outline our compatibility and deprecations policy in our docs: Compatibility and deprecations — Elixir v1.20.2

This means that, if there is something effectively broken in a new release, it is one of:

  1. We messed up and accidentally introduced a regression, which we have to fix
  2. We fixed a bug that you were accidentally relying on. It is still our bad. We will either have to accept this or find a better way to do a bug fix (for example, introduce a new function and deprecate the old one)
  3. Some project (it could be yours) was using private API

Thanks to everyone, we are able to catch the huge majority of the occurrences of 1 and 2 during the RCs. However, there are still many cases where developers are simply using private Elixir modules and functionality - which may be renamed or completely removed in new Elixir versions. We do not provide any guarantees for private functionality. Needless to say, DO NOT USE PRIVATE APIs.

Using private APIs generate a negative cascade effect in the community because it makes developers unable to update to the latest Elixir version.

How to help

If you are a user of a project and you notice it depends on private functionality, consider reporting a bug or submitting a pull request.

If you are a library author and you are using private APIs, don’t. :slight_smile: If Elixir has a functionality that you need but it is private, please open up a feature request to make the desired functionality public.

Also, consider running your CI builds against Elixir master. It is as easy as:

- wget https://repo.hex.pm/builds/elixir/master.zip
- unzip -d elixir master.zip
- export PATH=$(pwd)/elixir/bin:${PATH}

Here is more info on available precompiled Elixir versions, branches and tags: GitHub - hexpm/bob: The Builder · GitHub

While I wrote this thinking about Elixir, it likely applies to most libraries out there.

Showing Posts 1 to 10

bbense

bbense

It might be nice to give people a reminder about the @moduledoc false convention. Or even better a tool that flags uses of private apis from the standard lib as a compile time warning.

josevalim

josevalim OP

Creator of Elixir

Good point @bbense! We talk about @moduledoc false in our documentation too: https://hexdocs.pm/elixir/master/writing-documentation.html#hiding-internal-modules-and-functions

kelvinst

kelvinst

Also, it would really be nice to have at least a warning when a private API is being used by a module outside its application. I remember that being discussed somewhere else but don’t remember the outcome of it, maybe that’s not even possible, IDK.

CptnKirk

CptnKirk

Wouldn’t it be better to be able to have truly private APIs built into the language so that compilation against “private” modules and functions would fail? Seems like something the language owners could add.

Something like a defpmodule macro that accepted either as part of the module def or possibly a well known constant, a list of module friends able to access internally public functions.

josevalim

josevalim OP

Creator of Elixir

Yes, that would be the best approach, although it is not trivial to implement because of how modules are dynamic. Unless we take some liberties, such as, private modules always need to be required before using or something of sorts. Somebody would have to write such proposal. We should do it in another thread though.

tmbb

tmbb

Maybe, if you want to catch things like String.Tokenizer.tokenize(list). But if you’re happy with only detecting modules that are imported/used/required/aliased, then it’s pretty easy:

defmodule AvoidPrivateAPIs do
  require Logger

  def hidden?(module) do
    match?({:docs_v1, _, :elixir, _, :hidden, _, _}, Code.fetch_docs(module))
  end

  def private_module_message(verb, this_app, that_app, this_module, that_module) do
    """
    The module #{inspect(this_module)}, which belongs to application #{inspect(this_app)}, \
    has tried to #{verb} the hidden module #{inspect(that_module)}, \
    which belongs to application #{inspect(that_app)}.
    """
  end

  defmacro import(quoted_module, opts \\ []) do
    {that_module, _} = Code.eval_quoted(quoted_module)
    this_module = __CALLER__.module
    this_app = Application.get_application(this_module)
    that_app = Application.get_application(that_module)

    if this_app != that_app and hidden?(that_module) do
      message = private_module_message("import", this_app, that_app, this_module, that_module)
      Logger.warn(fn -> message end)
    end

    quote do
      import(unquote(that_module), unquote(opts))
    end
  end
end

defmodule PrivateAPI do
  @moduledoc false

  def hey() do
    "Hey!"
  end
end

defmodule TryToUsePrivateAPIsFromOutsideTheApp do
  require AvoidPrivateAPIs
  # Will log a warning, because String.Tokenizer is defined in the :elixir application
  AvoidPrivateAPIs.import String.Tokenizer
end


defmodule TryToUsePrivateAPIsInsideTheApp do
  require AvoidPrivateAPIs
  # Will not log a warning
  AvoidPrivateAPIs.import PrivateAPI
end

The example above shows only how to deal with imported modules, but the idea might work for other kinds of modules. There are probably some subtleties I’m not capturing here, of course…

mgwidmann

mgwidmann

I would say some of the issue stems from the fact that its not easy to be able to identify if a module is intended for public consumption or not. The elixir language is well documented and that makes it clearer, but it doesn’t make it black and white. How should one tell the difference between “maybe its missing documentation” and a module which is intended to be private?

Theres a larger issue to solve here, as has been mentioned. However, to address the problem at hand in the moment perhaps all internal modules should be marked as such. String.Internal.Tokenizer would make it plainly clear.

hpopp

hpopp

Shouldn’t @moduledoc false just become the default behavior for undocumented modules? I’ve always wondered why documentation has been considered opt-out in ex_doc

josevalim

josevalim OP

Creator of Elixir

For the Elixir language itself it should be black and white. Elixir categorically marks all private functionality with @moduledoc false and @doc false. Those do not appear on the official documentation, which means their usage is being lifted directly from the source code.

To clarify, this has never been an issue in Elixir itself. Or we explicitly document it or we tag it as false. Sure, there are projects in the community where this issue will arise but it is definitely not the source of confusion in this case.

Eiji

Eiji

@josevalim: Please take a look at @lexmag response:

It was written in almost 2 years ago and still ExUnit.Diff has no public API. It would be helpful if you will be able to change it.

Where Next? Top

Trending in Notices Top

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews