How to Build, Test, and Review Regex with Claude Code
Use Claude Code to create, test, and review regex for email, phone numbers, logs, named captures, and common pitfalls.
Regular expressions fail most often when the requirement is vague. A pattern that looks fine for user@example.com may reject user+tag@sub.example.co.jp, accept user@.com, or become impossible to review after one more capture group is added.
Claude Code is useful here because it can turn examples into a small implementation plan: allowed strings, rejected strings, the regex itself, executable tests, and a review checklist. A regular expression is a compact language for describing the shape of text. A named capture group is a way to give extracted parts readable names such as timestamp or requestId.
If you are new to the tool, start with the Claude Code getting started guide. For better task prompts, pair this with 5 tips for better prompts. The official references worth keeping open are Claude Code overview and MDN Regular expressions.
Workflow
flowchart LR
A["Allowed examples"] --> C["Ask Claude Code"]
B["Rejected examples"] --> C
C --> D["Regex and helper functions"]
D --> E["Node.js tests"]
E --> F["Review pitfalls"]
Do not ask only for “an email regex.” Give Claude Code concrete examples and the context: validation, extraction, replacement, or log analysis. The examples become the contract that tests can protect.
First Prompt
Create a JavaScript regex helper for email addresses, Japanese phone numbers, and application logs.
Requirements:
- Run directly on Node.js
- Allow user+tag@sub.example.co.jp
- Reject user..name@example.com and user@.com
- Allow 090-1234-5678, 03-1234-5678, and 05012345678
- Extract timestamp, level, service, requestId, and message from logs with named capture groups
- Add node:test coverage
- Explain any place where regex should not replace business validation
The important part is scope. Full RFC email validation is rarely what a sign-up form needs. In this article we use a practical pattern that catches obvious mistakes while leaving deliverability to confirmation email or backend checks.
Example 1: Email, Phone, and Log Helper
Save this as regex-helper.mjs and run node regex-helper.mjs.
import { fileURLToPath } from "node:url";
export const emailRegex =
/^(?!.*\.\.)[A-Z0-9_%+-]+(?:\.[A-Z0-9_%+-]+)*@(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,63}$/i;
export const emailSearchRegex =
/[A-Z0-9_%+-]+(?:\.[A-Z0-9_%+-]+)*@(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,63}/gi;
export const normalizedJapanesePhoneRegex = /^0(?:[5789]0\d{8}|[1-9]\d{8,9})$/;
export const looseJapanesePhoneSearchRegex =
/0\d{1,4}[-\s]?\d{1,4}[-\s]?\d{3,4}/g;
export const appLogRegex =
/^\[(?<timestamp>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z)\]\s+(?<level>INFO|WARN|ERROR)\s+(?<service>[a-z][a-z0-9-]*)\s+requestId=(?<requestId>[A-Za-z0-9_-]+)\s+message="(?<message>[^"]*)"$/;
export function isEmail(input) {
return emailRegex.test(input.trim());
}
export function normalizePhone(input) {
return input.replace(/[()\s-]/g, "");
}
export function isJapanesePhone(input) {
return normalizedJapanesePhoneRegex.test(normalizePhone(input));
}
export function extractContacts(text) {
const emails = [...text.matchAll(emailSearchRegex)]
.map((match) => match[0])
.filter(isEmail);
const phones = (text.match(looseJapanesePhoneSearchRegex) ?? []).filter(
isJapanesePhone,
);
return {
emails: [...new Set(emails)],
phones: [...new Set(phones)],
};
}
export function parseLogLine(line) {
const match = line.match(appLogRegex);
if (!match?.groups) return null;
return {
timestamp: match.groups.timestamp,
level: match.groups.level,
service: match.groups.service,
requestId: match.groups.requestId,
message: match.groups.message,
};
}
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const text = "Contact: user+tag@sub.example.co.jp / 090-1234-5678";
const log =
'[2026-06-02T10:15:30.000Z] ERROR billing-api requestId=req_123 message="payment failed"';
console.log(extractContacts(text));
console.log(parseLogLine(log));
}
Phone numbers are normalized before validation. This keeps the regex smaller and makes 090-1234-5678 and 090 1234 5678 comparable. The log parser uses named captures so reviewers can read match.groups.requestId instead of guessing what match[4] means.
Example 2: Tests That Lock the Behavior
Save this as regex-helper.test.mjs and run node --test regex-helper.test.mjs.
import test from "node:test";
import assert from "node:assert/strict";
import {
extractContacts,
isEmail,
isJapanesePhone,
parseLogLine,
} from "./regex-helper.mjs";
test("validates practical email addresses", () => {
assert.equal(isEmail("user@example.com"), true);
assert.equal(isEmail("user+tag@sub.example.co.jp"), true);
assert.equal(isEmail("user..name@example.com"), false);
assert.equal(isEmail("user@.com"), false);
assert.equal(isEmail("@example.com"), false);
});
test("validates Japanese phone numbers after normalization", () => {
assert.equal(isJapanesePhone("090-1234-5678"), true);
assert.equal(isJapanesePhone("03-1234-5678"), true);
assert.equal(isJapanesePhone("05012345678"), true);
assert.equal(isJapanesePhone("123-4567-8901"), false);
assert.equal(isJapanesePhone("090-123-456"), false);
});
test("extracts contacts from free text", () => {
assert.deepEqual(
extractContacts("support: user+tag@example.com, tel: 090-1234-5678"),
{
emails: ["user+tag@example.com"],
phones: ["090-1234-5678"],
},
);
});
test("parses application logs with named captures", () => {
const parsed = parseLogLine(
'[2026-06-02T10:15:30.000Z] WARN auth-service requestId=req_abc message="retry required"',
);
assert.deepEqual(parsed, {
timestamp: "2026-06-02T10:15:30.000Z",
level: "WARN",
service: "auth-service",
requestId: "req_abc",
message: "retry required",
});
});
Ask Claude Code to run the test, not just write it.
Run node --test regex-helper.test.mjs.
If a test fails, explain whether the regex or the test data is wrong before editing.
When widening email support, add both an allowed example and a rejected example first.
Example 3: Log Extraction Prompt
Log analysis is a good regex use case because the format is controlled by your application.
Read logs/app.log, extract only ERROR lines, and write requestId and message to CSV.
Use the same appLogRegex as regex-helper.mjs.
Do not silently drop unparsable lines; report the count at the end.
Do not write email addresses or phone numbers into the CSV.
That “unparsable lines” requirement matters. Real logs include old formats, truncated lines, and emergency debug output. A helper returning null is easier to review than a script that silently ignores data.
Review Template
## Regex review request
Files:
- regex-helper.mjs
- regex-helper.test.mjs
Review:
- Are allowed and rejected examples covered by tests?
- Are email, phone, and log responsibilities separated?
- Are named capture names readable?
- Is there any ambiguous repetition that could cause ReDoS?
- Could personal data leak into logs or CSV output?
Output:
- For each issue, include file, line, reason, and suggested fix
- Ask a question instead of guessing when the business rule is unclear
- Run node --test regex-helper.test.mjs after changes
ReDoS means a regular expression can take extreme time on crafted input. Tell Claude Code to look for it explicitly, especially when a pattern has nested repetition.
Common Pitfalls
| Pitfall | Better instruction |
|---|---|
| Only giving successful examples | Include at least three rejected examples |
Using .* everywhere | Use a concrete boundary such as [^"]* |
Reusing a g regex with test() | Separate validation regex and search regex |
| Relying on capture indexes | Use named captures or non-capturing groups |
| Treating regex as business validation | Use backend checks for deliverability and existence |
Regex is the right tool for shaping input and extracting from controlled text. It is not the right tool for proving that an email can receive mail, a phone number is currently assigned, or a requestId exists in a database.
When to Prefer Standard APIs
Claude Code should also tell you when not to use regex. For URLs, prefer the built-in URL class when you need protocol, host, path, search params, or hash. For JSON logs, parse JSON instead of matching braces with a pattern. For CSV, use a CSV parser because commas, quotes, and line breaks have rules that are easy to get wrong. A good prompt is: “Use regex only for extraction from plain text. If the platform has a safer parser, use that and explain why.” That instruction usually produces code that is shorter, easier to test, and less surprising in production.
In review, ask Claude Code to separate three decisions: input shape, business rule, and storage rule. Input shape is where regex helps. Business rules decide whether the value is allowed for this product. Storage rules decide whether the value may be written to logs, analytics, or CSV exports. Keeping those layers separate prevents a small regex helper from becoming a hidden policy engine.
CTA
Regex cleanup often sits close to revenue: lead forms, checkout logs, support intake, and downloadable-resource signups. Start with the free Claude Code cheatsheet for safe daily prompts. For reusable prompts and setup material, use the ClaudeCodeLab products. For a team rollout covering validation, log review, and review gates, use Claude Code training and consultation.
Summary
The best way to use Claude Code for regex is to provide examples, counterexamples, tests, and review criteria in one loop. In Masa’s trial, asking for “the regex and the tests together” caught the user+tag email case, hyphenated phone numbers, and named log captures earlier than a regex-only request. The result is easier to review and safer to change later.
Free PDF: Claude Code Cheatsheet
Enter your email and download the one-page Claude Code cheatsheet for commands, review habits, and safe workflows.
We handle your data with care and never send spam.
Level up your Claude Code workflow
Start with the free PDF, use Gumroad guides when you need repeatable workflows, and book consultation when rollout or revenue paths need human judgment.
About the Author
Masa
Engineer focused on practical Claude Code workflows. Runs claudecode-lab.com, a 10-language technical media site.
Related Posts
Claude Code Obsidian to CLAUDE.md Workflow: Stop Re-explaining Context
Turn Obsidian working notes into concise CLAUDE.md operating notes that make Claude Code sessions easier to resume.
Claude Code Revenue CTA Routing: Send Articles to PDF, Gumroad, and Consultation
A Claude Code workflow for routing article readers to the free PDF, Gumroad products, or consultation by intent.
Claude Code Team Handoff Rules: Review Evidence, Permissions, Rollback, and Revenue Paths
A practical Claude Code handoff format for team review, proof, permission rules, rollback, free PDF, Gumroad, and consultation paths.
Related Products
50 Battle-Tested Claude Code Prompt Templates
Copy, paste, ship. 50 production-ready prompts.
Use proven prompts for code review, refactoring, testing, documentation, debugging, architecture, and incident response.
The Complete Claude Code Setup & Configuration Guide
From install to team-ready workflow.
A practical guide to installation, CLAUDE.md, hooks, MCP servers, permissions, IDE setup, and CI/CD workflows.