DEV Community

Cover image for DataWeave 2.5 Generics: How Call-Site Type Parameters Caught 3 Production Bugs
ThaSha
ThaSha

Posted on

DataWeave 2.5 Generics: How Call-Site Type Parameters Caught 3 Production Bugs

I maintained a shared DataWeave utility library across 12 Mule apps for 2 years. Functions like topN, pipe, and safeGet. All untyped. All accepting Any. All silently producing wrong output when called with wrong types.

Last quarter I rewrote them with DataWeave 2.5 call-site generics. Three production bugs surfaced on the first compile.

TL;DR

  • DataWeave 2.5 adds Java/TypeScript-style call-site type parameters: fun topN<T>(...)
  • The compiler validates T at every call site — type mismatches become compile errors, not runtime surprises
  • Requires Mule 4.5+ — older runtimes throw parse errors on <T> syntax
  • I caught 3 bugs that had been producing wrong output for 4 months

The Problem: Untyped Utility Functions

My library had functions like this:

fun topN(items, n, comp) = (items orderBy -comp($))[0 to (n - 1)]
Enter fullscreen mode Exit fullscreen mode

Takes anything. Returns anything. Works on Arrays of Numbers, Strings, Objects. But there's no type checking at the call site.

If someone calls topN(arrayOfStrings, 3, (s) -> s) expecting numeric sorting, they get alphabetical sorting. No error. No warning. Wrong data.

The Fix: Call-Site Generics

DataWeave 2.5 introduced type parameters at the call site:

fun topN<T>(items: Array<T>, n: Number, comp: (T) -> Comparable): Array<T> =
    (items orderBy -comp($))[0 to (n - 1)]
Enter fullscreen mode Exit fullscreen mode

Now calling it:

topN<Number>(payload.numbers, 3, (n) -> n)
Enter fullscreen mode Exit fullscreen mode

The compiler checks that payload.numbers is Array<Number>. If it's Array<String>, you get a compile error. Not a runtime surprise 4 months later.


100 production-ready DataWeave patterns with tests: mulesoft-cookbook on GitHub


The pipe Function: Type-Safe Composition

The pipe function chains transformations:

fun pipe<T>(value: T, fns: Array<(T) -> T>): T =
    fns reduce (fn, acc = value) -> fn(acc)
Enter fullscreen mode Exit fullscreen mode

Usage:

pipe<Array<Number>>(payload.numbers, [
  (arr) -> arr distinctBy $,
  (arr) -> arr orderBy $
])
Enter fullscreen mode Exit fullscreen mode

Every function in the array must accept and return Array<Number>. If one function returns a different shape, the compiler catches it.

Before generics, a function that returned Array<Object> instead of Array<Number> would silently change the data shape downstream. With <T> enforced, every step in the chain must maintain the type contract.

The 3 Bugs Generics Caught

Bug 1: Wrong sort order for 4 months. A flow passed Array<Object> (customer records) to topN expecting Array<Number>. The comparator extracted a String field instead of Number. Sorting was alphabetical, not by magnitude. "Customer #9" ranked above "Customer #10" because "9" > "1" in string comparison.

Bug 2: Shape mismatch in pipe chain. Step 1 in a pipe chain returned Array<Object> but step 2 expected Array<Number>. The output was nested objects where flat numbers were expected. Downstream calculations used the nested objects as numbers — JavaScript-style coercion produced non-obvious wrong values.

Bug 3: Comparator type confusion. A topN call used (r) -> r.score where r.score was a String "95" not Number 95. The ordering was lexicographic: "87" > "100" because "8" > "1". Top 3 by score returned the wrong 3 records.

The Trap: Runtime Version Incompatibility

This requires DataWeave 2.5 (Mule 4.5+). The <T> syntax doesn't parse on older runtimes.

If your team runs mixed Mule versions — some apps on 4.5, some on 4.4 — a shared module with generics compiles on one server and crashes on another.

I version-gate my modules now:

  • modules/utils-v25.dwl — generics, for Mule 4.5+ apps
  • modules/utils.dwl — no generics, backward compatible

The 4.4 apps use the untyped version until they're upgraded. Not ideal, but better than breaking production.

When to Use Generics

Use generics when Don't bother when
Shared utility functions used across 3+ apps One-off inline transforms
Functions that accept multiple types (topN, pipe, map wrappers) Functions with a single known input type
Your team is on Mule 4.5+ Mixed runtime versions with no upgrade path

100 patterns with MUnit tests: github.com/shakarbisetty/mulesoft-cookbook

60-second video walkthroughs: youtube.com/@SanThaParv

Top comments (0)