Engineering Leader, Former Magician and Explorer of Curiosities

The Worst API Integration I Ever Built: A Horror Story in Three Acts

The reason I learned to code was a TV on a wall.

Shopify’s support team in Ottawa had these leaderboard dashboards: who closed the most tickets, who had the best Smiley scores, who was winning. I wanted to put my own data on those screens. I wanted to integrate with the internet, build things, display information my way. So I taught myself Ruby, started deploying little apps to Heroku, and eventually someone noticed.

Shopify's Dashing framework — the default dashboard that started it all The default Dashing dashboard. Shopify open-sourced this framework and every team ran their own version. It’s defunct now, but in 2013 these screens were everywhere.

My personal Dashing dashboard — hydro data, cat gifs, weather, and whatever else I felt like scraping My own dashboard. Hydro Ottawa usage graphs (apprently lagging badly!), cat gifs, weather and countdowns — if I could scrape it, it went on the screen.

“Hey, you know code now right? Can you (you can) fix our Zendesk integration?”

I should have said no.


Act 1: Optimism

It was 2013. Shopify had maybe 100,000 merchants, and support was a massive part of the business. Every ticket, every call, every chat all flowed through Zendesk. The sales team lived and died by attribution. If a ticket wasn’t synced properly, their numbers didn’t show up on the leaderboard. With no leaderboard credit there was no bonus. People cared about those dashboards the way traders care about Bloomberg terminals.

The mega dashboard at Shopify's Waterloo office The Waterloo office mega dashboard. This thing was massive in the former distillery’s barrel-lined walls.

Dashing dashboards lining the walls of Shopify's Montreal office The Montreal office dashboards. This is the best representation of what those screens actually looked like day-to-day: mounted on brick walls, always on, always watching.

The integration was supposed to be straightforward. Sync data between Shopify’s internal systems, phone system and our Zendesk instance. Push events in, pull reports out. I’d been writing Ruby for a few months. I’d deployed a few apps. How hard could it be?

I want to tell you that I had a bad feeling from the start, but I didn’t. I was a junior developer who’d just discovered that code could make things happen in the real world. The developers at Shopify were rockstars. They could push one change and it would ripple across thousands of stores and be seen by millions. I wanted that. And now someone was handing me the same kind of blast-radius work. Me, the former magician and support guy who taught himself Ruby off a leaderboard TV.

That thrill lasted about 48 hours.


Act 2: The Documentation Lies

Here’s the thing about the Zendesk API in 2013: it wasn’t broken, exactly. It was just non-obvious in a hundred small ways that compounded into something painful.

The docs would say one thing. The API would do something slightly different. Rate limits would kick in at seemingly random thresholds, which matters a lot when you’re a hypergrowth company sending all sorts of data to a single Zendesk instance. And we were sending a lot. Probably too much, honestly.

I’d write a sync job, test it with a handful of records, ship it, and then watch it choke at scale. Retry logic wrapped in more retry logic. Error handling that was 80% of the codebase:

# I wish I was kidding
def sync_ticket(ticket)
  retries = 0
  begin
    zendesk_client.tickets.update(ticket)
  rescue ZendeskAPI::Error::RateLimited
    retries += 1
    sleep(retries * 30) # "exponential" backoff (it's hope)
    retry if retries < 5 # why 5? no reason. felt right.
  rescue ZendeskAPI::Error::RecordNotFound
    # it existed five seconds ago but sure, ok
    create_ticket_instead(ticket)
  rescue ZendeskAPI::Error::InvalidEndpoint
    # the docs said this endpoint works. the docs lied.
    try_the_other_endpoint(ticket)
  rescue => e
    # look, if we've gotten here, god help us
    log_and_hope_nobody_notices(e)
  end
end

The worst part wasn’t any single bug. It was the slow accumulation of workarounds. Each one made sense in isolation. Together, they formed something that looked less like an integration and more like a Jenga tower held together with hope.

And then someone from the sales floor would walk over: “Hey, my tickets aren’t showing up on the board. My numbers look wrong.”

Translation: your code is costing me money.

No pressure.


Act 3: The Jaded Pixels

Shopify’s original corporate name was Jaded Pixel Technologies Inc. Our emails were @jadedpixel.com. I didn’t think much of the name at the time.

But when you’re a junior developer staring at a failing sync job at 6 PM, and you walk over to a senior dev’s desk hoping for guidance, and they glance at your screen, say “yeah, that endpoint’s weird,” and turn back to their monitor: you start to understand the name.

They weren’t bad people. They’d just seen it all. Every API quirk, every rate limit dance, every integration that was supposed to take a week and took three months. They were jaded. And when you’re jaded, you don’t have the energy to walk a junior through the ninth weird thing they’ve hit today. You give them a cryptic one-liner and hope they figure it out.

I figured most of it out. Eventually. Through the time-honored method of reading Stack Overflow until 11 PM and deploying code that technically worked but that I wouldn’t want anyone to review.

The integration stabilized. The leaderboards updated. The sales team got their numbers. Nobody sent me flowers.


The Contrast That Stuck With Me

Years later, I heard the story about Twilio’s CEO walking into a pitch meeting with nothing but a laptop. He live-coded an integration right there and sent a text message from a few lines of code. That was the whole pitch deck. The API was the product demo because it was simple enough to explain to non-technical people in real time.

I think about that a lot. A great API is so simple it’s a pitch deck. A bad API is a horror story in three acts.

The Zendesk API wasn’t malicious. It wasn’t even the worst API I’ve encountered since. But it was my first, and I was alone with it. That’s the part that actually matters.


What Junior Devs Absorb

Bad tooling hurts everyone, but junior developers absorb the pain differently. When a senior dev hits a weird API, they know the API is weird. When a junior dev hits a weird API, they think they’re weird. They can’t tell if the docs are wrong or if they’re reading them wrong. They don’t know if the rate limit is unreasonable or if they’re doing something unreasonable.

This matters more now than it did in 2013. AI tools are turning non-technical people into junior developers overnight. Someone who’s never written code before can vibe their way into a working app with Claude or Copilot, and that’s genuinely exciting.

I’ve always thought individuals from a non-technical background (eg not-CompSci grads) make the best developers because they bring a fresh perspective and experience to the table. But the moment they hit a bad API, a confusing error, or docs that don’t match reality, they have zero frame of reference. At least I knew what an HTTP status code was. The new wave of AI-assisted builders might not even have that.

I spent months wondering if I was just bad at this. It turns out that the API was genuinely painful, but I had no frame of reference to know that! I just assumed everyone else would have figured it out faster.

If you’re senior and you see a junior wrestling with a bad integration: don’t just say “yeah, that endpoint’s weird.” Sit down for ten minutes. Confirm that the API is, in fact, the problem and not them. Pair programming is not only a force-multiplier but that validation is worth more than any code review.

And if you’re a junior reading this and currently fighting an API that makes you feel stupid: it might not be you. Some APIs are just bad. The fact that you’re still trying means you’re doing fine.


Shameless plug: At Victoria Garland, we build Shopify integrations that don’t make people cry. I’ve done my time in API purgatory so our clients don’t have to. If you’re building something on Shopify and want it done right, let’s talk.