· 4 min read
How to Read a Regex You Didn't Write
Heshan Fernando
Co-founder & COO
A validation function has a regex in it. Forty characters of brackets and backslashes, no comment, written by someone who left two years ago. It rejects an input that looks valid and you need to know why.
Reading regex is a skill, and the two things that make it hard are that the syntax is dense and that a pattern can behave very differently between languages.
Reading it in pieces
A regular expression is a sequence of tokens, and each one does exactly one thing. Read left to right, naming each:
^(\w+)@([\w.-]+)\.(\w{2,})$
^— anchor at the start of the string(\w+)— capture one or more word characters@— a literal at sign([\w.-]+)— capture one or more word characters, dots or hyphens\.— a literal dot, escaped because a bare dot means “any character”(\w{2,})— capture two or more word characters$— anchor at the end
Read that way it’s a rough email pattern, and the escaping of the dot — the difference between . and \. — is the detail that most often trips people reading unfamiliar patterns.
The part that matters for safety
Some patterns don’t just match slowly, they match catastrophically.
When a regex engine can match the same input in many different ways, it tries them. Nested quantifiers — a repeated group that itself contains a repetition, like (a+)+b — create an exponential number of possible paths. Against a matching input it’s fine. Against a long non-matching input it can take seconds, minutes, or effectively forever.
That’s catastrophic backtracking, and on a server processing user input it’s a denial-of-service vulnerability rather than a performance quirk. OWASP documents it as ReDoS, and it has taken down real services.
The shapes to look for: a quantifier applied to a group containing a quantifier, alternations with overlapping branches, and any pattern where two parts can match the same text.
Engines are not interchangeable
The same pattern behaves differently across languages, which matters when copying a regex from an answer written for another stack.
Go’s RE2 guarantees linear-time matching, and pays for it by not supporting backreferences or lookaround at all. PCRE and JavaScript support both and can backtrack catastrophically. Lookbehind support differs. Named group syntax differs.
| Engine | Backreferences | Lookaround | Backtracking risk |
|---|---|---|---|
| PCRE / PHP | Yes | Yes | Yes |
| JavaScript | Yes | Yes | Yes |
Python re | Yes | Yes | Yes |
| Go RE2 | No | No | None — linear time |
Common mistakes to avoid
- Copying a pattern between languages without checking the engine supports its features.
- Using
.where\.was meant, which matches any character rather than a literal dot. - Writing a regex to parse HTML, which cannot work reliably for nested structures.
- Attempting RFC-complete email validation with a regex — a simple pattern plus a verification email is both easier and more correct.
- Deploying a user-facing pattern with nested quantifiers and no timeout.
How to do it with Regex Explainer
The Regex Explainer breaks a pattern into tokens and flags the risky constructs.
- Paste the expression exactly as it appears in your code.
- Choose the flavour, since lookbehind and named group support differ between engines.
- Read the token breakdown to understand what it matches.
- Read the performance notes — nested quantifiers are the ones that matter.
Other developer tools are in the tools directory.
Frequently asked questions
What is catastrophic backtracking?
A pattern where nested quantifiers create an exponential number of ways to match, so a slightly wrong input takes seconds or minutes. (a+)+b against a long run of a’s is the classic example, and it’s a real denial-of-service risk on user input.
Why do the same regexes behave differently between languages?
Because engines differ. Go’s RE2 guarantees linear time and drops backreferences and lookaround entirely; PCRE supports both and can backtrack catastrophically.
Should I use regex for HTML or email?
For HTML, no — use a parser. For email, a simple pattern plus an actual verification message beats any attempt at RFC-complete validation.
Final thought
Read the pattern token by token before changing it, and look specifically for a quantifier wrapping a quantifier. That one shape is the difference between a slow regex and an outage.