Human intuition is pretty amazing. It allows us to do fairly complex tasks without even thinking about them. Take walking for instance. Walking around our environment is actually quite complex. Just look at DARPA where the best of the best in the field are trying to make Robots walk and this is the outcome of their efforts. (Yes I have seen the recent amazing jumping robot video but that didn’t help my case)

We are able to walk and move around with complete ease once we learn the basics as a baby. We do this intuitively.

Intuition is the ability to acquire knowledge without understanding how the knowledge was acquired.

Why is intuitive code so important?

I want to be able to understand the code I am looking at without having to think about it or look for outside explanation. Working with well constructed code should be as easy as walking.

The only real valuable thing is intuition — Albert Einstein

Much of what I do during the day is change context to many different unrelated projects in many different programming languages/frameworks. Python, Django, Flask, Ruby, Rails, Sinatra, Middleman, Node, Express, PHP, Laravel, React, ReactNative, Ember, Angular to name a few. Given that changing context so often is a requirement of my job, I have a different perspective than most developers about what I value in a codebase. I am continually in a quasi-onboarding state with all of these projects, which makes me focus on the intuitiveness of a codebase more so then someone who works on it all day every day. My opinion is that developers should treat a projects onboardability as a top level concern.

* I think I made up the word onboardability but I like it

How do you achieve intuitive code?

Luckily for us, the programming languages and frameworks that most of us use have all that we need to allow for intuitive code. Here are some things that I keep top of mind when writing and assessing code.

The fewer API’s/DSL I have to know to work on the project the better.

This includes things like: template languages, additional libraries, abstracted helper or service object/classes/methods/functions. Every time a new DSL or API is added to a project it becomes less intuitive in that it relies on external explanation to understand and work with the code.

One example is JSX vs template languages. I strongly favor JSX because the API is essentially two things that web developers already know: HTML and JavaScript. Take the example of iterating over an Array. In a template language like Handlebars, you would have to look up the syntax for how to iterate. In JSX you would just use your favorite JavaScript syntax for iterating like forEach() or map().

In Handlebars:

In JSX:

In the JSX example, assuming you know JavaScript you already know how to iterate.

Another example is in Rails projects DSL’s are very common. One such example is the access_granted gem in Rails.

While at times a well thought out DLS can be intuitive, you have to add them to your projects with caution. Looking at this code above, it is fairly intuitive on its own but if you can imagine adding many DSL’s like this to a code base and it becoming cumbersome to switch back an forth between DSL’s in different parts of the app and recalling the specific options and configuration for each.

2. Keep your code as simple as possible

Controlling complexity is the essence of computer programming. — (Brian Kernigan)

2.1 Complex code is bad code.

The last thing you need is a developer trying to explain what they implemented to you like:

This is certainly an oversimplification because there are a lot of complex things that need to be expressed in code and it can be very difficult achieve simple code that handle extremely complex use-cases.

However, in most cases the complex code is bad code rule applies. If the code is too complex to understand within a reasonable amount of time, then it will become much too hard to maintain or change, greatly decreasing its future value. Here are my rules on complexity:

I should be able to understand a file in under 2 minutes.

I should be able to understand any given function in under 15 seconds.

I should be able to grok a whole, roundtrip implementation of any given feature. This means, I can fit in my head at one time everything I would need to change or create a new feature (i.e. data, state, connections, etc) for any given feature.

*note this does not mean the entire system at once, only a single feature in that system.

2.2 Automate complexity checks

Another way to manage complexity is to automate code complexity analyzers. For example, we run pre-commit githooks which ensure functions are below a threshold and that legacy functions can only be reduced in complexity if touched and above the threshold. There are many ways to enforce complexity with githooks, CI, etc.

2.3 Keeping down overall complexity of a project

To achieve this you will likely need to follow best practices in both testing and refactoring which is too deep a topic for this blogpost, but my favorite refactor technique for simplicity is extract method and extract class.

3. Conveying context

This is a big one so I will break it up into a few sub sections.

3.1 Naming! Is! Super! Important!

All of the programming languages I use give ample opportunity for developers to convey context. We have:

Directory structure and names

File names

Class names

Function names

Variable names

Parameter (and property) names

That is actually a lot of opportunity to tell other developers (or your future self) to convey the intent of the code. For example you may have a directory called src/models with a file called teacher.js, a developer who has familiarity with server best practices and patterns could intuit that this file is likely a JavaScript interface with a data store that holds information about a list of books. Then if we look at the code we may see something like:

There are a lot of clues here that can convey context. Just by glancing over this without any explanation, we can tell many things about this code and what its intent is. We can tell that we are dealing with a school of some sort. There is likely a database with a table of teachers and students. The function in this class takes a JS representation of a student and a paper, grades the paper using a aptly named function, sends the parent the students grade on the paper and then returns the grade.

3.2 Explicitness over Brevity

Here is the exact same code as above but we have optimized for brevity. Same line-count, same functionality only it is impossible to know what this code is actually doing from just reading it.

I almost always prefer explicitness over brevity. There is, of course, a tradeoff but the downsides of brevity almost always outweighed by the benefits of explicitness. When I am training Jr developers I will write ridiculously explicit code on purpose. Something like:

enlargeMapComponentToAdujstForAddedMarkers(markers)

The Jr devs will almost always laugh when I write this way and I don’t actually want them to ultimately write this way but it give me an opportunity to make a point that I would rather have the above than:

resize(data);

3.3 You can read the code in *mostly* english (or whatever language you are fluent in)

If you have followed the last two points, this one should come along automatically but it is worth stating as its own. Outside of language/framework syntax, you should be able to read most of the code in english. There are exceptions to this, like cases where math and science context is actually better to convey intent. Even in this case, there would be times where it is better to use english and times where it is better to scientific/math notation. For the cases where math/scientific notation is better, the person who is viewing or working on the code would need to understand the notation to be able to understand the context, so the same principles apply there as well.

So what is the beauty of intuitive code?

The beauty of intuitive code is that it actually comes naturally when applying a little bit of empathy, common sense, deliberate practice and best practices to your work. By taking a bit of time each day while you are writing code to think “How can I make this easier for someone new coming onto the project?” you will likely start making decisions that will naturally lead to more intuitive code.