Make Interfaces Easy to Use Correctly and Hard to Use Incorrectly

One of the most common tasks in software development is interface specification. Interfaces occur at the highest level of abstraction (user interfaces), at the lowest (function interfaces), and at levels in between (class interfaces, library interfaces, etc.). Regardless of whether you work with end users to specify how they'll interact with a system, collaborate with developers to specify an API, or declare functions private to a class, interface design is an important part of your job. If you do it well, your interfaces will be a pleasure to use and will boost others' productivity. If you do it poorly, your interfaces will be a source of frustration and errors. Good interfaces are:

A good way to design interfaces that are easy to use correctly is to exercise them before they exist. Mock up a GUI - possibly on a whiteboard or using index cards on a table - and play with it before any underlying code has been created. Write calls to an API before the functions have been declared. Walk through common use cases and specify how you want the interface to behave. What do you want to be able to click on? What do you want to be able to pass? Easy to use interfaces seem natural, because they let you do what you want to do. You're more likely to come up with such interfaces if you develop them from a user's point of view. (This perspective is one of the strengths of test-first programming.)

Making interfaces hard to use incorrectly requires two things. First, you must anticipate errors users might make and find ways to prevent them. Second, you must observe how an interface is misused during early release and modify the interface - yes, modify the interface! - to prevent such errors. The best way to prevent incorrect use is to make such use impossible. If users keep wanting to undo an irrevocable action, try to make the action revocable. If they keep passing the wrong value to an API, do your best to modify the API to take the values that users want to pass.

Above all, remember that interfaces exist for the convenience of their users, not their implementers.

– Scott Meyers

A few useful links

in roughly decreasing order of how cool I think they are

Read-only key-value store

A high-performance implementation of a read-only disk-backed hash table. On my laptop it can answer point-lookups in ~1.9 microseconds on a 100M-entry table. Heavily inspired by D. J. Bernstein's cdb (constant database), but using more modern hashtable developments (i.e., Cuckoo hashing) to reduce long-tail latency.

Sandboxed server-side code execution as an API

An experimental web-server where rather than exposing normal RPCs, we expose a single endpoint which accepts Javascript that is sandboxed and executed server-side. The sandboxed code is allowed to locally interact with any of the server's APIs, so the caller can self-serve much more complex use-cases than normal. I think this is a neat pattern for two specific use-cases: performance-critical infrastructure, and end-user-accessible APIs-as-a-service. For performance-critical infra, it's nice because you can avoid repeated round-trips between services. Databases have been doing this for a long time as User Defined Functions and co-processors. For end-user-accessible APIs-as-a-service, it's nice because end users have a huge variety of use-cases that are difficult to serve elegantly with single-purpose RPCs, and this approach gives them a lot of flexibility. It also saves on bandwidth and round-trips, which may be critical for good performance on mobile devices.

Pairwise Ranking

WebAssembly implementation (code) of an interactive sorting algorithm. It will repeatedly ask you to compare pairs of items until it has enough information to sort the entire list. Uses an implementation of the Ford-Johnson sorting algorithm, which is optimized to require very few comparisons.

Go Links

Link shortener with namespaces, implemented (GitHub repo) using Cloudflare Workers and Cloudflare Workers KV (a read-optimized eventually consistent key-value store).

Namespaces are used instead of user-based authentication. Pick a namespace (any short-ish string, preferrably unguessable) and save/shorten links within that namespace. Namespaces themselves are not discoverable, so unless someone can guess your namespace, they can't access any of your saved links.

There are significant downsides to namespaces over normal authentication and authorization mechanisms, but the major upside is that you don't need a username or password, and you don't need to grant the extension any real permissions. It's easy to share and easy to implement. I think it's an under-utilized way of protecting privacy.

Nine-board tic-tac-toe

Elm implementation (code) of a neat variant of tic-tac-toe.

My GitHub profile

Some Rust, a bit of old Haskell, and a few haphazard explorations with Elm and TypeScript.

Dots and Boxes

WebAssembly implementation (code) of a simple board game.

Ramsey's Theorem R(3,3) game

A tiny bare-bones game based on R(3,3).

Visualizing randomly nested functions

A tiny algorithmic art generator that constructs a random tree of nested functions and then draws is on the [-1,1]^2 square.