Actively Maintained Work
TalentScan
Next.js TypeScript PostgreSQL Aurora Prisma Redis Docker AWS ECS AWS Elasticache
Overview
Reviewing hundreds of resumes against a job description is slow, inconsistent, and easy to get wrong. TalentScan automates the first pass: recruiters paste in a job description, upload resumes in bulk or one at a time, and get back a ranked shortlist with match scores. The platform parses each upload, splits multi-resume PDFs into individual candidate profiles, and runs keyword matching against the JD, with fully adjustable weightings so recruiters can tune how much each skill or requirement matters before re-ranking on the fly. Built to handle real hiring volumes, with Redis caching the hot paths and Aurora PostgreSQL backing the candidate and scoring data.
What I built
- Batch resume upload with automatic splitting and per-candidate profile creation
- Job description parser that extracts and weights keywords for matching
- Configurable weighting UI, recruiters adjust per-keyword importance and scores update live
- Scored shortlist ranked by match percentage with per-keyword breakdown
- Redis (Elasticache) caching for JD keyword sets and scoring results
- Aurora PostgreSQL for candidate, JD, and scoring data with Prisma ORM
- Deployed on AWS ECS with Docker, backed by Elasticache and Aurora
What I learned
- Resume parsing strategies, handling varied PDF layouts and multi-resume documents reliably
- Designing a flexible scoring model where weights are first-class and not baked into the algorithm
- Using Redis effectively for caching computed scores and invalidating on weight changes
- Aurora PostgreSQL on AWS, provisioning, connection pooling, and the differences from standard Postgres
- Building UIs where user-tunable parameters feed back into live results without a full reload
Architecture
flowchart TD
Recruiter([" Recruiter"])
subgraph AWS["AWS ECS · Docker"]
subgraph App["Next.js Application"]
Upload["Resume Upload
(batch / single)"]
Parser["Resume Parser
& Splitter"]
JD["Job Description
Parser"]
Scorer["Keyword Scoring
Engine"]
UI["Shortlist UI
(live reranking)"]
end
Cache[("Redis
Elasticache")]
DB[("Aurora PostgreSQL
(Prisma)")]
end
Recruiter -->|"upload resumes"| Upload
Recruiter -->|"paste JD + set weights"| JD
Upload --> Parser
Parser -->|"candidate profiles"| DB
JD -->|"keyword weights"| Cache
DB --> Scorer
Cache --> Scorer
Scorer -->|"scored shortlist"| UI
Recruiter -->|"adjust weights"| UI
UI -->|"invalidate + rescore"| Cache