
The problem
Job boards match on keywords, which is how a React developer ends up reading Java listings. Matching on someone’s declared stack means first knowing which technologies a listing is asking for, out of free-text prose written by whoever posted it.
Decisions
Real listings, pulled from a live job feed
Roles are ingested from the open Arbeitnow API and tagged with the technologies each one asks for at ingest time. A user declares a stack and roles are scored by overlap, so every score shows its working.
Third-party HTML is sanitized in the ingest path
Descriptions are someone else’s HTML rendered into the page, so the sanitizer runs once, on the single route that writes them. Sanitizing at each render site instead would depend on every one of those sites remembering to do it.
Ingest is an upsert over the current feed
Listings that roll off the feed keep their stored copy indefinitely. That means a change to the sanitizer needs a repair pass over the existing rows as well as a re-ingest. That is obvious once and invisible forever after, so it is written down next to the script.
The hard part
Detecting a technology in prose without inventing it
The first version matched bare abbreviations. “go”, “js” and “ts” produced 143 false Go listings out of ordinary German prose. It was a matcher confident enough to be useless. Patterns are narrow now, and an abbreviation is only added if it cannot collide with a normal word in any language the feed carries.
The second failure was subtler. Some listings arrive entity-escaped, so their markup renders as visible tag names unless it is decoded before sanitizing. The obvious test, decoding when the description has no real tags, misses every one of them, because those same listings still carry a couple of genuine tags in the footer the feed appends. The decode fires when escaped tags outnumber real ones, which describes the problem itself, not the first example of it.