Frontend
Our regex found 199 records in a 1,723-record corpus and reported no errors
Thedolceway DEV Community
2 views
We maintain a corpus of 456 role-specific resume examples in TypeScript. Someone asked me
what a good bullet point actually looks like, and rather than answer from taste I decided
to measure the thing I already had.
Fifteen minutes later we had a script, a set of numbers, and a conclusion. The conclusion
was wrong, because the script had silently read about twelve percent of the data.
This is a post about that failure mode, and then about the numbers I got once the script
worked.
The corpus
Thirty-one TypeScript files, each exporting an array of role objects. One role looks roughly
like this:
{
slug: 'cloud-architect',
title: 'Cloud Architect Resume',
category: 'Information Technology',
sampleData: {
summary: '...',
experiences: [
{
company: 'Amazon Web Services',
position: 'Senior Cloud Architect',
description: '- Designed multi-region architecture...\n- Led migration of...',
},
],
skills: [...],
},
tips: [...],
}
The interesting field is description. It holds a newline-delimited list of bullets as a
single string, so the whole corpus of bullets is sitting there in source, greppable,
without a database or an export step.
Version one
const descs = [...text.matchAll(/description: '((?:[^'\\]|\\.)*)'/g)].map(m => m[1]);
Nothing exotic. Match description:, then a single-quoted string, allowing escapes so an
apostrophe inside the text does not terminate the match early.
It found 199 description strings. I did not question that, because I had no prior for what
the number should be. 199 sounded like a lot of text. We computed medians off it, looked at
the opener distribution, and started writing.
The number that saved me was on a different line of the same output: roles 456. The slug
count was fine. So 456 roles between them had 199 job descriptions, which would mean the
overwhelming majority of roles had no work history at all. I knew that was false, because
I had rendered these pages.
Why it read twelve percent
Prettier. That is the whole answer.
Some of these files were formatted at a width that keeps the value on the same line:
description: '- Led enterprise sales team of 30+...',
Others were formatted with the value pushed onto the next line:
description:
'- Led enterprise sales team of 30+...',
The pattern had a literal space after the colon. Every record in the second shape failed to
match. Not with an error. Not with a warning. The regex simply found fewer things, and a
regex that finds fewer things looks exactly like a regex running over a smaller corpus.
// before
/description: '...'/g
// after
/description:\s*('...'|"...")/g
Adding \s* took the count from 199 to 1,723.
The general shape of this bug is worth naming, because it is not really about regexes.
A parser that silently skips malformed input converts a crash into a wrong answer. A
crash costs you ten minutes. A wrong answer costs you however long you spend believing it,
which in my case included a first draft of this post.
Two habits that catch it:
Have an expected order of magnitude before you look at the output. 456 roles times
three jobs each is well over a thousand. 199 should have been alarming on sight.
Cross-check one extracted count against a different one from the same run. Slugs and
descriptions come from the same files through different patterns. When one is plausible
and the other is not, the pattern is the suspect, not the data.
What the corpus actually looks like
With the parser fixed: 1,723 description blocks, of which 1,267 use the bullet format,
yielding 3,815 individual bullets.
Length. Median 14 words. The 10th percentile is 11, the 90th is 17, the full range is
7 to 25. That is a tighter distribution than I expected, and it is not the product of a
style rule, because there was no style rule. It is what a claim plus its evidence happens
to cost in English. Under about ten words you have stated a duty. Over about twenty you
have written a paragraph that gets skimmed.
Numbers. 89.0 percent of bullets contain at least one digit. 1,081 contain a percentage,
555 contain a currency amount. That leaves 418 bullets with no number in them at
all, and reading a sample of those is instructive: they are the ones describing scope
rather than outcome, which is a legitimate thing to describe when you cannot quantify it.
The failure is not "has no number", it is "has neither a number nor a specific".
Verbs. 332 distinct opening words across 3,815 bullets. The top ten (managed,
developed, led, built, created, designed, implemented, conducted, reduced,
manage) account for 40.9 percent of all openers. 143 verbs appear exactly once.
That distribution is the interesting part. It is not flat and it should not be flat. A long
tail of one-off verbs (architected, migrated, authored) carries the specificity, while
a small head of common verbs carries the ordinary work. A resume where every bullet opens
with an unusual verb reads like a thesaurus exercise.
Weak openers. 149 bullets, under five percent, begin with supported, assisted or
similar. Zero begin with responsible for. That one is a genuine consensus in the corpus
rather than something I enforced.
Density. The median role shows three bullets per position, never more than four. The
456 summaries run a median of 37 words, with the 10th to 90th percentile band sitting
between 29 and 45.
Duplicates: zero. Not one bullet text appears twice across 3,815. I checked because I
assumed generated-looking content would have collisions, and finding none told me the
corpus was written per role rather than stamped from a shared list.
The part that transfers
The measurement I would suggest to anyone writing bullets for their own history, since it
takes a minute and needs no tooling:
Count words. If a bullet is under ten or over twenty, it is probably a duty or a
paragraph.
Look for the digit. If there is none, check there is at least a proper noun, a system
name, or a scale.
Read only the first word of every bullet, top to bottom. If the same verb appears three
times in one job, two of those bullets are describing the same thing.
Number three is the one that finds real problems fastest, and it is the same trick as the
script: look at one narrow projection of the data instead of reading all of it.
The corpus these numbers came from is public. It is the set of
resume examples on the builder I run, organised by
role, free to open and free to preview without an account. It was not written in order to
be measured, which is exactly why the measurements were worth taking.
And the script stayed in the repo, with the \s* and a hard assertion that the description
count is within an order of magnitude of the role count. A checker that can quietly read
twelve percent of its input will do it again.
Read original: https://dev.to/thedolceway/our-regex-found-199-records-in-a-1723-record-corpus-and-reported-no-errors-31eh
Related
Our site served every URL the same 3,780 bytes, and Google believed it
Frontend
2
DEV Community
How JavaScript Can Limit AI Crawler Access to Your Website Content
Frontend
3
Dev.to (EN Zone)
Measure a context plugin on your own repository before you install it
Frontend
3
Dev.to (EN Zone)
Zero-Budget Web Dev: Moving from Discord/Drive to Google Sites
Frontend
2
DEV Community
Comments0
No comments yet — be the first