Charlie KrugThe Build Log

← All posts

The who's-hiring thread, searchable, with no model in the loop

Grepwork turns HN's monthly Who is hiring? thread into a filterable board. It parses a few hundred messy, human-written job posts with regex, not an LLM. Here is why that works.

Once a month, an account called whoishiring posts "Ask HN: Who is hiring?" and a few hundred companies reply, each with a freeform job listing as a comment. It is one of the best job sources a developer has: no recruiter noise, no LinkedIn spam, written by the people actually doing the hiring. It is also a flat wall of plain text in whatever order things got posted, with no search and no filters. Answering "which of these are remote and use TypeScript" means reading all of them.

Grepwork turns that thread into the job board it always should have been: search across every post, filter by remote type, stack, and seniority, browse past months, expand any card to the untouched original comment. The interesting part is what it does not use to get there.

No model reads the posts

The obvious way to structure a few hundred inconsistent human-written blurbs in 2026 is to hand each one to an LLM and ask for JSON. Grepwork does not. Extraction is deterministic: regex and keyword lists tuned to how these posts are actually written, run once at build time.

That sounds like the worse choice until you look at what these posts have in common. HN's thread has a decade-old convention: the opening line is usually Company | Location | REMOTE | stack, pipe-delimited, because that is what everyone before you did. Stacks are named from a finite, slow-moving vocabulary (there are only so many ways to write "Postgres"). Remote status shows up in a handful of recognizable phrasings. It is messy, but it is messy in patterned ways, and pattern matching is exactly the tool for patterned mess.

The payoff is that every extraction is explainable. When a post gets tagged remote and rust, I can point at the substring that triggered it. The keyword lists live in the repo and are a one-line change to extend. It costs nothing to run, produces the same output every time, and never hallucinates a company that was not there. An LLM buys you a little more recall on the weird posts and pays for it in opacity, cost, and the occasional confident fabrication. For this shape of data, that trade is not worth it.

Honesty about the ones it can't parse

The flip side of deterministic parsing is that you know exactly when it fails. Plenty of posts ignore the convention: no pipes, company name buried in prose, location never stated. A parser that guesses anyway would quietly present a wrong company as fact, which is worse than saying nothing.

So Grepwork flags those. When a post does not follow the usual Company | Location opening, the card marks the auto-guessed fields as unconfirmed rather than dressing a guess up as data. Same spirit on the filters: an explicit "no remote" post gets classed as onsite, not swept into the remote bucket because the word "remote" appeared in a sentence saying they don't do it. The stack and seniority chips are ranked by how often each tag actually shows up in the active month, so the signal-heavy filters sit first instead of a wall of one-hit tags.

It's just static files

There is no backend. scripts/build-data.ts fetches the six most recent threads through HN's public Algolia Search API, which is free, unauthenticated, and already indexes full comment trees, so there is no HTML scraping and no rate-limit risk. Parsing happens at build time, and the output ships as a plain HTML/CSS/JS bundle with relative asset paths. Cheap to host, nothing to protect, and it works served from any subpath. TypeScript runs end to end, with the parser and the UI sharing one JobPosting type, so a change to what gets extracted is a compile error in the UI until it is handled.

Try it

Open the board, type your stack into the search box, and flip the remote filter to "remote". You will have the handful of posts worth reading in about ten seconds, down from scrolling a few hundred comments. Use the month picker to run the same filter over past threads, and expand any card when you want to read the real post in the poster's own words.

Grepwork is live. Free, in your browser, no signup.

This post is part of the build log: every app my automated factory ships gets written up here, honestly. Browse everything at apps.charliekrug.com. Comments are open below.

Comments

Loading comments…