DCI tutorial for TypeScript: Part 2

DCI tutorial for TypeScript: Part 2

Welcome back to the DCI tutorial series for Typescript! If you're new, check out part 1 first.

This second part is more theoretical, but some background is needed to understand why we should consider coding in a quite different way than what we are used to.

Who do we code for?

I think it's fair to say that most of the time, we code for others. All but the tiniest of hobby projects will eventually reach someone else than the programmer - a user. From that point of view, a priority should be to design software that is both understandable and usable for those people that aren't programmers.

Once upon a time, this was common sense. Back in the 70s, in Palo Alto, people like Alan Kay were researching how to make computers as easy as possible to use, with the vision that the computer, with its speed and processing power, could be an extension of the human mind. It was an exciting time, when design, psychology and other disciplines melted together in this new medium.

The key idea was how to make the computer think as the user, not the other way around, as unfortunately happened when engineers after the 70s decided that their structures are what software should be based upon, not the user's mind[1]. This is something that DCI tries to remedy.

DCI and mental models

If we could design a system that reflects the user's mind in program code, that would be a huge step forward. The programs would look different as well, since you cannot find many users that speak about programming-related things like "abstract factories", "visitors", "polymorphism" or even "return values" when they describe their view of a system.

Therefore, if we want to make the computer think as the user, we need to put things like patterns and classes in the back seat, or at least consider them as plumbing - useful, but hidden and not in the way of the user and the architecture that we're trying to build.

With this in mind, let's see how a user could describe a program:

"I want to proclaim something to the world."
Like a phrase?
"Yes, I want to proclaim a phrase to the world!"
And who are you?
"I'm a speaker, and I want to proclaim a phrase to the world."
What should the world do with your proclamation?
"It should listen to it."
What if it won't?
"Well, it should at least note it."

Let's compare that to a slightly modified version of the Context from part 1:

/**
 * @DCI-context
 * A speaker proclaims a phrase to the world, that dutifully notes it
 */
function HelloWorld(
  // Roles - Objects required for the Context functionality
  Speaker: { phrase: string },
  World: { log: (msg: unknown) => void }
) {
  // RoleMethods - Describing the interaction between objects

  function Speaker_proclaim() {
    World_note(Speaker.phrase);
  }

  function World_note(phrase: string) {
    World.log(phrase);
  }

  {
    // System operation - Starts execution of the Context
    Speaker_proclaim();
  }
}

Looking at the RoleMethods, we have a Speaker that proclaims a phrase, and a World that notes it. Seems to be quite a match of the description, doesn't it?

What we have done is expressed in code the user's mental model of a Hello World program. The Wikipedia definition of a Mental model as "an explanation of someone's thought process about how something works in the real world" works in this case, but only scratches the surface. Indi Young puts it in much more detail in her excellent book "Mental Models":

"Designing something requires that you completely understand what a person wants to get done. [...] You need to know the person’s goals and what procedure and philosophy she follows to accomplish them. Mental models give you a deep understanding of people’s motivations and thought-processes, along with the emotional and philosophical landscape in which they are operating."

In a mental model, the user quite consistently speaks about objects as Roles. Let's examine that.

The difference between thinking and doing for users

Remember how we executed this Context? We passed two simple objects to it:

const you = { phrase: "Hello, World!" }

HelloWorld(you, console)

However, in this Context, you transform into a Speaker and the console into the World. This is a key difference between thinking and doing. When a user thinks about something (data), objects become what they are. You is a simple object that represents a person with a phrase. But when it's time to do something (function), we label things differently. The person-object plays the Role of a Speaker, and the console isn't just a computer screen anymore, according to the user it's the World!

You could say that we are upholding an illusion here, just as an actor playing a believable Hamlet is upholding that illusion, to the enjoyment of the audience. And this is what happens when the mental models match. When you play a computer game and the controls are matching your idea of piloting a starship, the controller becomes an extension of your mind, and the illusion of being a starship pilot is upheld, for your enjoyment. Which was the goal of the humane computer research, back in the 70s.

Welcome to DCI, where we express and uphold mental models for the sake of the most important part of the system - the user.

In the next part, we will get much more practical with modeling a realistic example of a DCI Context. Keep reading in Part 3.

Footnotes

[1] For a deeper dive into users and computers, see my article Rediscovering MVC.