Practicing as a Software Engineer
Continuing to grow as a software engineer can become increasingly hard as you become more experienced. When you first learn to code, there are an infinite number of new things to learn; every algorithm, framework, or concept is brand new, so there’s learning at every turn. Best of all, this happens organically. If you've never written code before, the mere act of typing into a computer and making it work is challenging and pushes you to grow. Over time though, that growth path starts to peter out: there are only so many language features you can learn or libraries to master before you start hitting diminishing returns. Past that point, the skills you need to learn become abstract questions like “how do I design better software” or “how do I lead a team”, and to grow those we need to think a little differently about how we build skills.
Why's it gotta be so hard?
One of the reasons it's easy to hit a plateau as a software engineer is much the educational material which exists is about accumulating knowledge. If you want to learn something, whether it's how to use MVP architecture, or how to leverage experience to become successful technical lead, you can probably find some content which will give you the knowledge. However, as Anders Erickson points out in his book Peak: Secrets From The New Science of Expertise, this accumulation of knowledge alone is not enough. Knowledge is great, but it's useless on its own; knowledge must be turned into skills for it to be valuable, and there's only one way to turn knowledge into skills: practice
.Practice is something software engineers rarely talk about. We frequently say things like "surround yourself with the best people" or "stay up to date by going to conferences" and assume that people will take those opportunities and just figure out how to turn them into new skills. Sometimes this works well and you get motivated people who immerse themselves in knowledge and learn how to apply it to their own lives. However, this approach can also result in people being confused and frustrated if they hit a wall and are unsure needs to change in order to improve.
Ideally, we can break this cycle by using “deliberate practice”, which Peak describes as “the gold standard of practice”. In deliberate practice, students work with experts in their field to master specific exercises which push them to improve against clearly defined criteria. If you're training a skill with clear measures of excellence, like ballet or musical performance, this type of practice simply cannot be beat.
Unfortunately, we can’t apply deliberate practice in its strictest form to software engineering; we don’t have the same objective measures of “goodness” that ballet dancers do, so we can’t design specific training programs to reach those measures. However, that doesn’t mean we should throw practice out the window. We can take the lessons from deliberate practice and apply them to what Erickson calls purposeful practice. Purposeful practice doesn’t have the same objective measures of success as deliberate practice, but it does have a few key elements which make it far better than doing the same thing repeatedly and hoping for new results. In particular, purposeful practice uses focused exercises which push the practitioner outside their comfort zone. These exercises incorporate feedback so that the practitioner can see where they are improving. This means that we can apply purposeful practice to pretty much any skill by doing the following:
- Decide on a thing you would like to learn
- Figure out an excercise which will force you to do that thing better by pushing your boundaries in some way.
- Get feedback on how you did on the exercise
- Figure out how to do the exercise better next time
Applying This to Software Engineers
Now that we have this model of practice, but how can we apply it to improving as software engineers? The first step would be to identify the skills which make great software engineers. While a full list is outside the scope of this post, it's safe to say that all great software engineers can:
- Write correct code with good velocity
- Effectively communicate orally with peers
- Design extensible and maintainable systems
Next we need to define exercises that incorporate feedback for how we’re doing. This is where things start to get tricky. How exactly can we tell if we’re writing “more correct code”? Do we shoot for 100% test coverage? Start using TDD and just assume that things are getting better? Measure the number of outages over the course of six months? None of these really feel like they can provide rapid feedback on whether we’re actually doing a better job at writing correct code. It's easy to see that and throw your hands up, saying "well, software is hard, we'll just do our best and keep muddling through", but there are some ways that we can push through that and design a purposeful practice regimen around these skills.
The first is what Peak calls a top gun approach to practicing, named after the famous flight school where students continually dogfight against top pilots. In the top gun approach, you don’t know the right way to practice ahead of time, so you just try to do the task you want to improve at. After you’re done, you go back and evaluate what worked and what didn’t.
Top gun pilots do this by reviewing videos of their dogfights, and software engineers can do the same with regular retrospectives where they discuss how to improve. These are most frequently applied to team processes, but it’s also possible to apply the same methodology to your own skills. For example, after shipping a large feature, you could ask “What parts of the system had few errors when we went live?” “What classes of errors did we encounter?” and “How will I prevent those errors next time?”. In this way, we can treat our past projects as exercises which help us improve in future projects.
What if we’re unhappy with this approach, and we want something more direction? Another option proposed by Erickson is that we watch expert performers perform a task and see how they accomplish the task. This helps us understand their mental representations so that we can emulate the same mental representations ourselves. With those representations in hand, we can design specific exercises which help build up each aspect of the desired mental representation.
For example, it may be hard for us to specifically train "how should we design better systems", but we may see that a great software engineer (perhaps one of our mentors) follows a three step process: they list list out all the major for the system, group the requirements them into a variety of different groupings, and list the tradeoffs between different groups. Once we understand this process, we can create exercises which help grow each of these skills and get feedback from the engineer on how well we did in each step.
What does it look like in practice?
So now we've talked about this idea of applying a practice-first mindset to growing our skills as a software engineer, but what does that look like in practice? Let’s take a couple of examples to see how we could apply our model of purposeful practice to improve in a variety of areas.
Technical Leadership
What's the class of skills to improve? Choosing the right solution for the current circumstances
What's a specific skill to improve? Generating Multiple approaches to solving a problem before settling on one.
What's an exercise? Before deciding on an implementation direction, write down all of the options along with their pros and cons.
What's the feedback mechanism? After generating a list of ideas, make a decision and show it to a senior member of the team (seeking feedback from an expert performer). Ask if they have any pros/cons that may have been missed, and which decision they would make. If different than the decision, discuss why they would make that and help get a clearer idea of what mental representations we should use in the future.
Becoming a Better Code Reviewer
What's the class of skills to improve? Code reviewing
What's a specific skill to improve? Taking time to evaluate the code review as a whole rather than just focusing on the individual pieces.
What's an exercise? Before clicking "submit" on a code review, take a moment to list alternative ways that we could approach this problem.
What's the feedback mechanism? Take a top gun approach and review the same code as someone else. See if you come up with similar to suggestions to them; if not, figure out how you can create similar suggestions to their sin the future
Becoming a better communicator
What's the class of skills to improve? Communication
What's a specific skill to improve? Succinctly communicate ideas to a variety of audiences.
What's an exercise? Mentally collect major bullet points before starting to talk.
What's the feedback mechanism? Take a top gun approach and ask a teammate for whether they thought you were too verbose or too brief in your statement. If so, try to and more or less content as appropriate in the future.
That just gives a few ways that we can apply this mindset of practice to growing skills as a software engineer. Almost all of them though follow a similar pattern: they’ve taken a complex skill and devised specific exercises to improve that skill, along with feedback mechanisms to see if we’re actually improving. After that, it’s a matter of doing the practice until you’re happy with the results. Of course, this doesn’t throw knowledge accumulation out the window. You still need knowledge to understand which skills to grow and how to build them. However, you want to be sure to combine any knowledge you gain with a practical practice path in order to really get the most out of what you learn.