Articles

Scaling Custom B2B Pricing: Why We Migrated Shopify Functions from TypeScript to Rust

Bruce Mead

CTO & Founder

3/12/2025

To bypass Shopify's strict resource limits for complex B2B pricing, this guide explores the migration from TypeScript to a high-performance Rust extension. By combining native compilation with intelligent caching, the team eliminated processing redundancy and quadrupled cart capacity. This shift ensured the checkout could reliably handle over 40 complex custom items, far exceeding the performance of the original implementation.

At B2, we specialise in pushing Shopify Plus to its absolute limits. Recently, we encountered a complex pricing scenario for a large-format printing client that exposed the hard performance boundaries of Shopify’s new extensibility platform.

The Challenge

A cart containing 40+ unique custom items, or hundreds of duplicates, without breaking the checkout.

While Shopify Functions are powerful, they are governed by strict resource constraints. In this post, we’ll break down how we moved from a standard TypeScript implementation to a high-performance Rust extension. This shift quadrupled our processing capacity and ensured 100% reliability for complex B2B orders.

1. The Functionality: Beyond Simple Maths

For standard retail, pricing is simple: Variant A costs £10.

For B2B manufacturing, specifically large-format printing, pricing is dynamic and geometric. A customer might order a banner that is 6.5 metres wide by 2 metres high. That single line item triggers a cascade of mathematical logic that must happen instantly in the cart:

  • Dynamic Unit Conversion: Automatically normalising input units (millimetres, centimetres, inches) into metres.
  • Area & Perimeter Logic: Calculating base costs (£Price = Area x Rate).
  • "Join" Logic: If a print is wider than the physical printer (e.g. 5 metres), the system calculates how many "welds" are needed to stitch the material together. It then adds a surcharge for every metre of welding required.
  • Finishing Surcharges: Calculating costs for grommets and hems based on the perimeter.
  • Tiered Pricing: Applying bulk discounts not just on quantity, but on the total aggregate area of the order (e.g. "If total print area > 50m², apply 10% discount").

2. Hitting the Limit: The "Instruction Count" Error

Shopify Functions run on WebAssembly (Wasm). To ensure the checkout remains fast, Shopify imposes a strict Instruction Count Limit of 11 Million instructions.

If your code takes more steps than this to execute, the function is killed. The cart fails to update, and the merchant (or developer) sees errors like these in the Partner Dashboard:

  • InstructionCountLimitExceededError
  • LinearMemoryLimitExceededError
  • StackMemoryLimitExceededError

The Duplicate Product Bottleneck

The limit became critical when dealing with duplicate products with unique configurations.

In B2B printing, a customer might order 100 different "Vinyl Banners". They are technically the same Shopify Product ID, but each line item has different custom attributes (different dimensions, different finishing options).

In our initial TypeScript implementation, the code treated every line item as a blank slate. If the cart had 100 banners, the function would:

  1. Wake up the JavaScript runtime (Javy).
  2. Parse the heavy JSON configuration (pricing matrices, surcharge rules) for Line 1.
  3. Calculate Line 1.
  4. Re-parse the exact same JSON configuration for Line 2.
  5. Repeat 98 more times.

By the 10th item, the overhead of re-parsing that data repeatedly pushed the instruction count over 11M, causing the InstructionCountLimitExceededError shown above.

3. The Solution: Rust + Smart Caching

To solve this, we rewrote the core pricing engine in Rust. Unlike TypeScript, which requires a JS interpreter inside the Wasm module, Rust compiles directly to machine code.

However, the language switch wasn't enough. We also implemented a robust memory caching strategy.

How We Implemented Caching

Instead of processing the cart linearly, our Rust extension now groups the data intelligently before doing any maths.

  1. Product Grouping: We iterate through the cart and group line items by their Product ID.
  2. Single-Pass Parsing: For each group, we extract the complex Metafield configuration (tiered pricing, join rules, surcharge definitions) and parse it once.
  3. In-Memory Cache: We store this parsed configuration in a HashMap.
  4. Batch Processing: We iterate through the lines in that group, applying the cached logic to the unique dimensions of each item.

By parsing the configuration only once per Product Type rather than once per Line Item, we drastically reduced the instruction count.

The Results

4. Automated Limit Testing

In the B2B world, reliability is paramount. We cannot risk a checkout failure because a client added "one too many" banners.

We implemented a rigorous automated testing suite that tests against the Instruction Count itself. Using a custom pipeline, we pipe cart payloads (with 5, 20, 25, 40, and even 100 items) into the compiled Wasm binary to measure the exact computational cost.

This guarantees that every deployment is "safe" for the high-volume orders our client expects.

Conclusion

For standard retail stores, TypeScript functions are fantastic. But for high-volume B2B merchants with complex, dimension-based pricing, Rust combined with intelligent caching is the only viable path forward.

It allows us to deliver the complex "Excel-sheet style" logic B2B buyers expect, without triggering the dreaded InstructionCountLimitExceededError.

Ready to optimize your checkout performance? Contact us to discuss if a Rust migration is the right strategy for your platform.