We technologists often have a tendency to think “implementation up.” That is, when we see a business or technical problem we almost automatically jump directly to the technical solution. Even though it can be uncomfortable to wait, it pays off significantly to spend time dwelling on the problem first. In particular, take time to put yourself in the shoes of the “users” of the software you are developing.
Users exist at all levels of the software stack. At the very top we have the “end users”—the people exercising the software through its user interface—for example, the users of a web or mobile client, or the developers in your external ecosystem using your productized APIs. In a software product, the end users are generally the people from which your company derives direct or indirect monetary value. But at all the levels of the software, we have users or “customers”—though they could be fellow members of your own engineering organization.
For example, the developers who write the client web or mobile applications are “customers” of the experience APIs and maybe an SDK. The people who write the experience APIs are “customers” of the composition API and platform services or microservices that embody the functionality. The people who write the platform services are “customers” of the core systems APIs, shared services and other systems. And so on, down to the microcode.
If we invert the normal tendency to think implementation up, and instead think “user down,” we often end up with insights we wouldn’t otherwise have—and a better system with more robust abstractions. For example, I talked to a developer recently who was responsible for writing a “portfolio” view of assets in a particular legacy system. The APIs the developer had to work with were pretty much pass-throughs of the database table structure—which was different for each asset type. The developer had to first figure out what types of assets the system supports (which required basically reverse-engineering the API set), make individual calls to each asset type to determine which assets a particular user actually had, and then fetch that user’s assets individually, type-by-type.
The developer also had to deal with creating a “mashup” of the information provided, reverse engineering the various naming conventions and data structures, to figure out what attributes were actually common across asset types. This was difficult because the names and data types of these common attributes differed from asset type to asset type, because they had been added by various people at various points in time. Creating what should have been a simple report was a lot of work, and error-prone because of the various reverse engineering of asset types, naming conventions, and data structures that was required.
Suppose instead that the original API developer had thought first about how a developer calling that API would likely use it (user-down), rather than thinking first about how the data was stored (implementation-up). I think that if they were thinking user-down, the API developer would probably have implemented a “createAssetType” API, a “getAssetTypeList” API, a “CRUD Asset” API—along with a handful of others. Think about how this would have made life much easier and less error prone for our “portfolio” developer.
The “cost” of making life easier for the user is that it generally makes life harder for the implementer. In our API example, the API developer has the responsibility of creating an abstraction of an “Asset” and an asset type, and for hiding the details of the database schema used to store these from the user of the API. I would argue that the design principle that “each interface should hide something” is a good one, and that it’s worth the time it takes. Any successful interface, be it a technical interface or a “UI,” will have many callers, often over a long period of time. By investing extra effort in taking your end user’s point-of-view, you greatly simplify life and reduce errors for all of those users, all the way up the stack.
In the industry, the jargon for this approach is “user-centered development” or “user-centric design.” This is not a new concept, and many developers already think this way naturally. But adopting a systematic user-centric or “design-centric” approach to every interface in your product can make a huge different to extensibility, productivity and many other areas.