Compatibility and Deprecations

Elixir is versioned according to a vMAJOR.MINOR.PATCH schema.

Elixir is currently at major version v1. A new backwards compatible minor release happens every 6 months. Patch releases are not scheduled and are made whenever there are bug fixes or security patches.

Elixir applies bug fixes only to the latest minor branch. Security patches are available for the last 5 minor branches:

Elixir versionSupport
1.10Bug fixes and security patches
1.9Security patches only
1.8Security patches only
1.7Security patches only
1.6Security patches only

New releases are announced in the read-only announcements mailing list. All security releases [security]will be tagged with .

There are currently no plans for a major v2 release.

Compatibility between non-major Elixir versions

Elixir minor and patch releases are backwards compatible: well-defined behaviours and documented APIs in a given version will continue working on future versions.

Although we expect the vast majority of programs to remain compatible over time, it is impossible to guarantee that no future change will break any program. Under some unlikely circumstances, we may introduce changes that break existing code:

  • Security: a security issue in the implementation may arise whose resolution requires backwards incompatible changes. We reserve the right to address such security issues.

  • Bugs: if an API has undesired behaviour, a program that depends on the buggy behaviour may break if the bug is fixed. We reserve the right to fix such bugs.

  • Compiler front-end: improvements may be done to the compiler, introducing new warnings for ambiguous modes and providing more detailed error messages. Those can lead to compilation errors (when running with --warning-as-errors) or tooling failures when asserting on specific error messages (although one should avoid such). We reserve the right to do such improvements.

  • Imports: new functions may be added to the Kernel module, which is auto-imported. They may collide with local functions defined in your modules. Collisions can be resolved in a backwards compatible fashion using import Kernel, except: [...] with a list of all functions you don't want to be imported from Kernel. We reserve the right to do such additions.

In order to continue evolving the language without introducing breaking changes, Elixir will rely on deprecations to demote certain practices and promote new ones. Our deprecation policy is outlined in the "Deprecations" section.

The only exception to the compatibility guarantees above are experimental features, which will be explicitly marked as such, and do not provide any compatibility guarantee until they are stabilized.

Compatibility between Elixir and Erlang/OTP

Erlang/OTP versioning is independent from the versioning of Elixir. Each version of Elixir supports a specific range of Erlang/OTP versions. The compatibility table is shown below.

Elixir versionSupported Erlang/OTP versions
1.017 - 17 (and Erlang/OTP 18 from v1.0.5)
1.117 - 18
1.218 - 18 (and Erlang/OTP 19 from v1.2.6)
1.318 - 19
1.418 - 19 (and Erlang/OTP 20 from v1.4.5)
1.518 - 20
1.619 - 20 (and Erlang/OTP 21 from v1.6.6)
1.719 - 22
1.820 - 22
1.920 - 22
1.1021 - 22 (and Erlang/OTP 23 from v1.10.3)
1.1121 - 23

While Elixir often adds compatibility to new Erlang/OTP versions on released branches, such as support for Erlang/OTP 20 in v1.4.5, those releases usually contain the minimum changes for Elixir to run without errors. Only the next minor release, in this case v1.5.0, does effectively leverage the new features provided by the latest Erlang/OTP release.



Elixir deprecations happen in 3 steps:

  1. The feature is soft-deprecated. It means both CHANGELOG and documentation must list the feature as deprecated but no warning is effectively emitted by running the code. There is no requirement to soft-deprecate a feature.

  2. The feature is effectively deprecated by emitting warnings on usage. This is also known as hard-deprecation. In order to deprecate a feature, the proposed alternative MUST exist for AT LEAST THREE minor versions. For example, Enum.uniq/2 was soft-deprecated in favor of Enum.uniq_by/2 in Elixir v1.1. This means a deprecation warning may only be emitted by Elixir v1.4 or later.

  3. The feature is removed. This can only happen on major releases. This means deprecated features in Elixir v1.x shall only be removed by Elixir v2.x.

Table of deprecations

The first column is the version the feature was hard deprecated. The second column shortly describes the deprecated feature and the third column explains the replacement and from which the version the replacement is available from.

VersionDeprecated featureReplaced by (available since)
v1.11Mix.Project.compile/2 (v1.0)"compile", args)
v1.11Supervisor.Spec.supervisor/3 and Supervisor.Spec.worker/3 (v1.5)SupervisorThe new child specs outlined in
v1.11Supervisor.terminate_child/2 and Supervisor.start_child/2 (v1.6)DynamicSupervisor
v1.11System.stacktrace/1 (v1.7)try/catch/rescue in __STACKTRACE__
v1.10Code.ensure_compiled?/1 (v1.0)Code.ensure_compiled/1
v1.10Code.load_file/2 (v1.7)Code.compile_file/2 (v1.0) or Code.require_file/2
v1.10Code.loaded_files/0 (v1.7)Code.required_files/0
v1.10Code.unload_file/1 (v1.7)Code.unrequire_files/1
v1.10Logger.log/2Passing non-chardata to (v1.0)to_string/1Explicitly convert to string with
v1.10 app environmentLogger in :compile_time_purge_level app environment (v1.7)Logger in :compile_time_purge_matching
v1.10Supervisor.Spec.supervise/2 (v1.5)SupervisorThe new child specs outlined in
v1.10Supervisor strategy in :simple_one_for_one (v1.6)DynamicSupervisor
v1.10Task.Supervisor.start_link/1 in :shutdown and :restart (v1.6)Task.Supervisor.start_child/3 in :shutdown and :restart
v1.9Map.take/2, and Map.split/2, Map.drop/2Enumerable keys in on the second argument before hand (v1.0)Enum.to_list/1Call
v1.9Mix.Project.load_paths/1 (v1.0)Mix.Project.compile_path/1
v1.9String.replace/4 to :insert_replacedPassing (v1.0):binary.replace/4Use
v1.8for in :intoPassing a non-empty list to (v1.0)Keyword.merge/2 or Kernel.++/2
v1.8Enum.into/2Passing a non-empty list to (v1.0)Keyword.merge/2 or Kernel.++/2
v1.8, and the like:milliseconds, :secondsTime units in its plural form, such as: , and so on (v1.4):millisecond, :secondUse the singular form, such as:
v1.8Inspect.Algebra.surround/3 (v1.0)Inspect.Algebra.nest/2 and Inspect.Algebra.concat/2
v1.8Inspect.Algebra.surround_many/6 (v1.6)Inspect.Algebra.container_doc/6
v1.9Kernel.CLI in --detached (v1.0)--erl "-detached"
v1.8Kernel.ParallelCompiler.files/2 (v1.6)Kernel.ParallelCompiler.compile/2
v1.8Kernel.ParallelCompiler.files_to_path/2 (v1.6)Kernel.ParallelCompiler.compile_to_path/2
v1.8Kernel.ParallelRequire.files/2 (v1.6)Kernel.ParallelCompiler.require/2
v1.8's callbackMix.Compilers.Erlang.compile/6 from :error or {:ok, contents}Returning (v1.6){:error, errors, warnings} or {:ok, contents, warnings}Return
v1.8System.cwd!/0 and System.cwd/0 (v1.0)File.cwd!/0 and File.cwd/0
v1.7Code.get_docs/2 (v1.7)Code.fetch_docs/1
v1.7Enum.chunk/2,3,4 (v1.5)Enum.chunk_every/3,4 and Enum.chunk_every/2
v1.7 callbacksGenServer insuper/1Calling (v1.0)super/1Implenting the behaviour explicitly without calling
v1.7not left in right (v1.5)left not in right
v1.7Registry.start_link/3 (v1.5)Registry.start_link/1
v1.7Stream.chunk/2,3,4 (v1.5)Stream.chunk_every/3,4 and Stream.chunk_every/2
v1.6Enum.partition/2 (v1.4)Enum.split_with/2
v1.6Keyword.replace/3 (v1.0)Keyword.put/3 + Keyword.fetch/2
v1.6Macro.unescape_tokens/1,2 to traverse over the arguments (v1.0)
v1.6Map.replace/3 (v1.0)Map.put/3 + Map.fetch/2
v1.6Module.add_doc/6 module attribute (v1.0)@doc
v1.6Range.range?/1 (v1.0)_.._Pattern match on
v1.5nil to mean () (v1.0)nil
v1.5 typechar_list/0 type (v1.3)charlist/0
v1.5Atom.to_char_list/1 (v1.3)Atom.to_charlist/1
v1.5Enum.filter_map/3 comprehensions (v1.0)for or + Enum.filter/2
v1.5Float.to_char_list/1 (v1.3)Float.to_charlist/1
v1.5 moduleGenEvent (Erlang/OTP 17):gen_event (v1.3);<br/>GenStage (v1.0);<br/>GenServer and Supervisor
v1.5EEx in middle and end expressions in <%= is allowed only in start expressions) (v1.0)<%= (<%Use
v1.5 typeInspect.Opts.t/0 value in :as_char_lists value (v1.3):as_charlists
v1.5 typeInspect.Opts.t/0 key in :char_lists key (v1.3):charlists
v1.5Integer.to_char_list/1,2 (v1.3)Integer.to_charlist/2 and Integer.to_charlist/1
v1.5Kernel.to_char_list/1 (v1.3)Kernel.to_charlist/1
v1.5List.Chars.to_char_list/1 (v1.3)List.Chars.to_charlist/1
v1.5Module in @compile {:parse_transform, _}None
v1.5Stream.filter_map/3 (v1.0) + Stream.filter/2
v1.5String.rjust/3 and String.ljust/3 with a binary padding (v1.3)String.pad_trailing/3 and String.pad_leading/3Use
v1.5String.rstrip/1 and String.lstrip/1 (v1.3)String.trim_trailing/1 and String.trim_leading/1
v1.5String.rstrip/2 and String.lstrip/2 with a binary as second argument (v1.3)String.trim_trailing/2 and String.trim_leading/2Use
v1.5String.strip/2 and String.strip/1 (v1.3)String.trim/2 and String.trim/1
v1.5String.to_char_list/1 (v1.3)String.to_charlist/1
v1.4-> with no expression after Anonymous functions (v1.0)nilUse an expression or explicitly return
v1.4 overridableprivate functionsSupport for making (v1.0)public functionsUse
v1.4Variable used as function callUse parentheses (v1.0)
v1.4Access.key/1 (v1.3)Access.key/2
v1.4 moduleBehaviour module attribute (v1.0)@callback
v1.4Enum.uniq/2 (v1.2)Enum.uniq_by/2
v1.4Float.to_char_list/2 (Erlang/OTP 17):erlang.float_to_list/2
v1.4Float.to_string/2 (Erlang/OTP 17):erlang.float_to_binary/2
v1.4 moduleHashDict (v1.2)Map
v1.4 moduleHashSet (v1.1)MapSet
v1.4IEx.Helpers.import_file/2 (v1.3)IEx.Helpers.import_file_if_available/1
v1.4Mix.Utils.camelize/1 (v1.2)Macro.camelize/1
v1.4Mix.Utils.underscore/1 (v1.2)Macro.underscore/1
v1.4OptionParserMulti-letter aliases in Use single-letter aliases (v1.0)
v1.4 moduleSet (v1.1)MapSet
v1.4Stream.uniq/2 (v1.2)Stream.uniq_by/2
v1.3 inside strings/sigils/charlists\x{X*} (v1.1)\u{X*} or \uXXXX
v1.3 moduleDict (v1.2)Map (v1.0) or Keyword
v1.3Kernel.defdelegate/2 option in :append_firstDefine the function explicitly (v1.0)
v1.3Enum.group_by/3Map/dictionary as 2nd argument in (v1.0)Enum.reduce/3
v1.3Keyword.size/1 (v1.0)Kernel.length/1
v1.3Map.size/1 (v1.0)Kernel.map_size/1
v1.3Regex option in /r (v1.1)/U
v1.3 behaviourSet data structure (v1.1)MapSet
v1.3String.valid_character?/1 (v1.0)String.valid?/1
v1.3Task.find/2Use direct message matching (v1.0)
v1.3URI.decode_query/2Non-map as 2nd argument in Use a map (v1.0)
v1.2 behaviourDict data structure (v1.1)MapSet
v1.1?\xHEX (v1.0)0xHEX
v1.1 protocolAccess behaviour (v1.1)Access
v1.1require/2 and alias/2 in as: true | falseNone