← Back to blog

Why I built resume scoring to run entirely on-device, with no backend

When I started building a resume-to-job-description matching tool, the default architecture was obvious: a client uploads a resume, a server parses it, a model scores it against a job description, and the client polls for results. That's how basically every "ATS checker" on the internet works today. I built it, tested it, and then threw it away and rebuilt the entire scoring pipeline to run locally on the phone, with zero network calls. Here's why, and what that actually took.

The problem with the obvious architecture

A resume is one of the most identity-dense documents a person owns: full name, phone number, email, address history, employer names, dates of employment, sometimes salary context buried in bullet points. The moment you build a "upload your resume, get a score" product, you've also built a resume collection pipeline, whether you meant to or not. You now need a data retention policy, a breach response plan, and an answer to "how long do you keep this and who can see it" — for a feature that is, at its core, string matching and light NLP.

That mismatch bothered me more than the infra cost did. So the constraint I set was: parsing, scoring, keyword extraction, rewriting, history, and PDF export all had to happen without the resume or the job description ever leaving the device.

Parsing without a server

Most resumes arrive as PDF or DOCX. Both formats have selectable text layers you can extract without any external service — PDFKit on iOS gets you most of the way for text-based PDFs, and DOCX is just a zip archive of XML (word/document.xml) you can walk directly. The harder edge case is scanned or image-based resumes, where there's no text layer at all. Rather than bolt on OCR and inflate the app just to handle a minority case, I made a deliberate tradeoff: Rezumate asks for a text-based resume and tells the user clearly when it can't extract usable text, instead of quietly producing a garbage score.

Scoring without calling out to an LLM API

The scoring engine breaks into four components, each independently computable on-device:

None of this requires a large model or a network round trip. It's closer to a well-tuned rules engine with weighted scoring than to a chatbot — which turned out to be a feature, not a limitation. It's fast, it's deterministic, and it's auditable: I can tell you exactly why a resume scored 62 instead of 87, because the score is a sum of inspectable parts, not a black box.

Rewriting bullets without inventing facts

The trickiest constraint wasn't technical, it was ethical. A resume "improvement" feature is one bad prompt away from fabricating a metric the user never achieved. I explicitly designed the rewrite flow to only ever: strengthen the verb, tighten the phrasing, and surface missing job-description keywords that genuinely apply — never to insert a percentage, a dollar figure, or an outcome that wasn't already implied by the original bullet. If the user didn't say it, the app doesn't say it for them.

The PDF export pipeline

Even export stayed local. The final resume is rendered as a LaTeX-style, ATS-safe PDF — centered header, clean section rules, tabular experience entries, fully selectable text — generated directly on-device with no rendering service in between. That closes the loop: import, analyze, improve, export, all without a single byte of the resume touching a server I run.

What this cost me

Building this way is slower and less flexible than shipping a backend. I can't fix a scoring bug with a hotfix deploy — it goes through App Store review like everything else. I don't get server-side analytics on which resumes score poorly and why. Every scoring rule has to earn its place in the binary. But the tradeoff bought something I think matters more for this specific product: there is no database of people's resumes sitting on a server I have to defend, monitor, or eventually explain in a breach notice. For a document that is basically a condensed identity file, that felt like the right default, not an afterthought.

I turned this into a full app called Rezumate — it does the ATS scoring, keyword matching, and bullet rewriting described above, entirely on iPhone.

Rezumate: private, on-device ATS resume scoring for iPhone. No account, no cloud upload.

View on the App Store