Defining A Good Software Developer
- Introduction
- Software development as house building
- Having a product mindset
- Solving problems, not writing code
- Making good software
- Working with other humans
- Pitfalls
Introduction
“You did not learn to ride a bike by reading about it. You learnt by jumping on a bike and falling a lot. Over time you figured out how to stay upright and steer. In the same way, you can only truely learn to become a better programmer by writing a lot of code. In the beginining that code will be bad. You will make a lot of mistakes. But then you will learn from all those efforts. Books are just a collection of the hard lessons authors have learnt over thier careers as working engineers.”
– Senior Engineer @ Shopify
Software development as house building
A house is meant to solve a real problem for someone. It provides shelter from the elements, storage for belongings and supplies, and grows with the changing needs of the occupants.
Houses are more than a combination of a roof and four walls. There are other concerns such as electricity, plumbing, heating, insulation, aesthetics, and more. Every decision made when building the house, from the design to the materials chosen, comes with tradeoffs.
Any contractor worth their invoice can build a house. What separates a good contractor from a bad one, is the considerations and care taken in the craft of house building.
Imagine two beautiful homes built by two different contractors. House A is built by Jane, an experienced contractor who’s learned some important lessons throughout her career. House B is built by Joe, a contractor who does not yet have the same level of experience as Jane.
At first glance, both houses look amazing. But when the occupants of Joe’s house move in, they find that some details have been missed and some cupboards are misaligned - okay not so bad they think.
After a few weeks, they find that the plumbing stops working at random times of the day. Kind of annoying. After a few months, they decide to install a new hot water heater. But the schematics that Joe left them are difficult to make sense of. The owner decides to ignore the schematics and look at the pipes directly to trace how they’ve been put together. But the implementation is such a black box that Joe needs to be brought back to do the installation himself.
After a few years, the family of Joe’s house have had new family members move in - their needs have changed. They would like to renovate part of the house to accommodate their new situation. Having learned their lesson, they decide to hire Jane to oversee the renovations and expansion. She tells them that, unfortunately, the structural layout of the house and the placement of key electrical and heating components are so fixed that making the changes will require an entire rebuild of the house.
Even though both Jane and Joe accomplished the fundamental goal of a contractor (building a house that meets the needs of the occupants), Joe’s house was not:
- Flexible (easy to change)
- Reliable
- Maintainable
- Understandable (easy to reason about)
- Scalable with growing the needs of the occupants
Having a product mindset
The first and most important goal of a good software developer is to remember that the business value of their role is to provide solutions to business problems. This is where both Jane and Joe accomplished their main goals.
Does their work contribute to:
- Reducing the time and financial cost of operations
- Increasing product quality
- Increasing the customer base
- Increasing the number of products to sell
- Innovating and creating new product categories to sell
This is in opposition to seeing software development value solely as:
- Implementing the most cutting-edge technology
- Constantly shipping product features (many features do not make a good product)
- Accumulating technical expertise (this will naturally happen over time)
Solving problems, not writing code
Although it may sound surprising, the primary focus of a software developers job is not writing code but rather creating value through the use of software that was written. Code is simply a tool to achieve this end goal. Code -> Software -> Value.
What you write needs to fill some need in the world — some tool that users will use, some automation that reduces costs, something people will pay for (with their time, money, or attention). We can simplify it. If you build something with shitty technologies that provides great value to the users — you’ve served your purpose as a software developer. If you’ve built something with great technology that offers shitty value to the user — you didn’t.
Elegant code, best practices, smart solutions, design patterns — these are done for the sake of your fellow software engineers who will work on the codebase after you rather than helping you fulfill the purpose of bringing value. (Mind you, bringing value can also mean building a scalable solution that doesn’t crash, which requires the code to be at least somewhat decent.)
Making good software
Making good software is an art versus a science. It’s learned over time and with experience. Many of the lessons learned will be through painful and expensive mistakes, and that is okay.
Security
One of the fastest ways to lose customer confidence is through a security breach. People trust companies with their private data and engineers owe it to them to do everything possible to learn and optimize for security. Rely on the simple and battle-tested solutions over the fancy and brittle.
Flexibility (easy to change)
The only constant in software is that things will change. Business requirements will change, the team with learn more about the domain, new features will need to be added, etc.
All the considerations of patterns and best practices are about making software easy to change. Software systems that are tightly coupled, not well tested, complex, and hard to understand are painful to change. The goal is not to optimize for the computer but to optimize for the humans who will make changes to the software.
Simplicity
Build simple solutions. Avoid clever tricks, “elegant” one-liners, or using the newest trendy tools. Simple solutions, with no more moving pieces that are needed, are much easier to maintain by a wider variety of people and skill sets.
Sometimes software developers can even achieve the primary goal of finding solutions to business problems without writing any code at all.
Understandability (easy to reason about)
Don’t build black boxes.
When an event happens in the system, team members should be able to logically piece together what happened and communicate exactly why it happened. Anyone looking at the architecture diagram or logic flow should be able to make sense of how it’s put together.
Black boxes can look like convoluted methods and functions, confusing components, poor documentation, and overly complex system architectures. Just like the simplicity principle, strive to make solutions easy to hold in your brain.
Understandability can be improved with a mindset of building for the next person to maintain. When building, imagine yourself having to dive into the software a year later to fix a time-sensitive bug.
Reliability
Reliability is all about how often your system breaks, how easily your system can break, how your system handles itself when things go wrong, minimizing data loss and business interruptions when systems break, and the practices around getting the system back online and building back better.
Maintainability
“If something blows up, we need to have made it possible and easy for the correct state of the system to be recoverable, either by the system itself or by those operating the system”
– Senior Engineer @ Shopify
Good software should strive to be easy to maintain. Writing easy-to-read code, building simple solutions, avoiding “clever tricks”, keeping updated documentation, good git commit practices, and writing good tests should help with making a system easy to maintain.
Scalability
Scalable software grows and shrinks to meet the demands of the business. But be careful with premature and untargeted optimizations. These are the root of all evil.
Working with other humans
Attitude
Be kind. Be humble. Be curious. Ask LOTS of questions. Fight against FOLD (Fear Of Looking Dumb). Treat others as you would want to be treated.
Communication
⚠️ Work in progress
Productivity
Focus on one needle-moving task per day.
Additional resources
Pitfalls
Trying to memorize everything, learn everything, then burning out
The value of a software developer is not measured by how much technical knowledge a person has accumulated. Technical knowledge comes, goes, and accumulates as a person navigates a career and the different projects they are exposed to.
Software developers are professional problem solvers that use computational tools and engineering craft to do needle-moving amounts of “work” for other people. This is done via a systematic way of identifying, breaking down, and clarifying problems to their smallest components. Then researching potential solutions to the problems, while exploring trade-offs. And then (the final 20% of the time) implementing the solutions.
Pick a core set of tools, and master them. When researching other solutions, it’s important to timebox the process to keep from rabbit trailing and going deeper into the technology than the problem warrants.
Instead of trying to learn everything at once, focus on knowing enough to solve the problems in front of you. Trust that when you need to solve a problem, you will be well-equipped to learn what’s necessary to solve it (Just In Time learning).
Follow your curiosity and spend extra time outside of work projects learning new things. Focus on understanding the “shape” of the thing. Some questions to ask while exploring a new technology or topic:
- What is it?
- How does it work under the hood?
- What is the historical context around this thing?
- What problem is it meant to solve?
- What are the alternative ways of solving the same problem?
- What are its trade-offs?
- Can I build a prototype with this thing?
Chasing the shiny, new, and popular
Boring is good. A lot of the new and shiny technologies in the industry have been built to solve extremely niche problems for large tech companies. for 80% of use cases, the old battle-tested solutions are perfectly fine.
Many of today’s $10M+ businesses are sitting on “boring” tech stacks:
- Ruby (est. 1996)
- Monolithic Rails (est. 2004)
- Postgresql (est. 1986)
- Redis (est. 2009)
- Sidekiq (est. 2012)
- Heroku (est. 2009)
Instead of chasing tutorials for the newest framework, tool, or technology, focus on mastering the foundational things that have stood the test of time.
But don’t completely ignore the new and shiny. As with most of life, moderation is key and it’s nice to explore out of curiosity.
Not thinking in terms of tradeoffs
Every decision has tradeoffs - nothing comes for free. A good practice is writing RFCs for major decisions to help process the pros and cons of a decision.
Blindly following “best practice” (without understanding the why)
Understand the “why” behind the best practice. This will help with gauging when it’s appropriate to break the best practice. For example:
- Blindly following DRY may result in picking the wrong abstraction too early
- Blindly following YAGNI may keep you from building your software in a way that anticipates coming changes
Rushing to ship
Rushing to ship can mean putting out software that does not meet the above considerations of “good quality”.
Whatever time and money is saved in today’s shortcuts will be small in comparison to the time and money lost in tomorrow’s bugs, customer frustrations, and painful feature additions.