Mintly, hiring platform, 2022-present

A hiring platform that keeps agentic work offline and the live path deterministic

9 production services1,000+ employers20,000+ jobseekerszero LLM calls on the live path

Context

Mintly is a two-sided hiring platform I have architected and owned end to end since December 2022, on weekends, alongside the day job. Nine services in production: a Next.js storefront, an internal control center gated by role-based access control, a Spring WebFlux GraphQL API over reactive MongoDB, a Python agentic recommendation service, and a set of event-driven notification and ingestion workers. It now serves 1,000+ employers and 20,000+ jobseekers, with three engineers I mentor alongside me.

The problem

Matching employers to candidates is exactly the kind of problem agentic systems are good at: fuzzy, judgment-heavy, improved by context a language model can hold that a SQL query cannot. But a hiring platform’s system of record cannot inherit that fuzziness. An employer needs to know why a candidate was ranked where they were, and that answer has to be the same on Tuesday as it was on Monday. The risk was never building the recommendation engine. It was letting it become load-bearing for anything that had to be provable after the fact.

Constraints

  • This runs on weekends with three mentored engineers, not a platform team, so anything that pages someone at 2am has to not exist in the first place.
  • Model calls cost money per request, against real production traffic rather than a demo.
  • The platform has already faced a 188,000-request injection campaign with no dedicated security function behind it, so the architecture has to absorb abuse on its own terms.

Approach

I split the system in two and kept the seam explicit. The Spring WebFlux GraphQL API over MongoDB is the system of record: deterministic, auditable, the source of truth for what an employer or candidate actually did. All the agentic work happens outside it, and offline. A six-agent pipeline on FastAPI and LangGraph extracts signal asynchronously and generates 1536-dimension embeddings into MongoDB Atlas Vector Search, batch by batch. None of that reasoning touches the live path. A feed load at request time is vector search over those precomputed embeddings plus a seven-signal weighted re-rank, arithmetic, not agentic, so it is fast, cheap and deterministic. Every LLM call the platform makes happens in that offline step, off the critical path entirely.

The same discipline shows up in messaging and in how the team itself operates the platform. Fan-out notifications run on MongoDB change streams into SES, Slack, WhatsApp Cloud API and FCM, with exponential backoff, crash recovery and deduplication built in rather than bolted on. Hourly batching cut Slack traffic from 500 messages an hour to one, and scoping every notification to just the employers and jobseekers actually affected, instead of broadcasting to a full list, keeps messaging spend a small fraction of what naive fan-out would cost. The admin console is a control center rather than a flat back office: role-based access control governs what the Mintly team can see and do inside it, so a support action on an employer’s listing does not require the permissions a platform engineer needs, and every action stays scoped to the role that performed it.

On the platform side, GitHub Actions ships to ECR, Terraform owns EKS, S3, SES and IAM, and every service gets its own Helm chart with accessibility and design-system checks as build gates, not manual review. Checkout verifies price server side through a dual-gateway path so client-side tampering cannot set its own total. After the injection campaign, I added rate limiting, fail2ban and a behaviour-based auto-blocker rather than a static blocklist, since the attack pattern kept shifting.

Outcome

Nine services in production, 1,000+ employers, 20,000+ jobseekers, run on weekends with three engineers I mentor. Batching and scoping keep messaging spend a small fraction of what naive fan-out would cost. The agentic work has never touched the system of record, nothing on the live path calls a model, the control center keeps every internal action scoped to a role, and the platform absorbed a six-figure request attack without a security team on call.

What I'd do differently

I would have built the notification idempotency store before the first campaign rather than after the first duplicate. It was the one place where I let the deadline pick the architecture.