Regex is a find dialog with superpowers, not a language
Regular expressions have a terrible reputation, and the famous line-noise examples deserve it. But here is the reframe that made regex click for me: it is not a programming language, it is the find-and-replace box in your editor after it has had coffee. Normal find matches exact text. Regex matches shapes of text: any digit, any word, something repeated, something optional. That is the entire idea.
And the vocabulary is tiny. The MDN guide documents dozens of features, but in fifteen years of editing text, ten symbols have covered roughly 90 percent of everything I have needed. This is a rule of thumb from my own usage, not a measured statistic, but I would bet most developers' histories look the same. Learn these ten, ignore the rest until a specific job demands them, and you are functionally regex-literate.
The ten symbols
Each of these does one small job, and each is worth about ten seconds of study. Everything scary you have ever seen in a regex, every 80-character monster on a forum, is just these small pieces stacked together in a row.
- \d matches any single digit, 0 through 9
- \w matches a word character: letter, digit, or underscore
- \s matches any whitespace: space, tab, newline
- . matches any single character at all, the wildcard
- + means the previous thing, one or more times
- * means the previous thing, zero or more times
- ? means the previous thing is optional, zero or one time
- [abc] matches exactly one character from the set inside the brackets
- ^ and $ pin the match to the start and end of a line
- ( ) groups a chunk and captures it so the replacement can reuse it
Real job 1: finding every US phone number in a file
Say a colleague hands you notes containing phone numbers formatted three different ways: 555-867-5309, 555.867.5309, and 5558675309. Plain find is hopeless. The regex is a direct translation of the shape: three digits, maybe a separator, three digits, maybe a separator, four digits. Written out: \d{3}[-.]?\d{3}[-.]?\d{4}. The [-.]? reads as one character that is a hyphen or a dot, optionally present.
Read it aloud and it stops looking like line noise: three digits, an optional dash or dot, three digits, an optional dash or dot, four digits. That is the skill, honestly. Regex is written left to right as a description of the text you are pointing at. I build patterns like this incrementally in a regex tester, adding one piece at a time and watching what highlights, because writing the whole thing blind is how you match either nothing or everything.
Real job 2: swapping name order with capture groups
This is the trick that converts people. You have 400 lines like Smith, Jane and you need Jane Smith. Find: (\w+), (\w+) and replace with $2 $1. The parentheses capture the two names, and $1 and $2 replay them in the replacement in whatever order you want. Four hundred manual edits become one keystroke.
The same move reformats dates. US spreadsheets want 08/11/2026 while an export gives you 2026-08-11. Find: (\d{4})-(\d{2})-(\d{2}) and replace with $2/$3/$1. Group one is the year, two is the month, three is the day, so $2/$3/$1 emits month, day, year. I use this exact pattern several times a month on CSV exports, and it pairs well with a case converter when the same file also needs headers normalized from SCREAMING_SNAKE to Title Case.
Real job 3: cleaning up messy text
The unglamorous workhorse jobs. Trailing whitespace at line ends: find [ \t]+$ with nothing in the replace box, and every line is trimmed. Runs of three or more blank lines squashed to one: find \n{3,} and replace with two newlines. Double spaces after sentences: find a space followed by +, so the pattern is just two spaces then +, replaced with a single space.
For pasted-from-the-web text, the ^ and $ anchors earn their keep. Lines that are only a page number: ^\d+$ replaced with nothing. Bullet characters some site prepended: ^[-*] with a space, replaced with nothing, strips them from every line at once. For the common cases like these I usually reach for a text cleaner first, because it has the standard cleanups as one-click presets, and save handwritten regex for the weird stuff the presets do not cover.
The mistake that taught me about greed
My admission for this post: early on, I ran a find-and-replace across a config file using ".*" to match quoted values, and the replace mangled half the file. The reason is that * is greedy: it grabs the longest possible match. On a line containing two quoted strings, ".*" does not match the first string, it matches from the first opening quote to the last closing quote on the line, swallowing everything between, including text I needed. I had a backup. I did not enjoy needing it.
Two protections, use both. First, the lazy modifier: ".*?" with the question mark after the star matches the shortest possible run instead of the longest, which is nearly always what you meant between delimiters. Second, never run a regex replace across a whole file without previewing matches first; every serious editor highlights them, and a tester shows you each match before anything is destroyed. Greedy-versus-lazy is the single sharpest edge in all of regex, and it is disarmed by one keystroke.
When not to use regex
Regex has a hard ceiling, and knowing it is part of literacy. Do not parse HTML or JSON with regex: those formats nest, and regex fundamentally cannot count nesting depth, which is why every regex-based HTML parser eventually breaks on a comment, an attribute containing a bracket, or a tag split across lines. Use a real parser; they exist in every language and they are one import away.
Do not write the mythical complete email regex either. The full official grammar for email addresses is famously baroque, and the pragmatic check, something before an @, something after, a dot in the domain, catches typos just as well as a 400-character monster while rejecting fewer real addresses. And skip regex entirely when plain find-and-replace already works: if the text you are hunting is literal, regex only adds ways to be wrong. The best regex users I know reach for it late, not early, the way you reach for a power tool only after the screwdriver fails. Ten symbols, three real jobs, one greed warning, and two hard limits: that is the entire working curriculum, and it fits on an index card.
Questions people ask
The ten in this post do, near enough. JavaScript, Python, VS Code, and most modern editors share this core. Exotic features vary by flavor, which is one more reason to stay inside the basic vocabulary until a job forces you out.
+ requires at least one occurrence, * accepts zero. So \d+ demands a digit is present, while \d* also matches empty nothing, which is a common source of surprising matches. Default to + unless absence is genuinely fine.
Star is greedy: it takes the longest match it can. Between delimiters like quotes, use the lazy form .*? to take the shortest, or better, match everything except the closing delimiter with a negated set.
Only loosely. A sanity check like something@something.something catches typos fine. A complete validator is impractical in regex, and the real test of an address is whether mail to it arrives, which no pattern can prove.

