By the time I opened cv_v47_dataeng_final_v2.docx, I already knew I was not going to send another application that night. It was a Sunday in late 2023. The tracker spreadsheet, unopened for a week, sat behind three other tabs I had been using to avoid looking at it. Eleven rows deep in "Applied." None in "Responded." My back ached from a day and a half of sitting; the browser tab next to the CV was a Kaggle notebook I had opened that morning and had not touched since. I closed the laptop.
That year, a serious job search had become very time-consuming. Every hour spent restructuring the same three roles into a slightly different order was an hour not spent on the projects that were supposed to be making me more employable in the first place. Every version of the CV, cv_v46 and cv_v47 among them, swapped one keyword cluster for another. Every long evening at the desk ended in the same low-grade ache in the lower back and the same unopened tracker. Tailoring applications had become a full-time task, and it took the hours I should have spent improving my skills.
Some engineers solve their own problems with side projects. I made myself a rule: the next time I faced this market, I was not going to face it by hand. Two and a half years later, with graduation on the horizon and a dissertation to finish, that rule had a plan behind it. Once I had submitted my dissertation, I planned to build the tool that could have saved me those Sunday nights: a system to run the mechanical half of a job search, so that I had time for the human decisions (whether to apply, whether to accept, what to spend the recovered hours on).
In March, I found an open-source project online that already did half of what I needed.
Scrolling one lunchtime, I stumbled onto a post about an open-source project called career-ops. Its author, Santiago Fernández de Valderrama, had used it to evaluate more than 740 job postings on his way to a Head of Applied AI role. He had built, in public, the thing I had been sketching in a notebook for months. The polite reaction would have been to close the tab and start writing my own version. The useful reaction, arrived at after a full day of stubbornness, was to fork his repo and start extending it.
How the job market changed
Before writing any of my own code, I wanted to check whether my memory of the job market was accurate or distorted by burnout.
The data confirmed my memory.
Jobscan's 2025 audit found applicant tracking systems on the careers pages of 97.8% of Fortune 500 companies. A 2021 Harvard Business School study of 2,275 executives across the US, UK and Germany found that more than nine in ten employers using such a system rely on it to make the first cut, and 88% concede their systems screen out qualified candidates. The often-repeated line that ATS software rejects three-quarters of CVs is folklore; the citation trail leads to a 2012 sales pitch by a vendor that folded the following year. The honest picture is worse than the myth anyway. The systems rank CVs and place untailored CVs at the bottom of the list; they do not reject them automatically.
Tailoring shifts the odds. Platform data from over a million tracked applications shows tailored CVs converting to interviews or offers at roughly twice the rate of generic ones. But volume has climbed to meet it. Applications on Workday grew four times faster than openings in the first half of 2024. LinkedIn now processes around 11,000 applications a minute, up 45% year on year. Industry reporting puts the average UK graduate opening at about 140 applicants. Between 18 and 22 percent of postings on Greenhouse are what the Congressional Research Service politely calls "ghost jobs", listings employers never intend to fill.
Entry-level candidates are affected most. New-graduate hiring at Big Tech firms is down more than 50% versus 2019. US recent-graduate unemployment sits above the overall rate for the first time in decades. Stanford's Digital Economy Lab, using payroll data from 4.6 million workers, has documented a persistent decline in employment among 22-to-25-year-olds in the most AI-exposed occupations. Economists disagree about the cause of that finding (Daron Acemoglu is sceptical; Google's economists blame interest rates), but nobody disputes the decline itself.
In this market, precise applications work better than a high volume of applications, precision takes time, and that makes it a task worth automating.
Reading before writing
When I read through Santi's repo before changing anything, I saw that he had already solved most of the problems I had planned to work on. He had built the evaluation rubric I would have built, the mode-routing architecture, the Playwright PDF pipeline, the tracker discipline, and the ethical rule that ended every application with a human clicking submit rather than a machine. His codebase was working, opinionated, and better in most respects than my whiteboard sketches.
It did not cover the UK job market.
Santi had built for the ecosystem he was applying into. I needed something UK-first: a system that knew a British employer can only sponsor a Skilled Worker visa if it holds a licence on the gov.uk register, and that flagged the unlicensed employers before I wasted a Sunday tailoring a CV for a company that legally could not hire me. I needed something that connected to Notion, because I already organise my work and life there. I needed something that could tell me, honestly, whether its own confidence in a match score was any good.
So I forked career-ops the same week I found it, and started extending. Four months in, the system I have been building on top of Santi's foundation runs every day. It is called applyd, and I am writing this now because it is time other people had the option to use it too, and to help me push it further.
One shared definition for all metrics
The technical decision I am proudest of has almost nothing to do with AI.
Early versions of what I was building had six different scripts producing metrics: a funnel report, a rejection-pattern analysis, a pacing alarm, a dashboard, a batch summary, and a follow-up calculator. Each script had its own definition of what "applied", "responded", "screened" and "ghosted" meant. Two of the dashboards disagreed about my response rate. I stared at that discrepancy for an evening before I understood it: the definitions had drifted, quietly, across six files nobody had thought to reconcile.
I pulled the definitions into a single shared module and rewired every consumer through it. The same layer now holds the match-score calibration: bands of predicted fit are back-tested against actual interview outcomes, so the system can tell me when its own scores are systematically wrong rather than trusting its first guess. This is dbt-style semantic-layer discipline transposed onto a personal-scale system, and it is the piece I would show a data engineer first.
Where the language model does not enter
The next decision worth naming is where the deterministic code stops and the language model begins.
applyd's scanner hits Greenhouse, Ashby, Lever and Workable APIs directly. It pays zero tokens per sweep. A cross-portal dedup pass collapses the same posting when it appears on multiple boards. A zero-LLM lint enforces the CV quality rules (banned words, banned constructions, dash policy) as pure rule-based checks that never call the model. The model enters only where judgement is required: evaluating the job description against my CV, drafting the tailored cover letter, running a bounded QA loop over the generated CV.
Every LLM call has a defined role and a bounded output. Nothing is deferred to the model that a deterministic check can handle. The reasoning is cost, latency, and trust. Language models are good at judgement work and expensive to run in a loop; deterministic code is cheap, testable, and never invents a keyword. If you set that boundary in the wrong place, you either waste money or lose track of what the system is doing.
A tracker that belongs to me
Notion holds the system of record. On first run, applyd provisions an Applications database through the Notion API with a canonical schema: nine pipeline stages from Discovered through Offer, deduplication on job URL enforced before any insert, and three dashboard views (a pipeline kanban, a table ranked by match score, an active-interviews board). A local markdown tracker runs in parallel as a cache and a portable fallback.
The interesting design question was not the API integration. It was the contract: which fields the agent owns, which the human owns, and how conflicts resolve when both write to the same row. That contract is defined in a single file and enforced at every write site.
The register
The UK sponsor check is the smallest piece of the system and the one I use most.
Every UK employer that appears in a scan is matched against a locally cached copy of the gov.uk licensed-sponsor register before applyd drafts a word. Unlicensed employers are tagged and demoted; if I need visa sponsorship, they simply cannot help me, no matter how good the role sounds. The pipeline treats that as a hard filter enforced upstream of drafting. It has already stopped me from spending three Sunday evenings on applications that had no legal path.
The rest of the system is agent-neutral by design. A single AGENTS.md file defines the project contract, and a dispatcher routes scheduled headless runs to adapters for Claude, Codex or Gemini CLIs based on an environment variable. The same modes run under any supported runtime. That matters less for a single-user tool than the pattern matters generally: an AI-integrated system tightly coupled to one vendor's runtime is a system you cannot move.
What forking taught, and what comes next
Four months inside someone else's codebase teaches more than four months on a greenfield build. Reading before writing forces you to understand a design before you are allowed to disagree with it; disagreement, when it comes, has to be built in a way that leaves open the possibility of being wrong.
applyd is on GitHub, MIT-licensed and open for contributions. Portal scanners for new markets are top of the list, and anyone job-hunting in the UK or EU can install it today. Santi's career-ops is on GitHub too, and for anyone building AI-integrated systems, the mode-routing architecture and the never-auto-submit ethic are the first two files worth reading. My additions (the semantic layer, the Notion contract, the sponsor check, the deterministic-vs-LLM boundary and the agent-neutral runtime) are built on his code, and his work deserves full credit.
The 2023 version of me, the one with the aching back, the unopened tracker, and the Kaggle notebook that never got worked on, would have found the whole thing useful. The current version uses it every week. I expect the job market to keep giving me reasons to use it.
View applyd on GitHub → The original career-ops →
Sources: Jobscan ATS Usage Report (2025); Harvard Business School / Accenture, "Hidden Workers" (2021); Huntr Job Search Trends (2025); Workday Global Workforce Report (2024); LinkedIn application data via NYT (2025); UK graduate-market industry reporting (2024 to 2025); Greenhouse State of Job Hunting (2024 to 2025); Congressional Research Service IF12977; SignalFire State of Tech Talent (2025); Federal Reserve Bank of New York, Labor Market for Recent College Graduates (Q1 2026); Stanford Digital Economy Lab / ADP, "Canaries in the Coal Mine" (2025 to 2026).
I am an analytics engineer in Manchester, UK, and I build data platforms with dbt, Dagster and PostgreSQL.