My software engineering journey is a pretty common one:
- Learn to code as a kid.
- Study STEM subjects through school.
- Get a degree.
- Become a full-time Software Engineer through some grad scheme or internship.
It’s been 3 years since I started my first job. In that time, I’ve worked in 4 teams across 2 jobs where I’ve been surrounded by plenty of excellent people who have helped shape my views and opinions.
I’m writing this post to distill some of the wisdom I’ve picked up along the way.
Strong opinions, lightly held
As a software engineer, there are always a million ways to solve the same problem. If you ask n
engineers to “design a scalable architecture that allows users to order food from a restaurant online”, I bet you’ll get n
very different answers:
- Someone will want to write it in PHP (pls no).
- Someone will propose a fully serverless cloud architecture with 6 lambdas, 3 API gateways, 5 S3 buckets and a WAF for good measure.
- Someone will want to deploy microservices on Kubernetes.
- Someone will want to run a Java monolith on their beefy home PC.
An important skill in software engineering, and many other disciplines, is the ability to form a consensus around different ideas. Forming a consensus is important because it allows the team to move in a single direction. Forming it quickly is equally important because it enables a team to truly accelerate their delivery.
‘Strong opinions, lightly held’ is a common phrase that you’ve probably heard before, but I think it sums this up nicely.
Let’s break it down 👇
Why hold strong opinions?
Everyone has a different way to solve the same problem. To demonstrate why strong opinions are important, let’s imagine a scenario where two engineers are solving a problem together. Alice is a junior engineer and holds very strong opinions about the solution architecture. Bob is a mid-level engineer but hasn’t really given it enough thought to form an opinion.
How does this play out?
Alice: “We can keep track of user preferences by building a serverless architecture using 6 AWS lambdas and an API gateway.”
Bob: “Yeah, sounds good, I think that will be really scalable and lambdas are usually quick to set up.”
Consensus reached, we can start development. Great! Or is it?
Let’s introduce Charlie. He’s a senior engineer and really cares about his work.
Charlie: “I like the idea of a serverless architecture, but I think 6 lambdas introduce far too much complexity. I try to follow KISS (Keep It Simple, Stupid). Can’t we get away with a single lambda here?”
Alice: “Actually, yes, if we make proper use of HTTP verbs we might be able to get it down to one or two lambdas.”
Charlie: “Perfect! I think that will make it much more maintainable.”
Bob: “Sounds good 😊”
Now, let’s compare the interactions.
In the first interaction, a consensus was reached quickly but Bob had little input and the solution they were aiming for was vastly more complex than it needed to be.
After introducing Charlie, who holds strong opinions on KISS, the solution was vastly simplified. This will be great for speed of delivery as well as ease of operation, but also holds a significant benefit to Alice. She realised she made a mistake, and figured out how to fix it. In other words, she learned from Charlie. This carries far greater benefits to her development than producing her own sub-par solution would have.
Bob didn’t really hold any strong opinions here – he just went along with the crowd. He may have learned something from observation of Alice and Charlie’s interaction if he was paying attention.
What’s the point in a strong opinion if it’s only lightly held?
That covered why we should hold strong opinions, but that’s only half of it. Why do they need to be lightly held? Shouldn’t strong opinions be strongly held?
This is where consensus comes in. Reaching a consensus is as important as finding a good solution in the first place. If a decision can’t be reached, nothing will happen. Then what’s the point in your solution?
In the previous interaction, Alice demonstrated an opinion that was lightly held. When Charlie suggested an improvement, she saw the merit of it and adapted her idea. This let them reach a better solution in record time.
Let’s replay the interaction from Charlie’s suggestion, but replace Alice with Chad. Chad is a mid-level engineer who strongly holds his strong opinions.
Charlie: “I like the idea of a serverless architecture, but I think 6 lambdas introduce far too much complexity. I try to follow KISS (Keep It Simple, Stupid). Can’t we get away with a single lambda here?”
Chad: “But 6 lambdas let us keep the code and infrastructure separate.”
Charlie: “The code is related and keeping it separate would mean we have to create libraries or introduce a lot of duplication.”
Chad: “Then let’s create libraries so we can do it without duplication.”
This interaction could go back-and-forth for a while as Chad ripostes Charlie’s suggestions to defend his opinion, but you get the point. Chad stuck to his opinion and missed the point of Charlie’s proposal. By the end, we actually end up with a more complex solution as the discussion moves towards creating unnecessary libraries!
I hope these examples demonstrate why holding strong opinions lightly is important. It allows us to reach a good consensus quickly.
The best answer is not always the right answer
That segues nicely into my next point. This one is about accepting that you cannot be perfect, and acknowledging that aiming for perfection is a waste of your time.
Whoa! Hold up. Are you saying that I shouldn’t be striving to be the best software engineer there ever was? To produce perfect solutions that our customers love?
Not at all. Those are great goals. We should all be aiming to improve ourselves and produce great outcomes.
I’m simply asking you to reconsider your definition of ‘perfect solution’. To some, the perfect solution is the one that is infinitely scalable. Others might not consider it perfect until it has 10 pages of documentation. Some people want to rewrite it in their favourite language.
All of the examples above are contextual, but you get the point. You could go on forever defining what the perfect solution is, and continue developing until you reach it. The problem is that you are almost certainly paid a salary. If you’re paid a salary, a company somewhere is paying for it. To keep that little arrangement (job) going, the company needs to make money!
For me, the ‘perfect solution’ is the one that delivers the required outcome and nothing more. If your software needs to handle 100 concurrent users over the next 6 months, why bother building something now that can cater for 10,000? Design your solution in a way that makes it easily extensible, but does only what is asked of it right now. Be smart, but lazy. Then deliver it in record time, and improve it iteratively as-and-when the requirements change.
My view prioritises fast delivery and a good enough outcome over delayed delivery and a perfect outcome. There is always a cost to delayed delivery - if not loss of reputation or time-to-market then it is simply the cost of salary. You have to consider whether the extra value you squeeze out of your perfect solution outweighs the very real cost of the extra time you spend building it.
One small problem with this view is that you can easily fall into a trap. To save yourself a few weeks of development time, maybe you give the tech debt credit card a little swipe. This isn’t a problem on its own, but like any debt it will compound over time. To keep this under control, you need an effective process for managing your tech debt. That’s a big topic though, and worth saving for another time.
I hope you can see by now why the best answer is not always the right answer. By harnessing this idea, you can really bend scope to your will and accelerate your pace of delivery.
Ask forgiveness, not permission
This one was shared by a colleague in a somewhat sarcastic manner while working in a fairly restrictive environment: lots of red tape, mind-numbing process and resistance to change.
The quote actually comes from Admiral Grace Hopper, who was a US Naval officer and in 1952 developed A-0, the first compiler. You can read more about her on YaleNews.
What Hopper meant by this quote is not that you should steal your colleague’s cookies without asking and hope they forgive you. It goes deeper than that: excessive rules and red tape make it difficult for anyone to change anything. Change can often be for the better. If your intentions are in the best interest of the company, then skipping over some process or red tape to make a positive change can be worth it.
If you ask for permission first, the answer might be “no, it’s against the rules” or “no, we don’t do that here”. If you succeed first and beg forgiveness afterwards, what is there to forgive? You’ve already made your positive impact.
It’s about trusting yourself to do the right thing, and then doing it.
Of course, I’m not advocating being completely reckless or arrogant here. Most rules are there for a reason. Use your own judgement.
That’s all.
To summarise:
- Have strong opinions, but hold them lightly.
- Accept that the best answer is not always the right answer.
- Take action and ask for forgiveness, not permission.
I have a few more I wanted to write up but this post is already quite long. I’ll save those for another time.