The website you’re reading is mostly static HTML, just web pages on a CDN, but for newsletter subscribers, it does offer a few bits of interactivity, and those bits require code to run somewhere other than your browser.
For my projects, that “somewhere” has lately and reliably become cloud functions. They fit at least two ways:
practically, because everything in my ~digital universe~ is driven by newsletters, which means sharp spikes of readership (and code execution) that attenuate into trickles; and
emotionally, because I’ve always found the care and feeding of public servers, even just little EC2 instances, totally stressful. A regime of CDNs and cloud functions eliminates, at last, the whispering worry: is it still running … ?
Back in January, when I was putting this website together, I’d already implemented the subscriber tools on AWS Lambda when Google announced that their Cloud Functions would support Ruby. I did a little investigation and was so impressed that I switched everything over; now, I want to share a couple of findings.
This message was emailed to lab newsletter subscribers. The assumed audience is subscribers who maintain their own websites or other small apps. (Here’s more about assumed audiences.)
I appreciate AWS Lambda’s role in kickstarting the whole “floating wisp of code” model, but/and I have found the system itself very funky to work with. Google’s Cloud Functions, particularly in Ruby, particularly with the scaffolding of the Functions Framework, has been a better experience in every respect.
There’s a simple, useful local development mode. The function runs exactly as it would in the cloud, with no special setup or extra tinkering required.
Dependencies are as easy as a Gemfile. AWS, by contrast, requires funky “layers” that you need to build and maintain separately.
The single-page view of each function is terrific. Google’s dashboard shows you everything you need to know in one screen: the function’s activity over time, how quickly it’s responding, how many copies are running, etc. It’s perfect.
All in all, Google’s system has provided the most potent dose of like, “suddenly expanded technical capability” I’ve received since, I don’t know … Slicehost??
The mega function
One well-established drawback of cloud functions is the “cold start” problem. When your function is getting used a lot, the system spins up as many copies as needed; very slick. When it’s not getting used, the system spins up: zero. So, the first request after a period of slumber can be quite slow. One imagines the system nervously patting its pockets: “Where did I put that code … ?”
It’s really not a huge deal, but there is a noticeable difference between the performance of a function that’s “cold” vs. one that’s being pinged by a few thousand newsletter subscribers. The latter purrs; the former engages with a palpable ker-thunk.
The solution I’ve chosen might be “bad” practice, but it works for me. Instead of deploying each of my functions as Actually Different cloud functions, I’ve rolled them up into one “mega function”—really almost a tiny app.
The result is that when someone arrives to, say, modify their newsletter subscription, they rouse the code not only for future subscription-modifiers but for anyone who might need any part of it. That might include their future self, performing a different operation.
For routing, I use a parameter in the payload called
case body["method"] when "get_tags" Society.get_subscriber_tags(body) when "update_tags" Society.update_subscriber_tags(body) when "send_response" Society.send_response(body) when "check_email" Society.check_email_status(body) end
Another advantage, for me, of putting everything into one mega function is that it “fails fast”; if something isn’t working, nothing is working.
This is all in the context of an inconsequential personal website … but that’s not an uncommon context! Just because critical enterprise databases exist doesn’t mean we all need to program as if we’re supporting them.
Sloan’s mega function: I recommend it.
March 2021, Oakland