You write a pattern, it passes your tests in a Python service, and then the same string in a Node script returns null. Regular expressions are "the same everywhere" right up until they aren't — and the differences are exactly the ones that cost you an afternoon.
Here are the four that bite hardest.
1. Named group syntax is not shared
Python uses (?P<year>\d{4}). JavaScript uses (?<year>\d{4}). Paste the Python version into a .js file and you get a SyntaxError: Invalid regular expression with no line number pointing at the offending group.
2. \d and \w disagree about Unicode
In Python 3, str patterns are Unicode-aware by default: re.findall(r'\w+', 'café') returns ['café']. In JavaScript, \w is ASCII-only — even with the u flag — so 'café'.match(/\w+/g) stops at caf. If you need Unicode categories in JS, you must spell it out with property escapes: \p{L}, \p{Nd}. Python spells them \w and \d and calls it a day.
Rules extracted from a mixed-language codebase break in whichever language you didn't test.
3. Lookbehind: fixed width vs variable length
Python's re requires lookbehinds to be fixed width. (?<=foo|foobar) raises error: look-behind requires fixed-width pattern. JavaScript's engine accepts variable-length lookbehinds — and Safari only shipped them in 16.4, so a pattern that passes in Chrome can throw on an older iOS client.
4. match() does not mean the same thing
re.match() anchors at position zero; re.search() doesn't. JavaScript's String.prototype.match() behaves like Python's search, not its match. That single-word difference is behind a lot of "but it matched the whole string in my test!" confusion. If you need Python's strict behaviour in JS, anchor both ends explicitly — ^...$ — because JavaScript has no fullmatch equivalent.
The fix: test the pattern, not the language
None of these are hard once you see them — the trick is seeing them in seconds instead of in production. I keep a regex tester open in a browser tab while writing patterns: paste the pattern and a sample of the real input, watch the matches highlight as you type, and check the capture groups panel before the pattern ever lands in code. It uses the browser's own engine, so what you see is exactly what JavaScript will do with it — no round-trip to a server, and nothing in your test string leaves the machine (which matters when the sample is a log line full of tokens).
The PyPI-style patterns I copy into JS now get a two-second sanity check first. That's the whole habit, and it has saved me more debugs than any linter.
I use CodeToolbox's Regex Tester — it is free, runs entirely client-side, and has preset patterns for email, URL, IPv4 and dates that you can bend into shape instead of starting from an empty box.
Top comments (0)