Native records have been implemented as per EEP 79. They are a true data type rather than a tuple in disguise, and are considered experimental in Erlang/OTP 29 and possibly in Erlang/OTP 30 as well.
Multi-valued comprehensions have been implemented as per EEP 78, so that each iteration can contribute more than one element.
The new guard BIF is_integer/3 tests that a term is an integer within a range, following the design of EEP-16 with is_between renamed.
Function application is now left associative, so f(X)(Y) can be written instead of (f(X))(Y).
Matching in a comprehension qualifier used to compile and then fail at runtime, and is now rejected at compile time. The new compr_assign feature makes P = E behave as the strict generator P <-:- [E] instead.
Compiler and JIT improvements
The JIT generates better code for matching and creating binaries with multiple little-endian segments.
The compiler generates more efficient code for map comprehensions whose values do not depend on the generator.
Functions can be marked with the new -unsafe attribute, which is separate from deprecation: it says the function is unsafe to use at all, and calls to those in Erlang/OTP are warned about by default.
There are new warnings for exporting a variable out of a subexpression, for the catch operator, for matches that unify constructors, and, with the warn_obsolete_bool_op option, for the and and or operators.
The old-style type tests in guards (integer, atom and so on) have been scheduled for removal in Erlang/OTP 30.
The compile documentation has a new section with recommendations for implementors of languages running on the BEAM.
ERTS
In the default code path, the current working directory (.) is now last rather than first.
Iteration order over maps remains undefined, but is now guaranteed to be the same for every way of iterating over the same map.
The runtime system can generate encrypted crash dumps, enabled with --enable-encrypted-crash-dumps when building Erlang/OTP.
io_ansi is a new module for emitting virtual terminal (ANSI) sequences. It reads the terminfo database of the terminal being written to, including across nodes, so a remote shell session renders correctly.
erl_tar streams entries to disk in chunks rather than reading each one into memory, and the new {max_size,Size} option limits the total extracted size.
array has several new functions. Its internal representation changed to allow them, so arrays serialised by earlier releases with term_to_binary/1 cannot be read back.
json decodes long strings up to 55% faster, and string:length/1 and string:slice/2,3 can be up to twice as fast.
rand:shuffle/1 and rand:shuffle_s/2 return a random permutation of a list.
common_test
ct_doctest is a new module for testing the examples in module documentation and documentation files, including examples that are meant to fail.
tools
xref applies the ignore_xref attribute itself rather than leaving it to build tools, and can find calls to functions that are undocumented, marked -doc false, or marked unsafe.
ssh
The daemon no longer enables the shell and exec services by default, so an authenticated user cannot execute arbitrary Erlang code unless it is asked for.
The SFTP subsystem is likewise no longer enabled by default.
The default key exchange algorithm is now mlkem768x25519-sha256, a hybrid combining ML-KEM-768 with X25519, falling back automatically for peers that do not support it.
Public Key / SSL / Inets
The post-quantum hybrid x25519mlkem768 is now the most preferred key exchange group, and ML-DSA and SLH-DSA the most preferred signature algorithms where such a certificate is configured. All of them were available in Erlang/OTP 28.4, but none were preferred.
There are new hardening guides for ssl and for inets.
Deprecations
The odbc application is deprecated and planned for removal in Erlang/OTP 30.
The ftp and ct_ftp modules are deprecated and planned for removal in Erlang/OTP 30.