Nobody Gets Promoted for Simplicity
43 points by nickmonad
43 points by nickmonad
Start with how you talk about your own work. “Implemented feature X” doesn’t mean much. But “evaluated three approaches including an event-driven architecture and a custom abstraction layer, determined that a straightforward implementation met all current and projected requirements, and shipped in two days with zero incidents over six months”,
This is just the best possible advice for a new engineer. Nobody can see the code you don’t write, so you need to paint a picture for them.
I struggle with this even as a senior with 10+ years professional experience. I have a tendency to just assume people will see and appreciate the simplicity (when I can manage it).
I also think it's important for shared design review to take place, especially on larger projects. It shouldn't have to be all on the one engineer tasked with the work. If multiple options are presented ahead of time, there is (hopefully) shared understanding the simple option is really the best option for the time.
The sad reality is that at a job you are the product and you need to sell yourself.
It's like with other products. Even when they are objectively worse, overpriced, etc. with the right amount of marketing, sales, contacts, etc. they can still become market leaders.
So in my experience the most important factor deciding whether you will be promoted or what salary you get is how you are perceived. During consulting I learned this also goes the other way round. Asking for higher pay also means you'll perceived better.
On top of that the technology is in itself even for tech companies the thing that makes them stand or fall. At the end of the day they need customers. And while your product must be in a state where it can be sold a lot of thing that make a product good (usable, efficient, bug-free) might not even be relevant on the business side, depending on what when and to whom you sell it as well as a lot of other factors and circumstances.
Sometimes it's even making something worse that causes it to be a success. Eg. having it break, or having it bad enough that it needs support, maintenance, etc.
For people in jobs it's similar. Sometimes it from a career point it can even be good to have something in a bad or horrible state to loudly fix it being better than having it work perfectly in first place.
Another thing in tech is that doing tasks quickly isn't usually rewarded, simply because it gives the impression - especially to non-tech people - that you did something easy or trivial, while doing something easy and trivial but spending a long time on it can give the impression that you are working hard, or are particularly precise, etc.
What's more is that one of the times you are having a chance of actually being noticed is meetings. So if you are working hard on your projects and are exhausted or mentally still in "programming mode" doing a good job in a sense might be detrimental.
Similar things are true when you don't interrupt (unless you have someone good at moderating meetings).
While these things seem kind of odd and counter-productive, even unjust there are surprisingly few counter-examples - at least in my career.
Try: “Here’s what it would take to add that later if we need it, and here’s what it costs us to add it now. I think we wait.”
This is such a huge thing. Sometimes the cost of adding something later is almost exactly the same as the cost of adding it now, and sometimes it's much cheaper to add it now. You need to take that into account when planning!
This is part of why, when I'm working as a senior manager, I always push for metrics - despite a well-founded mistrust of same by many developers.
Simplicity is rewarded by good metrics, if you're gathering good metrics :) Teams that simplify sensibly will see payoff in reduced breaking change %, increased deployment frequency, reduced TTR, etc. etc.
Most C-levels give exactly zero fucks about how simple and elegant your architecture is. They do care, a lot, if you can point to the organizational and especially financial benefits of it. And product managers can make informed trade-offs in terms of development investment.
Let's review the distinction John Ousterhout makes in A Philosophy of Software Design between tactical and strategic programming.
The problem with tactical programming is that it is short-sighted. If you’re programming tactically, you’re trying to finish a task as quickly as possible. Perhaps you have a hard deadline. As a result, planning for the future isn’t a priority. You don’t spend much time looking for the best design; you just want to get something working soon. You tell yourself that it’s OK to add a bit of complexity or introduce a small kludge or two, if that allows the current task to be completed more quickly. This is how systems become complicated. As discussed in the previous chapter, complexity is incremental. It’s not one particular thing that makes a system complicated, but the accumulation of dozens or hundreds of small things. If you program tactically, each programming task will contribute a few of these complexities. Each of them probably seems like a reasonable compromise in order to finish the current task quickly. However, the complexities accumulate rapidly, especially if everyone is programming tactically.
If you approach systems design like the Author, you will continually find yourself mystified that a series of simple choices produces an unmaintainable mess anyways. Thinking tactically and ignoring long-term strategy leads you to make bad choices, and creates more complexity in the long run. Oftentimes strategic choices look like unnecessary complexity to a programmer in a tactical mindset.
It’s not acceptable to introduce unnecessary complexities in order to finish your current task faster. The most important thing is the long-term structure of the system. Most of the code in any system is written by extending the existing code base, so your most important job as a developer is to facilitate those future extensions. Thus, you should not think of “working code” as your primary goal, though of course your code must work. Your primary goal must be to produce a great design, which also happens to work.
So the current programming task in front of you is not sufficient information to decide on the best approach. You need to be thinking strategically about the future, and the needs of the business. You need to be assessing tradeoffs between designs, and making choices about what the consequences will be if you put off necessary work to the future.
Avoid rule-of-thumb-based programming.
Avoid rule-of-thumb-based programming.
A good rule of thumb!
Sincerely, though, thanks for sharing the quote about tactical / strategic- very applicable here.
With that lens: I read the linked post as highlighting the need to call out / sell when something that looks like a tactical choice is actually strategic.
From the article (emphasis added):
evaluated three approaches including an event-driven architecture and a custom abstraction layer, determined that a straightforward implementation met all current and projected requirements, and shipped in two days
That is a strategic choice: as you point out, thinking "about the future, and the needs of the business." If something meets current and projected requirements, and can be done in two days... Great. Then we can move on to something with higher value.
I don't think the author disagrees with
the current programming task in front of you is not sufficient information to decide on the best approach.
...but I think they're drawing out: consider what you can forsee, not what you imagine.
I feel like this critique is talking past the point of the post. The author isn't arguing for speed of implementation per se, nor are they arguing that one should ignore the needs of the business and forgo a strategic mindset.
The primary point is that in many observed and experienced cases, the "implemented something simple in 2 days that solves the problem at hand" is the strategic move, because it solves for the problems you have today, not the problems you believe you will have a year from now. Having worked with external contractors on critical projects, I've seen plenty of tactical programming, and I don't believe the author is advocating for that at all. There is nothing in the post that suggests one should avoid revisiting design and implementation as the need arises.
Now, let me be clear: complexity is sometimes the right call. If you’re processing millions of transactions, you might need distributed systems. If you have 10 teams working on the same product, you probably need service boundaries. When the problem is complex, the solution (probably) should be too!
I honestly don't think that there is a tradeoff here. it's a matrix isn't it? strategic-tactical x complex-simple. you ideally want to be in the strategic-simple quadrant, but being in the tactical-simple quadrant is second best, and being in either "complex" quadrant is almost always a red flag.
If you work at a giant company with design reviews with engineers trying to one-up each other, and employees putting together promotion packets to compete for limited spots, you're probably better off just playing that game than trying to change the rules. Even so, the advice in the article about what pleases your manager, and how to talk about your accomplishments in job interviews is quite good.
As an engineering manager primarily on smaller teams where I control more of the culture of my team and coworkers, my baseline is "the simplest solution that fulfills the requirements," and I'll need to hear a pretty convincing argument to accept something more complex. However, even at these types of companies there are a few situations where complexity creeps in that aren't mentioned in this article:
Product Managers / Product Owners with an overly strict or idealistic spec - Words like "nice to have" in requirements are a big red flag of this. If it's not necessary, it doesn't ship, and if it becomes necessary after shipping then we can do it then. This also comes in the form of overly specific requirements that don't have a meaningful effect on the feature itself. These can slow things down a lot; letting the engineer choose the path of least resistance on trivial details speeds up development and keeps everyone happier.
The curious engineer who is looking for an excuse to use a certain technology - Someone who has just read about a specific language/technology/pattern is likely to want to use it; even if it's just subconsciously it will be higher up in their mental hierarchy of tools if they've thought about it recently. There's also the more malicious version, where an engineer wants to add a technology to their resume, so they force it into a project that it doesn't fit.
Perfectionist engineers who want to implement everything in the "correct" way - I've seen proposals for features that are quite literally 100 times more complex than they need to be, just because an engineer spends so much time in their head thinking about all the details and abstractions that they lose sight of the end goal. Usually it's pretty easy to bring them back down to earth, but they can sometimes be quite convincing, since they've clearly spent a lot of time thinking about it, even though they're well beyond the requirements of the project.
I think the situation in the OP is pretty hard to overcome, since you're fighting an ingrained system with the players are used to and comfortable with the rules of the game. In other situations like the ones I mentioned, if you have a bit of influence there's a bit more hope of bringing things back to a simpler norm.