Build1 distinct publisher3 min readPublished
A dev.to walkthrough scores WebVTT on four axes instead of one, on the argument that a file can read 96% accurate and still be unusable. The interesting part is which failures the other three axes catch.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
Look at what the scoring function actually does. It normalizes both strings, passes them to `jiwer.process_words()`, and hands back substitutions, deletions, insertions and a reference word count [6]. Every edit costs one, so the worked example in the walkthrough, "fifteen milligrams" heard as "fifty milligrams", returns a WER of 0.2 from a single substitution against five reference words [11][19]. Read that upward and the acceptance gate gets thin: a file described as 96% accurate is a 4% error rate, which is roughly 40 wrong words in a 1,000-word transcript, and the number does not say whether they were articles or units [18].
Normalization is the part that decides whether anyone else's number means anything to you. Skip it and "3" against "three" or "Dr." against "doctor" score as errors even though the recognizer heard correctly [8]. The walkthrough treats the normalizer from the Whisper paper's Appendix C as the closest thing to a default, and describes it as stripping bracketed phrases, dropping fillers like "um" and "hmm", and expanding contractions [9]. That is also the argument the author makes for why vendor-published WER figures are not comparable [10]. For a vendor figure to transfer to your workload, you would need the same normalizer, the same reference transcription conventions, and audio that resembles yours. Two of those three are things vendors do not publish.
The entity axis is a bag-of-terms diff, not an alignment. It iterates a sorted lexicon with `re.findall` so a term said three times counts three times, then walks reference terms and removes each match from a copy of the hypothesis terms, reporting a missed rate and the first ten misses [13][14]. Because the text handed in is cue text joined with spaces [21], a term that was recognized but landed in the wrong cue still counts as present. So this axis catches dropped and mangled domain terms, not displaced ones. Placement is the timing axis's job, and WER cannot help there at all: it discards every timestamp before it starts [5].
Numbers are collected by a separate regex, `\b\d+(?:\.\d+)?\b`, appended to the found terms [15]. That matches digits. Whether "fifteen" ever becomes "15" depends on what the normalizer emits, and the only normalizer behaviour the source claims is brackets, fillers and contractions [9]. Run your own dose strings through it before you trust the entity count on a medical or financial corpus.
The readability plumbing has one sharp edge worth knowing before you aggregate. `chars_per_second` divides flattened cue length by duration and returns `float("inf")` when the duration is not positive [16], so a single zero-length cue poisons any mean; use a median or a percentile. Line breaks are preserved on load deliberately, because they are what the readability check measures [17]. What the material does not supply is a pass value for either reading rate or median cue offset, so those thresholds are an editorial decision you own and have to defend.
Then pin everything. `jiwer.compute_measures()` is gone in recent versions, replaced by `process_words()` returning a `WordOutput` dataclass, and the author notes most blog posts still use the old name [6] - which is a reliable way to date a tutorial. The instruction is to pin what you install and pin it in CI, because these APIs move between majors [7].
Ranked by verification strength, evidence, and original report placement.
The harness scores a WebVTT file on four axes instead of one: word error rate under a fixed normalizer, missed entity rate on domain terms, median cue timing offset, and reading rate in characters per second, and the author says to run it on every model or vendor change.
The stack is Python 3.12 with jiwer, whisper_normalizer and webvtt-py.
A caption file can score 96% accurate and still be unusable.
WER counts substitutions, insertions and deletions and weighs each one the same, so "fifteen milligrams" becoming "fifty milligrams" costs exactly as much as "the" becoming "a".
WER throws away every timestamp before it starts, which makes synchronization and readability invisible to it.
jiwer.compute_measures() is gone in recent versions and is now jiwer.process_words(), which returns a WordOutput dataclass; the author notes most blog posts still use the old name. The wer() helper normalizes reference and hypothesis, calls process_words(), and returns wer, substitutions, deletions, insertions and a reference word count.
Follow any of these and your For You feed starts watching them — no settings page required.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Self-contained code, single author, no external validation
The technical claims are unusually checkable for a single-source story: runnable module listings, a specific library API rename, a named normalizer with described behaviour, and a printed output dict that is internally consistent (1 substitution over 5 reference words equals 0.2). But everything comes from one dev.to post by one author, with no independent benchmark, no reference dataset, no correlation study against human quality ratings, and a body that is truncated inside the timing function, leaving the fourth axis unscored and no thresholds published.
No adoption signal in supplied material
The supplied source contains no release, deployment, usage disclosure, benchmark run, pricing or licensing event. It is a tutorial describing a harness the author is building; nothing indicates that any team, product or vendor has adopted the four-axis scoring approach, so no adoption score can be computed without inferring facts the material does not contain.
Mildly overstated: four axes promised, fewer delivered
The framing is modest by tutorial standards -- the headline argument (WER charges the same for a wrong dosage as for a wrong 'the') is demonstrated with real output rather than asserted. The overstatement is scope: the post opens by promising scoring on four axes and instructing readers to gate every model or vendor change on it, while the supplied material never scores reading rate, publishes no pass threshold for timing offset or characters per second, and breaks off inside the timing function. There is also no evidence offered that the three added axes actually predict caption usability, which is the implicit promise behind '96% accurate and still unusable'.
No disclosed stake or funding relationship
The supplied source names only open-source libraries and a research-paper normalizer, and discloses no employer, vendor relationship, sponsorship, or commercial product being sold. Publisher-platform dynamics for developer blogging cannot be scored from this material without inference, so no incentive value is asserted.
Method claims solid, scope and impact unverified
Confidence is moderate-low overall: the library, normalizer and code-level claims are precise and independently checkable, which supports the descriptive core of the story, but the cluster has one publisher, zero adoption evidence, no thresholds for half the advertised metrics, and a truncated body. That is enough to trust what the harness does and not enough to trust that it works as a quality gate at scale.
build
Stop timing your GraphQL tests and start counting loader calls1 distinct publisher
build
Force the tool call, then hand Lightsail a long-lived key1 distinct publisher
build
A cache hit is a quota refund: semantic caching with trigrams and no vector database1 distinct publisher
build
Agent memory rots by accumulation, and the missing primitive is a supersession key1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 27, 2026