Push through an API instead of writing to the database
Why
The API owns validation and the upsert rules, the crawlers never
hold database credentials, and the ingest endpoint can be throttled
like any other.
The cost
It costs one HTTP request per job. When the API moved hosts, its
address was hard-coded in the crawlers, and about 26,700 submissions
failed until it was updated.
One module per company
Why
Every career site is different. Keeping each in its own module means
a site's quirks stay in one file, and a site that changes breaks only
its own crawler.
The cost
There is no shared base class, so headers and helpers are repeated
across modules, and a change to what crawlers return needs 38 edits.
A browser only where it is needed
Why
Headless Chrome is slow and heavy, so it is used for the five sites
that need it. JSON APIs are used where companies have them, because
they are far less brittle than scraped HTML.
The cost
The Selenium crawlers start a fresh browser for every page, so they
are the slowest part of each run.
The source URL is the identity of a job
Why
It is stable, it comes with every listing, and it makes repeated
runs idempotent without any extra bookkeeping.
The cost
The job table has no unique index on that column yet, so two
simultaneous submissions of the same job could both insert it.
Freshness tracked per company
Why
Each run stamps when a company's page was last crawled, and the app
shows it, so people can see how current a listing is.
The cost
Individual jobs are never marked expired, so the total count grows
over time. The site shows about 1,400 listings collected, while a
recent run found 366 open ones.
Split settings that fail fast
Why
Production turns on HTTPS redirects, HSTS, SSL to the database and a
JSON-only API, and the app refuses to start without an ingest key.
The cost
Throttle counters live in each process's memory, so they are not
shared if the API ever runs on more than one instance.