The first time I encountered regex I did what most people do: stared at something like /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/ and immediately closed the tab. It looked like someone had fallen asleep on a keyboard.
Here's the thing though - regex follows rules, and once you understand a handful of them, most patterns stop being mysterious. You won't memorize everything. Nobody does. But you'll be able to read a regex without panicking and write basic ones without copy-pasting from Stack Overflow every time.
Start here: what regex actually does
Regex is just a way of describing a pattern in text. 'Find me any string that starts with a digit, has some letters, and ends with @gmail.com' - that's a regex in plain English. The cryptic-looking syntax is just a compact way to express that description so a computer can understand it.
You use regex whenever you need to find, validate, or extract text that fits a pattern rather than matching exact characters. Is this a valid phone number? Does this log line contain an error code? Extract all URLs from this block of text. Pull the date out of this filename. All regex jobs.
The bits you'll actually use
A period matches any single character. So c.t matches cat, cot, cut, and also c4t or c$t. If you want a literal period, escape it with a backslash: c.t only matches c.t.
Square brackets define a character class - a set of characters where any one of them matches. [aeiou] matches any vowel. [a-z] matches any lowercase letter. [0-9] matches any digit. Add a caret inside to flip it: [^0-9] matches anything that isn't a digit.
The shorthands save you typing. d is the same as [0-9]. w matches word characters (letters, digits, underscore). s matches whitespace. Their uppercase versions are the opposites: D means not a digit, W means not a word character.
Quantifiers control how many times something repeats. ? means zero or one time (optional). + means one or more. * means zero or more. {3} means exactly three times. {2,5} means between two and five times.
Anchors and groups
A caret at the start of a pattern (outside square brackets) means 'match from the beginning of the string.' A dollar sign at the end means 'match to the end.' Without these, your pattern can match anywhere in the text. With them, it has to match the whole thing.
Parentheses create groups. They're useful for two reasons. First, you can apply a quantifier to a whole group: (ab)+ means one or more repetitions of 'ab', not just one or more 'b'. Second, groups capture whatever they match, which lets you extract that piece of text separately from the full match. That's how you pull specific parts out of a string.
A few patterns that are actually useful
Matching a US phone number: (?d{3})?[-s]?d{3}[-s]?d{4} - handles 555-867-5309 or (555) 867 5309 or 5558675309.
Finding lines that contain an error in a log file: ^.*ERROR.*$ - matches any line that has the word ERROR anywhere in it.
Validating that something is only letters and spaces: ^[a-zA-Z ]+$ - no numbers, no punctuation, just letters and spaces from start to finish.
These aren't perfect for every situation, but they cover the most common basic cases and they're readable enough that you can actually understand what they do when you come back to them six months later.
Testing is non-negotiable
Never deploy a regex you haven't tested against a range of inputs, including inputs that should not match. It's embarrassing how many times I've written a validation pattern that cheerfully accepts invalid inputs because I only tested the happy path.
The most common mistake is forgetting to escape special characters that appear literally in the text. A period in a domain name, parentheses in a phone number, a plus sign in an email address - these all need backslashes in front of them or they'll be treated as regex operators instead of literal characters.
Use a regex tester. There are good ones online that highlight exactly which parts of your input match and show you the captured groups. Ten minutes of testing catches problems that would take hours to debug in production.
