Design Principles

These principles attempt to reduce the number of abstractions that programmers must keep in their heads by turning some of them into interface elements that can be referred to and manipulated. The hope is that this will reduce the cognitive burden on programmers, freeing them to reason about higher-level aspects of their code.

These principles were developed not only to guide the design of the Tangible Code interface, but also to explain it to a design audience unfamiliar with the processes and abstractions involved in reasoning about the behavior of code. The images below serve as visual metaphors for the principles described, but should not be taken as suggestions for the design of the actual interface elements.


Tangible Values. A variable is nothing more than a container for a value but usually we see it only as a name. To understand how a piece of code affects the value of a variable, we need to run the code in our head, or step through it with a debugger. In neither case can we see the progression of the value of a variable laid out within or alongside the code in a way the facilitates comparisons over time. The principle of tangible values calls for making values a first-class part of the interface, so that we can interact with the actual data manipulated by a program, not just the instructions for doing so.


Tangible Time. Programs execute in time: the static structure of code is rearranged as execution passes between various functions, files, and modules. But our interfaces don't show us this time-based ordering of code as it runs. If we want to get a sense of what happens when, we need to reason it out in our heads or step through it with a debugger. The principle of tangible time calls for the interface to represent time as sequences of elements (e.g. functions) in the order they execute, allowing the programmer to navigate forward and backward through time as needed.


Tangible Connections. One part of a program connects to the rest of the code in multiple ways: it might call other functions or be called by them; it might use data types defined elsewhere, or define a type used elsewhere; etc. These connections should be as easy to follow as hyperlinks, in which a single-click jumps to the related resource. In addition, these connections should be analyzed, and higher-level dependencies between parts of the program made visible to the programmer.