Are you Coding in The Upside Down?

framelang

Mark Truluck

Posted on April 30, 2021

Are you Coding in The Upside Down?

In the Netflix show "Stranger Things", some 1980s kids go to an alternate universe they call The Upside Down under the evil control of a horrible creature called the Mind Flayer. In the Upside Down, things sort of look like our universe, but are confusingly different and it is decidedly easy to make a deadly wrong move.

Programming can often feel similar, especially when coming in cold to a new piece of complex software and trying to figure out what is going on. This common situation is made harder by a typical lack of documentation and comments. The standard refrain of "the code is the best documentation" is woefully uncomforting when traipsing through a tangled nest of inexplicable conditional logic.

Alt Text

[Keno Leon]

Like so many things, we know spaghetti code when we see it, but is it possible to characterize bad code in a more objective way? Can you recognize a rhetorical question? Of course you can!

The Leaderboard of Horrors

Although there are many ways to obfuscate the purpose of code, my list for worst offenders are:

  1. Implicit Logical State
  2. State Fragmentation
  3. State Entanglement

What we will see is that these are simply emergent properties of the typical way to code object-oriented classes! So these problems are baked right in to business-as-usual.

As elevators feature prominently in the battles against the Mind Flayer, we will use one as a poster monster for all the ills of coding-as-usual.

Alt Text

Let's investigate these terrors one-by-one.

Implicit Logical State

Everyone knows software has state. Most often the term is discussed with regards to the actual values in the data. For instance, a signed 32 bit integer has a range of [-2147483648 to 2147483647]. That's a lot of possible states!

However we don't often assign logical meaning to each and every distinct value. Instead we chunk up that total range into just a few subranges with meaning, sometimes quite arbitrarily. For instance we might make this map of value => logical state for an elevator simulation:

Values Logical State
0 to 2147483647 Going Up
-2147483648 to -1 Going Down

(And no, I'm not concerned about boundary conditions. Going to floor 10 gazillion is totally possible on this elevator.)

It's not a very useful elevator yet, but often system design starts with the most common modes of operation and then adding nuance to it.

Let's take a look at a simple skeleton implementation of this elevator (I know you are worried about how to get off the elevator but don't. It's just an example and you can't really get on yet anyway).

Alt Text

In our table above we have two logical states declared. In our code, however, there is no explicit declaration what they are!

There is nothing about (direction >= 0) that just screams this means "Going UP" to the uninformed reader. Instead it's a total superposition of a logical meaning on an arbitrary range of values. This is bad, and as much as you may be saying "I never do this", I'm guessing you've seen it done by somebody else. Am I right?

Additionally, this boolean can be constructed in a number of ways and get the same functional output:

(direction > -1)
!(0 > direction)

etc.

This is the problem of implicit logical state. Code structured to use boolean tests to determine ranges of low level data state that implicitly define logical state are 1) hard to understand and 2) incredibly fragile.

I know you all are thinking of 10 ways to avoid this - and they would all help. But the fact that you have to devise strategies to mitigate a problem that shouldn't exist in the first place is the point of this article. But more to come on the solution later.

Now, we can't always avoid performing conditional tests and, in fact, they aren't inherently "bad" in the first place. However, they are often used badly. And when it relates to determining what our object's internal state is, we can certainly do much better in a lot of cases.

So while you may be able to dodge the worst effects of implicit state by good software engineering practices, not so with the next situation. Let's add some comments and expose the next kind of serious problem this kind of code structure enforces. (There is no escape for you this time).

Alt Text

State Fragmentation

People naturally think in terms of context - where am I, when is it, what is the situation. By understanding context we can then better understand what is happening.

In this example, the two contexts are Going Up and Going Down. However, notice how the code for each of these contexts are fragmented across the event handlers:

Alt Text

I bet you are so used to this that it doesn't strike you as bad, but it really is.

This scattering of a logical state throughout event handlers is one of the most challenging aspects of object-oriented programming languages. And it is bad for you (the developer) for the same reason fragmenting files across a hard drive is bad for your computer:

Alt Text

Your brain has to do the same work as the hard drive - stitch together the parts of a logical "thing" to make complete sense of it. This just isn't how we perceive the world effectively.

Instead, people and hard drives like logically related things kept in the same place. I will assert people in particular (don't know about hard drives) think "contextually" - where am I, what am I doing, what is the situation - and then think about events to decide what to do.

And this gets to the heart of the matter, as all of the programming languages we are most likely using on our projects have no syntax or concept of explicit logical state. Instead, as we have seen, logical state is inferred from low level data.

The solution that I will be proposing fixes this problem.

But first, we must check off the last of the worst offenders - and its a doozy.

State Entanglement

In the Upside Down I don't know why you would, but say we actually wanted to get on our elevator. Then we'd just hack out a new range and shove a Stopped state right in! Easy peasy.

Values Logical State
1 to 2147483647 Going Up
0 Stopped
-2147483648 to -1 Going Down

Ok so in our specification we have now redefined the logical states the software can be in, but we haven't yet changed our code. So before we do, let's look at how our existing conditional logic maps our newly specified logical state ranges:

Alt Text

Here we can see where the "State Entanglement" problem arises. Our existing rat's nest of boolean logic has already mapped that new logical state right into the existing behavior for Going UP. What to do?

Well, we have to go through ALL our code and disentangle this mess. Let's go step-by-step to see how we get to play "whack-a-mole" with this new state.

First lets fix Going Up and get Stopped out of there:

Alt Text

So this is a partial fix, but it is interesting to get a visual on what would happen if the developer got distracted and checked it in accidentally when invited out to a spaghetti lunch.

Now we have a situation where the stopped state is mapped to be entangled with GoingDown in the first method and GoingUp in the second. If you were to look at this code while the developer was out enjoying meatballs, how would you know that actually both were wrong?

As a final bad situation to explore, here we have all of the methods updated with the proper conditional logic for the existing two states:

Alt Text

However notice that the else clause has "captured" the new Stopped state. Else clauses are a catch-all, and in many situations when a new logical state is added they will accidentally wind up collected there like leaves in a storm drain. It's not a great outcome.

Fixed! Or is it?

Here we can finally see the proper introduction of our Stopped state to the elevator:

Alt Text

While each state is now properly disentangled, we see that the logical states are being torn apart up and down the object-oriented class.

Do you feel your mind being a bit flayed? I'm afraid it is going to get worse before it gets better.

Getting Spagged

This next example doesn't add any new twists to our terrifying tale, but it does help to visually emphasize the Cambrian explosion of fractured logical state metastasizing throughout the codebase:

Alt Text

Eventually it evolves (or devolves?) into a creature you are scared to touch, much less eat:

Alt Text

Flying Spaghetti Monster

Just the kind of thing you might expect to find in the Upside Down.

So what can turn the world right side up? What can save us from going mad from the mind-flaying effects of Implicit Logical State, State Fragmentation and State Entanglement? Glad you asked.

Put Your Code In a Box!

A place for everything and everything in its place - English proverb

There is nothing tidier or less spaghetti like than putting things in boxes. When you put things in boxes (with a good lid of course) they are much less likely to wriggle out and ruin your day.

Alt Text

But how do you choose which box to put something in?

For code, the best answer is an appropriate, small contextual box. We've identified three contexts for our strange elevator:

Alt Text

The "right side up" way to think about what our elevator does is encompassed by being in one of those three contexts, or states. Everything you can do in our elevator maps to being in one of those states.

Happily, computer science provides a theoretical, as well as practical, approach to working with states - the state machine (aka automata).

If this is the best way to organize software, why isn't everyone doing it? The simple answer is that the popular languages programmers use do not make it a natural act to structure code as a state machine. And that is where Frame comes in.

Frame - a Language for State Machines

Frame is a markdown language for specifying state machines, or automata more generally. Frame's additional big benefit is that it can generate code in many object oriented languages (and more to come) as well as documentation.

To tie this all together, here is a demo of our elevator specified in Frame and implemented in JavaScript:

As you can see, the code is structured around the logical states of the system and not around the events that can happen. This inversion of the structure of code immediately makes the purpose of the software evident and it's organization "natural". Developers no longer have to toil in the bizzare realm of the "Upside Down" and can instead frolic in the light with the clarity of context.

Alt Text

Conclusion

To find out more about Frame, have a look at the Frame Website as well as the online version of the Framepiler. The Framepiler is also an open source project that is available under MIT License to use in any way you can imagine. It will lead you to a happier place.

Happy Framing!

💖 💪 🙅 🚩
framelang
Mark Truluck

Posted on April 30, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related