Writing ridiculously reusable Dart code

Matan Lurey
2 min readApr 28, 2017

--

One of the most common questions I get for Dart, even within Google, is “how do I make my code more reusable?”. We work on large projects like AdWords, AdSense, and Google’s internal CRM, Greentea — which are all written in Dart.

Reusable bags are sort of like reusable code, right?

I was working with an engineer who was iterating on a new API for the AngularDart router, where we are modeling route changes as immutable state changes:

You can try this demo on DartPad.

A question was asked while developing this, though:

A [large] customer says that the default behavior of stripping the query parameters as you navigate from a route to another route was not desired — they’d like to keep all query parameters.

As you can see if you try the demo on DartPad, we print the following:

Route {/ {referredId: 1234}}
Route {/contact {}}

Let’s imagine how we’d implement that — a simple way would be to create a new Route object and copy the previous Route’s query field — or we can even merge it using the Map#addAll API:

Now, if we look at our console, we’ll see:

Route {/ {referredId: 1234}}
Route {/contact {referredId: 1234}}

Now we’ve run into a software engineering conundrum — will all of our users need this functionality? If we provide too little flexibility, teams will fork our code or choose another product. On the other hand, providing too much flexibility and we’ll have built a monolith of code for a simple task.

A naive but not incorrect approach would be to add a flag:

class Router {
final bool persistQueryParameters;

Router({this.persistQueryParameters: false});
}

OK, what’s the problem here? Now you’ve added to your public API, and if the team comes back and says “well, we’d like to persist most query parameters, but not these” you’ll have to change your API or introduce a new flag.

What if instead we injected a function that lets users customize how state changes occur? Let’s try it:

You can try this demo on DartPad.

— — Router 1 — —
Route {/ {referredId: 1234}}
Route {/contact {}}

— — Router 2 — —
Route {/ {referredId: 1234}}
Route {/contact {referredId: 1234}}

We’ve given a little bit of flexibility — teams can now define a transformation function that lets them define how they will transition from a previous routing state to a new one — without hard-coding the transformation a specific team wanted.

Do you have your own strategies for writing ridiculously reusable Dart code?

--

--

Matan Lurey

Software engineer @Google and @Dart_Lang. Opinionated.