GIFs courtesy of Giphy
When you learn something new, you don’t know what you don’t know. There are always secrets to getting good at something and learning to code is no different. These secrets are the meta-learnings that provide the approaches, toolsets and mindsets to make the breakthroughs you need to be a kick-ass coder. I am here to spill the beans on the meta-learnings I have discovered on my coding journey at Makers Academy, so that you can accelerate yours.
“Growth is painful. Change is painful. But nothing is as painful as staying stuck”
N. R. Narayana Murthy
If you read that first line and thought ‘I don’t know if I have a real debugging process’; then let me tell you, you are in for a treat.
With coding (and arguably life) process is always king versus the solution to a specific issue. Fostering a robust debugging approach you can apply to any coding problem is the best way to squash your coding bugs, no matter how big or small.
Enter: the beautifully simple “The 3-step debugging process”, as developed at Makers Academy and shared with me by Mary Rose Cook
Step 1: Wring ALL useful information out of the error message
Sounds simple. In practice this is a classic trap for both new and experienced coders. Often when we run our code (or run a test on our code), we try to get straight to the solution to solve it as fast as we can, recklessly skipping over the stack trace and any error messages. This is easy to do, trust me; I do it a lot myself.
When you see a stack trace (example below), wring out every last damn detail you can find, for example:
An example of a stack trace with error messages from an RSpec test on Ruby code
In modern coding languages, error messages are more useful than ever — ensure to extract every bit of information you can. A helpful approach to forcing you to take this process seriously is to write out the information you find on your error as you read it, to ensure you have honed in on the juiciest bits.
Step 2: Tighten the loop & get visibility
Tightening the loop. This is where you use the information from the error message and review the different sources of your code to find WHERE the error takes place. The key is to find the first wrong line of code (which may or may not be the exact line your error message offers you).
Get visibility. Now you have tightened the loop and know roughly where the error is, get visibility i.e. LOOK/PRINT OUT/ INSPECT all the variables and coding logic in that space. Where there is referential transparency (references to code logic that has been extracted and lives elsewhere), explode this out and work out what the variables within this code look like.
By breaking your complex code into small chunks and LOOKING at what each of these aspects evaluates to, you will have a much better understanding of what your code is doing in practice. Continue breaking your code into chunks and get visibility of all the elements at play. Your understanding of what is exactly happening at each point in your code will improve and you will see where the coding logic is breaking down or acting differently to your expectations.
Step 3: Fix that bug
At this point, you know what is causing the error from your stack trace, you have narrowed down to where you know the error is, and you know exactly what each element of your code is doing in practice. This process will reveal why your bug is occurring. If you know the fix, go ahead and do it! Alternatively, you will now know where and why the issue is happening, but to fix the bug itself you may need to research a solution.
Coding research is a skill and process in its own right which I won’t tackle here. If the bug is still causing you issues, alongside careful research of the issue, don’t forget to take a step back and have a higher-level think about the problem. Remind yourself that the coding context and logic is always the key to fixing the problem. Fixing the bug itself by randomly typing potential solutions you think might work can be tempting. However, this is unlikely to solve the problem and, even if you solve it, you won’t know why it worked for the next time you tackle a similar bug.
Final tip: remember that the hardest problems often have the simplest solutions. Don’t forget that your bug fix may be a simple spelling/typo/syntax issue.
Solving tricky coding bugs: Slimy, yet satisfying
Just because your code worked before (or you thought it would) doesn’t mean it still does. You may have become stuck because fulfilling a new user requirement has changed the game on what you need from your code, and made aspects of it obsolete. Yet we can find ourselves trying to force new functionality into old code, even if it doesn’t make sense any more. Whilst we should all aspire to build code that is easily flexible and extendable, there is a limit to which this is possible (particularly on complex code bases with multiple developers). Therefore, the best approach to unstick you may well be to DELETE YO DAMN CODE.
“In a time of destruction, create something.”
Maxine Hong Kingston
By deleting your code, you start beautifully fresh without the cognitive burden of your previous codebase. You bring a light, bright ‘beginners mind’, turning your code into a greenfield opportunity again where any new, cool idea can be the future.
Delete your code, or Panda will DELETE IT FOR YOU
Fortunately, unlike building with bricks, code is cheap. It costs literally nothing but the storage space (enjoy that aspect, let it sink in — the code on your screen is not what is worth something, your ability to write awesome new code is). Get used to deleting your code, and rewriting it. It’s easy to get precious over code you’ve written — but having the confidence of knowing you’re able to reproduce code from scratch is invaluable. You will have problems deleting your code and starting again, but it may well be the most powerful thing you learn to do.
This is the most dangerous weapon of a tricky, stick bug: assumptions. When we code, we make constant assumptions about how things are working. These assumptions are the mental models and metaphors that simplify our coding model and enable us to hold it in our brain, so that we can interact and work with it in a practicable way. Whilst this helps us reduce cognitive load, it can be are absolute undoing and be the main cause for staying stuck. Most evil of all dangerous assumptions: assuming you know why your error is happening.
When you bring a mind brimming with assumptions about your code and the error you are stuck on, you don’t know the true reason for your bug. Thankfully, other than being conscious of the scary power of assumptions, there are 3 practical approaches to combatting this natural trick of the mind.
Assuming will lead to this — a living bug hell, built just for you :)
(i) Run your code and your tests EVERY TIME YOU ADD OR CHANGE A LINE OF CODE. Make playing with your model and running your tests second nature. If you leave it too long between changes, you will have no idea WHICH CHANGE was the one that had the impact you are now seeing in your code. Before you hit run: know, really know, what you think this additional element should do (even say it out loud). If you change one element and the error messages. Then when your change changes the stack trace of your code run, internalise why this change happened and what that means in helping you to get unstuck.
(ii) Git Commit your code early and often. That way, you can easily go back to a previous and specific version of your model that was working, to see which changes were the ones that caused you to get stuck. Rather than assuming what change got you stuck, you can easily see which one did, and revert back to a time before it even happened.
(iii) If you really want to overachieve: eliminate assumptions entirely through the futuristic power of live-testing. In other words, set up your testing infrastructure so that it runs your code and tests LIVE, AS YOU TYPE. I know, WHAT!?! Instant, beautiful feedback, no assumptions about what your code is doing required, as you can see LIVE what is happening. The ultimate tool for getting unstuck. As an example: if you are using the ubiquitous Javascript, a helpful pointer to help you overachieve: wallaby.js
When you write code, the temptation can be to begin refactoring it before knowing if it really works the way you want it to. When you are stuck, it can feel great to busy yourself refactoring your code base, rather than trying to solve the icky, sticky, horrible problem you are actually stuck on.
Resist this urge, and only refactor your code once you know it is fully functioning the way you want it to and passing all of your tests (read about the world of test driven development and the red-green-refactor cycle at point 1 here).
If you refactor before this point, you will enter the twilight zone cycle of red-refactor-red-refactor ad-infinitum. Refactoring code that doesn’t work may keep you busy and distract you from the problem you are stuck on, but will only lead to refactoring from the beginning again after you get it working.
A visual representation of a brain, stuck in stickiness
Never forget: your brain is ultimately what you are bringing to this party of tricky problem solving. The mind has a lovely way of compounding your stickiness, yet treating it well can be the key to becoming unstuck: be aware of the mental aspects of being stuck so that you can get have a breakthrough as soon as possible.
(i) Don’t spend too long being stuck on a problem. When learning how to code, particularly at the start of your journey, you should never be stuck on one specific problem for longer than 30 minutes. Your problem solving process should include escalations where at that 30 minute point you have escalated and hit a nuclear option to unstick you. For example: phone a friend, talk to a mentor, or if there is one: looking at the solution and learning from it. This is not admitting defeat, this is having a sensible, tiered problem solving process with appropriate escalation points, rather than trying the approach you have stuck in your head over and over.
“Insanity: doing the same thing over and over again and expecting different results.”
Unknown
(ii) Ensure to take breaks: give your brain the space for epiphanies
A natural mental trap of getting stuck is to slog away at your code, so frustrated by a bug that tearing yourself away from it seems like it would be intensely painful. Taking a break can feel like admitting defeat, giving up on your problem.
However, this natural impulse to strive for victory without breaks doesn’t take account of how the brain actually works. The way the human brain achieves epiphanies, is by learning about something, and then getting space from the direct problem to process this learning. Why do you think people have fantastic ideas and breakthroughs at the weirdest time: at the gym, in the shower, on a walk?
Space from a particularly sticky problem is probably exactly what you need, but the last thing we want to let happen. Believe in the science: take a real, cognitive break and allow your subconscious to solve your problems for you, like a real problem-solving pro.
(iii) Relish progress and celebrate victory
As you strive towards unstuck nirvana, relish the mini-successes on the road. Even changing the message of your error is something to stop and congratulate yourself for. By using the above tools and techniques, you should feel empowered to unstick yourself from tricky, sticky coding problems. When you finally get unstuck, make sure to mentally reward yourself and take a break to celebrate; you earned it.
If you enjoyed this blog post and would like to read more like it, please click ‘Follow’ and give me a few Claps by clicking the Medium claps button below 👏