8 things I learned in my first eight months as a Software Engineer
In this article, I detail the eight things I’ve learned in my first seven months as a software engineer. This list of lessons could have easily been much longer but I wrote about the things that popped up in my mind first. Some of the lessons are concrete details that can be applied to you work today while others are simply observations I’ve made. This list is in no particular order. Lastly, this writing will probably be most relevant to you if you work on a software product with a client-server architecture.
Pair programming is a great way to learn
A lot of this list is owed to the fact that I paired with, and learned from, more experienced engineers. Pair programming is when two engineers work together at the same work station. One engineer “drives” by writing code and the other engineer simultaneously reviews their code and thinks ahead. The practice of pair programming is a controversial topic from both a business and engineer’s perspective. Personally, I’ve found it to be invaluable to my growth as an engineer. I get to observe and question the choices and tradeoffs a more experienced engineers makes. This means I’m constantly introduced to new things and get to absorb some of the lessons of a more experienced engineer. On a personal level, motivation and enjoyment seem to be higher when pair programming with someone who you get along with. All that said, I think pair programming has the potential to be problematic at times. For example, sometimes I feel apprehensive while driving on a difficult problem and this can lead me to being too passive. Because of this, I like alternating between working as a pair and working solo.
Know what to abstract away
Coming into Wanelo, I was dealing with a massive codebase that had evolved for over four years. I felt intimidated and overwhelmed by its size. I’d watch my coworkers and they seemed to have a magical ability to hold it all in their head. They were able to look at a thicket of code and deftly manage their way through to a solution. Meanwhile, I would be left behind poking and prodding through the thicket stumbling through to a possible solution. Their familiarity with the codebase certainly had a lot to do with their adroitness but I had also observed a new hire senior iOS engineer seem to be able to hold a guiding light through the shadows. Over time, I noticed it was because they knew what to abstract away. In my opinion, this comes with experience. For example, when implementing a feature, I’d get caught up in the details of the surrounding code. I’d read an entire class over and then I’d look inside every method call to see what they did. Then I’d go further down the rabbit hole and check what those methods did. Soon I was caught up in unimportant details and further away from a solution. I hadn’t learned what to abstract away. I think there’s two ways to learn what to abstract away. One, just keep writing code and learn from experience. And two, observe how a more experienced engineer works.
Use a “binary search” to track down the source of your bugs
When tracking down the source of a bug in a client-server architecture, reduce your search space by effectively “half” by checking the payload first. For example, if you’re trying to see why a view is displaying only three images instead of four. Check the payload from your API response. If it’s returning only three images, then you know the problem originates from the server side. Otherwise, if the response returns the correct data, then it’s safe to assume the problem exists on the client. You’ve now performed the first step of a binary search! I learned this after pairing with a senior engineer. When working solo, I’d often start at what seemed the most proximal source of the bug. So in my three out of four images loading example, I’d breakpoint at the spot where the views themselves are being loaded with image data and then I’d work backward from there. Eventually, I’d find the source of the bug was all the way back in the API response. This was much less time effective. Once you determine which side of the client-server architecture the bug originates, see if you can perform another “binary search” step and half the search space again.
Know how to read a stack trace
A stack trace is a report of the method calls that the program executed before breaking. I never really learned how to read a stack trace in university. Sure I picked up on reading simple errors like triggering a segfault while iterating over an array means you probably iterated past the arrays bounds. But, beyond that, I typically looked at the stack trace as a pile of undecipherable gobbledygook. However, I noticed my colleagues would use the stack trace as an effective debugging method. They seemed to know how to look at the pile of mess spit out to console and magically interpret it. I wanted to learn how to do the same and found this YouTube lecture that did an excellent job of simplifying the process. The short of it is:
The stack trace reports a bunch of noise and you need to teach your brain how to ignore the noise.
You can “collapse” noise from frameworks your code uses by ignoring the method calls the framework made and only reading the method calls from your own code.
Ask yourself: Where did it break? Where did you call it? How did you get there? Why did it happen?
Come up with the simplest working solution first
When trying to tackle a weighty problem I’d try to solve the whole thing in one go. However, this was often ineffective because it’s hard to get from point ‘A’ to ‘E’ when you still have ‘B’, ‘C’, and ‘D’ to accomplish and a grand solution can be overkill. My coworker and mentor Alex Bush has illustrated an example of how to move from a simple working solution to a more sophisticated one. In his book, The iOS Interview Guide, he explains how one can build a data store by starting out with a simple associative array and then building up to a SQLite database. Often, the “good enough” solution is all that you need and going beyond that is unnecessary. You can always improve your solution in the future if need be. And doing so will be much easier because you’ll be starting from point ‘C’ instead of point ‘A’.
Too much mutable state can be problematic
Out of interest, I’d watched Robert C Martin’s lecture, “Functional Programming: The Failure of State”. In his talk, Bob talks about the benefit of using functional programming to avoid state. What is state? Put simply, state is the value of all the variables of your program at any given point in time. My interest in talks like these had always been more academic but I began to have a genuine appreciation for reducing state once I started programming in a complex environment.
For example, debugging becomes a pain if you have a set of nested ‘if’ statements that evaluate the state of several variables. You then have to know the state of every value at runtime in order to determine the flow of control. This becomes problematic quickly. Given a set of boolean values to evaluate, the number of possible states your program can be in grows exponentially (2^n). Now you’re in the business of trying to keep this in your head and making sure all those potential states execute correctly. Things become even more complicated when these variables can be mutated from outside the function scope. Now you’re in the business of tracking down where these variables are mutated.
I’ve also noticed that re-computing the state of an entire data structure is sometimes much more effective than maintaining a mutable data structure. For example, you might have an array that you only want full of objects whose public variable ‘a’ is ‘true’. You could iterate through the array and remove the objects and update their placement individually but doing so now opens you up to error if you improperly maintain the state of your array. Instead, I’d opt for creating an entirely new array on the fly and filling it only with the necessary objects. Of course, doing so is more algorithmically expensive because you’re having to allocate and populate an entirely new array but the benefits to programmer productivity far outweigh the costs. And you can always come back and optimize your solution if you notice performance problems.
What is the software deployment process?
I had no idea what a deployment process was or what it entailed. A whole slew of unfamiliar vocabulary was introduced. Words like “staging”, “production”, “continuous integration”, and “continuous deployment” were communicated. Describing the deployment process in detail would take a writing of its own so I’ll try to touch on it briefly.
Put simply, the deployment process is all the things you do to make your software product available to users. It can be as simple or complex as necessary. For example, the deployment process for a single engineer working on a hobby project might be as simple as them building their application on their own machine and saving the application binary to a folder accessible to users. However, as the software project becomes more complicated and more engineers work are added to the project, you’ll find such a simple process lacking. Being able to coordinate the work between different people will require multiple version control branches. Being able to rigorously test the software before releasing requires the use of a production and staging environment. Being able to automate the deployment process so that certain steps aren’t omitted on accident requires the creation of different build scripts. Being able to automatically run tests and log feedback requires a continuous integration setup.
As I said before, I won’t be able to cover the details of all these intermediary step. But, I think the key takeaway is that the software deployment process is simply all the steps taken to make the software available to users. And that process often grows to be quite complicated out of necessity as the software project involves in complexity.
It’s fun and challenging
Writing my own small projects was fun and challenging in its own way but I often lacked a real sense of excitement. Working with a team of intelligent people and implementing features on an app with thousands of monthly users is much more gratifying. If you’re someone who gets invigorated by intellectual challenge and growth, then software engineering is a great field.