Versioning and stability#
REAL is calendar-versioned: YEAR.MONTH.PATCH, the patch counting releases
within the month. Every surface — the C++ headers, the Python package, the Rust
crate, the C ABI — carries the same version, single-sourced from
pyproject.toml and checked by make version-check; the Go module carries its
own v0 line. A calendar version is valid Semantic Versioning, and this policy
makes it mean what SemVer tools read: the year is the major. A caret pin
(^2026.7 in Cargo, >=2026.7,<2027 in pip) accepts every release of the year
and no break.
What is covered#
A surface is stable when this site documents it: the classes, functions
and members in the C++ reference and
Python reference, the Rust crate’s public items,
the functions of bindings/c/real_capi.h, and the Go package’s exported names.
Everything else is outside the promise:
Internal —
real::detail,real::prof, and every name the reference does not show. They change in any release.Experimental — public names listed with a reason in
docs/site/reference/unpublished.yaml(for examplereal::dfa_munch_memo). They may change in any release until they are published; the release notes say when one does.
Behaviour is part of the promise where it is documented: which strings a
pattern matches, the spans it reports, the errors it raises and their codes.
A fix that makes REAL agree with its documentation, or with the reference
engine a surface claims to follow (re for the Python drop-in, RE2 for
compat-re2), is a bug fix, not a break, and the release notes name it.
Within a year: additions and fixes only#
A release inside a year may add names, overloads, flags, error codes and enum values at the end, and may fix bugs. It does not remove or rename a stable name, change a signature, narrow what a stable function accepts, or change documented behaviour.
A name that is to go is deprecated first — [[deprecated("...")]] in C++,
a DeprecationWarning in Python, #[deprecated] in Rust, a // Deprecated:
paragraph in Go — with the replacement named in the message, and stays for at
least three months. It is removed only in a new year.
Across a year: how a break ships#
Breaking changes are developed on the next branch and ship together in the
first release of the following year (YEAR+1.1.0), with a migration section in
its notes. main keeps releasing the current year from the same engine, so a
break never forces an out-of-season version number, and never reaches a caret
pin. The one exception is a security fix that cannot be made compatibly; it
ships when it is ready, and its notes say what it breaks and why.
The C ABI#
bindings/c/real_capi.h is frozen-additive: functions, enum values and flags
are added, never changed or removed. REAL_ABI_VERSION (and
real_abi_version(), which reports the value the library was built with)
counts incompatible changes, and moves only with a new year; a binding that
links the library rather than compiling it compares the two at load time.
tests/bindings/capi_abi_golden.txt pins the surface, and make check-abi-bump refuses a golden that removes or changes a line against the
last release unless REAL_ABI_VERSION moved with it.
The Go module#
The module lives at github.com/RECHE23/real-regex/bindings/go and is tagged
bindings/go/v0.MINOR.PATCH. The patch follows every engine release; the
minor moves when the Go API breaks, which follows the same rule as above. The
module moves to v1 once its API has held through a year.
Exceptions so far#
v2026.9.7 changed the Rust RegexSet::matches return type in a release that
Cargo reads as compatible. That is the break this policy exists to prevent;
the next change of its kind waits for next.