Idiomatic Design vs. Designing for Adoption

One of my favourite parts about working with the .NET Framework and its derivatives is that early on in its life there was a slavish devotion to design guidelines for libraries. Most folks that have ever developed a set of reusable libraries for the CLR would have at least looked at the Framework Design Guidelines (or its predecessors). Over the years as the capabilities of the languages and runtime have increased new idioms have crept into the framework but it has still retained a large amount of consistency.

.NET is not alone in its principled approach to framework design. Despite there not being a single current source of truth for design guidelines around Java framework design, there still remains a very consistent style to Java code, similarly with other languages and frameworks. Perhaps one of the key places that we fall down is in C/C++ code where you do tend to find quite a spread of styles. This is probably an artefact from the places the language has found itself and the longevity of the language itself.

Learn the Local Lingo

When I move to the new language/runtime one of the first things that I try to get a handle on is the idioms so that I can attempt to "speak code like a native". Sure it takes a little longer to get started, but it helps build an appreciation for the patterns that other developers in that space use.

But is there a case for ignoring the idioms of one platform in an attempt to drive adoption from users of another? One example that I've been looking at recently is the Windows Remote Arduino framework which is developed by Microsoft.

Case Study: Windows Remote Arduino

The Windows Remote Arduino framework is a component that allows developers to connect to Arduino devices that are exposed via WiFi, Bluetooth or USB and control them from a Windows device using the Firmata protocol.

The Firmata protocol is useful because it allows software to be written to control the Arduino device remotely without having to write code for the micro-controller itself (and come up with a protocol for connecting to and exchanging data with the rest of your project).

If you are familiar with coding an Arduino directly, then it is pretty easy to get a grasp on driving the Windows Remote Arduino framework from a Windows environment once you get the boilerplate code set up. This is partially because the framework that Microsoft has provided closely matches the Arduino libraries.

The mimicry of the Arduino framework is so close that it actually violates some of the idioms associated with the .NET framework. For example, public methods defined in camel-case, as opposed to pascal-case). In the Windows Remote Arduino framework the method name to read the voltage level on a particular pin is analogRead matching the capitalisation on the Arduino libraries themselves.

From a .NET developers perspective this would be better written as AnalogRead or perhaps ReadAnalog depending on how far you wanted to go. In fact, because the analogRead method results in a call remote procedure call it would actually make more sense to use the async pattern which is now ubiquitous across much of the .NET framework. This means that you'd have code that looks something like this.

var result = await arduino.ReadAnalogAsync(13);  

Instead of:

var result = arduino.analogRead(13);  

Sure, the second version is shorter, but the first version is perhaps more correct from a .NET design perspective. Whilst this probably isn't a big deal in the simple case, the ability to exploit language features like async methods does become important as complexity increases. For example, let's say that we wanted take readings from the Arduino and store them in a database (another remote call). Well, with async methods we could easily express that as:

var database = new Database(connectionString);  
var pendingConnection = database.OpenConnectionAsync();  
var pendingResult = arduino.ReadAnalogAsync(13);

(await pendingConnection).WriteReadingAsync(await pendingResult);

Note that the database API shown above is pseudo code, there are generally a few more steps involved establishing a connection (but that only strengthens my case). The above code is good in that it doesn't wait for the database connection to be established before it heads off to read the analog signal from the Arduino. Here we eliminate delays created by waiting for two operations to complete serially and instead do it (potentially) in parallel.

Final Thoughts

The choice to mimic the Arduino library this way in .NET is doubly interesting because in reality the RemoteDevice class is really a driver for the Firmata protocol, not the Arduino. Other wrappers around the Firmata protocol have opted not to follow the Arduino library exactly. For example this Ruby implementation uses the Ruby convention for method naming.

I don't intend to pick on the Windows Remote Arduino framework here. The framework is extremely useful for embedded projects and I really like the way that Microsoft has embraced two open source initiatives here in Arduino and the Firmata protocol.

This post is really just a reflection on the consequences of picking one API design approach over another. In one case you can reduce the barrier to entry for developers coming from other platforms, and on the other you open yourself up to better leverage the capabilities on the platform you are actually running on.

Perhaps the path forward is to break up the Windows Remote Arduino framework further. Underneath the covers the NuGet package for the remote wiring framework contains three class libraries, these are Microsoft.Maker.Serial, Microsoft.Maker.Firmata and Microsoft.Maker.RemoteWiring.

The RemoteDevice class sits within Microsoft.Maker.RemoteWiring. It might make more sense to split out another class library called Microsoft.Maker.RemoteWiring.Compatibility which provides a version of the API which mimics the Arduino libraries. The Microsoft.Maker.RemoteWiring would provide the underlying implementation but be implemented in a fashion that is consistent with the idioms of the .NET framework.