A framework for understanding ourselves.
2024-08-20
This article presents a framework for considering software developers as vehicles. This will help us understand the strengths and weaknesses of different types of SWEs, the motivations behind their thoughts and actions, and how coworkers with radically different approaches can still amicably work together.
The content is based on my personal experiences and reflections, and is built upon an analogy that was originally shared with me by a former manager.
I started working at my previous job immediately after they had secured their series A funding round. The team was small, and there was a lot of work ahead of us. It was also a highly competitive, fast-moving space. (LLMs, but before ChatGPT changed the game.) As a result, the engineering culture was very fast-paced.
Additionally, I was just pivoting into backend engineering from data engineering.
Although I'd proven my intelligence and learning abilities during the interview process, I had role-specific knowledge gaps.
Thanks to my relatively low self-esteem ego, I started suffering from a pretty bad case of imposter syndrome.
I felt like I was surrounded by "rockstar" developers. They were able to churn out code more quickly than I could, and they seemed a lot more confident than I was. I couldn't help but compare myself to them, and I felt like I was falling short.
One of these rockstars would sometimes make me feel stupid for not figuring out problems or solutions as fast as them. They were brilliant, well-respected, and had deep knowledge of the codebase. I worried that they could get me fired if they wanted to.
We would often get into conflicts because we disagreed on how to implement features. Despite me feeling confident enough to argue with them, I felt that they were on a fundamentally higher level than me.
Whenever I met with my manager for our one-on-ones, I would ask for constructive feedback. I was convinced that I was doing a terrible job, and I wanted to know how I could improve, or at least to determine if I was about to be fired.
Instead, they would always have positive things to say about my work, and would assure me that I was on a good path. However, I didn't feel like I was getting the same respect from my teammates. I couldn't tell if my manager had wool over their eyes, if they were too nice to tell me the truth, or if I was being too hard on myself.
After a few months, my manager finally came to me with constructive feedback. They told me that in their experience, developers fell into one of three categories: tanks, motorcycles, and cars.
Tanks write highly robust code, but they can be slow. Motorcycles execute very quickly, but they can be fragile. However, cars are the best of both worlds. They are as fast and as sturdy as they need to be for the task at hand.
As you can probably guess, I was a tank. My manager told me that they wanted me to be faster, because that was what the company needed from me. However, they wanted me to become a car, rather than overshooting and becoming a motorcycle.
I asked my manager if they had examples of developers who fit the other archetypes. For the motorcycle, my manager quickly named the developer who I'd been comparing myself to. For the car, my manager named a frontend engineer with many years of experience.
It was like a fog had been lifted from my eyes. Suddenly, I realised that I'd been viewing myself and others through a very narrow lens, shaped by my limited conception of an ideal developer. That wasn't fair to me, or to anyone else.
The "rockstar" wasn't perfect or necessarily better than me, just different. The person who told me that I was the only good backend engineer (yes, really) was a tank too. And because I didn't work on the frontend codebase, I had been overlooking the car in our midst.
My manager left the analogy there, but it's stuck with me ever since. I've found it to provide a very useful framework for understanding the developers around me, as well as myself.
With support and smart decisions from my manager, I was able to hit my stride. First, I was made responsible for a product that was highly suited for a tank-leaning backend engineer. The code was very complex, brittle, and buggy, due to the motorcycle-style development that had been required to get it out the door. I was able to drastically improve the reliability and maintainability of the product, making use of my unique skills while I developed my backend knowledge.
Second, my manager found a mutually beneficial opportunity for me to collaborate with the car frontend engineer. They wanted to shift to full-stack engineering, and needed a backend engineer to train them. From them, I learned how to tread the line between motorcycle and tank behaviour. While we developed features together, I paid attention to the decisions they made and how they evaluated the tradeoffs between different approaches.
Through both of these opportunities, I was able to enter and remain in a flow state. I became comfortable quickly making changes and calling shots, because I could understand the entire system and consider the tradeoffs. This both increased my growth as a developer and maximised my usefulness to the company. I also earned the respect of my motorcycle coworkers, who would commend me for my PRs.
So, did I become a car? I'm not sure, and I don't think it's for me to say. I definitely made progress, and I believe I have continued to do so since then. However, this article isn't about that. It's about what the vehicles framework can tell us about how we think and work, alone and together.
Let's delve deeper into the different mindsets driving tanks, motorcycles, and cars. We'll consider how these archetypes approach different aspects of software development. In general, tanks focus on long-term stability, motorcycles focus on short-term speed, and cars balance the two while considering the present situation.
While tanks want to fully plan and motorcycles want to dive in, cars balance both foresight and flexibility.
Tank: We need to plan this out fully and consider all possibilities. We should ensure that our implementation is intelligent, scalable, and future-proof.
Motorcycle: Let's take the path of least resistance. We can just work with our existing systems and worry about scale and complexity later.
Car: Let's get the MVP out quickly to gather user feedback. We can re-architect it once we understand the final design. However, we should take care to avoid making future refactors painful.
While tanks want to fully test and motorcycles want to just ship, cars balance both quality and delivery.
Tank: Every feature requires unit tests and integration tests before it can be considered complete. We can't risk shipping bugs to production.
Motorcycle: Testing takes too much time and gets in the way of shipping. We'll know it works if it works. We can always fix it later if it breaks.
Car: Tests are important, but we need to balance the time with the risk. Let's cover the critical paths now and exhaust the edge cases later. We should improve our testing infrastructure between projects.
While tanks want to analyse everything and motorcycles want to act quickly, cars balance both exploration and execution.
Tank: Let's analyse the data and consider all the outcomes before we make a decision. It's better to delay the project than to make the wrong choice.
Motorcycle: We should move quickly and decisively. We can't afford to analyse all the information. We can always pivot later if we need to.
Car: Let's make a decision that's good enough for now with the information we have on hand and our best judgement. We should be prepared for future changes, but we can't get stuck in analysis paralysis.
While tanks want to refactor early and motorcycles want to push forward, cars balance both technical debt and progress.
Tank: We should refactor our code early and often to ensure that it remains clean and maintainable. We need to pay down our technical debt as soon as possible.
Motorcycle: Refactoring should only be done when absolutely necessary. If the code works, let's leave it alone and focus on our next project.
Car: It's okay to accrue technical debt in the short term, but we should address it when it limits our development speed or product quality. We should avoid premature optimisation but we can't ignore the long-term consequences of our decisions.
It's important to note that it's rare for someone to be perfectly in the centre of this spectrum. Additionally, even a "perfect" car might not act "perfectly" in every situation. Most people will lean towards one side or the other, and that's okay!
The goal of this framework isn't to idolise cars, or to decide that you're a car and therefore perfect. Instead, it's to help you recognise your tank and/or motorcycle behaviours and mindsets, and consider how to best work with those who lean the other way. It's also to help you understand what you might be overlooking in your own approach, and how you can grow as a developer.
It's notable that this spectrum is also a set of tradeoffs. Different vehicle archetypes may be more or less useful in different roles, teams, or companies. For example, tanks may generally be more common in platform engineering teams or mature companies, while motorcycles may be more common in product engineering teams or young companies. However, the opposite archetypes are also valuable in these situations to create a diverse and balanced team.
From this point onwards, we'll focus on tanks and motorcycles specifically, since those are the two extremes of the spectrum.
At my previous job, there was a lot of conflict between developers, and it could often get nasty. This lead to an unhealthy work environment where collaboration felt toxic, at least from my perspective. This affected my happiness and productivity, and was one of the reasons I decided to leave.
Looking back, I feel that many of these conflicts were caused by clashes between strong-minded individuals with different mindsets: tanks versus motorcycles. If you think your mindset is the correct one, someone with the opposite mindset may seem foolish to you. However, it's important to understand and appreciate your coworkers' skills and attitudes, even if they may be different from yours.
Of course, toxic behaviour is not intrinsic to either tanks or motorcycles. It's perfectly common to be a wholesome, non-toxic developer while falling firmly into one of these archetypes. Additionally, cars can still have conflicts and engage in toxic behaviour with coworkers. However, in my experience, this type of toxicity is often rooted in either a tank or motorcycle mindset. Remember, there is no perfect car.
Let's consider how toxicity can manifest in different aspects of collaboration. We will see hypothetical toxic thoughts or statements from tanks and motorcycles, and contrast them with non-toxic alternative thoughts or responses.
Instead of assuming that coworkers are unjustifiably ignoring or adding requirements, we should consider their reasoning.
Toxic tank: You always over-simplify everything.
Toxic motorcycle: You always over-complicate everything.
Non-toxic: There are many considerations. Which ones are actually important?
Instead of assuming that coworkers are deliberately choosing the wrong approach, we should consider the tradeoffs.
Toxic tank: It's not hard to do correctly; you're just reckless.
Toxic motorcycle: It's not hard to do quickly; you're just incompetent.
Non-toxic: There is a fast way and a robust way. Which one makes sense for our needs?
Instead of assuming that coworkers are trying to sabotage us, we should consider their goals.
Toxic tank: You would cause way more incidents if it wasn't for me.
Toxic motorcycle: You're always blocking me from getting stuff done.
Non-toxic: We should all work together to produce the best outcome. Are their intentions positive?
Instead of assuming that coworkers are inferior to us, we should consider their strengths.
Toxic: You don't deserve to be here.
Non-toxic: Our differences make us a better team. Are you measuring others by your own stick?
Some time after leaving that job, it occurred to me that I had definitely been a car in previous roles right away. It dawned on me that perhaps my strong tank behaviour had not been purely intrinsic. I realised that one's position on the tank-motorcycle spectrum may not be fixed, and might change over time or in different environments.
So, let's talk about external factors that motivate developers to act like tanks or motorcycles. Determining these motivators helped me gain a lot more empathy and understanding for coworkers, whether tanks or motorcycles, who had been causing conflict or engaging in toxic behaviour. It also helped me understand the "other side" to my fears, which enables me to move past them.
Tank: If something goes wrong, I'll be held accountable. I need to ensure that my work is flawless so that no one can say I'm incompetent.
Motivator: Fear of being blamed for failures.
Downside: This can lead to over-cautiousness, where tanks might avoid taking on risky projects, resist substantial changes which could improve the product, or spend excessive time on planning and testing.
Motorcycle: If I don't ship this feature ASAP, I'll be seen as slow and replaceable. My performance is measured by how quickly I can deliver.
Motivator: Fear of being seen as inefficient.
Downside: This can lead to rushed work, where motorcycles might cut corners, deliver incomplete features, ignore potential issues, or generate substantial technical debt.
Tank: If this code breaks, whoever is on-call will be forced to clean up after me. I'd rather spend extra time now to avoid waking up at 3 am in the future.
Motivator: Fear of causing incidents.
Downside: This can lead to over-engineering, where tanks might delay small but high-impact changes, or block releases to address every possible edge case.
Motorcycle: Incidents are a cost of doing business. I'd rather deal with them as they come than hold up a release for extra precautions.
Motivator: Fear of delaying releases.
Downside: This can lead to frequent incidents and bugs, where motorcycles might strain team dynamics, or cause stress and burnout for themselves and others.
Tank: If my implementation doesn't align perfectly with the product team's vision, it could lead to wasted effort. I need to fully understand the requirements and implications before moving forward.
Motivator: Fear of misalignment.
Downside: This can lead to feature delays, where tanks might spend excessive time seeking clarity or approval before beginning development work.
Motorcycle: The product team wants this feature yesterday. I need to deliver quickly to keep them happy, without wasting time writing planning documents.
Motivator: Fear of missing deadlines.
Downside: This can lead to motorcycles shipping products that don't meet the right goals or have implementation issues, requiring refactoring or starting over.
Tank: If we ship buggy features to our users, we'll lose their trust. I need to ensure that everything is polished and reliable before it goes out the door.
Motivator: Fear of negatively impacting users.
Downside: This can lead to perfectionism, where tanks might spend excessive time testing edge cases and small details, or avoid shipping features that are "good enough".
Motorcycle: If I don't ship this feature, deals won't close. I need to deliver quickly to keep our users happy so that that they don't leave us for our competitors.
Motivator: Fear of not meeting user demands.
Downside: This can lead to motorcycles shipping rushed features that are buggy, don't meet user requirements, or are difficult to maintain.