All Work

CV Screener

Not just a score — an explainable three-dimensional breakdown of why your CV fits (or doesn't) a job.

RoleSolo Developer (Full-Stack + ML)
Year2026
DurationActive Development
TeamSolo
Status🟢 Live
Landing Page — Hero with Live Compatibility Scan Preview

At a Glance

50Canonical Skills in Taxonomy
166 KBONNX Model (from 9.4 MB corpus)
3Scoring Dimensions
8Frontend Pages

The Problem

Job seekers send CVs blindly and never know why they're rejected. Recruiters process hundreds of applications manually with no objective ranking. Existing ATS tools give a score but no explanation — candidates can't act on "68%". CV Screener was built to surface exactly which dimension is dragging the score down and give users a concrete next step to fix it.

The Solution

A fixed three-component weighted hybrid score: Text Similarity via a TF-IDF model (trained on 9.4 MB of real job descriptions, exported to ONNX, inferred in .NET — no Python in production); Skills Score via a curated taxonomy of 50 canonical skills with alias mapping; Experience Score via regex year extraction with a continuous ratio formula. Every result shows all three scores separately, a Matched/Partial/Missing skill breakdown, and a personalized learning path for every gap.

Architecture

Key Features

Screenshots

Code Highlight

Hybrid Scoring Orchestration — MatchingService
// MatchingService orchestrates all three engines and persists the result.
// Weights are fixed: Text 50% + Skills 35% + Experience 15%
public async Task<AnalysisResult> AnalyzeAsync(string cvText, string jdText, Guid userId)
{
    var cleanedCv = TextCleaner.Clean(cvText);
    var cleanedJd = TextCleaner.Clean(jdText);

    // Three independent engines — run in any order
    var textSimilarity = await _tfIdfService.ComputeSimilarityAsync(cleanedCv, cleanedJd);
    var skillsResult   = _skillsEngine.Evaluate(cleanedCv, cleanedJd);
    var expResult      = _experienceEngine.Evaluate(cleanedCv, cleanedJd);

    // Fixed hybrid formula — prevents single-dimension gaming
    var overallScore = (int)Math.Round(
        (0.50 * textSimilarity +
         0.35 * skillsResult.Score +
         0.15 * expResult.Score) * 100
    );

    var result = new AnalysisResult
    {
        UserId          = userId,
        OverallScore    = overallScore,
        ScoreLabel      = ScoreLabel.FromScore(overallScore), // Server-side always
        TextSimilarity  = textSimilarity,
        SkillsScore     = skillsResult.Score,
        ExperienceScore = expResult.Score,
        MatchedSkills   = skillsResult.Matched,
        PartialSkills   = skillsResult.Partial,
        MissingSkills   = skillsResult.Missing,
        ExperienceData  = expResult,
        AnalysisVersion = "v2"
    };

    await _analysisRepository.SaveAsync(result);
    return result;
}

Challenges & Solutions

🎯 Running a Python-trained ML model inside a .NET production environment without a Python runtime dependency

Trained a TF-IDF model with scikit-learn, exported it to ONNX format via skl2onnx, then loaded it inside .NET via Microsoft.ML.OnnxRuntime as a Singleton service. InferenceSession is thread-safe — the model loads once at startup and serves all concurrent requests with zero Python involved.

🎯 Skill matching that handles terminology variance — ".NET" vs "ASP.NET Core" vs "dotnet"

Built a curated Skills Taxonomy JSON with 50 canonical skills, each with a list of known aliases. The SkillsEngine performs canonical matching first, then alias matching (counted as partial at 0.5 weight) — ensuring skill coverage is accurate across the wide variation in how technologies are named in CVs vs. job descriptions.

🎯 Designing a scoring formula that is explainable and resistant to single-dimension gaming (CV keyword stuffing)

Implemented a fixed three-component weighted formula (50/35/15). Each dimension is surfaced separately in the UI — users can see exactly which score is dragging them down, and a candidate who keyword-stuffs their CV will see high Text Similarity but low Skills/Experience scores, exposing the manipulation.

Tech Stack

.NET 8 Web APIC#Next.js 14TypeScriptPostgreSQLSupabaseClerk AuthPython (ML Training)scikit-learnONNX RuntimeTailwind CSSVercel + Railway

What I Learned

Links

Interested in a similar solution?

Let's discuss how I can build something like this for your business.