Against Generality (2016)
novalis.org
novalis.org
If I'm lucky I can shift them into thinking about inputs and outputs instead of execution. One exercise that was sometimes successful was to reason by analogy to sculpture, and the concept of "negative space" in painting. In this model the program starts out being able to do anything, and it's the programmer's job to carve away the probability space until the only remaining behavior is the correct[2] one.
Another was forcing them to diagram the system with unlabelled boxes, and every line labelled by the data it carried. I would ask questions like "for this RPC, is it possible to determine if the response is correct?" -- you'd be amazed (or maybe not) how often the architecture was too generic to allow that. I think this is one of the less-advertised benefits of automated testing, even a trivial test imposes some restriction on the domain of input/output values.
[0] Most memorably, a monitoring system where all data (including numeric metrics and log lines) is represented as a frame of a distributed stack trace.
[1] A common outcome of this thinking is a big-ball-of-mud, where all of the logic in some sort of giant central executable. You see, it's "simple" because there's only one service (that does everything) and only one build artifact (that depends on all code), and only one test suite (which must run in totality on each change), and so on.
[2] "Correct" being an intentionally flimsy word. Some times it meant a 100-page spec with official test vectors, some times a unit test suite, some times just eyeballing the output in a terminal.
Reality does not, it's irreducibly complex and as a result that trick quickly becomes an annoyingly overstretched metaphor.
This is missing some qualifiers methinks. An interface is generic in that you don't have a concrete implementation, surely that can be used to decrease complexity rather than bloat it?