In September of 1995, I was a newly minted developer. I had some formal education in computer science and a master’s degree in engineering, but no CS degree. Fortunately, I was surrounded by some very skilled colleagues. It was a positive and encouraging environment and since I was quickly able to make myself useful, I got a lot of mentoring. Over the next 12 months my experience grew to the point where I felt comfortable and confident programming professionally in C++.
As I gained expertise with the techniques and tools of programming, my focus started to change. The work became less about HOW to wrangle the machine into doing what I needed it to do, and more about WHAT to ask it to do. I started to take on larger tasks and propose new projects on my own initiative. I began to transcend my entry-level “coding” job by learning to design software. It was my first step towards working on software at a higher level; a process which continues to this day!
In this post, I will try and describe how I developed the software design skills that enabled me to level up from junior developer to senior developer, tech lead, and ultimately software entrepreneur and CTO @ AppMap. Hopefully, you’ll find some advice in this post that will help you to do that too, because software design is difficult. It’s more difficult than coding, because mistakes in design are much harder to rectify than low-level coding problems. Helping everyone to make better software design has become my mission. (At the bottom of this article, you can read a bit about AppMaps, a tool that I’ve developed to help developers produce great software designs).
Here’s a quick overview of the rest of this article:
- Master the fundamentals - Only once the tactical aspects of coding become automatic can you start to think strategically.
- Understand why design matters - Gain a visceral understanding of the positive and negative impacts of design.
- Develop and refine your personal process - Learn to unconsciously and optimally allocate your time between design, implementation, and testing.
- Always be shipping - A given design is only optimal for a single set of circumstances; and circumstances are always changing. So ship code and always move forward.
Now, let’s get started.
Master the fundamentals
Like an artist, a software developer must begin by mastering a set of fundamental skills. Fundamentals are critical because we can’t effectively focus on design until the mechanics of coding feel automatic. Like an artist, you need the mechanics of your craft to be second nature so that your mind is free for higher level creativity.
I had a key strategy for developing mastery of fundamentals: Never let anything be a mystery. Have you ever heard someone say that a bug occurs “randomly”? Or start to change code at random in hopes of resolving a problem? No. A computer is a deterministic machine. Higher level code is compiled to byte code. Byte code is executed as machine code. Messages are sent over the network and can be inspected. The bug you are investigating is somewhere in your code (or a dependency), and you can find it. Dig in, break down the problem, come at it from different angles, ask for suggestions from more senior folks, until the computer yields it secrets.
Understand why design matters
Why does good design matter? Creating design costs money, so good design needs to be justified. It’s important to develop a strong understanding of this question which is personal and experiential.
At AppMap, we are currently conducting a survey of software architecture quality. In the survey, we ask senior developers “Which is the most challenging trade-off that your organization faces today?” (compared with “delivering faster”). Here are the answers, along with the fraction of experts who selected each answer:
- Overall quality [42%]
- Maintainability [33%]
- Performance [13%]
- Security [8%]
- Reliability (bugs) [4%]
These senior folks know that when a code base loses its inherent quality and maintainability, all other considerations (features, usability, stability, performance, security, etc) are moot. If the code base isn’t maintainable and has fundamentally low quality, then it becomes impossible to move the codebase forward in any way. And when a code base can’t be improved or advanced, that’s real bad news for the dev team and for everyone else. Quality and maintainability are driven by good software design. Design matters; ask Citibank.
Develop and refine your personal process
Once you internalize the importance of good software design, it’s important to develop personal work habits that align with your values. In this way, you’ll be more likely to product the results you want. Here’s how those habits developed for me.
When I was about two years into my career, my company hired an expert in Personal Software Process (PSP). This expert (hi Steve!) taught use to evaluate our own effectiveness by carefully tracking the time we spent in three phases: design, implementation, and testing. In PSP, these phases occur sequentially, and once you advance to the next phase you can’t go back. So, if you find a bug during testing that was introduced in design (say, due to an incorrect assumption about the problem) then all the time required to rework that design and fix the bug is counted as testing time. The lessons become clear pretty fast:
- The earlier in the process that a bug is introduced, the longer it takes to fix.
- If you spend inadequate time in a phase in order to “rush” the work and deliver quicker, you’ll almost always spend twice as much time getting the solution right as you would have if you were more deliberate.
Initially, I spent about 15% of my time in design. As a result, I was forced to spend a lot of extra time in implementation and testing in order to fix my design errors. So I increased my commitment to design and increased my allocation of “design” time to 30-40%. The result? I finished projects in half the time, AND with better design (and less frustration).
By tracking my time and honestly evaluating the data, I developed a “sixth sense” about knowing when my design was ready to start coding. I believe that anyone can develop this intuition, if they take the time to study themselves and the ways that design, implementation, and testing interact and affect both speed and quality. Pilots say, “Slow is smooth, and smooth is fast.” Rushing leads to mistakes, and fixing a mistake takes a lot longer than doing it right the first time (and that’s assuming you aren’t a smoking hole in the ground).
Always be shipping
There’s a shocking truth about software design that may not be evident at first: perfection is unattainable! And it’s not because we aren’t smart enough to create a perfect design, or that your boss never gives you enough time. What I mean is that design can never be perfect because requirements are always changing. You’re coding a product to a market. The customer needs are changing as the world is changing. Tech stacks are changing, cloud tech is advancing, open source libraries are being written that solve a particular problem better than you code does. As soon as you create the perfect solution, it’s made imperfect by changing conditions.
Don’t try and address this problem by making your design “future proof” with complicated extension hooks and customizability. Embrace it and understand that no design is final. Focus on making your design easy to understand and easy to refactor. The true hallmark of a great software design is simplicity.
Remember, unmaintainable code is a tech lead’s greatest fear. Rigid, inflexible design is the enemy of maintainability, no matter how “perfect” it may have been 1, 5, 10 years ago.
Those are four techniques that I used to improve my software design skills. I hope you find them helpful! Thank you for your time.
About AppMaps - the developer tool for creating great software designs
As you can see from this article, I’m passionate about software design. To help developers create great software designs, our team at AppMap has created AppMap for VSCode. AppMap is a tool which creates accurate software design diagrams, drawn automatically in your code editor. It’s free and open source for Ruby, Java, and Python.
State of Architecture Quality Survey
To participate in the survey that I mentioned in this article, visit the State of Software Architecture Quality Survey.
Originally posted on Dev.to