Nov 08, 2022 ruby-on-rails voting

Sincere has helped volunteers send over 3.5 million postcards!

In 2020, I built a simple SaaS product called Voterletters to help volunteers run postcards-to-voters campaigns. The project took off, and by 2022 it had helped volunteers from all across the country send over 3.5 million postcards and letters to other Americans. It’s been a huge hit with its users, and has received glowing praise, like this quote from Lauren Kunis, the Executive Director of VoteRiders:

Sincere has been a dream come true! It’s been an ideal platform for VoteRiders to dramatically expand our letter-writing activities, and it’s been a breeze to use for our staff and volunteers alike.

Sincere’s tech stack is intentionally very simple. I use:

  • Ruby on Rails
  • Postgresql
  • Sidekiq
  • Tailwind (and the excellent TailwindUI component library)


I originally hosted Sincere on Heroku, but have since moved onto Render.

PDF Generation

Probably the most technically interesting part of the Sincere stack is its PDF generation capabilities. At its heart, the product allows users to request a number of names and addresses from a campaign. Based on a set of rules, the system either automatically grants the user’s request, or forwards it along to an administrator for manual approval. Once the request is approved, the system assigns a set of names and addresses (Contacts) to the user, builds a PDF containing the Contacts, and emails it to the user.

Originally, the PDF generation was performed with wkhtmltopdf and a Rubygem that wraps the library. wkhtmltopdf is neat: you give it a webpage and it spits out a PDF, which makes building custom PDFs into a trivial task. Under the hood, the library is using the WebKit rendering engine to perform the rendering. Unfortunately, this also makes wkhtmltopdf a memory hog, which caused serious problems with rendering a large number of Contacts.

I ended up rebuilding the PDF generation code using a Rubygem called Prawn, which is much more ‘low-level’ than wkhtmltopdf, but also has far lower memory requirements.