Sending Mail in 2022
It seems that we need a tech event around transactional emails in our applications. Lacking that, we are writing down our experience rebuilding email in Koliseo 3. Fasten your belts, because there is a bit of good, and a lot of bad and ugly.
We knew our previous implementation lacked a lot of transactional notifications, so we made a list and ended up with 20+ different types of emails between events, sessions, acls, tickets, and whatnot. This is about what we learned in the process.
New reply-to address, who dis?
One quick note about sending stuff for others: in the good old days you could just use the
From: field, but we are not that trusting anymore. If you try to send mail from a domain you do not control, your email will fail will not pass DKIM / SPF validation and will be cataloged as spam. You can still use
Reply-to instead, which makes sense: "This mail is from us, but you can reply to it to get in contact with the event organizer".
Do I still need to inline all the CSS styles in 2022? Yes you do! Welcome to 1999, happy times. There are a couple of alternatives to implement this:
- Foundation for emails is a CSS template and preprocessing solution to create your templates. It comes already with a set of predefined templates to get started.
- MJML: A markup language to generate responsive emails. It also comes with templates.
Instead, we went with secret option number 3, we precompile our templates using juice and keep the markup as simple as possible using - hugh - tables where needed. We also take the loss with horribly outdated email clients, the same as we do with old browsers: any bandwidth to support Outlook 97 or Lotus Notes will be invested in supporting blind users instead.
Having said this, we use Litmus to check how our email looks in multiple email clients -- running blind would just be irresponsible.
Images in your email
Even with minimalistic email, there are occasional images, like avatars or the website logo. For these, we have three options:
- Base64-encode images: don't do that. Your emails will be bigger, and the client support is not great anyway.
- Just link the image from an external source.
- CID Attachments, linked from the HTML body.
You can read the full story here.
At face value, linking the image is the easiest solution, but it comes with a series of caveats:
- It may affect your spam rating.
- You now need to host that image forever.
For the website logo, this is not a big deal: be aware of not changing the image dimensions, and never drop the GCS bucket that stores assets. For avatars it's a different story though, as the image hash is part of each URL that we generate. If a user changes their avatar, existing emails would stop working.
So, image attachments it is. Moving on.
As long as it's transactional, you don't need a one-click unsubscribe link in the email, but it would be good to replace it with something like "you received this email because you purchased a ticket to this event," as Google does with their notifications.
We still want to add one-click disabling of specific types of transactional email, but that will happen later down the road.
Sending the thing
Transactional email carries high latency and a (relatively) high probability of failing, which would need retries and logging. For these reasons, we put a PubSub topic between the actual transaction and the act of sending the email. No surprises here.
We considered several gateway options and ended up testing Mandrill, Sendgrid, and SES. Sendgrid was quite OK, until they pulled the rug from under us by canceling our account because it was an old, retired plan and the new ones were more expensive (reason given: "you did not send any emails during this COVID year"). In any case, this move helped us compare features and pricing against SES and finally switch platforms. We don't need much, and it's way cheaper than the alternatives.
Today we are running 99% on Google Cloud, but for all their goodness they don't have a decent email service, and that point goes to AWS. We first tried using SES templates, where the template lives on AWS and we pass JSON when sending email. However, we didn't like the prospect of deploying incompatible changes across our application and the HTML templates. It's easier maintenance if the whole thing is generated from the server, and we send plain HTML / TXT to SES.
TL;DR Email is boring and hard
It looks a lot like sending mail in 1999, but tools are better: it's a multi-client, responsive design based on a CSS implementation from 1999, where internationalization has to be implemented by hand, with tools for abuse prevention that makes your life miserable along the way.
Big hugs to anyone reading this, and if you have to implement your own solution, let us know how you are doing.