A Regex Meant To Catch My Own Mistakes Started Flagging Real Code As A Mistake
Published
My publish pipeline has a script that checks a draft folder before it writes the social handoff file. If a draft still has an unfilled placeholder like {BLOG_URL}, the script blocks the handoff and tells me what's missing. It started blocking a deploy that had no placeholder problem at all: the post being published had a code example in it, and the validator flagged the code itself as an unfilled placeholder.
What The Validator Actually Checks
The check exists for one specific failure mode: shipping a social post with a literal {BLOG_URL} still sitting in the text, because the real URL never got filled in before publish. That's a real mistake worth blocking automatically, not something to catch by eye every time.
It runs right before the handoff file gets written, the file another part of the pipeline reads to know a post is ready to go out on LinkedIn, X, and Facebook. If the validator passes something with a real placeholder still in it, that placeholder ships publicly.
Catching it here, before the handoff, is the only point in the pipeline where it's still cheap to fix.
The implementation is a single regex, scanning every markdown file in the draft folder for anything matching an uppercase word wrapped in curly braces.
PLACEHOLDER = re.compile(r"\{[A-Z_]{4,}\}")
Four or more uppercase letters, wrapped in braces. That matches {BLOG_URL} exactly, which is the whole point.
What Actually Tripped It
The post being published had a code example showing a small API call, written in JavaScript. The code used template literals to build a URL and an auth header.
const BASE = "https://a.klaviyo.com/api";
const HEADERS = {
Authorization: `Klaviyo-API-Key ${API_KEY}`,
};
${API_KEY} and ${BASE} are both completely normal JavaScript, string interpolation syntax that every JS developer writes constantly. Strip the leading $, though, and {API_KEY} is an uppercase word in curly braces. Same shape the placeholder regex is looking for.
The validator didn't know the difference between a prose placeholder I forgot to fill in and a code example doing exactly what it was supposed to do. It just saw the same bracket pattern and blocked the publish both times.
That code example was the actual point of the post, not decoration on top of it. My own rule for posts that claim to build or figure something out is to show the real piece of it, actual syntax, actual logic, something a reader could adapt.
A post describing a Klaviyo API call in words alone, with no code, reads as a plain anecdote. The validator ended up penalizing the exact thing that makes a post like that worth publishing in the first place.
The Fix Wasn't A Smarter Regex
My first instinct was to make the pattern stricter, require a {BLOG_URL}-style name specifically, or exclude common JS constant names like API_KEY and BASE. Both of those are the wrong kind of fix.
They solve today's specific false positive and leave the next one waiting, the first time a code example uses a different all-caps constant name.
The actual fix was simpler: stop scanning code blocks for prose placeholders in the first place. A placeholder like {BLOG_URL} only ever shows up in regular text. It never appears inside a fenced code block, because code blocks aren't where I write that kind of note to myself.
FENCED_CODE_BLOCK = re.compile(r"```.*?```", re.DOTALL)
def check_drafts(folder):
problems = []
for path in sorted(folder.glob("*.md")):
text = path.read_text(encoding="utf-8")
text_without_code = FENCED_CODE_BLOCK.sub("", text)
for match in PLACEHOLDER.finditer(text_without_code):
problems.append(f"{path.name}: unfilled placeholder {match.group(0)}")
return problems
Strip every fenced code block out before running the placeholder check at all. The regex itself didn't need to get smarter about what counts as a real placeholder. It just needed to stop looking in a place where placeholders never actually live.
Ran the validator again right after the fix, against the same draft folder that had just failed. It passed clean, and the actual handoff file wrote on the next run, no change needed to the post itself.
What This Says About Writing Checks That Assume Plain Text
A check written against prose breaks the moment the file it's reading stops being pure prose. My validator was written back when every draft folder file was plain commentary with the occasional link.
Once posts started including real code examples as part of showing actual work, that assumption quietly stopped holding. Nothing told me until a real publish got blocked over nothing.
A validation script's scope is a decision that goes stale on its own, the moment the shape of the content it's checking changes. Working correctly the first ten times doesn't mean the scope is still right the eleventh.
Same root pattern behind the Netlify deploy that failed for a reason I guessed wrong about: an assumption that made sense once stops getting re-checked, until something concrete forces the question.
I write about Klaviyo API scripts often enough that code blocks in a draft aren't rare. The validator just hadn't caught up to that yet.
Want a publish pipeline that actually catches the real mistakes?
I build automation and tooling that gets the small stuff right, including the checks meant to catch my own mistakes before they ship.