Underdog.io logo

This post was written by Todd Wolfson, Senior Software Engineer at Underdog.io

Underdog.io is a curated marketplace that matches high-caliber candidates with top-tier startups. A person looking for a new job will submit their application to us, we review them, and if they fit our criteria, we send them to our select set of companies.

Every week, we send out a batch of candidates. This post describes how we transitioned our batching and candidate delivery process from a manual one to an automated one that allowed our company to scale.



We keep most of our information in house (e.g. candidate and company data). We grade candidates and manage companies via an internal tool. More information about our entire architecture can be found at the bottom of this post.

The Problem

Previous to our automation, each week we created a list of that week’s candidates in an Excel spreadsheet. Then, we hand matched the nocalls (e.g. Candidate A doesn’t want to talk to Company X), downloaded all the resumes, and combined them as PDFs/ZIPs for each of the nocall combinations.

This process wasn’t going to scale and as we grew human error would only become more likely (e.g. accidentally send a nocall match). We solved this by building a semi-transparent batching system (e.g. each part of the system is accessible/modifiable to prevent trapping ourselves in a corner).

  • We added the ability to flag candidates for batches and preview the batch as we did with the spreadsheet.

  • This reduced the mental overhead and segmentation between the batch and candidate information.

  • We added filtering for different sets of companies in a batch depending on nocall matches.

  • This made double checking nocalls by hand trivial as now can pay attention to catching edge cases instead of keeping track of all cases.

  • Additionally, we started to collect our own heuristics (e.g. a candidate provides “Google Inc” but we have them as “Google” in our system).

  • We added endpoints to export each set of assets we need for a batch (e.g. combined PDFs, zipped PDFs, JSON blobs).

  • This is a backup system in case anything ever breaks while an engineer is unavailable.

  • It also gives us flexibility with trying different email templates or other mediums entirely.

  • We added an email endpoint which sends an email for each nocall group along with the generated assets. From here, we double check the information and forward the email to our customers.

Here is an example of the final result:


Further Information

Beyond this process, we have a service-oriented architecture. It is comprised of:

  • Public-facing website written on Flask (Python)

  • This manages signups for candidates and startups

  • Database service written on Flask (Python)

  • Handles permissions and data across services (e.g. candidate info)

  • We use PostgreSQL as our database
  • We use Elasticsearch for searching against candidates, resumes, and startups
  • We use Redis as a caching layer against external services
  • We use Celery as our queue system for resume processing and email sending

  • Internal tool written on Express (node.js)

  • This is where we review/grade candidates and create weekly batches

  • We use Redis for session management and caching

For DevOps, we use:

  • AWS for server management/hosting (e.g. EC2, RDS, Elasticache)

  • Vagrant for keeping development/production environments consistent

Thanks for reading. Check out our GitHub page to browse our open source projects, and stay tuned for more engineering posts from the Underdog.io team.

Todd, a Senior Software Engineer at Underdog.io and one-time Underdog.io candidate, is an open source enthusiast who previously worked at top-tier startups like Uber and Behance.

Apply to top startup jobs in 60 seconds →

Sign up for Ruff Notes

Every week we send out a newsletter called Ruff Notes with our personal thoughts on something interesting we’ve read, as well as product updates and news from our community.