66 comments

[ 3.5 ms ] story [ 120 ms ] thread
I wrote on characteristics of a great software developer a while back.

http://www.supercoders.com.au/blog/50characteristicsofagreat...

The problem is that people try to recruit for these things.

When recruiting, follows Joel's advice - smart and gets stuff done.

Probably the first time a see a list of 50 points in which I wouldn't object to any of them. Impressive.
How did you finish the list? I'm still processing the last two items.
Really impressive work. You've taken "smart and gets things done" to the next level.
That is a fantastically comprehensive list - I was racking my brain for a while to try and think of something before I replied. Not sure if this one is covered already but maybe...

Student; is able to ask for help.

A lot of these are hard to objectively test for (in a recruiting sense) because they can be subjective or be dependant on the working environment itself. It never hurts to have more information that less, though!

This list is depressingly long.
Great write up. One of the challenges is definitely finding a system that recruiters can use to prioritize these things.
I'm generally not a fan of recruiters simply because the vast majority of them are sales people first, and tech people second (or often no technical experience at all). Compounded by the fact some people interview very badly but are good engineers and visa-vera.

Even if you have experienced engineers doing the screening process (separate from the company being applied for) I still think it would be challenging because theres no "correct" way for a team to work and companies are so vastly different.

It definitely is something to think about. If you do some up with something, let me know :)

Great list. One I would add would be understanding what the client's problem and building an appropriate solution. Too many times I've seen developers just dive in coding up the solution the client suggested without trying to understand the problem the client is trying to solve. For example, by adapting the problem to using an existing CMS or SAAS you can avoid a huge amount of custom coding.

A good developer would be able to write simple code for a given solution but a better developer would suggest a simpler solution leading to even simpler code.

This is exactly the thing that makes a person a "10x" developer. Being able to write lots of reliable code in a short amount of time is a good trait, but knowing when not to write code is equally important.
For me this falls in to the "understanding" category. The problem your trying to solve and what the client wants are often mixed up as the same thing - but they seldom are.

You shouldn't expect that the client will know what they want even if they ask for it. There is however always an underlying reason why they think they need it. Take the time to understand that rather than just more billable hours. It will be better for you and them in the long run.

In my experience, laziness is one of the most important traits of a good developer.
To be truly lazy you must get it right the first time, and write the minimum possible code.
You need the right kind of long term laziness - but yes.
You have to be careful how you define 'lazy' in this case. Suppose a developer just quickly writes code that fixes the current problem, but doesn't care about the consequences long term, or doesn't fix the underlying problem - not good.

Now, if you define 'lazy' as someone who will go to great lenghts to not do boring tasks in the future, yes, I completly agree :)

Great article. I think you have hit on something here that only comes with a lot of experience and looking back and what really worked. I rarely see an article that I think YES. TOTALLY. Well done!
Being able to step outside software engineering when engineering software.
Yeah! This.

'Perspective' ...

Mini perspective: step away from the desk, do something literally creative. I play my guitar.

Major perspective: get the hell out of the city, actually do something on a weekend. Come back - all of a sudden you have fresh eyes and see things more clearly.

I actually think that soft devs should take more vacation.

You know that professional concert musicians only practice a few hours a day - any more is pointless. You just can't concentrate that long.

A really gutsy move would be for Google to give a bunch of 'long weekends' where nobody can work, or even more vacation. And see what the results are.

Fresh eyes on the problem is always good, usually after lunch or you have slept on it.

I find when my brain starts to grind to a halt I just go an do another un-related task to force myself to completely forget about the original task. It does help because when I go back to the original task my brain has to context-switch back into it and in doing so it may look at the problem a different way.

Extra holidays would be great! However, I think more holidays would make some engineers more productive and some less productive... I'm not sure if Google is still doing their 20% time, and if they are I know it doesn't apply to all the FTE engineers.

I've heard people say laziness is important. However that is a misnomer, the reality is the best inventors and engineers are sort of long-term lazy. They will spend 40 hours building something to automate a thing you can do in 15 minutes. But, they know that they or others will push that button so many times the 40 hours will come back quickly over time.

An example of this is a strong senior engineer might take a lot of time laying the foundations of a project, the scaffolding needed to really make coding a breeze and enjoyable in the future. This is because they're lazy, they want the future to be easier so they invest today to make the future just a little bit easier.

>They will spend 40 hours building something to automate a thing you can do in 15 minutes.

That's because building something new is exiting and interesting, while doing something boring for 15 minutes is ... boring?

The point is more that by saving those 15 minutes, it's a cumulative saving for all future use cases. Though, I don't think I fully agree with this in all cases. It can be hard to predict when something really is going to be used often enough that this is important.

IMO you build the minimal thing that does the job, then after understanding the use case go back and refactor to deal with the accumulation of 15 minute pain. Doing it right at the beginning is the definition of premature optimization.

And on the topic of building new, this is a mistake many developers make: that it's easier to build new, then to refactor existing. What they fail to realize is that the old code they think is too difficult to maintain is battle tested, in production, has operational understanding associated with it. All software goes through this cycle, and so while it's fun to build something new, you are inviting debt while doing so b/c of the unknown production issues and edge cases that need to be figure out after it goes live. It's not always wrong to rewrite, but IMO, it's a last resort. BTW, this is why SOA is important: strong defined contracts/APIs between services allow for components to be replaced with a higher degree of confidence.

And will start to pay for itself after repeating 120 times. Although, this assumes the 40 hours includes any necessary maintenance. Let's be honest: It doesn't. Although, your coworkers lack proper checklist vigilance, and forgetting steps may have other costs!
The mouseover text of that last one is very relevant to the discussion.
Yep, here it is for everyone that doesn't want to waste a browser tab:

"I find that when someone's taking time to do something right in the present, they're a perfectionist with no ability to prioritize, whereas when someone took time to do something right in the past, they're a master artisan of great foresight."

#974 is great! I know I have been guilty of #1319, when I really should of just done the repetitive task instead of spending so much time thinking it would save me some time in the future.
Totally agree. I think when people hear the word "lazy" they think "incompetent", like "they didn't know how to solve the problem, so they took a lazy route by not doing it very well" when this is not what is meant by the phrase at all, it is exactly what you said.

I would also say that it's doubly important with the experience because I've seen people spend far too much time one something that they think will save them time in the future, but that future changes. It's a balancing act.

Good post. I think too that reducing code is key. Here is how I like to show it.

Let's see what impact "size of code" has on "time it takes to add a feature". Just consider a graph where the X axis is "size of code" and the Y axis is "time it takes to add a feature". Obviously, the function is increasing, and probably faster than linearly.

Let's see what impact "size of code" has on "number of bugs". Same thing.

Let's see what impact "size of code" has on "time it takes to find a bug". Same thing.

Let's see what impact "size of code" has on "time it takes to resolve a bug". Same thing.

Let's see what impact "size of code" has on "time it takes to refactor a piece of code". Same thing.

Let's see what impact "size of code" has on "time it takes to a newcomer to be productive on that codebase". Same thing.

And on and on and on.

There is two canonical points on those functions. The first canonical point is when the codebase is too big for the value it brings. When you are here, you are in deep trouble. Basically, the cost of doing anything in that codebase is too high in comparison of the value it brings. A sign you are here is when you spend more time in meetings discussing what to do, than time coding.

The second canonical point is when the codebase is notably small for the value it brings. When you are here, the project looks like magical. The programmers look like 10x programmers. Indeed, doing anything on the codebase costs surprisingly little in comparison of the value it brings. A sign you are here is when you are not asked for estimations.

You should totally turn this into a xkcd-like comic ;)
> 1. Simplicity Reduces Code

I always follow the KISS principle, if possible. It makes maintainability so much easier, not only in Software but also in Hardware. Every additional layer of complexity adds to the probability of failure.

Fewer lines of code don't always mean simpler code. Sometimes it means cleverer code (read: harder to understand and maintain). Three quotes I frequently repeat in this context:

1. Duplication is better than the wrong abstraction

2. Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.

3. Code must be written for people to read, and only incidentally for machines to execute

Yeah. This.

I think that sometimes people that 'more compact code' is what we mean be 'less code' and it's really not.

I think the differentiation is at the algorithmic level: it's not algorithm density, it's 'system clarity' that's key.

I find a nice rule of thumb is 'don't code two steps ahead'. Just because you may think you need to write a couple of 'duplicate lines' here and there, don't even bother abstracting for it. Wait until there's a few more.

Software people tend to outsmart themselves and build way ahead of what they need.

I think it's better to 'discover' the minutia of architecture than to plan, it all but the grandest elements.

An artists drawing a figure will 'sketch out the form' - that's the basic architecture you can do up front, but it lacks detail. The 'art' comes out incrementally piece by piece.

And the human aspect: it should hopefully read like English to a casual observer.

It also helps to think of it from a business perspective:

+ What the code does is an economic asset

+ But actual lines of code are a cost centre and an ongoing liability.

Yeah when I find duplicated code I always ask myself is this duplicated because this is representing the same domain concept in two different places? Or is it duplicated by coincidence?

I'm much more aggressive about refactoring the former than the latter. When I was first starting off it was always hard for me to tell the difference so I abstracted a lot of things that I shouldn't have, and you could tell from the terrible method names that follow leaky abstractions.

One of my biggest pet peeves is code duplication from lazy copy/pasting. I find it's a good exercise for myself to sometimes pretend like the copy/paste key are not available to me.

I had to think about the first one for a moment, but I do agree. It's better to abstract later with greater knowledge than the much more difficult task of trying to debug or fix it later.

It's an outstanding list and great rules to work by!

> I find it's a good exercise for myself to sometimes pretend like the copy/paste key are not available to me.

This is a great idea! I'll try to remember this whenever I have the urge to lazily duplicate some code (which is quite often now that I think about it).

But you have to have a model for various costs ( and risk is a cost ) of copypasta vs. Well-Organized-Completely-Normal-Independent-Code.
What do you most developers hate? Legacy systems!

Why? so difficult to change, poor documentation, no tests, etc.

A great software engineer, goes for simplicity but not in terms of laziness.

1. They keep the system simple enough and decoupled in the right way so that it's easy to change!

2. They have tests, not just unit tests, unit, functional, integration.

3. They document everything, code docs, design docs, design decisions docs, data structures docs, how to build, how to debug, how to maintain and change, etc

4. They do the really difficult stuff, they don't write the software for themselves, the develop for humans, customers. It's not about what you like, it's about what your customers like. This means having meeting and talking to others, getting feedbacks.

5. They keep learning, but they don't jump and adopt every fancy new technology, they stick to well proven stacks, so they tend to be boring.

There should not be long strings of commits that focus on features. Continuous development means continuously looking after developers, too: intersperse commits that remove code, increase documentation, update versions of dependencies, fix ancient bugs or whatever else eases maintenance.

Test suites should not just be thrown together, where every test is a "diff" of a giant log file! You do NOT want to be constantly fixing “tests” that “fail” due to trivial differences. Ensure that your code can completely expose all relevant state for one feature so that you can write precise checks and ignore any other output.

There should be no compile-time options (like #ifdef) that are not automatically built before every commit. Developers change things, and they have to know exactly what can break by doing so (especially with languages like C++, where a tiny change can cause true chaos). Put another way, don’t set yourself up to discover problems weeks from now at the worst possible moment; build defensive code and infrastructure.

The unspoken subtext is, of course, "as simple as possible while still adding business value". For example, there may be external constraints (contracts, regulations) that require more convoluted code. There may be certain business situations where there just has to be complexity.

In these cases, we should strive to understand: * the complexity and the sources of it * how the complexity will change, and how much (as best we can, as predicting the future is hard) * what, if any, of that external complexity needs to be in the system we're building, and what can be pushed into human hands * how best to encapsulate that complexity so it doesn't spill over into parts of the system where it needn't be

It's all well and good to say "keep it simple", but that can be difficult when there are external forces that are saying "the system must do X when Y, A when B, and C when D, except when X. Oh, and if Y then C, except when A", etc,etc.

It's not just contracts and regulations.

Example: Bob the sales manager doesn't want to view reports through your handy web portal. Bob wants them formatted in Excel sheets, zipped, and emailed to his corporate Exchange account daily. Bob is the only person in the whole company who doesn't like your portal, but he is powerful enough that his whims automatically become mission critical requirements. You either give Bob what he wants, messing up your clean design to implement functionality just for Bob, or you have failed to meet the business's needs and will lose the contract, end of story.

Before you aim to become great by reducing your code please become competent in absolutely necessary basics: DRY, separation of concerns, basic patterns, security, etc.
Yes and no. I think we read these things in textbooks and either don't truly understand why they exist until we've made the mistakes or we go too far the other way and start dictating best practice on to everything.

It's best to be be somewhere in between. It can only be learned by doing, not studying.

My thoughts conveying similar message from a different angle: http://hakunin.com/cms-trap (for those who might've missed it when it got upvoted on HN 3 years ago).
There is a fine line between short code and readability, which is, by far, one of the most important factors of code quality. Say you have a code:

  $this->executeAfter(5);
This is a short code, but it's not readable. What the hell is 5 here? Let's improve it a little bit.

  class Time {
    
    protected $seconds;

    private function __construct($seconds)
    {
      $this->seconds = $seconds;
    }

    public static function inSeconds($seconds)
    {
      return new self($seconds);
    }

  }

  $this->executeAfter(Time::inSeconds(5));
I've just added ~10 lines of code, but the readability of executeAfter is way better. In addition, the executeAfter method is more flexible, since it can manipulate the Time object now.

This is a very basic example to illustrate what I meant when drawing a line between the short code and readability.

For magic numbers, just use a well named variable, constant or configuration parameter.

private const TIME_DELAY_SECONDS = 5; $this->executeAfter(TIME_DELAY_IN_SECONDS);

I'm a fan of named parameters, too:

    # python
    self.executeAfter(func=myCallback, delay_in_seconds=5)
    # or
    self.executeAfter(func=myCallback, after=delay(seconds=5))
What this example is really lacking is a TimeFactoryFactoryAbstractSingleton to make it extra clear what that whole '5' thing is about.
I believe you have missed the author's point - "Using more code is actually the lazy fix." The author would recommend something like:

    $this->executeAfter(5); // Execute after 5 seconds
The above code is only 1 line, and is very clear. It does not add a brand new, unnecessary class, nor does it increase the surface area for bugs (adding a new class opens up the potential for new bugs, while adding a comment does not).
This is not entirely true. It depends on the language, but the definition of executeAfter for the most of dynamic languages would be something like

  public function executeAfter($seconds)
This code does not guarantee that the value of $seconds is correct, whereas this:

  public function executeAfter(Time $time)
...will help engineer to not make silly mistakes.

It definitely depends on the language, but I personally like self-explanatory code and tend to think that inlined comments should only describe things that require explanation, such as complex logic.

Or, you know, you could just rename it

    $this->executeAfterSeconds(5);
Actually, this is the kind of complexity and misunderstanding that makes software more difficult to maintain.

If you step back and understand that the problem is the method name is not clear, creating more code to get around that is totally unnecessary - just fix the method name. The other repliers hit the nail on the head with better options.

I personally would opt for the renaming of the method first:

    $this->executeAfterSeconds(5);
If it was a third-party library or I was able to refactor the method itself I would go for constant named after what it represents, rather than what it is:

    $this->executeAfter(FIVE_SECONDS);
As a last resort (like if you had some reason why would couldn't physically change the source) a comment would also be totally acceptable.
"The best engineer will write zero code" - unfortunately, the author has forgotten to tell us what she does do.
I'm a software engineer. If that's not clear from the article, you should be able to tell from the bio at the top and bottom of the page.
I'm sorry, I may have been ambiguous. I was actually wondering what the best engineer does.