Erlang/OTP 29.0 Ships Native Records and Quantum-Resistant SSH
Erlang/OTP 29.0 is out, and it's a major release with several long-awaited language features, security improvements, and new compiler warnings. Here's what you need to know.
Native Records (EEP-79) – Experimental
After years of using tuple-based records, Erlang now has native records as a true data type. They're declared with -record as before, but under the hood they use a dedicated type rather than tuples. This means pattern matching and record access can be more efficient. The feature is experimental in OTP 29 and likely OTP 30, so test carefully before using in production.
Multi-Valued Comprehensions (EEP-78)
List comprehensions now support multiple values per iteration. For example:
[-I, I || I <- [1, 2, 3]].
%% Output: [-1, 1, -2, 2, -3, 3]
This eliminates the need for flattening nested comprehensions in many cases. Combined with the new compr_assign feature, you can also bind variables inside comprehensions:
[H || E <- List, H = erlang:phash2(E), H rem 10 =:= 0].
Quantum-Resistant SSH and SSL
SSH now defaults to mlkem768x25519-sha256 as the key exchange algorithm, a hybrid combining ML-KEM-768 with X25519. This protects against both classical and quantum attacks, with automatic fallback for peers that don't support it. Similarly, SSL now prefers x25519mlkem768 for key exchange.
Security by default: The SSH daemon now disables shell and exec services unless explicitly configured. SFTP is also disabled by default. This prevents authenticated users from running arbitrary Erlang code through SSH.
Compiler Warnings – Clean Up Your Code
OTP 29 enables several new warnings by default:
catchoperator – deprecated, usetry...catchinstead.- Exporting variables from subexpressions – e.g.,
file:open(File, AllOpts = [write, {encoding, utf8}]). and/oroperators – useandalso/orelseinstead.- Match alias patterns – e.g.,
{a,B} = {X,Y}should be{a=X, B=Y}.
All warnings can be disabled with nowarn_* options. Note: old guard tests like list(L) will be removed in OTP 30.
New Functions and Modules
rand:shuffle/1andrand:shuffle_s/2– random permutation of lists.io_ansimodule – emit ANSI escape sequences for terminal styling.ct_doctestmodule – test documentation examples in Erlang module docs.is_integer/3guard – check integer range:is_integer(I, 0, 100).
Compiler and JIT Improvements
The JIT generates better code for binaries with multiple little-endian segments. Map comprehensions with constant values are now optimized – e.g., #{K => 42 || K <- List} no longer recomputes the constant.
Breaking Changes
- Current working directory is now last in the code path (was first).
- No more 32-bit Windows builds.
ignore_xrefattribute now works directly withxrefinstead of requiring build-tool filtering.
What You Should Do
- Update to OTP 29 and test your projects. Pay attention to new warnings – they guide you toward better code.
- Review SSH configurations – if you rely on shell/exec or SFTP, enable them explicitly.
- Experiment with native records in non-critical code to get familiar with the new type.
- Replace
catchand old guard tests before OTP 30 removes them.
The full changelog is available on erlang.org.



