DEV Community

Cover image for 9/10 Programmers Make the Same Mistakes
GaryBarnes17 for Developers @ Asurion

Posted on

9/10 Programmers Make the Same Mistakes

Introduction

Note: If you are wondering where this title and cover image
came from, it is a reference to the title and cover of 9/10 Climbers Make the Same Mistakes by Dave MacLeod, which heavily inspired this blog post

"I do not think much of a man who is not wiser today than he was yesterday." - Abraham Lincoln

Learning in the Age of Information Overload

Visual Representation of Information Overload

As developers, we put a lot of thought into what we want to learn. A quick Google search of “Web Development 2023” will present you with an overwhelming amount of tutorials, lessons, and information about the hottest new tech.

Of course, there is nothing wrong with this. Learning new technologies can be invigorating, and can often add a substantial amount of value to the work you do. However, if we put all of our attention and effort into deciding what we want to learn, we miss the opportunity to examine and change how we learn.

In fact, I would argue that in the long run, how we learn is much, much more important than what we choose to learn. Consider the different frameworks and languages that you were taught in school, the ones that were popular at the beginning of your career, the ones you use now, and the ones you are interested in learning in the upcoming year.

If you think about it, the act of learning is one of the only true constants in our field.

Consequently, I would argue that examining and optimizing how we learn would pay dividends for most developers. In this blog post, I would like to examine a few models for learning, and explain how we can use these to enhance our daily workflow.

It’s Not You, It’s Your System

The Book Cover of Atomic Habits

To make a case for this, I would like to reference Atomic Habits by James Clear (the summary of which can be found here). I’m sure many of you have heard of this, as it is one of the most popular self-improvement books in the past few years.

One of the key points Clear seeks to drive home is powerful effect that small habits we create can have on our lives. He goes on to explain how we can use these small habits to create systems that allow us to achieve our goals. In fact, Clear emphasizes that fact that goals should really be a secondary objective, and we should instead focus on improving our system for achieving them. In the words of the author:

The purpose of setting goals is to win the game. The purpose of building systems is to continue playing the game. True long-term thinking is goal-less thinking. It’s not about any single accomplishment. It is about the cycle of endless refinement and continuous improvement. Ultimately, it is your commitment to the process that will determine your progress.

In the context of our lives as software engineers, the goals may be the particular projects we want to complete, products, we want to launch, frameworks we want to learn, etc. Whereas our “system” is the means by which we accomplish these goals. In this case, I would argue that the system we should be focusing on is the means by which we accumulate knowledge and then synthesize this knowledge to solve unique problems.

With this blog post, I hope to help you optimize this system by taking a look at a few different cognitive models for learning, and referencing some tools you can use to enhance your learning based on the takeaways from these models.

Caveat: this blog post is heavily inspired by the following blog posts from Terry Heick:

Cognitive Load Theory

Diagram with a visual representation of Cognitive Load Theory

Image Source

Cognitive Overload Theory Explained

At a high level, the Cognitive Load Theory is a theory built on the idea that the brain can only handle a certain amount of new information while learning. It was developed in 1998 by John Sweller, an educational psychologist at the University of New South Wales. The School of Education published a paper on Cognitive Load Theory in 2017, which provides the following as the definition for Cognitive Load Theory:

  1. Human memory can be divided into long-term and working (short-term) memory
  2. Long term memory is stored in the form of schemas, which are “set of preconceived ideas that your brain uses to perceive and interpret new information” (from Clear blog post)
  3. Processing new information results in a “cognitive load” on the working memory, which can have an effect on how much you are able to effectively learn

So basically, the Cognitive Load Theory tells us that we can only learn so much at once without overloading our working memory.

Why is this important? Well, consider the fact that information processing can be largely divided into two primary activities: knowledge acquisition and problem-solving. Knowledge acquisition is concerned with taking new information in our working memory and creating schemas for it to store in our long term memory.

Problem solving, on the other hand, is concerned with synthesizing unique insights from these schemas to solve the problem at hand. Cognitive overload theory suggests that combining these two activities will ultimately reduce our capacity to do either of them effectively.

With this information, we can infer that the order in which we learn things is rather important. Generally speaking, we should first seek to understand what something is, how it works, and then how we can apply this knowledge to solve new problems (from Terry Heick’s post on this theory).

This is because gaining context surrounding a problem and solving that problem occupy the same mental bandwidth, and doing both at the same time prevents us from doing either well.

What Does This Mean for Me?

With this in mind, imagine that you are assigned a ticket that requires you to update various parts of a web application based on input from a user. The ticket references the potential need to use a state management tool, such as Redux, Recoil, or the Context API, none of which you are familiar with.

While you may be able to find examples in other teams’ codebases that you can copy and adapt, The Cognitive Load Theory suggests that trying to build context for this problem while simultaneously working on solving it will prevent you from doing either well.

Based on this theory, a wiser approach might be to:

  • Start by understanding what state is in React, what functionality state management libraries seek to add on top of this (without moving into any of the technical details), and why this additional tooling is necessary for the problem you are facing
  • Then, move on by learning how state works in react, and how to work with the various state management libraries to extend this functionality
  • Finally, only once you understand the context in which this problem exists and how to use the tools you will need to solve it, focus on applying this knowledge to the particular problem at hand.

While this approach may seem a bit slower, it allows you to understand the reasoning behind a particular tool or approach, and allows you to build an understanding of the problem context before jumping into a solution.

In the long run, this will provide you with a greater understanding of the ecosystem you work with and the tools within it, and free up precious mental bandwidth to focus on solving challenging problems.

Of course, this approach is not limited to solving technical problems. In the blog post I wrote about using Styled-Components, I spent most of my time explaining some of the key concepts of React and how Styled-Components fits into and extends these patterns before I jumped into any of the specific problems this library seeks to solve.

Feedback Loops

A Froot Loops fake cover that says

Understanding Cognitive Load Theory is particularly helpful for moving from a place of building context within a particular domain to solving a problem within it, but it does not inform us on how we should about solving a particular problem.

To help with this, let’s turn to the concept of feedback loops. According to the definition for feedback loop son Wikipedia, “Feedback occurs when outputs of a system are routed back as inputs as part of a chain of cause-and-effect that forms a circuit or loop”.

In the context of behavior and learning, this means that the outcomes of certain decisions we make are used to influence how we make future decision. In his blog post on Feedback Loops, Terry Heick breaks this loop down in to four distinct steps in the context of learning:

  1. The learner receives input, in the form of either external stimuli or an observation they previously formed
  2. The input is stored as data
  3. The learner analyzes this data, makes observations, and draws conclusions about it
  4. The learner uses these conclusions as input in the context of similar external stimuli in the future to make decisions

I hope you can see how immediately relevant this is to us as software engineers. We write and debug code and the output of this code (i.e. warnings/errors from our linter, syntactic errors, run-time errors, logical errors) informs how we iteratively change it and build upon it in the future.

With that in mind, I would like to focus on two key aspects of feedback loops: cycle time, and the quantity and quality of information that comes from each cycle.

Cycle Time

Image of a Stopwatch

Put simply, the shorter a feedback loop is, the more effective it is as a learning experience. If we drink coffee before we allow it to cool off, we burn our mouth immediately, and consequently learn to wait until the cup has cooled sufficiently.

However, if we eat loads of unhealthy food, it could take weeks, months, or even years before we see the full set of affects this has on our health. Consequently, it is harder to learn from this feedback loop.

With this in mind, we should take great care to optimize our workflow to be as efficient and streamlined as possible in order to quickly provide feedback, so that we can learn from the mistakes we make while programming.

This idea happens to be what lead to the creation of this blog post. I was talking to a former team member, Kaeden Wile, about how to go about testing and debugging the pipeline for Asurion UI. In order to test our action handles releasing our package to our consuming teams, we were having to wait until a PR was merged in order to update and trigger this workflow. As an alternative, he mentioned that he once forked the Asurion UI repo, and was able to push directly to the main branch in order to get this action to run.

While something likes this takes extra time to configure (especially since you need to comment out all the parts of the action that actually publish something), it pays dividends in the sense that it allows you to test and iterate on your code much, much more quickly.

In order to shorten your own feedback cycles, ask yourself:

  • Are there any parts of your pipeline that you can shorten or optimize? Are there any actions that are being triggered by some events when they might not need to be? Do you really need to run all my tests on every commit, or can you push these back to run on every push? Are you building our library or project multiple times, and can you optimize this to only happen once?
  • Do you really need to be running every unit and integration test we have, or can you only run the tests that are relevant to this feature until we are ready to merge? Of great relevance here are the jest -o and --watch flags (documentation here).
  • If you are building a library, are you able to set up a local sandbox that allows you to quickly pull in a local build and test and changes you have made? Personally, I have a small web-app that points that a local build of Asurion UI, which I use to test any changes I make to the library
  • Can you alias commands that you repeatedly use? In my local sandbox, I often need to clear out node_modules and point to an updated build of Asurion UI, which I can alias to cd ../asurion-ui && yarn build:core && cd ../sandbox && rm -rf node_modules && yarn && yarn start.
  • Can you increase the speed at which you type and navigate around in your editor? The faster you can take ideas that come to you and implement them by writing code, the less risk you run of losing some of these ideas. A great resource here for optimizing your use of your tools is the MIT Missing Semester Course.

Finally, consider adopting a more iterative workflow, which Jack Herrington covers in great detail in this video. I highly recommend watching the video, but the key point is that instead of writing all of the code you think is required for a feature and then testing and debugging it, add frequent safety checks along the way.

For example, if you are working on a new section of a page, begin by rendering a “Hello World” div in its place. Then, if this new section needs to hook into some state, make sure you can render the simplest version of that state. Continue to incrementally make the smallest change you can based on the level of risk you are willing to take on.

While this may take longer than writing all of the code at once, it will likely save you time when it comes to debugging and testing. Furthermore, these “safety checks” provide as a good foundation for a refactor in the future, and can serve as inspiration for writing unit tests for this feature.

Quantity and Quality

Book Cover for 9/10 Climbers Make the Same Mistakes

Just as important as the cycle time of these feedback loops is the information we get from them. It follows that if you have done all you can to optimize the time it takes to complete a feedback loop, you should look to increase the quantity and quality of information you receive from each loop.

I am an avid rock-climber, and in his book titled 9 Out of 10 Climbers Make the Same Mistakes, Dave MacLeod addresses this point directly. He says:

Climbing rock is a fairly short-lived activity… In a three hour bouldering session you might spend less than 30 minutes actually on the wall making moves, and the remaining time resting. Those who spend that resting time with the mind wandering elsewhere or just getting bored waiting for for their body to be ready to climb again… learn to climb slowly and often stop learning altogether. Those who replay the movements of the climb just done, recording which moves felt good or bad and looking back at the holds trying to understand why, and then plan their next attempt to try the movement a subtly different way, progress fast.

As software engineers, we have plenty of downtime in our daily workflow. We have to wait for our pipelines to run, for things to build, for tests to run, we have time in between meetings, we have time in between the launch of an A/B test and the user feedback from it, the list goes on and on.

While this is often a great time to make a cup of coffee or grab a bite to eat, consider using this downtime to proactively address the problem at hand.

If you just pushed a change to a feature branch and suspect some tests may fail, try to anticipate which ones might, and the reasons behind this failure. Pull up any documentation you can find on the testing library, as well as documentation that relates to the piece of code you think may be causing this failure.

If you are often confused by the error messages that come from this code that could potentially fail, take some time to read the relevant documentation, familiarize yourself with some of the most common errors, and even find some threads on the internet full of others dealing with these issues.

If you are waiting for reviews from teammates on a pull request, use this time to go over the PR yourself, and proactively add comments to address any areas you think your team might leave feedback. Better yet, take this time to do some dogfooding for the feature you just built.

The point here is that, while cycle times are often fixed, there is almost always something you can do to get more out of each iteration of a feedback cycle by proactively putting yourself in a position to be able to respond quickly and adequately to the outcome of each attempt at solving a problem.

Conclusion

Software Engineering is a uniquely challenging field in the sense that we are expected to always be learning. If we are not actively keeping up with industry trends, it can feel as though we are falling behind. This, coupled with the fact that we are expected to use this knowledge to solve unique problems across many domains underscores the importance of learning to teach ourselves. Through this blog post, I hope I have convinced you of this importance, and have provided a few helpful frameworks for doing this.

Top comments (0)