Frustrations of Programming & How to Avoid Them
I've been coaching junior programmers for a few years and none of them picked up the craft effortlessly. In fact, they often hit against the same obstacles. So here's my attempt at organizing those obstacles and offering high-level solutions.
Hopefully, the ideas below can help you avoid frustration in general and make your learning curve smoother.
Note that the problems outlined below are felt hardest by junior programmers but definitely apply to all.
By far, the greatest evil is getting stuck. Perhaps you're trying to
npm install a package and you get an error saying
Node.js/Windows error: ENOENT. That's not helpful (but this Node.js Best Practices guide might help). Or perhaps you're trying to add a route to your Angular app and the app becomes a blank screen with no error message. What do you do then? The way forward is not clear.
Examples like this are a dime a dozen and will stump every programmer—not just juniors. We shouldn't be too hard on ourselves: some days, being stuck is the normal state and we should be grateful for any sliver of progress.
If you are so inclined, you can develop your empathy and a sense of kinship by going through some of the most common issues on Stack Overflow for npm, Angular or your favorite technology (make sure to sort the results by the number of votes).
To get unstuck, the easiest way out is to simply solve the problem so you can move on—but that's easier said than done. See the next section on troubleshooting for more on how to get out of the hole.
Another way to get unstuck is simply to restore a sense of sanity by using the obstacle as an opportunity to learn. Instead of banging your head against the problem and trying to find a fix ASAP, it's useful to sit back and read around the problem, familiarize yourself with the tech stack, and make sure that your understanding of the problem improves even as the software refuses to work. The more you learn from each obstacle, the less stuck you get in the long run.
The Lone Programmer
Another evil is the solitary practice of programming. When you're getting started with programming, you're not forced to join a team. More often than not, you will be alone going through some tutorial or stuck on a problem. Moreover, programmers have been known to fetishize "uninterrupted blocks of time" and "the experience of flow". This can make it uncomfortable to interrupt your colleagues and ask for direction.
Programmers can be solitary creatures.
Programming isn't a team sport but it is clearly a social activity. One obvious way in which programming is social is in how we come to consider code good. A piece of code isn't good because someone likes it. It is good if it is considered good by the team. Good code is made good by the team's consensus.
Rules for what makes code good are constantly being discussed. You have probably heard of the DRY principle, or the SOLID principles of object-oriented programming, or YAGNI (You Ain't Gonna Need), KISS (Keep It Simple, Stupid), SRP (Single Responsibility Principle), SLA (Single Layer of Abstraction), etc… These principles come about as programmers maintain each other's code and learn about what's maintainable and what's not. This dialectic makes everyone a better coder.
Programming is also social in the sense that the practice of it can be learned by doing. And learning by doing works best when there are other colleagues around to bounce ideas and offer feedback. Juniors learn best when they have a learning buddy, or when they collaborate online (hello Github!). It also helps to find a team where long bouts of pairing are encouraged. (Pairing refers to two colleagues working together at the same computer.) Pairing can be exhausting and if you lack confidence, you might find it nerve-racking—but it is one of the best ways I know to learn the ropes.
You can also help your memory through repetition. A good trick is to write about the things that you find difficult. Keep a tumblr going with short posts about your discoveries. You can share it with friends and colleagues and ask for feedback. An extra benefit of this strategy is that you will have something to look back on as you become a better developer. Sometimes it's good to realize how far we've come.
You can also achieve repetition by getting into the habit of thinking out loud. Get a colleague to pair with you (or breaking the glass in case of emergency, a rubber duck) and talk it out as you walk through a tutorial or as you refactor a tough piece of code.
Another stumbling block is impostor syndrome. This occurs when developers—juniors and seniors—cannot recognize their own achievements and are constantly afraid of being found out as a fraud. This syndrome has been discussed a lot so let's just list out a few recommendations for getting rid of it.
An easy way to ward off the anxiety is to explicitly highlight your successes. This can be done by keeping a blog and reading past entries once in a while. Most juniors are quite impressed by how much progress they've actually made when confronted with their past selves.
Another way to highlight successes is to make sure you discuss positive happenings during your retrospective. It's easy for retrospectives to turn into a meeting where the team lists a bunch of issues and promises to address them. Don't fall into that trap and dedicate some time to highlighting the positive steps that were taken so you can reproduce them in the future (and keep morale high).
Perhaps you're not feeling confident in using the technical language or acronyms everyone else seems to be spewing out effortlessly. In this case, I believe a good solution is to talk about code in low-stakes situations. In practice, that means discussing code with colleagues over lunch, going to user group meetings, and listening to podcasts (e.g. Software Engineering Daily). You might not understand everything in those conversations but over time, your awareness will build up and your contributions and questions will become more precise. Also, feel free to ask your colleagues or manager if you can sit in on meetings you're not usually invited to. You might think this is not a productive use of your time but it's an excellent way to acquire context (not to mention visibility for yourself). In my experience, your manager will praise your enthusiasm and initiative and completely disregard the "lost" time.
Coding Is Troubleshooting
There's no two ways about it: programming involves a lot of troubleshooting. It's not unusual for coding to proceed in a stop-and-go fashion: you proceed smoothly for a while and then hit a roadblock and get stuck for an hour. You clear the roadblock and continue smoothly for a few minutes just to hit another problem.
No programmer, no matter how senior, can write more than a few lines of code, or set up a cloud instance, or integrate an API, without having to troubleshoot an issue along the way. That's just the way it is: programming often feels like running really fast towards a wall.
Most problems can be fixed by consulting your colleagues or the internet. Or by futzing around with the code. But sometimes it's hard to see how to get past the issue. Error messages can be cryptic or non-existent and an Internet search might reveal nothing. Stack Overflow comes up empty! This is the dark night of the soul.
Here are a few ways to get out of the hole:
Test your assumptions. What's not working? Why did you expect it to work? Can you verify assumptions somewhere between your code and the problem? For example, if an object isn't being created properly after a form is submitted, check if the form is sending the right value and track that value on its progress towards your object (through the frontend component or the HTTP request or the API controller or the white list of parameters or the database). Somewhere along the way, your expectation won't match reality: find that mismatch.
Read documentation. This is not the most glorious solution but it will give you more context and it will allow you to learn more. If you take the long view, you're not wasting your time reading the docs. So just take a break and learn something new about your framework or your language. Documentation is also useful as a way to offload things you would otherwise have to remember.
Rewrite the code in a different way and pay attention to the way your app behaves. Often, you will simply get a better error message—and that's half the battle.
Use test-driven development. Test-driven development is all about building software in small increments while testing your assumptions along the way. It's a great way to never get stuck in the first place.
We've all been in this situation where we are faced with a problem that we think we can solve quickly. We then go into Google-mode and we search the web for the error message we're getting. We dive into a lot of blog posts and Stack Overflow answers, trying every recommended answer one after the other.
Sometimes that works well. But if we're not careful, we can spend an hour doing this and realize that we've gotten absolutely nowhere.
I call this breadth-first troubleshooting: trying different strategies one after the other in the hope that one of them will work out.
There are two problems with breadth-first troubleshooting:
- It's only appropriate to certain kinds of problems. Perhaps if you have a syntax error, you can play around with parentheses until the problem goes away. But in other situations, the fix is not that simple. If your database model is not appropriate for the new requirements or if your polling mechanism is stretched too thin, breadth-first troubleshooting won't help.
- It doesn't lead to deep learning. It's the equivalent of shaking the puzzle pieces in the hope that the right pieces will stick together.
A better way to troubleshoot is by going depth-first. Instead of jumping from one alternative solution to the other, we can stick with the problem at hand and ask:
- What is going wrong?
- Why did I expect it to work?
- Which one of my assumptions is false?
These questions are high level and will lead you to think deeply about the software you are writing. In the process, you will have to get comfortable with unit testing, debuggers, browser tools, and command line utilities. In short, you will learn.
Depth-first troubleshooting can feel time-consuming but in the long run, it's a major time saver.
My first piece of advice would be to dismiss unsubstantiated claims and to trust running code above all. It can also be useful to separate out opinions in your mind: is a certain claim an engineering claim? Is it a social claim? Is it a claim local to a particular team or project? Also, when communicating yourself, be clear, guard your statements, and back your claims with evidence.
On a more human level, you might find that your team or your community or your subreddit of choice does not empathize with newbies. You might have colleagues who don't reach out to you and make you feel bad when you don't understand all the references to Office Space, Star Wars, or the latest meme. The somber side of this problem yields sexism, agism, or any kind of discrimination.
If that's your situation, know that you are not alone. Value diverse teams, don't keep the discrimination you're experiencing to yourself and reach out to newcomers. You can also learn about initiatives that make tech more inclusive like the Code Newbie podcast, RailsGirls (this article can also help motivate women in tech), or Open Tech School. And don't neglect to search for local user group meetups.
In general, frustration is caused by a feeling of being stuck. The frustration can be alleviated by applying a fix so you can move on, but it can also be alleviated by restoring a sense of moving forward, a sense of constant learning. Stalling certainly does not lead to learning.
If you have any other advice on how to avoid frustration, feel free to chime in below on the comments section.