← Back to Blog

Building a Resume Tailoring Pipeline with Claude Code

July 2026

Every job posting is different. The backend infrastructure role at a defense contractor wants to see C++ and real-time systems. The Series B startup wants full-stack product sense. The quant fund wants data pipeline experience. If you're applying with the same resume each time, you're leaving signal on the table.

I got tired of manually copying and trimming my resume for each application, so I built a pipeline that does it for me. The whole thing runs on Claude Code, a master resume file, a slash command, and about 150 lines of Python.

The Master Resume

The centerpiece is a single resume.md file that acts as a content library rather than a finished document. It contains everything I might want on a resume—every bullet, every project, every skill—organized with conditional markers.

Three alternate summary paragraphs are labeled as variants: one for distributed systems and backend roles, one for founder and full-stack roles, one for robotics and ML. Situational bullets are wrapped in HTML comments so they can be selectively included:

<!-- Situational — include for ML/HPC roles -->
<!-- * Implemented a 950x faster vectorized bitonic
       merge sort in CUDA for GPU-accelerated workloads -->

Projects that are only relevant to certain roles—a voice-controlled IoT app, a board game AI, a garden computer vision tool—live in commented-out blocks ready to be surfaced when they matter.

The Slash Command

The pipeline is triggered by a Claude Code slash command called /tailor. I paste in a company name, the full job description, and any notes about what I want to emphasize. Claude then:

  1. Reads the master resume and internalizes all the available content
  2. Analyzes the job posting for stack, seniority, role type, and culture signals
  3. Makes editorial decisions—which summary variant to use, how to reorder sections, which bullets to cut, which situational content to include
  4. Writes a clean, tailored markdown file with no residual template markers
  5. Converts it to PDF and validates it against ATS requirements
  6. Reports the results and waits for my approval before committing

The command definition is about 60 lines of markdown. It encodes the decision framework—lead with the startup experience for product roles, lead with big-company systems work for infrastructure roles, surface the relevant skills first—but leaves the actual judgment to the model.

The Generate-Validate Loop

The most important part of the pipeline isn't the generation—it's what comes after. Every tailored resume goes through a two-step validation cycle: first, md-to-pdf converts the markdown to a PDF, then a custom Python script audits that PDF for ATS parseability. If the check surfaces warnings, Claude can fix the markdown and re-run the cycle until the output is clean.

I think this is where AI-assisted workflows actually work well: not in one-shot generation, but in tight loops where the model generates, a deterministic check provides concrete feedback, and the model iterates. The check doesn't need to be smart—it just needs to be reliable. The model handles the creative work; the validator keeps it honest.

The ATS checker itself uses PyMuPDF to extract both raw text and bounding-box coordinates from the PDF, then runs several checks:

It exits with structured codes—0 for pass, 1 for warning, 2 for fail—so it could slot into a CI pipeline if I ever wanted that.

What the Output Looks Like

After running the pipeline for a dozen-plus applications, the differences between outputs are genuinely substantive. A few patterns:

These aren't find-and-replace keyword swaps. The section ordering changes, bullets get rewritten to emphasize different aspects of the same work, and irrelevant content gets cut entirely. Each output reads like it was written for that specific role.

The Human in the Loop

One deliberate design choice: the pipeline never auto-commits. After generating the resume and running the ATS check, Claude reports its tailoring decisions and any warnings, then stops. I review the output, decide if anything needs to change, and approve the commit.

This matters because resume tailoring is fundamentally a judgment call. The model is good at analyzing a job posting and making defensible editorial choices, but I know things it doesn't—which roles I care most about, which projects I want to talk about in an interview, where I'm willing to stretch a framing versus where I want to stay conservative. The pipeline handles the tedious parts (reformatting, PDF conversion, ATS validation) and surfaces the interesting decisions for me.

The Stack

The entire pipeline is remarkably thin:

No database, no web app, no CI/CD, no templates engine. The "intelligence" is in the model and the prompt, not in the infrastructure.

What I'd Do Differently

If I were starting over, I'd structure the master resume as structured data (YAML or JSON) rather than markdown with HTML comment conventions. The current approach works because Claude is good at parsing loosely structured text, but a schema would make the conditional logic explicit and let me validate the master resume itself.

I'd also add a comparison step that diffs the tailored output against previous versions for the same company, so I can see exactly what changed when I update the master resume and re-run.

But honestly, the current setup does what I need. Each /tailor invocation takes a couple of minutes from paste-the-job-posting to reviewed-and-committed PDF. That's the kind of automation that actually matters—not eliminating human judgment, but eliminating the friction around it.