Swizec Teller - a geek with a hatswizec.com

    Always put side effects last

    wp content uploads 2016 10 salesforce tower panorama 1024x358

    You know how some lessons you can only learn from experience? This is such a lesson. I'm sharing it with you so that I may remember it better.

    Swizec Teller published ServerlessHandbook.dev avatarSwizec Teller published ServerlessHandbook.dev@Swizec
    @brian_d_vaughn That thing that you can't learn in a 6 month bootcamp but takes years of trial and error to hone. I don't know a name for it.

    Let's say you're building a system for drip campaigns. Because that's how I learned this lesson.

    Your system is given a set of leads. You have to send them a series of messages, update metadata in your database, and keep the sales CRM (customer relationship management) system up-to-date so when a lead eventually replies to your message, a sales person knows what to do.

    Hundreds of these systems have been developed by thousands of developers. You're building your own because startups.

    The core of your system is a send_next_message function. Something like this πŸ‘‡

    def send_next_message(lead)
    campaign = lead.campaign
    Twilio.send_message(lead, campaign.message)

    It makes sense, right? You take the drip campaign, send the current message with Twilio, update some metadata on your CRM, then advance your campaign to the next message.

    Works perfect πŸ‘Œ

    You run it in a cronjob once a day. Like this:

    def send_campaigns
    Lead.active.each do |lead|

    You go through active leads, pick their campaign, and spawn a new background worker with perform_async. Eventually, your queue will run the code and send your campaign.

    If anything goes wrong, each campaign is isolated in its own worker. Errors don't affect other campaigns. πŸ‘Œ

    Even better, if something goes wrong while sending the campaign, you can retry the worker.

    And you've just spammed your leads and made them angry

    Let's review our send_next_message code.

    def send_next_message(lead)
    campaign = lead.campaign
    Twilio.send_message(lead, campaign.message)

    An error can happen at any point here.

    Maybe something goes wrong when you read campaigns from the database. The worker terminates on 1st instruction, and all is good.

    Things can go wrong when talking to Twilio as well. Their service could be down, your internet could act up, or something else entirely. The worker terminates on 2nd instruction, and all is still good.

    What if things break when updating your CRM? Third party service, complex set of instructions with multiple API calls, a lot can go wrong.

    Worker terminates on 3rd instruction.

    Now you're in trouble. You've already sent a message to your user, but you weren't able to save that info. Your system doesn't know sending happened, and your CRM doesn't know either.

    When the worker retries, you send the message again.

    And again. And again. Until the whole worker succeeds.


    Your lead just got a bazillion messages, and they're upset.

    Here's what you should do instead πŸ‘‡

    def send_next_message(lead)
    campaign = lead.campaign
    message = campaign.message
    Twilio.send_message(lead, campaign.message)

    Fetch campaign, save the message in a local variable. Then update data in your CRM. Now if updating the CRM fails, you can keep retrying until it succeeds.

    Updating the CRM was least reliable in my experience.

    After updating the CRM, you update your local database. This is a reliable operation, but the most common source of programmer errors and nil failures. It happens.

    Break fast and move things, right? :)

    At the end, when you're absolutely certain all your data is updated, you send the message to your lead. This is least likely to fail, and if it does, screw it.

    With this approach, you are guaranteed never to spam your leads. The worst that could happen is that you advance the campaign too early and they get 4 messages instead of 5 and your sales pitch is less polished.

    That's okay πŸ™πŸ» Your copy is robust, and annoying people is bad.

    And now I know why sales automation companies have so much money. This stuff is hard. πŸ˜…

    PS: There are ways to further improve your worker. You could do your DB first, then rollback if an error occurs. You could try to rollback changes on the CRM if sending fails etc. But that gets complicated fast.

    Did you enjoy this article?

    Published on January 10th, 2018 in Startups, Technical

    Learned something new?
    Want to become an expert?

    Here's how it works πŸ‘‡

    Leave your email and I'll send you thoughtfully written emails every week about React, JavaScript, and your career. Lessons learned over 20 years in the industry working with companies ranging from tiny startups to Fortune5 behemoths.

    Join Swizec's Newsletter

    And get thoughtful letters πŸ’Œ on mindsets, tactics, and technical skills for your career. Real lessons from building production software. No bullshit.

    "Man, love your simple writing! Yours is the only newsletter I open and only blog that I give a fuck to read & scroll till the end. And wow always take away lessons with me. Inspiring! And very relatable. πŸ‘Œ"

    ~ Ashish Kumar

    Join over 14,000 engineers just like you already improving their careers with my letters, workshops, courses, and talks. ✌️

    Have a burning question that you think I can answer?Β I don't have all of the answers, but I have some! Hit me up on twitter or book a 30min ama for in-depth help.

    Ready to Stop copy pasting D3 examples and create data visualizations of your own? Β Learn how to build scalable dataviz components your whole team can understand with React for Data Visualization

    Curious about Serverless and the modern backend? Check out Serverless Handbook, modern backend for the frontend engineer.

    Ready to learn how it all fits together and build a modern webapp from scratch? Learn how to launch a webapp and make your first πŸ’° on the side with ServerlessReact.Dev

    Want to brush up on your modern JavaScript syntax?Β Check out my interactive cheatsheet: es6cheatsheet.com

    By the way, just in case no one has told you it yet today: I love and appreciate you for who you are ❀️

    Created by Swizec with ❀️