3 ms·
A good interface guides the user to doing the right thing. To quote Brad Adams (who borrowed it from Rico Mariani)[1]: Rico called this the Pit of Success.
by khaledh 3y ago
A good interface guides the user to doing the right thing. To quote Brad Adams (who borrowed it from Rico Mariani)[1]:
Rico called this the Pit of Success. That concept really resonated with me. More generalized, it is the key point of good API design. We should build APIs that steer and point developers in the right direction. Types should be defined with a clear contact that communicates effectively how they are to be used (and how not to).
This concept is also tied closely with the concept of "making illegal states unrepresntable," popularized by Yaron Minsky[2].
For example, a document workflow system might naively model Document as a single entity that has all possible fields for all possible states. This could result in the need to raise exceptions (or return error codes) when the Document state is invalid, e.g. you can't approve a draft document until it's submitted. A better API models each state separately (e.g. using a union type, if your language supports it); so you'd have `DraftDocument`, `SubmittedDocument`, `ApprovedDocument`, `RejectedDocument`, where only `DraftDocument` would offer a `submit` method, and `SubmittedDocument` offer a `approve` and `reject` methods, returning an `ApprovedDocument` or `RejectedDocument`, respectively.
[1] https://learn.microsoft.com/en-gb/archive/blogs/brada/the-pit-of-success https://learn.microsoft.com/en-gb/archive/blogs/brada/the-pi...
[2] https://blog.janestreet.com/effective-ml-revisited/ https://blog.janestreet.com/effective-ml-revisited/
- moonchrome 3y agoAnd what happens when you have orthogonal traits ? You have to implement a matrix of all valid permutations ? Sounds like "you should wear a straightjacket because you might fall down when running with scissors" kind of a solution.
- greiskul 3y agoThen you do something else. Engineering is a matter of tradeoffs. There is no silver bullet, we don't throw off an approach because it doesn't solve everything.
- Supermancho 3y ago> You have to implement a matrix of all valid permutations ? I think the example was pretty bad, however the union type being referenced is that matrix, afaik. Let's start with a different example. I have a business process that runs off a plan-document. The plan-document includes things like caps for number of times things can happen over a period (including, all-time), or day parting specifying when things can happen at all (only saturdays), and absolute controls like "active" or "inactive", on top of the creation/deletion paradigm. When the process runs, what is the state of the business process at any given time? "inactive because capped out" or "inactive because day parting"? This set of labels will grow, in permutations, over time as well as discoverable business needs. eg Was it "inactive" because it was created that way or someone manually deactivated it with an update? Now the program needs to reference a history of changes as well as referencing run-state. A business that is building a new product, especially within a domain that few people understand, requires more than building a set of states and assume they will always meet the needs. This is a recipe for lots of large-scale rework and bugs. A set of states (be it a bitfield or json blob or whatever) fed into a rules engine (or component) will likely be more extensible over time than looking at simple labels. Granted, this is predicated on the software being a non-trivial system.
- lmm 3y ago> And what happens when you have orthogonal traits ? You have to implement a matrix of all valid permutations ? No, you'd use polymorphism. As a silly example (but it demonstrates the technique), look at the phantom-typed builder pattern, where you can set the fields in any order you like but you can't build until you've set all of them.