September 16, 2026 · Steve Johnson
Five Job Platforms, One Shared Core
I've built automation that applies to freelance and contract opportunities across five different platforms, each with its own site structure, its own login flow, its own application form. The temptation with a project like this is to write five independent scripts, one per platform, because each one genuinely is different enough that copy-pasting and adjusting feels faster than the alternative on day one. It stops feeling faster around the third platform.
Where the platforms actually differ, and where they don't
The parts that differ across platforms are real and can't be abstracted away: how you authenticate, what the job-listing page's structure looks like, what fields a given application form asks for, whether the platform requires a headed browser session for one-time login versus a clean API. Those differences are the whole reason five separate adapters exist rather than one universal client.
But underneath those differences, every one of these automations needs the same handful of things: a consistent way to decide whether a job is worth applying to given a fixed set of criteria, a way to generate a cover letter or proposal that reflects verified background rather than invented claims, a rate floor below which an opportunity gets skipped regardless of how well it matches otherwise, and a dedup log so the same job never gets applied to twice across separate runs. None of that has anything to do with which platform the job came from. Writing it five times, once per platform-specific script, is the thing that actually costs time and creates drift — a rate-floor bug fixed in one script silently staying broken in the other four.
The shared core, and what stayed platform-specific
The fix was pulling everything platform-agnostic into one shared library — the qualification logic, the cover-letter and proposal generation, the rate floor, the dedup log — and keeping each platform's adapter responsible only for the parts that are genuinely platform-specific: scraping that platform's listings into a common shape, filling that platform's specific form fields, submitting through that platform's specific flow. A fix to the qualification logic or the dedup mechanism now lands once, in the shared core, and every platform's adapter picks it up automatically on its next run, rather than requiring five coordinated patches that inevitably land out of sync.
The signal that told me it was time to extract the shared piece
I didn't design this split up front — the first platform's automation was written as a single self-contained script, which was the right call at the time, because you don't know yet which parts are actually shared until a second, meaningfully different case exists to compare against. The signal that it was time to extract a shared core was fixing the same category of bug twice, in two different scripts, and recognizing it was the same bug. That's a more reliable trigger than trying to predict shared structure in advance, because premature abstraction guessed wrong just as often as it guessed right, and costs real time when a script is later than it needed to be to start with.
The pattern generalizes past job-application bots: any time you're building the second instance of something that rhymes with the first, the question worth asking isn't "how similar do these look" — it's "if I fix a bug in the shared behavior, does fixing it in one place fix it everywhere it exists." If the honest answer is no, that's the actual signal, not a hunch about elegance.