公司:Shopify
Shopify Inc.是加拿大的一家跨国电子商务公司,总部位于安大略省渥太华,Shopify也是该公司所有的电子商务平台的名称。Shopify为在线零售商提供一整套服务“包括支付、市场营销、运输和客户契合工具,以简化小型商户开设在线商店的过程”。
根据公司披露的文件,截止2019年6月,Shopify平台在大约175个国家或地区有超过一百万笔业务,2020日历年的商品总成交额1196亿美元,较2019年增长96%。
Mastering React’s Stable Values
In this post, Colin Gray, Principal Developer at Shopify, delivers a crash course in how to make sense of stable values in React and when they matter most.
10 Tips for Building Resilient Payment Systems
Top ten tips and tricks for building resilient payment systems from a Staff Developer working on Shopify’s payment infrastructure.
Data-Centric Machine Learning: Building Shopify Inbox’s Message Classification Model
Shopify Inbox is a single business chat app that manages all Shopify merchants’ customer communications in one place, and turns chats into conversions. As we were building the product it was essential for us to understand how our merchants’ customers were using chat applications. Were they reaching out looking for product recommendations? Wondering if an item would ship to their destination? Or were they just saying hello? With this information we could help merchants prioritize responses that would convert into sales and guide our product team on what functionality to build next. However, with millions of unique messages exchanged in Shopify Inbox per month, this was going to be a challenging natural language processing (NLP) task.
Our team didn’t need to start from scratch, though: off-the-shelf NLP models are widely available to everyone. With this in mind, we decided to apply a newly popular machine learning process—the data-centric approach. We wanted to focus on fine-tuning these pre-trained models on our own data to yield the highest model accuracy, and deliver the best experience for our merchants.
We’ll share our journey of building a message classification model for Shopify Inbox by applying the data-centric approach. From defining our classification taxonomy to carefully training our annotators on labeling, we dive into how a data-centric approach, coupled with a state-of-the-art pre-trained model, led to a very accurate prediction service we’re now running in production.
Spin Infrastructure Adventures: Containers, Systemd, and CGroups
The Spin infrastructure team works hard at improving the stability of the system. In February 2022 we moved to Container Optimized OS (COS), the Google maintained operating system for their Kubernetes Engine SaaS offering. A month later we turned on multi-cluster to allow for increased scalability as more users came on board. Recently, we’ve increased default resources allotted to instances dramatically. However, with all these changes we’re still experiencing some issues, and for one of those, I wanted to dive a bit deeper in a post to share with you.
The Story Behind Shopify’s Isospin Tooling
You may have read that Shopify has built an in-house cloud development platform named Spin. In that post, we covered the history of the platform and how it powers our everyday work. In this post, we’ll take a deeper dive into one specific aspect of Spin: Isospin, Shopify’s systemd-based tooling that forms the core of how we run applications within Spin.
The initial implementation of Spin used the time-honored POSS (Pile of Shell Scripts) design pattern. As we moved to a model where all of our applications ran in a single Linux VM, we were quickly outgrowing our tooling—not to mention the added complexity of managing multiple applications within a single machine. Decisions such as what dependency services to run, in what part of the boot process, and how many copies to run became much more difficult as we ran many applications together within the same instance. Specifically, we needed a way to:
- split up an application into its component parts
- specify the dependencies between those parts
- have those jobs be scheduled at the appropriate times
- isolate services and processes from each other.
At a certain point, stepping back, an obvious answer began to emerge. The needs we were describing weren’t merely solvable, they were already solved—by something we were already using. We were describing services, the same as any other services run by the OS. There were already tools to solve this built right into the OS. Why not leverage that?
The Journey to Cloud Development: How Shopify Went All-in on Spin
How we reinvented our developer environment by going cloud native to keep up with increasing complexity and exponential growth.
Navigating Recurring Payments in India: A Backend Perspective
In October 2021, a directive issued by the Reserve Bank of India (RBI) came into effect that introduced new requirements related to recurring payments in India. The requirements in the directive include the following:
- All recurring payments require a one-time authorization from the payer via an e-mandate that is registered through an Additional Factor of Authentication (AFA).
- Recurring payments up to and including the RBI threshold amount can be automatically collected with a successfully registered e-mandate in place, while recurring payments greater than the threshold amount require the payer to authorize the payment by completing an AFA. Each recurring payment will require a pre-debit notification to be sent to the payer at least 24 hours prior to any charge.
To address the new RBI framework for recurring payments in Shopify’s billing platform, we worked with a local payment provider that could accommodate both card-based payments as well as a local payment method called Unified Payments Interface (UPI).
From a backend perspective, all these complexities meant a few things for our internal payment service. Particularly, the system would need to:
- model the new e-mandate concept
- handle recurring and one-time payments
- work with cards and UPI
- do all of the above with a new payment provider
Refactoring Legacy Code with the Strangler Fig Pattern
Large objects are a code smell: overloaded with responsibilities and dependencies, as they continue to grow, it becomes more difficult to define what exactly they’re responsible for. Large objects are harder to reuse and slower to test. Even worse, they cost developers additional time and mental effort to understand, increasing the chance of introducing bugs. Unchecked, large objects risk turning the rest of your codebase into a ball of mud, but fear not! There are strategies for reducing the size and responsibilities of large objects. Here’s one that worked for us at Shopify, an all-in-one commerce platform supporting over one million merchants across the globe.
As you can imagine, one of the most critical areas in Shopify’s Ruby on Rails codebase is the Shop model. Shop is a hefty class with well over 3000 lines of code, and its responsibilities are numerous. When Shopify was a smaller company with a smaller codebase, Shop’s purpose was clearer: it represented an online store hosted on our platform. Today, Shopify is far more complex, and the business intentions of the Shop model are murkier. It can be described as a God Object: a class that knows and does too much.
Deconstructing the Monolith: Designing Software that Maximizes Developer Productivity
Shopify is one of the largest Ruby on Rails codebases in existence. It has been worked on for over a decade by more than a thousand developers. It encapsulates a lot of diverse functionality from billing merchants, managing 3rd party developer apps, updating products, handling shipping and so on. It was initially built as a monolith, meaning that all of these distinct functionalities were built into the same codebase with no boundaries between them. For many years this architecture worked for us, but eventually, we reached a point where the downsides of the monolith were outweighing the benefits. We had a choice to make about how to proceed.
Microservices surged in popularity in recent years and were touted as the end-all solution to all of the problems arising from monoliths. Yet our own collective experience told us that there is no one size fits all best solution, and microservices would bring their own set of challenges. We chose to evolve Shopify into a modular monolith, meaning that we would keep all of the code in one codebase, but ensure that boundaries were defined and respected between different components.
Each software architecture has its own set of pros and cons, and a different solution will make sense for an app depending on what phase of its growth it is in. Going from monolith to modular monolith was the next logical step for us.
To Thread or Not to Thread: An In-Depth Look at Ruby’s Execution Models
An in-depth look at threads vs processes in Ruby web applications, and when you should use each.
Implementing Equality in Ruby
Ruby is one of the few programming languages that get equality right. I often play around with other languages, but keep coming back to Ruby. This is largely because Ruby’s implementation of equality is so nice.
Nonetheless, equality in Ruby isn't straightforward. There is #==, #eql?, #equal?, #===, and more. Even if you’re familiar with how to use them, implementing them can be a whole other story.
Let's walk through all forms of equality in Ruby and how to implement them.
Lessons Learned From Running Apache Airflow at Scale
Shopify shares some of the lessons we learned and solutions we built in order to run Airflow at scale.
Double Entry Transition Tables: How We Track State Changes At Shopify
Explore how Shopify built a data pipeline using double entry transition tables to answer the question: how many Shopify merchants are using Shopify Balance?
Maestro: The Orchestration Language Powering Shopify Flow
An overview of Shopify Flow's new orchestration language Maestro, highlighting its design and implementation as well as how neatly it integrates with and addresses the requirements.
Our Experience Porting the YJIT Ruby Compiler to Rust
In this post, I want to give a nuanced perspective on our experience porting YJIT from C to Rust. I'll talk about the positives, but also discuss the things that we found challenging or suboptimal in our experience.
Building a Business System Integration and Automation Platform at Shopify
Companies organize and automate their internal processes with a multitude of business systems, Shopify, Salesforce, HubSpot, Airtable, Netsuite, Google Sheets and many more. Since companies function as a whole, these systems need to be able to talk to one another. At Shopify, we took advantage of Ruby, Rails, and our scale with these technologies to build a business system integration solution.