Elon Musk is kind of an unavoidable person nowadays. You can be a fan of him or hate him but it is hard to argue that his companies are doing very impressive engineering work. It is enough if we think of SpaceX. This company made reusable rockets a thing, lowering the prices of sending things to space considerably. With their new Starship development program, they once again spearhead space-related technological advancements. The new full-flow staged combustion Raptor engine is a technical miracle in itself.
As a space and technology nerd, I'm following a few channels to keep myself up to date on news. Tim Dodd made a series of interviews with Elon where they went pretty deep into engineering details, mostly about SpaceX but some Tesla examples came up too. In these interviews, Elon described a 5 step process of how to approach engineering problems.
The examples obviously came from mechanical engineering and manufacturing as he was involved in these fields recently. I really liked the steps and was thinking about how could these be interpreted from a software engineer's point of view. Here is what I have figured.
The 5 steps
1) Make your requirements less dumb
Your requirements are definitely dumb. It does not matter who gave them to you.
It’s particularly dangerous, if a smart person gave you the requirements, because you might not question them enough.
Everyone’s wrong, no matter who you are, everyone’s wrong some of the time.
It doesn't matter if you are working for a company making a single product or shipping custom solutions to various customers. In both cases, you or your team will receive requirements that you have to satisfy with the solution. In my experience in the second case (working for various customers who are not IT experts, just want a system for their business) requirements are often dumb. These customers don't know the possibilities of what can be built or what are some limitations that would introduce huge costs or other issues. It is the developer's, architect's, or business analyst's job to talk with the customer and refine these requirements based on the possibilities and limitations known to them.
In Elon's example, a smart person can be another engineer in our case. A team lead, the project manager who used to be an engineer but switched, or anyone in the company. These people may also have limited knowledge of particular aspects of the system you are working on, thus making dumb requirements for their requests. Another option that connects with points 2 and 3 is that your colleague may really want to use a specific technology or solution mainly because it is their favorite for some reason. Their personal fascination and interest do not mean that it is the best, simplest, easiest way to go. If you don't agree with something or see any issues always challenge it. In a healthy engineering culture, this is usually part of how things work. If the other party takes offense it can be an indicator of an unhealthy culture, or just that person is not fit (or you are super rude in the challenge, or made it personal which is bad, don't do it).
2) Try very hard to delete the part or process
The bias tends to be very strongly towards “let’s add this part or the process step in case we need it”.
If you’re not adding things back in 10% of the time, you’re clearly not deleting enough.
I think it is the classic overengineering issue. Let's add a few more endpoints for this model, we don't have a use case for those, but just to be sure, they may come in handy once. Or let's introduce this complex custom internal messaging system as it is technically super cool and interesting, but in reality, a simple built-in framework event could do the job. Not only the coding but the whole development process can be susceptible to this problem by having not necessary CI steps, or the use of a bunch of tools while there would be a simpler, better-integrated solution. The goal is to keep things simple and try to remove complexity. We don't need to store everything in JSON in your SQL column in case the model will ever change, you can do that later once really needed. We have to find the balance between developing the bare minimum needed and creating a solution that can be easily extended if needed.
3) Simplify or optimize
The reason it’s the third step is cause it’s very common, possibly the most common error of a smart engineer, to optimize the thing that should not exist.
There’s another important principle, which is that you really want everyone to be chief engineer. So if everyone is chief engineer means that people need to understand the system at a high level to know when they are making a bad optimization.
I think it is a very good point. Most of us engineers love to optimize things. My BSc thesis was about optimizing some piece of calculation going from a Matlab script to CUDA and I still to this day like to make things go faster and simpler. However, it is worth thinking about it first (see step 2) if that part is needed. Don't waste time optimizing things that can be deleted. If it can't be deleted, go ahead, and try to make it better.
I just recently had a good example of failing these steps. In my scenario, I had to update a bunch of database records and for that, I needed to access multiple tables to gather information to do the update. I was trying to optimize these calls, and figure out how can those be written more efficiently. In the end, after a discussion with a colleague and making the requirements less dumb it turned out I didn't even need half of those queries, and the related parameters can be set to fix values.
4) Accelerate cycle time
You’re moving too slow, go faster. But don’t go faster until you’ve worked on the other three things first. If you’re digging your grave, don’t dig it faster, stop digging your grave.
The first 3 steps were quite easy to interpret from a software engineering point of view. The points made sense as well as their order. The 4th and 5th are a bit more difficult. In manufacturing it sounds obvious that producing something fast is good, except what you produce is bad. In software engineering, we rarely do the same thing over and over again. However, there are a few scenarios where speed is important.
The first thing that came to my mind is when deadlines are getting closer and closer. In these cases, we may start to work overtime, write fewer tests, and make code reviews very shallow or even optional in cases. Depending on the situation we can use these methods to speed up development, but remember that we don't want to dig our own graves. We will have to deliver this software to the customer, we will have to support it and provide a warranty. If the customer wants it we will be the ones making updates and new additions to the system. If we make too many compromises quality will suffer and we will be in a very bad situation after the successful delivery on the deadline.
Also working on the first three points may help a lot with speed as we made the requirements nice and tidy, cut out everything that introduces unnecessary complexities, and optimized the rest. In a codebase built following these steps, new changes should be easier to introduce.
I have personally made the mistake of going backwards on all five steps multiple times. Literally I automated, accelerated, simplified and then deleted. Automating was a mistake. Accelerating was mistake. Optimizing was a mistake. We just deleted and just bypassed this $2 million robot cell as a complete pile of nonsense.
This point is also a difficult one to translate to software engineering. Let's look at the example Elon gives. In the Tesla factory, they set up an automated robot cell to do a manufacturing step of applying an insulator to the battery. With a lot of time and very hard work, they optimized and accelerated the process that this automated robot did, just to find out in the end, that the insulation is not important at all and can be omitted, rendering the robot cell and all work done on it worthless.
Automate yourself away.
Automation in itself is a very important thing in software engineering. There are repetitive tasks that eat up a lot of time. Running tests, deploying new versions, and so on. The obvious thing that comes to my mind when it comes to automation is CI/CD, but it doesn't have to stop there. From starting to learn and use some shortcuts in the IDE to creating scripts that automate some regular tasks there are many possibilities to automate things around us. Don't be lazy, as in the long run these small automated improvements can add up and save us a lot of time.
I wrote this article because the 5 engineering steps mentioned by Elon made a lot of sense to me, so I wanted to apply these in my field which is software engineering. I think the first three makes a lot of sense for us developers too in their exact order, number 4 and 5 can also be translated but the order may not be that important as there are obvious differences between coding and mass manufacturing, but their spirit can help us to become better engineers.