Regex is powerful, but it is also easy to get almost right. “Almost right” is the problem. A regex can match too much, too little, or the right thing in your test but the wrong thing in production.

That is why I test patterns with the Regex Tester before I put them into code. If I need replacement, I use the Regex Replace Tester too.

The simple idea

A regex is a pattern for matching text.

Example:

error

This matches the word error.

A slightly more flexible pattern:

errors?

This matches:

error
errors

because s? means “the s is optional”.

Step 1: start with real sample text

Do not test a regex only on perfect input. Paste realistic examples into the tester.

For example, if I am matching log lines, I paste several real-looking lines:

2026-09-27 10:00:01 INFO server started
2026-09-27 10:00:03 ERROR database connection failed
2026-09-27 10:00:04 WARN retrying connection

Then I build the pattern against that sample.

Good sample text should include:

  • lines that should match;
  • lines that should not match;
  • edge cases;
  • messy input if real input is messy.

Step 2: start simple

I do not start with a clever regex. I start with the smallest useful pattern.

ERROR

Then I add detail:

^\d{4}-\d{2}-\d{2} .* ERROR .*$

Plain meaning:

  • start at the beginning of the line;
  • match a date like 2026-09-27;
  • match anything after it;
  • require ERROR;
  • match the rest of the line.

Building in steps makes it easier to see where the pattern breaks.

Step 3: understand anchors

Anchors do not match characters. They match positions.

Common anchors:

^

means start of line or string.

$

means end of line or string.

Example:

^ERROR

matches lines that start with ERROR.

It does not match:

2026-09-27 ERROR failed

because ERROR is not at the start.

Step 4: use character classes carefully

A character class matches one character from a set.

[abc]

matches a, b, or c.

Common shortcuts:

\d

matches a digit.

\w

matches a word character.

\s

matches whitespace.

A date pattern might use:

\d{4}-\d{2}-\d{2}

Plain meaning:

four digits, dash, two digits, dash, two digits.

Step 5: watch greedy matching

This pattern is greedy:

<.*>

Given:

<b>one</b><i>two</i>

it may match from the first < to the last >:

<b>one</b><i>two</i>

not just:

<b>

A less greedy version uses ?:

<.*?>

Regex and HTML are a dangerous mix for real parsing, but this example shows why greediness matters.

Step 6: use capture groups intentionally

Parentheses capture parts of a match.

Example:

user_id=(\d+)

Given:

user_id=123

The whole match is:

user_id=123

The captured group is:

123

Capture groups are useful when code needs only part of the match.

Step 7: test replacement separately

Replacement has its own risks. A pattern can match correctly but replace badly.

Suppose I want to turn:

Doe, Jane

into:

Jane Doe

Pattern:

(\w+),\s*(\w+)

Replacement:

$2 $1

Use Regex Replace Tester to preview this before running it across real text.

Step 8: check flags

Flags change regex behaviour.

Common flags:

  • i: case-insensitive;
  • g: global, match more than once;
  • m: multiline anchors;
  • s: dot also matches newlines, depending on the engine.

The same pattern can behave differently with different flags. Always test with the same flags your code will use.

Step 9: know your regex engine

JavaScript, Python, PostgreSQL, nginx, and grep do not all support exactly the same regex features.

A pattern that works in one place may fail in another.

Before using advanced features, check whether your target environment supports them. For simple patterns, this rarely matters. For lookbehind, named groups, or Unicode features, it can matter a lot.

My regex checklist

Before using a regex in code, I check:

  1. Did I test it with realistic sample text?
  2. Did I include examples that should not match?
  3. Is the pattern too broad?
  4. Do anchors behave the way I expect?
  5. Are greedy parts safe?
  6. Are capture groups intentional?
  7. Are flags correct?
  8. If replacing text, did I preview the replacement?
  9. Does my target environment support this regex syntax?

Regex is useful when the pattern is clear. The tester helps make that clarity visible before the pattern touches real data.

Comments

Comments are welcome — please read the comment policy first. Powered by giscus and GitHub Discussions.