Ask HN: Strategies for working with engineers that are too smart?

509 points by throwitawaaay ↗ HN
There are a couple of engineers on my current team that I can only describe as being a little too smart for their own good, and I'm struggling a bit with how to work with them. I've worked with this sort of engineer on previous teams as well, and they all share a few traits:

- They're brilliant, I mean very smart people (in an almost academic way?)

- They have a big appetite for adding complexity to systems

- They also have a big appetite for adding work to their own plates

- Their code has no consistent style

I work in embedded systems, so I'm generally writing C for resource-constrained systems. This sort of environment is rife with footguns, and I spend most of my time just trying to avoid those. A big part of that is keeping the things that my team controls as simple as possible, and while we are resource constrained, it's a balance. The tension comes when someone's more than happy to, for example, implement a complex caching scheme from scratch to save a few hundred bytes here, a handful of microwatts there. To me, adding that type of complexity for those sorts of performance improvements is missing the forest for the trees.

When an engineer like this proposes something that adds unnecessary complexity, it's usually hard to argue with. The proposed change typically does indeed make the system objectively better, and they're the one taking responsibility for doing the work and ensuring its correctness. But the overall system becomes a little more brittle, and a little harder to reason about, two things that are much more difficult to measure than memory and power.

Here's an example: an engineer on my current team recently proposed adding a significant feature to a module I wrote to make something dynamic that's currently statically defined at compile time. I pointed out that we could just change a couple entries in a static table to accomplish his goals, and we were able to avoid the extra work. What was notable, though, was his immediate willingness to write that feature. Maybe I'm lazy, but I'd sit there and think of half a dozen other ways to do it before settling on changing the module itself. But because he's smart enough to easily reason about a complicated solution, and he's willing to take on the extra work, he stopped there without weighing it against the larger system.

Have you worked with engineers like this? Do you have any thoughts on how to work with them in a productive and friendly way?

--

One more example if you feel like reading more:

In a past role, I got into a debate about code style with the smartest person I've ever worked with. It boiled down to me advocating for more whitespace in his code and him arguing that adding whitespace made the code harder to read. It took a bit, but eventually I came to understand that he is so good at reading code that he just wants it all laid out in front of him as densely as possible. He can effectively run it in his head, as long as he can see it.

That was baffling to me, I walked away thinking that I must be pretty bad at reading code. I use a very consistent style with long variable and function names, I keep my solutions as simple as I can, and I use whitespace generously to provide visual cues about the code's structure. All of this is to minimize the amount of brainpower I needed to understand my code, so I can put that energy toward thinking about the problem itself.

486 comments

[ 4.0 ms ] story [ 122 ms ] thread
Have you tried explaining it to them in exactly the way you have written it here? What was their response?

In my experience, this happens quite often with people coming from academia because they're used to write throwaway code that doesn't get read by others and doesn't need to be maintained. It's only used temporarily to publish papers. However, most of the people I have worked with were aware of the fact that their code didn't follow software engineering best principles, they just didn't know how to do it better.

Another factor may be ownership. Sometimes people are excited to implement something because they like the idea of owning part of the stack, e.g. the caching implementation, instead of constantly implementing minor improvements in other people's code. Perhaps these people are looking for ownership of something more so than a specific complex solution?

You can always set up the editor to hide excess whitespace, but it's harder to tell the editor to insert whitespace at semantically meaningful places. So you should do it your way, as it makes it easier for more people.
> When an engineer like this proposes something that adds unnecessary complexity, it's usually hard to argue with. The proposed change typically does indeed make the system objectively better, and they're the one taking responsibility for doing the work and ensuring its correctness. But the overall system becomes a little more brittle, and a little harder to reason about, two things that are much more difficult to measure than memory and power.

I don't think you (and they) can be sure that the changes make the system objectively better. You may have a hard time measuring the downsides, but they're still here. And that may be the root of the problem. You can argue with an engineer about rate of bugs, power consumption, average time to deliver a feature. But it's hard to argue about "the system being more brittle".

One way out would be to try to have metrics on the downsides of the changes that they propose. Another would be to try to make them understand that even if something is very difficult to measure, it's their job as engineer to take it into account.

I will say this without any malice: the way you describe them, they don't appear to me as "too smart" but simply as focusing on the part of the job where the results are easily measurable.

If its more than one engineer I would say there is a very real chance that the issue is the manager.

Sometimes things need to be more complex than they first appear. Think all that pesky error checking that you totally don't need as long as everything is working perfectly for an extreme case.

If the engineers are experienced think strongly about letting the do the work as long as they are ones who will also take the pain of the complexity. Also having them explain why something is needed to the rest of the team is a good way to have everyone on board with a decision and it helps bring the rest of the team up to speed.

Being smart is not the end of the game, software is created by teams. You could ask them to make reverse code review so that they can enjoy each others smarteness.
The grug brain developer website might help articulate some of the ideas you're trying to communicate to your "smart" peers.

https://news.ycombinator.com/item?id=31840331

If you don't mind my asking, what does this mean? I've seen it a bunch lately and haven't been able to look it up because I haven't figured out how to convince Google that I want to search for a literal "* * *" and not wildcards.

Edit: When I wrote this I just saw a comment with the text "* * *".

Strange at no point did I have that as the comment. I haven't edited the comment. Maybe it's the 2 minute delay I have on my comments?
I have set up that email service that sends you a message when someone comments on your post and I've noticed an increase in the * * * notifications over the last while.

I think it may be a side effect of that delay feature or a change in the HN site that renders these comments in a way it shouldn't?

Thanks. I love that service!
This website is gold. It manages to be at the same time funny and full of actually smart observations.
> They have a big appetite for adding complexity to systems .... What was notable, though, was his immediate willingness to write that feature.

From your descriptions, it seems less about "adding complexity = good", and more about "solving things in a neat way = good" (for some definition of 'neat').

> Do you have any thoughts on how to work with them in a productive and friendly way?

I'm not sure you've articulated the problem very well?

It sounds like if they left & the code was handed to you, you'd be concerned about hard-to-spot bugs in the sophisticated code. -- But if the code & its correctness is your colleagues responsibility (& not yours), what's the problem?

> It boiled down to me advocating for more whitespace in his code and him arguing that adding whitespace made the code harder to read.

I think you came to the right conclusion about the differences. But, you might enjoy this blogpost from Steve Yegge:

http://steve-yegge.blogspot.com/2008/02/portrait-of-n00b.htm...

I think what you’re trying to say is that these types of solutions are fine, as long as they are decided upon by the entire team, and not just a single engineer: long-term maintainability is key here.

I’d also argue productivity is an issue: “neat” solutions may take much longer to develop, while the effort is not really justified. E.g. starting out with a system based on CQRS which is just a simple CRUD app.

Jesus fuckin Christ. I tried to read this blog post about how newbies make their code comments too verbose, and the post turned out to be longer and about as interesting as fucking Dianetics.

tl;dr, young programmers, ignore that schmuck. Please keep writing nice long comments to your heart's delight. You will thank yourself one day.

> about how newbies make their code comments too verbose

I don't Yegge is trying to make this point.

Instead, he's making a similar observation to the discussion OP had about whitespace/code:

Yegge reckoned that someone who's very familiar with the program/domain will want to have very dense code, so then they can make as much use of screenspace as possible.

Whereas, for someone who's unfamiliar with a domain will benefit from seeing less code on the screen at once. It will be easier to digest.

I don't really see that as a value judgement where "comments = bad".

One of us missed the point because I just added that to my permanent bookmarks as an excellent explanation of why some people write overly verbose (and hard to read and thus maintain) code, while others manage to write the same thing in a single, understandable screen-full.

" I would rather puzzle through a small, dense, complex piece of code than a massive system with thousands of files containing mostly comments and whitespace" <-- This, a thousand times. _I_ simply don't have the capacity to load giant nonsense class hierarchies into my head and actually execute such a program.. A small machine that I can actually see on on the screen, that I can manage..

really... if you've ever tried to write your own functionality into something like wordpress or bootstrap or whatever, you'd rather have that all in a single code file with less white space or comments? Would that make it easier to change the positioning of a particular dialog box? I thank God all the time for the white space and long comments left by writers of those kinds of libraries. Maybe those seem like trivial examples, but they're huge libs that don't often need a lot of changes; yet when you need to change things, they make it quite easy to find.

[edit] as I wrote that, I just realized that younger devs might actually look in horror on the idea of directly editing libraries like that for particular deployments because the tooling or shop mentality so forces automated upgrades that could overwrite custom changes... and institutional memory being so short, no one might remember the change some dev made to a bootstrap file. heh. But that's like someone calling themselves a mechanic without ever modifying an engine. You write a note, commit it with a custom marker like #weModifiedThis, and don't upgrade the library for 15 years or until you're dead or retired or the company goes out of business. Done.

There's a balance to being overly verbose and isolating _EVERYTHING_ from _EVERYTHING_ to such a degree that it's a major travel all around the codebase to follow one little path through a single call, and cramming _EVERYTHING_ into one file.

I'm arguing for a balance.

This is true and reasonable for small systems and code bases, and small teams. But it’s not generally a good idea to argue for a relaxed balance in large systems or code bases, nor with large teams, where you can travel all around the codebase via roads you built and mapped, or you can travel all around the codebase following bugs into code you don’t know caused by lack of isolation. Eventually, if you don’t isolate, then there’s no avoiding traveling all around the codebase, it’s just whether you want that to be a pleasant journey or not. Of course it’s really hard to argue over programming platitudes without any specifics in front of us to discuss, but chances are high that experience over time with large systems on large teams will make you reflexively avoid arguing for balance when it comes to either comment verbosity or code isolation.
Wow. I would LOVE to only work with "noob" programmers who nicely explain their reasoning for complex code. Comments are easy to ignore anyways.
Maybe give them hard problems to solve? My bet is that they are looking for recognition or excitement, or both.
Agree, when the problem is presented clearly, engineer will try to find a way around it.
It's a tricky balance between keeping smart engineers happy and keeping things simple.

The underlying problem is that you don't need that smart of an engineer and he's being underutilised.

You should move him to another team where he can do meaningful work without having to come up with complications of the existing codebase.

You can also put more on his plate in the form of people work / leadership; it's a different skill to master and if you constrain the time he has available to do work, he'll be forced to keep it simple.

I definitely had an "overcomplicating" phase (whether to play with something new or just because the job is boring). Right now I just constrain my time for tasks, by doing a lot of different things and I need to keep things as simple as possible.

I still write a few 2000 characters throwaway one-liners every once and then when I get something done quickly, though.

I want to support the proposed solutions in this comment.

A long time ago I was like the engineer that is described by OP. Basically what drove me was the search for engineering /coding challenges.

Being easy to reason about is so important. In my experience this is somewhere Security teams often make what I consider mistakes. Although something may appear to make the system more secure, it can make it much more complex, and then troubleshooting it or changing it becomes so burdensome that people skip upgrades, use weak passwords, and a litany of other workarounds to be able to actually use the thing to do their work. You also have no visibility into where holes might be. For example by creating unique interfaces for every operation, you do separate concerns. You also ensure no one will have time to review them all. Interesting tradeoffs.
Adding complexity is fast and easy, adding simplicity is slow and hard.

Any codebase that is simple to understand and modify is a second party's canvas to add complexity in record time, for the purpose of telling a third party they added complexity in record time.

Imagine you get permits, rip out a bathroom, choose tiles and spend a long time cutting them. Someone comes in overnight and superglues them to the floor, and not even the pattern that was intended, but they did the main work and in only a few hours.

Or you need two feet of pipe to hook up the sink in a straight line but you come back the next day to see your “smart” pipe fitter has made a 10 foot pretzel
The endless fights about whitspaces seem so ridiculous to me. The points made by both camps are entirely valid (I prefer adding whitespace to layout blocks of functionality), but it feels like this should be something that an IDE should take care of. There should be the least amount of whitespace-changes in the VCS as possible, and the IDE should present the code how the programmer wants it to see instead. Similarly, questions such as curly brace placement and whitespaces between operators could be dealt with.
That is how my company works, everything is automatically formatted before commit, there are no debates. That's how languages like Rust and Go (and JavaScript/TypeScript via Prettier) have ended up also.

Same with lints, set up a company-wide lint configuration, apply it automatically, auto fix if possible.

You can gracefully accept input and not act on it.

People often have a need to be heard, to expose to the world what's going on inside their mind, to be appreciated for their ideas. You can serve that need in a friendly way by listening attentively and being genuinely curious. You can also suppress that need inside yourself and avoid responding with your own critique and argumentation.

You just listen, ask some questions, show that you are interested, thank them for their idea, and then decide for yourself if you want to act on it, and if you don't, then don't.

In most cases the topic will never come up again. If they nag, just be very positive, remind them that you really liked their idea, that you identify what's valuable about it, but don't make any commitment to acting on it.

I can understand the approach; avoiding any confrontation, but from the other side it will seem like gaslighting.

I suspect you need some balance in the time it takes to debate these ideas, so it might be better to ask them to put their proposals on paper with a word limit and a time limit to debate the ideas; they are heard, and not only that but the ideas are debated. If you time limit not only the time available to debate ideas for each individual idea but also limit the time for debate overall it will require your reports to think selectively about what ideas are really good and which ones are worth the time to pursue.

I think arguing each case individually is a trap to avoid. A method I've used with lots of different groups of people, from groups of 4 year olds up, to dev teams, to groups of CEOs, is at the start of a project get everyone together and mutually come up with a set of ground rules around standards and conduct. So things like - we will use this coding style, we will ensure that every function is documented, we will not pursue premature optimisations as long as we are under x power budget, we will not introduce complexity that will be hard for others to reason about etc.

You will have ideas about what these standards should be and you should feel free to propose some yourself during the process, but the aim is not to impose them on the group but to come up with something cooperatively upon which you get consensus. Then write down what you have agreed, stick it in a prominent place and make sure people stick to it. The process should only take about 10 minutes.

Then whenever one of your engineers comes to you with something that goes against the mutually agreed contract you can just flat out say - no, you agreed we wouldn't do that and move on. If something isn't covered by it then have a discussion.

Speaking as a long-time lead, they're under-challenged and looking for hard problems.

I've dealt with the whitespace issue by 1) moving to clang format with Google style, and 2) getting people who want less whitespace etc to mentor more junior members of the team who can't read code that dense or quickly

"Objectively better" and "brittle" are at the opposites.

Perhaps they are not too smart but instead "too generative". I.e there are people who generate a lot of code but then don't do any weeding in what they did. If they continue to be responsible for that corner of the code base then everything might be fine, but if other developers pop in and out of it as well, then it will get increasingly difficult to manage.

I'd say mark out the area they are allowed to do this and keep them within those limits :). They probably will enjoy this as well as they'll have something they have autonomy and responsibility over.

It seems the people you describe lack empathy for their fellow developers. I would let them go...
What role do you play in your current team? Are your teammate relatively junior (writing one liner smart code does appear common in junior engineers in my experience)? Can you set the tone on what is defined as good behaviour/good engineering? The other side is also frowned upon sometime - making glue code that just works. Do you have a yardstick to measure team's success? Does the team's behaviour align with what the product/company set out to achieve? At the end of the day, all style works, at the end of the day it is about making product that's useful within a reasonable time frame

Obligatory mention https://xkcd.com/974/

Firstly this trait is not at all exclusive to smart engineers - everyone falls into this trap at some point.

I think one thing that's helped teams I've lead/helped lead in the past, and helps with a huge host of team working issues too, is by setting norms, and documenting them.

For example, in your team's "norms and values" or "way of working" doc, you could include simple things like coding style (in JS land most people just say "use AirBNB style" here).

But it can absolutely also have things like "don't add unnecessary complexity".

Reading your post, I think you can even quantify it too. You mention being able to count the number of bytes or microwatts any given approach will save or cost, that it's objectively measurable? This is great - you could decide (as a team, or let the team lead or whoever else decide) what the approximate limits should be. Complexity itself can also be measured (e.g. cyclomatic complexity). So you could actually be "real engineers" and decide what the trade offs are for your team, or for your target hardware.

All of that aside, it's also good to try and keep an open mind to other peoples' solutions and communicate with each other when deciding what the best approach is. Open, transparent and prompt communication is worth so much, so make sure your team keeps working on that too. (I've worked with some embedded teams that were distributed and almost never spoke to each other. It made things... interesting!)

IMO smartness is just a bar you need to pass. Once you pass that bar, then things like diligence and humility become more important.
You’re describing people who believe that code is an asset, when the reality is that it’s a liability: zero lines of code would be ideal if you could get away with it, but you can’t, so you have to compromise and limit that liability as much as possible by minimising the amount of code and relentlessly keeping it simple, easy to read, etc. Perhaps they’d be more sympathetic to your perspective if you framed it that way?
Though code is an asset as well... Just not as valuable as people might think. Code is not a liability if it is not used, the liabilities come from production usage.
Code is only an asset to the extent it fulfills a need that can't be otherwise fulfilled.
I agree with what you mean, but I’d nitpick and say it’s not the code itself that’s the asset, but rather the behaviour of that code when executed. If you could refactor and get the same behaviour with less code (whatever that means), that’d be a net win, right? So the quantity of code is making a negative contribution.
Put another way, the feature is an asset, the code is a liability.
I consider code to be a liability, and Git history to be an asset. If something's in version control, there's no need to keep unused cruft around "just in case" it's useful in the future.
Yeah but then they just argue with you about whether it's simple. There's a certain kind of person who loves taking complex things and calling them "simple".
True enough. I think when we say “simple” we often really mean “cheap” (to maintain), so maybe it’s better to cut to the chase and just say that instead.
They’ll say it’s simpler (and better) if everything is a microservice.
It might be that they use simple in relative terms.

Linux could be considered simple given the problem it's trying to solve and yaml complex.

it's more often that they don't understand what simple means for most people

For example they might think that in a mixed-paradigmatic language the "pure functional approach" is more simple even if it introduces more indirection and requires a deep understanding of various more advanced language constructs(*).

Or that the simplest explanation of monad is the one based on PL-theory instead of an explanation based on terms people without an scientific-PL background are more familiar with.

Or people which think that writing down things in predicate logic is just way simpler then writing them down in English (well, I guess thats somewhat me).

None of the "I find this is simpler" points I mentioned are bad, but not realizing/accepting that what is simpler for you isn't simpler for others is the problem.

Rich Hickey has an excellent talk "Simple Made Easy". He points out that "simple" and "complex" describe objective properties. The terms are often (incorrectly) used interchangeably with "easy" and "hard", which are subjective.
Valuing systemic simplicity over simplicity of a feature in isolation is one view that gets me into a lot of disagreements.

Sometimes the simplest overall solution isn't so simple in isolation.

I have explained security, maintenance, cost, etc. using pretty much these exact words.

It's really hit or miss when it comes to having any impact.

Unfortunately only 1 developer in 20 thinks like this.
The whole "code is a liability" mantra needs challenging IMO - it's too simplistic and "cargo culty". It's a misappropriation of a financial accounting term (where Assets = Liabilities + Equity) and liability also carries with it all kinds of negative associations.

It has a value in making businesses aware that production code has an operational cost just to keep it "alive". And it's useful for tempering junior developers who haven't yet absorbed principles like KISS and YAGNI.

But it fails to factor in the relation between code and an organisations ability to make (or save) money from that code. For example the capabilities of your code may be a competitive edge for your business. 10 lines of code which uses CPU and memory inefficiently may end up being more expensive than 1000 lines of code which uses CPU and memory _efficiently_ and leads to lower infrastructure costs as usage grows.

As far as I'm aware the term goes back to 2007 - https://web.archive.org/web/20070420113817/http://blog.objec... - a time where businesses hadn't really understood how to manage software based businesses, so it was a useful concept to help management become more mature about how they think about software.

But these days knowledge of how to run software based businesses is far more widespread - we don't need to simplify our thinking to "code is a liability".

Liabilities aren’t negative in financial accounting, they’re leverage for the business that carry obligations, exactly like code.
Leverage is a combination of an asset and a liability. You gain assets in the short term in exchange for a long term liability.

There is a difference between code and a liability. If a business could wave a magic wand and instantly remove all liabilities from the balance sheet, it would do so without question. They would not, however, do that for code (or the business would collapse).

Would you prefer "code is a depreciating asset"?
Something like: software is an amalgamation of the desirable capabilities it provides, which are assets, and the code itself which must be maintained and as such is a liability. Complex and convoluted code represents a greater liability.

Part of your job as a commercial software engineer is to maximize the balance sheet of the projects you work on.

what a load of crap, you mean linux kernel is a liability. this is typical thinking of folks who are removed from technical work & just consider development as 'cost'. Code & development history is an asset because it stores the time-series sum total of development decisions to get to this point. keeping it readable I can understand but pointlessly minimizing it without the bigger understanding of what it serves because .. 'hey it works' is trivializing it a bit.
That kind of developer is dangerous in a C code-base because they cannot leverage a type-system enforce proper usage of the code constructs that they create.
Requirements are a useful tool to direct the effort of a team of engineers in a productive direction. They don't have to be set in stone from the get-go - in fact, it's better if they are updated by different people as new information about your product becomes available.

This focuses discussions about when it is worth it to spend time micro-optimizing certain aspects of the system. If there's a requirement that you must consume less than X μW while in standby - then it may make sense to accept the increased complexity.

I agree that the problem is that the company doesn't seem to use requirements to guide the daily work. It should be easy to decide whether certain work is necessary - either you have a requirement or you don't.

Without requirements, people will always leave a trail of chaos in their wake as they do whatever is fun or good for their career and resume. The only advice I have for OP is to not be the sucker who acts as janitor for the messes they leave behind.

I hear you on this, I've worked with folks who have made what should be relatively simple code both undecipherable and unmaintainable, all in the name of some spurious benefit but really just to try out some approach they've read.

My response would be to ask them to support some other code for a while, turn the tables somewhat and argue against any whacky changes in the name of maintainability.

> - They're brilliant, I mean very smart people (in an almost academic way?)

That's good, though sometimes the "academic way" can be a problem when the aim is to build a commercial product: the engineering vs research dichotomy.

> - They have a big appetite for adding complexity to systems

That's not good. One way to influence is to promote simplicity on the grounds of testing and maintenance.

If you mandate unit tests and a minimum level of code coverage, for instance, which is useful but which developers tend to hate doing, it may nudge people into thinking about simplicity.

There's also the good practice of not doing more than currently necessary.

At some point it's down the the person of authority to accept or reject change proposals and to promote/enforce good practices.

> - They also have a big appetite for adding work to their own plates

That's good as long as they can consistently deliver and they don't overwhelm themselves .

> - Their code has no consistent style

That's usually sorted by having a formally described (in a document) coding style and conducting code reviews. People will quickly closely follow the prescribed style in only just to avoid having their code rejected in code review on style grounds.

At least in the Go community we have much less debate around whitespace in code because gofmt.