One Regex, Eight Engines: What Actually Changes When You Switch Languages
You wrote a regex in Python. It works. You paste the same pattern into a Go service and the program refuses to even compile it. Nothing about your pattern changed — but the engine did.
Most tutorials quietly assume one regex dialect. In production you meet eight of them: PCRE2, RE2, ECMAScript (JavaScript), Python re, Go regexp, Java, .NET, and Rust's regex crate. They disagree on syntax, on features, and on how fast they fail.
The three families
Backtracking engines — PCRE2, JavaScript, Python, Java, .NET. They try a match, and when it fails they backtrack and retry. That is what gives you lookarounds and backreferences, and also the classic ReDoS trap: (a+)+$ against a long string of as explodes exponentially.
Linear-time engines — RE2, Go, Rust. They compile patterns to automata and guarantee O(n) matching. The price: no backreferences and no lookarounds, at all. Go and Rust reject such patterns at compile time — which is exactly the moment your Python pattern "stops working".
PCRE2 is the superset most people mean by "regex": recursion (?R), conditionals (?(1)...), match-start reset \\K, possessive quantifiers. If an answer you found online uses (?R), it lives in PCRE/Perl territory and will not run anywhere else on this list.
The feature matrix
| Feature | PCRE2 | RE2 | JavaScript | Python | Go | Java | .NET | Rust |
|---|---|---|---|---|---|---|---|---|
| Lookahead | yes | no | yes | yes | no | yes | yes | no |
| Lookbehind | variable length | no | variable (ES2018) | fixed width | no | bounded | variable | no |
| Backreferences | yes | no | yes | yes | no | yes | yes | no |
| Named groups | (?<n>...) |
(?P<n>...) |
(?<n>...) |
(?P<n>...) |
(?P<n>...) |
(?<n>...) |
(?<n>...) |
(?P<n>...) |
| Atomic groups | yes | no | no | 3.11+ | no | yes | .NET 7+ | no |
| Possessive quantifiers | yes | no | no | 3.11+ | no | yes | no | no |
Recursion (?R) |
yes | no | no | no | no | no | no* | no |
Unicode \\p{L} |
yes | yes | with u flag |
yes | yes | yes | yes | yes |
*.NET has balancing groups, which cover some of the same ground as recursion.
Five breakages you will actually hit
- Lookbehind width. JavaScript (since ES2018) and .NET accept variable-length lookbehind like
(?<=\\d+\\.?\\d*). Python requires fixed width. Java allows bounded width. Go and Rust reject lookbehind entirely — pattern won't compile. - Backreferences.
(\\w+)@\\1style patterns work in five engines and are rejected by RE2, Go, and Rust at compile time. - Named group syntax. Guess between
(?<name>...)and(?P<name>...)and you have a 50% chance of a syntax error per engine. - Possessive quantifiers.
x*+runs in PCRE2, Java, and Python 3.11+, and throws a syntax error in JavaScript and .NET. - The ReDoS cliff. A pattern that is merely slow on PCRE2/JS/Python is impossible to blow up on RE2/Go/Rust. Moving user-supplied patterns to a linear-time engine is a security fix, not just a port.
Test before you ship
The cheapest insurance is running the pattern through every engine you actually target:
- RegexTester.dev runs real WebAssembly builds of all eight engines side by side — same pattern, eight result columns, so flavor differences become visible instead of hypothetical. It also benchmarks the same pattern across engines with P95/P99 timings.
- Its compatibility checker flags the exact constructs in your pattern that behave differently across flavors before they reach production.
- Per-flavor testers if you only care about one target: PCRE2, JavaScript, Python, Go, Rust.
Takeaways
- "Regex" is really eight languages wearing one syntax. Budget for translation, not copy-paste.
- The dangerous constructs are not the ones that fail loudly — a compile error is a good error. The dangerous ones compile everywhere and mean slightly different things (lookbehind width, Unicode case folding).
- Before shipping a pattern to a second language, run it through all engines once. It takes a minute and catches the whole class of portability bugs.
What is the worst regex portability bug you have shipped? I am collecting examples.
All rights reserved