How to Think Like a Software Engineer
When talking to people who are starting out in their journey to learn how to code, I sometimes get the question "How do you think like a software engineer?".
Depending on who's asking, the actual question being implied is "How do I learn to think like a software engineer?"
I don't think the answer is particularly unique to software engineering. Software engineering is just applied problem solving within the context of a specialized domain. This definition applies to any field whether that be designing, making music, plumbing, painting, carpentry, and even being a hair stylist.
In order to think like a software engineer, the prerequisite is to first become a problem solver. Being a problem solver requires having knowledge about the problem solving process and being able to apply that knowledge in the work you do. I'd like to break this article down into three parts:
- What kinds of problems do software engineers solve?
- Decomposing general problem solving ability
- How to become better at problem solving
What Kind of Problems do Software Engineers Solve?
Software engineers write code, and writing that code requires an understanding of the problem that needs to be solved with that code.
What kind of problems do software engineers solve? They fundamentally solve the need to move data around. The bulk of the work we do is moving data from one place to another. That could mean transforming the shape of the data from one to another, or relaying data from one system to a different entity, like an end-user or another system.
Take for example an application that shows the local weather on your phone. The purpose of the app is to present the underlying data of interest, the local weather relevant to your location, on your phone. Our phones can't possibly have that data, because aside from being a waste of resources to allocate storage space for weather data on a constrained device like a phone, the data we're interested in are predictions of the future and a phone can't possibly be equipped with reliable predictions of the future.
That means we have to fetch this data from a service that provides the data on-demand, in real time as the user views the weather app based on the current location that the device detects using its' GPS capabilities. The application has the job of fetching the information from a service that provides weather data and present it to the user.
In essence, this moving of digital information is what most code accomplishes.
But unlike a plumbers job where there's only so many ways to fix a leaking pipe or connect two pipes, there is a vast expanse of options when it comes to how to do something with code. This choice is more akin to a creative pursuit – there's plenty of bad and good ways to accomplish what is desired, and it's the job of an engineer to distill down the options and have an opinion on which is best.
The reason why this is complicated is not just because code can be written in a variety of ways, but there are often several competing demands that need to be balanced when choosing one of those ways.
For one, software engineers must keep the systems that are being mutated functioning in an expected manner, all while reducing or maintaining the current level of complexity to the best extent possible. This is often known as system health and it is vital to keep the state of the code maintainable.
Another demand is that of the user. Sometimes a particular user experience is absolutely necessary, but sometimes it's just a nice-to-have. To what extent do you invest to improving the user experience?
Then there are the demands of the business. Perhaps a bug needs to be fixed ASAP in order to stop the business from bleeding cash. Maybe a security bug popped up and the company's reputation is on the line. Or maybe it's something that is required due to compliance or regulatory demands.
A software engineer's work begins by disambiguating problem requirements. They lay out a technical specification that fits into an ecosystem of existing systems and components. They then propose solutions that uphold that specification, and evaluate the most appropriate solution based on the various demands from the system itself, the business, and the end-user. Lastly, they implement the best proposal. This process essentially repeats itself through the course of weeks or months, or even years depending on the size of the problem.
The general skill at the root of being able to perform this job is the ability to solve problems.
Decomposing General Problem Solving Ability
The way that most problems are solved breaks down into four modes of thinking before proceeding to implementation:
- Comprehension
- Ideation
- Estimation
- Evaluation
Comprehension
The thoroughness of one's understanding to a problem dictates the thoroughness of the proposed solution. After all, if you don't understand the problem or the nuances to the problem, you have no target to aim at.
As a software engineer, this looks like talking to stakeholders to understand business requirements, understanding current user pain points, understanding how the current system behaves and why it is that way, understanding how the system might be affected with this new change, understanding how other systems might be affected with this new change, and more.
There's a lot to comprehend and this is probably the single biggest reason why software engineers are valued the way they are in the modern economy. The nuances to the problems they have to comprehend are infinitely complex as human created systems tends to get deeply complex quickly as small imperfections are introduced over time.
In general, outside of the software engineering context, the trick to comprehend a problem is to just keep asking why things are the way they are. Go until you feel really good about why the problem exists in the first place and can point to a reasonable root cause. The deeper you go, the deeper you'll comprehend the problem.
Ideation
Once a problem is understood well enough, ideation begins to happen naturally. This is the creative process for proposing a solution. There's rarely ever only one potential solution, so it's easy to come up with multiple approaches.
The way that this is done effectively is to begin producing any solution, even bad ones, and get them down in writing. The more we write down our ideas, the more opportunities we have to go back and forth with those ideas or get feedback. This results in those ideas transforming into more comprehensive solutions.
This is similar to the creative process of how a writer might begin producing a story or how a designer might sketch wireframes for a particular business need. The more ideation that occurs, the more likely one of those ideas will click.
Estimation
Estimating the cost of doing something is where software engineering differs significantly from other creative processes like writing or the visual arts.
Unlike writing or art, where there is typically one or a small number of people that collaborate to create something, software engineering involve multiple, sometimes dozens, or even hundreds of people all working towards a solution. With software engineering, the cost to allocate engineering resources to implement something is extremely high, so it's critically important that the estimation for how many people it will take and for how long to get the job done is accurate.
From a business perspective, the opportunity cost of allocating the necessary resources to develop software is a significant one and there needs to be a good justification for prioritizing one feature over another.
How high is this cost? Let's do some napkin math. If you're a business that has 5 engineers employed and each one costs $200k/year in payroll and benefits, committing to go in a direction for 6 months is a $500k decision, and the business has to be able to justify that risk by having a plan on how the cost will be recouped, whether that be in increased brand value, direct increase in revenue, lowering customer acquisition cost, etc.
This opportunity cost can only be understood if the engineering estimates are dependable.
However, like "comprehension" and "ideation", "estimation" is not unique to software engineering. This part of the process exists in every problem we solve in our daily lives no matter how big or small the problem.
Do you buy a coffee or make coffee at home? What are the pros and cons? What is the opportunity cost of buying one vs making one? What can you do with the money instead of buying coffee? What is the opportunity cost of spending time at home to make one as opposed to buying one?
This ability to break down the true cost of decisions is an important skill to hone. Our decisions are better informed when we have a more complete understanding of the costs involved, and our futures depend on the decisions we make today.
Evaluation
Lastly software engineers spend time rank ordering the solutions to figure out which one is best given the idiosyncratic combination of needs from the company, the people within the company, and the users of the software.
This prioritization is often difficult as these competing demands are sometimes not hard-requirements.
In such cases, no decision can ever be perfect as there is infinite depth of historical context and knowledge surrounding a particular decision. We do the best we can with as reasonably complete of an understanding that we need to make a well-informed decision, and move into execution.
Outside the engineering context, the evaluation process is usually quick and answers come somewhat intuitively, or we simply bypass this part of the process by sticking to our gut instinct and making emotional decisions. This is true for most purchasing decisions that don't require us to really think about the monetary cost associated with making said purchase.
But for large decisions, especially ones that aren't easily reversible, like which city to move to, buying a car, buying a house, when to have kids, or how many kids to have, it's best to proceed methodically and use these four tenets of thinking like a software engineer to be intentional.
How To Become Better At Problem Solving
So exactly how does someone begin learning this kind of intentional problem solving? It's one thing to know about this stuff and another to be able to judiciously apply it in our work.
I think like most skills, great practice produces great results.
It's not enough to practice, it's vital that you deliberately practice. This starts with an awareness of what problems are around you in your daily life that you could solve, and then actively working to solve that problem.
Once you step back and observe the process for how you solved a particular problem, you can then apply the same process to solve a different problem. Repeatedly practicing this will result in the problem solving process becoming more intuitive and require less active thinking.
While I can't do the practice for you in your own life, I can lay out an example to highlight the process.
Say you work at a restaurant as a waiter and your coworkers are frequently ditching their shifts by calling in sick despite not being sick. You and your manager know that this a known issue that's causing a bad customer experience due to being understaffed.
How would you solve this problem?
Disclaimer: this is just a hypothetical solution, I haven't actually tested it.
Like in the described process above, we first need to comprehend the nuances to our problem. Not taking into account the instances where someone is actually sick and needs to take the shift off, why would someone be unmotivated to work? It could be a lack of intrinsic motivation. It could be that there is a particular coworker who is making it a living hell for everyone else. It could be a variety of things, and this answer will completely depend on the situation.
If this were a real problem, you would want to get to the root of what's actually causing people to skip their shifts.
Let's say there isn't some glaring problem and so we need to somehow incentives your coworkers not to take sick days or encourage your coworkers to cover for each other when someone calls in sick. One motivating factor to show up to work is money, so let's try manipulating this money dial to see how we can incentivize the right behaviors and disincentivize the wrong behaviors.
Here are some quick ideas:
- Tip-share program where if you take a day off without notice X days in advanced, the coworker who covers your shift gets half of your tip shares during your next shift.
- Give a small monthly bonus to those who do show up for every shift during the month.
These solutions motivate the right behaviors, like covering for someone and consistently showing up, and demotivates the wrong behavior of needlessly taking days off.
Next, let's estimate the cost for implementing such a solution. For the purposes of this exercise, we'll simply evaluate the direct financial costs and not the longer-term indirect effects it might have on employee motivation.
The tip-share program doesn't cost the business any money since it's just a redistribution of earnings.
Say the restaurant pays a 1% bonus for showing up for every shift during that payroll period. If all staff showed up consistently, this would increase the restaurant staffing costs by 1%.
Evaluating this solution, at least from the financial angle, would be a matter of estimating the potential cost of not implementing this reward system and choosing the option that saves more money.
The indirect effects of having a poor review is difficult to estimate, but a quick google search yields statements like:
- A single bad review drives away 1 potential customer out of every 10 (source).
- A single bad review costs you 30 customers every year (source).
If I were actually operating a business, I would want to research the validity of such statistics more. But for the purposes of this exercise, this gives us something to base our estimates on. Let's assume the following (these numbers are mostly set up for simplicity)
- The restaurant is open 365 days a year.
- You need 10 waiters in a day to be properly staffed.
- Each waiter makes an average of $20/hour.
- Every operating day is 10 hours (two 5-hour shifts).
- A bad review costs 30 customers every year.
- The average order for a customer is $50.
- You get one bad review a month due to understaffing.
- Implementing the solution eliminates the understaffing problem completely.
The cost of staffing for the restaurant in the current form is $2000/day (10 hours * 10 waiters * $20/hr), or $730,000/year.
The cost of staffing for the restaurant with the solution would be $2020/day (2000 * 1.01 ) or $737,3000/year.
The solution costs $7,300 a year to implement.
The bad reviews are currently costing $1,500/month (1 bad review * 30 customers * $50/order), or $18,000/year in lost potential earnings.
This means that implementing this solution and paying 1% bonuses to reward the right behavior would save the business $10,700/year ($18,000 - $7,300).
Obviously, these assumptions are basic guesses based on quick research, but I think there's an argument to be made for increasing the budget for staffing costs by a small percentage to incentivize the proper behavior. It all depends on the actual numbers at play to do a proper evaluation, but you get the point of the exercise.
Get Practicing
Pretty much any problem that you can think of can be broken down and solved using foundational knowledge and tools to solve it. In the case of the example above, the only foundational knowledge being utilized were basic arithmetic, basic researching skills, and utilizing the process of problem solving.
The best place to start this practice is to begin by assessing what problems you are solving in your day-to-day and make the 4 tenets to problem solving an explicit part of your thinking.
Another decent place to start would be to simply do this exercise with some other hypothetical problem, like what I did above and see what comes of it.
Happy problem solving!