James Hochadel

Two Types of Engineers

Two Types of Engineers

Thanks to our genetics and experiences, our reactions to every moment are shaped by a variety of “default settings”. Our default settings determine our instinctive responses to good and bad news, to unexpected inconveniences and unexpected opportunities. As software engineers, our default settings inform the way the approach creating and maintaining software. It takes deliberate mental effort to change them.

My mental model is that there are two default settings of engineers, existing as extremes on a spectrum. The “two types of engineers” mental model helps me understand my coworkers and myself better, which in turns helps me succeed in my work and help others succeed in theirs. The two types are Tactical Engineers and Strategic Engineers.

Tactical Engineers

Tactical engineers excel at writing code to accomplish a well-specified task. The advantages of the tactical mindset are appealing. These engineers take action quickly in unfamiliar codebases and with technologies that are new to them. They excel at getting code working quickly and are decisive about the thousand small choices one must make when writing a program. Their mental model of code is a heuristic one: They can treat parts of the codebase as a black box, learning relevant behaviors instead of learning the underlying rules of the system, which contributes to their speed.

The tactical way of thinking has downsides. Compared to strategic engineers, tactical engineers are more likely to implement the wrong thing because they dive into a task without considering the context. They are more likely to extend an existing architecture beyond its original intent than to simplify or refine it. They may introduce bugs by not considering the broader consequences of their changes. Open-ended research tasks may make them impatient; they want to be pointed at a problem and let loose on the code.

Strategic Engineers

Strategic engineers lie on the other end of the spectrum. These engineers look at the whole landscape, be that a system’s architecture or the political environment at work. As coders, they want to understand more of a codebase before contributing; when they contribute, they are slower and more careful at first, but their speed increases as their knowledge compounds. Because of these traits, strategic engineers may write higher-quality code that is more thoughtful and idiomatic to a codebase or language. Spending time “exploring” rather than “exploiting” lets them see the bigger picture, and they can find options that tactical engineers might miss in their eagerness to get started.

On the other hand, strategic engineers’ instinct for searching out a global maximum can be an obstacle to getting things done. Few real-world systems can be perfectly understand, and the pursuit of perfection can slow strategic engineers, get them stuck, or prevent them from ever starting implementation at all. These engineers can get stuck endlessly iterating on a piece of code, or get stuck unable to make a choice when there is no obvious best answer. This is especially true when they are working with a project, framework, or language that is unfamiliar to them.

Strategic engineers may feel tempted toward product roles where their skill at identifying optimal solutions combined with their technical knowledge is welcome. The same can be said for tech lead or product roles. When making this transition, strategic engineers should be careful to avoid becoming “architecture astronauts”, disregarding the practical challenges of developing software in favor of overly idealized designs.

When stuck, strategic engineers may procrastinate by spending lots of time reading about others’ optimal solutions to problems instead of solving problems themselves. In other words, they may read about cool programming ideas instead of actually doing cool programming.

They can trick themselves into thinking this is productive by thinking of it as research. Why reinvent the wheel when the answer is already out there waiting to be found? Indeed, reading for research is useful when done in a controlled and deliberate way. When reading is used to procrastinate from doing, it leads to surface level knowledge but no actual experience. Strategic engineers end up “knowing” the best solution to every problem — at least, according to other people — without knowing how to actually get anything done. Actual, experiential knowledge is not won by procrastinating when stuck; one must learn to be patient, understand how they got stuck, and repeatedly practice finding a way out until it is second nature.

Another concrete way strategic engineers can be distracted from solving real problems is by dogmatically pursuing design perfection using tools from other people. I learned design patterns in undergrad and tried very hard to use them perfectly in my first job. Because I was inexperienced, the best way I knew how to write working code is to repeat what I’d seen before, even if I didn’t truly understand the basics yet.

As a result, I spent too much time trying to fit the problem to my solution rather than finding the simplest possible solution to the problem. I also got stuck because I couldn’t create and hold the “perfect” design in my head. Design patterns aren’t inherently bad, but overusing them in pursuit of perfection instead of practicing the fundamentals was bad.

I am a strategic engineer by nature and I’ve experienced many of these failure modes firsthand. I work with a mix of strategic and tactical engineers. As an example of the differences between us, sometimes I will start working on a code change and encounter a confusing aspect of the codebase. I spend a lot of time understanding it so I have the context to make my change correctly. Occasionally I will feel self-conscious because I take so long to build the understanding, but when I talk to other team members, they tell me they moved more quickly because they just ignored the confusing parts when they made changes in that part of the code.

There are benefits to both approaches. The key is pushing yourself out of your comfort zone to become good at both, and learning from experience when to be strategic vs tactical.

The well adjusted engineer

In between the two extremes of this engineering spectrum lies the third, ideal engineer: The well adjusted engineer.

In his Kenyon commencement speech This Is Water, David Foster Wallace said this of default settings and adjusting one’s self well:

This is not a matter of virtue — it’s a matter of my choosing to do the work of somehow altering or getting free of my natural, hard-wired default-setting, which is to be deeply and literally self-centered, and to see and interpret everything through this lens of self. People who can adjust their natural default-setting this way are often described as being “well adjusted,” which I suggest to you is not an accidental term.

Software engineers should aim to be well-adjusted. Doing so means you will be more effective: Less able to be tripped up and more able to be let loose on difficult problems. I suggest paying attention to which tasks at work you instinctively avoid and deliberately practicing them. If you are a tactical engineer, try doing a research ticket. Be able to answer why relevant parts of the codebase are designed as they are, instead of just copying what’s already there. If you are a strategic engineer, practice writing code. Do code katas or even leetcode. Try memorizing the APIs of your favorite language’s standard library so you don’t need to reference the docs as much. Write POCs quickly and throw them away without sentiment.

Without leaning in to the discomfort, you will be forever limited in what you can do. By embracing it, you will be capable of solving any problem, given enough time, and you will build more accurate mental models that will pay dividends for the rest of your career.