Sinclair Schuller July 22, 2019
For most of us engineers, when we’re presented with a challenging, interesting problem, we become enamored with it. Our thoughts wander broadly and we come up with a variety of ways to solve the problem through code. Many times, our thoughts focus on tackling the problem head-on; for problem X, we dream of solutions that directly implement a workflow or algorithm solving problem X. This is absolutely the right approach to writing any piece of software for a sufficiently narrow problem. It’s also the absolute wrong way to think about solving problems in a platform, which by definition is broad rather than narrow. Let me explain.
Most domain-specific software focuses on presenting human or machine actors with complete workflows they can invoke to accomplish a specific task – run this analysis, generate this report, fly this drone until it finds a hot dog cart. Yes, a computer vision and AI equipped pocket drone that can quickly sweep a few block radius and tell me what street corner the nearest hot dog vendor is posted up at is a dream device for me. Don’t judge. While it’s fine for software to describe algorithms in such a coarse-grained way (invoke and get a real-world end-user outcome), this approach would lead to a failed platform design. To understand why, let’s look at the most fundamental reason why anyone would build a digital platform – for an organization and its partners to be able to rapidly create new digital products and services in a target domain (e.g. banking, healthcare).
If the platform presents 3rd parties with coarse-grained workflows, the types of solutions those 3rd parties can develop becomes severely constrained. Imagine, for example, that a drone vendor decides to create a platform for the development of drone apps and just exposes an API with the following primitives:
FindHotDogVendor, RetrieveHotDog, FindCandyBarVendingMachine, RetrieveSnickersBar.
The most sophisticated app anyone could create would be a “Get Me a Hot Dog and Snickers Bar” app. This composed app isn’t very creative, and the developer’s creativity was directly hampered by the coarseness of the primitives. In fact, composability increases inversely with the coarseness of a platform’s primitives and increases directly with the number of available primitives. Successful platforms become successful because they expose a useful number of domain-specific, fine-grained primitives.
In our drone platform example, a better API is one that exposes granular, discrete primitives like:
TrainToIdentifySimilarLocationByImage, FindLocationByTrainedLocationImageType, TrainToIdentifyObject, RetrieveByTrainedObjectType.
The usage of “Train” in this context is referring to machine learning. This sort of API affords end users the opportunity to branch out from the all-too-loved hot dog and snickers only diet. In fact, the same number of primitives as our coarse-grained example equips developers to build apps that can do most basic drone delivery tasks, whether it’s fetching food or delivering flowers. In fact, any platform should work hard to expose as few primitives as necessary; having too many primitives could lead to severe usability issues, not to mention confusion.
This notion that a platform should be built to expose granular primitives to maximize composability seems obvious to most. That said, it’s a mistake we see all too often in platform architecture efforts. Many platforms are not built with a “primitives first” mentality. As a result, composability suffers, as does general platform expressiveness.