One of the big questions I’ve been pondering lately is how to help junior engineers grow into senior engineers.
A full answer to that question would be an infinitely-long, constantly-changing post. Today I want to focus on a single foundational skill of any senior engineer: code quality.
Writing decent-quality code is not a sufficient skill to become a senior engineer, but it is necessary. Without that ability, an engineer may never get the opportunity to experiment and fail and learn in other areas like technical architecture or team leadership.
Earning a chance to fail is the real secret to professional growth. As they say, “Good judgment comes from experience. But experience comes from bad judgment.”
As soon as you start talking about code quality, you realize it’s largely in the eye of the beholder.
I might be thinking about the SOLID principles when reviewing your code. Or maybe I’m obsessed with the Gang of Four patterns. Or I recently finished one of Sandi Metz’s great books on OOP, and I’m focused on how you designed your objects. Or I watched a mind-bending Rich Hickey video, and I’m thinking about what functional programming approaches should have been used.
You put 5 principal engineers in a room, and you’ll get at least 6 opinions on what makes for good quality code. Yet I think there are some universal questions that high quality code must address.
First Draft Code
I recently read a good Farnham Street post on the differences between amateurs and professionals with this point:
Amateurs stop when they achieve something. Professionals understand that the initial achievement is just the beginning.
That resonates with my beliefs on code quality. If you’ve worked with me on a project in recent years, you’ve probably heard the phrase first draft code.
When I’m reviewing code, I ask two fundamental questions:
- Does this code achieve the business objectives?
- How will this code fail?
The first piece I’m trying to validate is simply whether code does what it’s supposed to.
For example, if our task is to write a file importer, my question could be something like this: Does the code successfully read the CSV file and save each row to the database?
I go into a code review assuming it meets that minimum bar of working correctly under expected conditions. Otherwise, you probably shouldn’t be submitting code for review, unless you’re asking for help, pairing, etc.
Okay, so the code can take a test file, read it, and import it. Yay, it works! And that’s where a lot of junior engineers stop.
That is what I call first draft code.
How will this code fail?
The next thing I look at is how the code will break.
This basic question is focusing aid that prompts a cascade of more valuable questions:
- What assumptions is the code making?
- How will I know it’s broken?
- How will I troubleshoot it when it does fail?
- What state will the system be left in?
- How much does it matter?
There are no always-correct answers to those questions. Like most engineering questions, the answer is it depends.
In our CSV import example, if one row is invalid, we may want processing to stop, or we may want processing to continue and just have error rows collected and logged. If we do want it to stop, do we need to wrap the database activity in a transaction and rollback? Will we know which row it failed on? How quickly does anyone need to know this process failed?
The specific answers matter less to me than knowing that the developer thought about them. And I expect a senior developer to have clear answers to those questions.
Once you start thinking about how your code will fail, it’s easy to go overboard. All code will fail under the wrong circumstances. You can’t guard against every error. You can’t log every detail. Your code would become excessively complex and have too much operational overhead.
When does it have enough guard clauses? Enough instrumentation? Enough test coverage?
Developing that sense of taste for when it’s enough is a lifelong pursuit in software engineering that depends on the project, its budget and operational needs.
The road to mastery goes ever on and on, but for today, if you’re a junior engineer looking to improve your code quality, this is my advice:
Crank out that first draft of the feature, get it working, ideally with some tests. Then go for a walk around the block. Come back to the code with fresh eyes and ask yourself one simple question.