Posted On: 2020-08-03
When developing software, one constantly has to decide whether to continue with the current approach, or to change to a new one. Knowing which to do is incredibly important for being an effective developer, but, unfortunately, it's not typically taught in academia. Instead, developers often have to pick it up on the job - either by figuring it out on their own, or (as was my case) with the assistance of a mentor. In light of this, I thought I should write a bit about my own journey of figuring this out (including some tactics I use now) - in the hopes that it may serve as a helpful guide for others on a similar journey.
When I first started professionally programming, solutions did not come easily. I was a beginner in the languages used in the office*, and otherwise simple solutions often took hours of research to find the language constructs and built-in functions that I could use to achieve my goals**. I approached these early problems with tenacity and focus: I would pick one approach, and tirelessly work towards trying to implement it, fully expecting numerous hurdles (many of which were merely products of my own inexperience.)
During this early stage of my office work, my manager served as a mentor and guide: she gave me tasks that would help build my skills and provided constructive feedback and advice along the way. Her wisdom, guidance, and support helped shape me into the developer I am today* and of all the lessons she taught me, none was more important than the simple question she asked whenever I was stuck on a problem: "what else have you tried?"
Time and again, she would ask that question, and each time I would admit that I hadn't tried anything else. Sometimes she would provide suggestions, other times she'd leave me to figure it out myself, but, over time, I came to expect the question. Although it didn't always solve all my problems (sometimes it really did need tenacity and effort), I nonetheless could see how this simple question could guide me to solutions that took mere minutes, rather than the hours I spent struggling through my first approach.
Eventually, I came prepared for the question: whenever I'd ask for help, I could answer the question with a list of approaches that I'd tried - all to no avail. Together, my mentor and I would work through why each approach failed, and we'd often find some common assumption that underpinned them all. More often than not, she'd be the one to spot it - and ask me if I'd tried anything besides the approach that underlying assumption was built on. In so doing, she taught me not just to try multiple approaches, but to make those alternatives meaningful - by analyzing the underlying biases that guided me to one in the first place.
Having internalized the mentality to try alternatives often and early, I now use some tactics that help me to make the most of any time an alternative might be better than my current approach. Of these tactics, the first is primarily a mindset. I have generally become pretty intolerant of getting stuck: if I am working on a problem and I can't see how to solve it, I have developed the habit of questioning the approach rather than trying to get myself unstuck. Instead of seeing a solution fail and saying "I don't know what's wrong", I now say "I must be missing something".
Just as important as the mindset, however, is how to go about resolving the belief that my approach is at fault. By closely examining the existing approach*, I can not only detect previously unknown assumptions baked into the code, but also potentially any implementation bugs as well. Thus, regardless of whether it's an incorrect approach or just an implementation bug, the same methodology can be applied to resolve it.
The second tactic that I use is to make heavy use of my debugger's immediate window (or console). While not all languages/frameworks support such immediate programming*, whenever I can, I try to find minimal tests that I can use to validate a particular approach before beginning actual development work. By drilling down into the deepest underlying assumption, and then trying to minimally test that (using one or two commands), I can quickly rule out fundamentally incorrect conceptions of a problem. So long as those minimal tests have no side effects, I can test multiple different approaches in a row, without the overhead/distraction of waiting for a recompile or repeating any necessary setup.
The third tactic is being rigorous about the cleanliness of source control. At any moment, I should be able to restore a working copy from source with zero (or near-zero) local modifications required to operate correctly. Additionally, while working, I minimize the amount of unrelated work that is simultaneously in progress: by assuming that the current approach will (at some point) need to be scrapped, I keep the local workspace clean and focused. Maintaining this level of cleanliness enables me to quickly shelve (aka. stash) changes and get right into trying an alternative with minimal downtime.
The fourth tactic is something of a culmination of all the others. By continuously believing that the error is in my ideas, not my code, I often switch between multiple possible implementations, putting one down whenever it becomes stuck and picking up whichever one I expect is the most likely to succeed at that time. Often, this leads to many short-lived solutions* - code abandoned after its first failure, that ultimately is replaced by some other, more appropriate, design.
Occasionally, however, none of the approaches work smoothly, and I find myself bouncing back and forth between those different solutions. Even more rarely, more than one will be developed to the point of being functionally complete, leaving me (or my team) with the choice of which one to actually commit. While this may look like a waste of time, the gains from stopping other failed attempts early far outweigh the cost of these occasional fully parallel implementations. What's more, from a personal growth standpoint, the experience of pushing wildly different implementations to completion helps build a wider skill range, allowing for faster future development across a wide variety of approaches.
As you can see, over my programming career I have gone from using just one approach to trying a wide variety of approaches - often close to immediately. Although my current tools (ie. Unity) make some of these approaches less effective than they used to be, I continue following that core idea of exploring alternatives often and early. Hopefully the story of my journey has been useful for you, wherever you are in yours.