DEV Community

Cover image for 5 Prompt Engineering Tricks That Made My AI Resume Checker Actually Reliable
Abdul Moiz
Abdul Moiz

Posted on

5 Prompt Engineering Tricks That Made My AI Resume Checker Actually Reliable

When I first wired up Gemini to grade resumes, the output was garbage.

Not because Gemini is bad. Because I was prompting it like it was a chatbot.

I've since shipped the app to Google Play, and the analysis is now consistent enough that users trust it. Here are the 5 prompt engineering tricks that got me from "sometimes right" to "reliable enough to build a business on."

1. Never ask for a score. Ask for the rubric first, THEN the score.

Bad prompt:

"Rate this resume from 0 to 100."

Result: 60. 65. 70. Always in that range. Meaningless. Same resume → different score every call.

Good prompt:

"Grade this resume against the following rubric. For each criterion, output an integer 0-25 based on the criterion definition. Sum the four criteria to compute the final score."

Contact Info (0-25): 0 = missing/broken; 12 = present but formatted awkwardly for ATS parsing; 25 = clean 3-line header.

Work Experience (0-25): 0 = missing dates or roles; 12 = generic bullets, no metrics; 25 = every bullet has numbers and specific outcomes.

(etc.)

Result: Deterministic scores. Same resume → same score every time. Because you gave the model a math problem, not a vibe check.

The insight: LLMs are terrible at making a subjective judgment call and returning a single number. They're great at applying a structured rubric to a subjective input. Never skip the rubric.

2. Force JSON output with a schema, not just a request.

Bad prompt:

"Return your response as JSON."

Result: Sometimes valid JSON. Sometimes markdown-wrapped JSON. Sometimes JSON with an apologetic explanation before it. Sometimes valid JSON with an extra "reasoning" field you didn't ask for.

Good prompt:

"Return your response as valid JSON matching exactly this schema, with no surrounding text, no markdown fences, no explanation:"

{
  "overallScore": number,
  "sectionScores": {
    "contactInfo": number,
    "workExperience": number,
    "skills": number,
    "education": number
  },
  "improvements": [
    {
      "id": string,
      "title": string,
      "category": "content" | "format" | "ats" | "keywords",
      "priority": number,
      "before": string,
      "after": string,
      "explanation": string
    }
  ]
}

Result: Reliable JSON. Wrap the response in JSON.parse() in a try/catch and validate with Zod — you'll fail on <1% of calls now instead of 15%.

Bonus: Gemini specifically has a responseSchema parameter — pass the schema as a first-class API argument and it enforces even more reliably than prompting alone.

3. Give it examples, not just definitions.

Bad prompt:

"For each improvement, provide a 'before' bullet and an 'after' bullet."

Result: The "after" bullet is often barely different from the "before." Or the "after" is wildly better but you can't tell what specific change was made.

Good prompt:

"For each improvement, provide a 'before' bullet and an 'after' bullet. Follow these examples for the transformation quality expected:"

Example 1:
Before: "Responsible for managing team of engineers"
After: "Led team of 6 engineers; shipped 4 major features and reduced deploy time by 40% over 2 quarters"
Change explanation: Replaced vague responsibility framing with specific team size + measurable outcomes.

Example 2:
Before: "Worked on backend infrastructure"
After: "Rebuilt payments backend in Go; reduced p95 latency from 320ms to 45ms and cut infrastructure cost by $60K/year"
Change explanation: Named specific technology + before/after metrics + business impact.

Result: The model actually understands what "quality transformation" means. Its outputs match the pattern.

The insight: LLMs learn from patterns in the prompt more than from instructions. One good example beats three paragraphs of guidelines.

4. Use "chain-of-thought" for reasoning, then discard it.

For the ATS keyword-matching feature specifically:

Bad prompt:

"Which keywords from the job description are missing from the resume?"

Result: Sometimes correct. Often misses obvious ones or fabricates keywords that aren't in the JD.

Good prompt:

"Follow these steps in order:

  1. Extract every noun phrase and technical term from the JOB DESCRIPTION. Output them as a list under 'jdKeywords'.
  2. Extract every noun phrase and technical term from the RESUME. Output them as a list under 'resumeKeywords'.
  3. Compare the two lists. For each JD keyword, mark whether it appears in the resume (fuzzy match — 'PostgreSQL' matches 'Postgres').
  4. Output the final result as JSON with 'present' and 'missing' arrays.

Show your work for steps 1-3 in fields called '_reasoning'. In production I'll strip these before displaying to the user, but the model needs to think through them."

Result: Much more accurate. The model is forced to actually enumerate before comparing.

Then in your parser: delete the _reasoning fields before returning to the client. You paid for the tokens, the model needed them to think — but the user doesn't need to see them.

5. Add a "fail loudly" instruction.

Bad approach:

Trust the model to always return good output.

Result: Sometimes the input is a corrupted PDF, a blank resume, or a joke resume ("Sole responsibility: dominating in Call of Duty"). The model gamely tries to grade it and returns nonsense.

Good approach:

"If the input does not appear to be a real resume (e.g., blank, corrupted, or clearly a non-resume document), do NOT attempt to grade it. Instead return exactly:"

{ "error": "not_a_resume", "reason": "<one-sentence explanation>" }

Result: The model gracefully rejects garbage input and you can show the user a helpful error message instead of a fake 43/100 score.

The reliability compound effect

Individually, each of these tricks buys you 5-15% reliability improvement. Together they turned my analysis from "cool demo" to "shippable product."

If you're building anything with LLMs where output structure matters — pricing engine, grading tool, classifier, extractor — apply these. They're not Gemini-specific. Same tricks work with GPT-4, Claude, Llama.

Where I applied this

My app is Crux — a free Android AI resume checker on Google Play. Everything above is what makes the actual scoring feel reliable to users.

If you want to see it in action, it's here: https://play.google.com/store/apps/details?id=com.crux.ai.resume

If you're building something similar and want more depth on any of these — happy to write follow-ups. Drop questions in the comments.

Follow for more posts like this as I keep shipping.

Top comments (0)