Trying to impress people with your code

By Vladimir Khorikov

In this post, I’d like to write about the personal traits we all possess. Namely, I want to talk about being proud of your code and trying to impress people with it.

Trying to impress people with your code

Most programmers like to program. No surprise here, of course. Otherwise they wouldn’t become programmers in the first place. It is also common for us, humans, to be proud of our craft and even brag about it from time to time. That is totally understandable and shouldn’t be frowned upon by anyone.

However, the subject of that brag often tells a lot about the values the programmer carries. It’s a good litmus test for the principles he or she thinks are the most important in their job.

I remember a couple years ago someone boastfully demonstrated me a piece of code similar to the following:

IReadOnlyList<Customer> customers1 = GetCustomersFromFirstSource();

IReadOnlyList<Customer> customers2 = GetCustomersFromAnotherSource();

 

foreach (var customer in customers1

    .Select(x => new { x, IsFromFirstSource = true })

    .Concat(customers2.Select(x => new { x, IsFromFirstSource = false})))

{

    if (customer.IsFromFirstSource)

    {

        DoSomething(customer);

    }

    else

    {

        DoSomethingElse(customer);

    }

}

What does this code can tell the reader? Well, it certainly tells you that its author knows LINQ and can work with anonymous types in C#. However, as far as the actual purpose goes, it is not as obvious.

This code can be easily simplified so that the need for both of these features vanishes:

IReadOnlyList<Customer> customers1 = GetCustomersFromFirstSource();

IReadOnlyList<Customer> customers2 = GetCustomersFromAnotherSource();

 

foreach (Customer customer in customers1)

{

    DoSomething(customer);

}

 

foreach (Customer customer in customers2)

{

    DoSomethingElse(customer);

}

This version is not as exciting, of course. Moreover, it’s a plain bore to write such code. How can we apply our abilities and demonstrate them to the colleagues when we are supposed to write code like this? Shouldn’t we as programmers try to fully use all our skills and knowledge at work? A little bit of brag wouldn’t hurt either.

The line of thinking above isn’t completely wrong. As I mentioned, there’s nothing wrong in trying to impress people with your code. A mild brag indeed wouldn’t hurt anyone. However, many programmers choose an improper subject for their brag. They try to optimize the solution by maximizing the number of features and unique programming techniques applied in it. Whereas, a better optimization target would be simplicity.

Instead of showing how many language or framework features you know, try to impress your colleagues with how simple the solution you came up with is. I’ve written about it before but it’s worth repeating it here: the simpler your solution is, the better you are as a programmer. A sign of the greatest mastery is building a code base the purpose and the execution flow of which is so obvious that it becomes boring to read it.

It doesn’t mean you should avoid difficult tasks. In fact, your ability to deliver simple solutions even for complex problems is what you should aim for. Programmers who would support your code base afterwards will be grateful for such an attitude of yours.

It may sound unattractive but to become a great software developer, you should embrace boring code. The simplicity of your solution and the boredom of the developers reading it is what you should strive to achieve. Don’t try to impress people with how many language and/or framework features your know. Instead, impress them with how you are able to simplify the problem you were given.

Trying to impress people with your architecture

The same is true when you are building a high-level architecture for your application. It’s often the case that a programmer tries to foresee the future needs and include as many “configuration points” to the application as possible. They try to build a framework which will stand the test of time in that it won’t require a lot of modifications even in the face of possible requirement changes.

It shouldn’t be a surprise that one of the reasons for such a behavior is an effort to impress people. Many programmers consider their ability to build an architecture which is complicated enough to handle requirements changes a good indication of their great abilities as software architects.

That is also a false goal. A general guideline when you design your application should be to delay as many architectural (i.e. “set in stone”) decisions to the future as possible. You never know for sure whether or not your current implementation will be sufficient for the future needs. And you never know whether or not the configuration points you laid in your code base will handle everything the business will require from it.

The only way to reduce the amount of work you will need in the future is to build as specific solution as possible for the requirements you have at hand. So specific that it will be easy to understand and change it if needed.

So, again, trying to impress people with your architecture is fine but there’s nothing impressive in an overcomplicated solution which tries to handle requirements no one even posed. The simpler your architecture for a given problem is, the better you are as an architect.

Here’s a nice figure that depicts all said above:

Trying to impress people with your code: types of problems vs types of solutions

Types of problems vs types of solutions

Summary

A little bit of brag in our work is fine, we all like to be proud of our craft. However, don’t try to impress people with how many features you used in your code or how complex the architecture of your code base is. Impress with the simplicity instead. Ideally, your code should be so simple that reading it becomes a plain bore.

Related articles:

LinkedInRedditTumblrBufferPocketShare




  • Anders Baumann

    “… to become a great software developer, you should embrace boring code.” Very well put, Vladimir. Great advice and I completely agree.

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Thanks :)

  • http://sidburn.github.io/ David Raab

    I somehow agree to what you write, but depending on what you have written on your blogs previously i think that i have something different in my mind than you want to tell.

    For example. Let’s assume we want to download something from a website. Well i’m using a “pseudo F#-language here”. Probably you have code like this.


    let fetchData url =
    let content = HTTP.get url
    let transform = someTransformation content
    return transform

    Well it works, but it is synchronously. So when you want to make your code “asynchronously” how do boring code looks like? This is boring code.


    let fetchData url = async {
    let! content = HTTP.get url
    let transform = someTransformation content
    return transform
    }

    And actually for me. This is also what is impressive. This is not only impressive it also is a simple solution. It is “simple” because “async” handles the asynchronous. You don’t have to care about it, you just write your code as if asynchronously doesn’t exists at all.

    But at this point is what i think our opinion diverge. What happens if your language don’t have “async”? Sure you should write a whole library to achieve a similar solution! At best is if you are already using a library that does that for you. If none exists, you have to write your own library doing that.

    And if you do that, you also have to look at all possibilities that can happen with async, and all ways how you can solve the problems. What you really need is a “simple” solution that handles async on its own.

    And i don’t think that this is what you meant. From what you describe, you always just write the minimum amount of code to somehow fix a problem. That is not simple, that is easy.

    What you will probably end up is not a simple solution. You end up with a complex solution. Probably everywhere at every place some kind of thread is spawning, concurrent data-structures or whatever is thrown throughout your code. Calls of event handlers for notification when an operation finished. and so on. What you end up with is a complex solution, not a simple solution.

    For a simple solution we have to look at the problem “asynchronous” itself. And try to provide a full solution around it. or in other words. You have to abstract all kind of asynchronous requirement until it becomes simple. If you don’t do that, you don’t end up with simple code.

    And that is also the reason why coming up with a simple solution for a complex problem is hard not easy. Sure, it is damn hard to write your own async solution! Absolutely, but if you want a simple solution, and there don’t exists libraries or language constructs that solve that problem for you, that is what you have to write on order to get a simple solution! But once you have your simple solution, everything becomes easy, or in other words, boring.

    I also cannot agree to your table. “Complex problem” and “complex solution” is not “OK”. It is also “Everyone can do that”. Not only “Everyone can do that” it is more a “Nearly everyone does that”.

    But overall i think a better term instead of “boring code” is the idea of “Use the least powerful concept to solve a problem”. Because “boring code” is pretty subjective. It also might be hard to discuss whether feature X is more powerful than Y, but a discussion around this is probably more helpfull as a discussion around whether some code is boring or not.

    As for your example. “foreach” is usually a powerful concept and it is better to a less powerful LINQ method. It is less powerful because it usually can do less compared to “foreach” and thus it is easier to understand. Sure your example is still a good one and we shouldn’t use Select/Concat in such a way. But the problem is moreover that “the least powerful tool” was not used. Select/Concat itself doesn’t solve the problem at all. They just add more useless stuff in that sense. A real better solution even compared to the foreach version would be if you have a more descriptive method. At least in F# we have “iter” for that purpose, so a simple code should more look like


    GetCustomersFromFirstSource() |> List.iter DoSomething
    GetCustomersFromSecondSource() |> List.iter DoSomethingElse

    And in that sense. If you want a simple solution it makes sense if you add an “Iter” Extension Methods, so you could write.


    GetCustomersFromFirstSource().Iter(DoSomething)
    GetCustomersFromSecondSource ().Iter(DoSomethingElse)

    It is more simple, because you don’t use a powerful “foreach”, and replaced it with something less powerful but more descriptive instead!

    And sure, at first this can create more work, because it is more work to create an Extension Method instead of just writing a plain old foreach.

  • Slade Stewart

    Let me say from the start that I agree wholeheartedly with your premise and I imagine there would be a lot of philosophical agreement in general between you and me (not 100% because that would be boring :) ). However, I think often programmers hide under the rubric of simplicity, a fear of or unwillingness to explore concerns that they haven’t been exposed to yet. One saying I like is “Strive to make everything as simple as possible… but no simpler.” I can probably never be accused of the latter (often, sometimes with justification, the former :) ). We should remember that when OO was first introduced, many programmers from the discipline of Structured Programming called it over-complex. Much of the ‘complexity was simply because it was new, and whatever complexity was actually there was helping to solve more complex problems (thus reducing overall complexity); but a lot of programmers hadn’t yet learned to recognize those problems, thus all they saw was the ‘added complexity’ of OO. Often it’s the same thing with design patterns, etc.: until someone is able to step back and see the complexity that is being solved, all they see is whatever complexity is added (and their perception is heightened because they equate unfamiliarity with complexity).
    As just one example, in many shops I’ve seen inheritance be the ‘go-to pattern’, overused, and it’s causing many problems and costing the company money. However, the programmers haven’t yet learned to see this, so if I try to introduce them to some of the patterns (Strategy, etc.) meant to make software more composable and **reduce** complexity, they will start to complain that my way of doing things is overly complex.
    Often with a little patience, etc., I’m able to make my case and we end up striking something closer to the right balance and trade-offs (with simplicity being one of the concerns) i.e. once the developers get past their concept of ‘one-size-fits-all’ simplicity, they’re actually able to contribute to making code that is often simpler than my original code, while still reducing overall coupling and complexity, etc.
    Some shops, though, will continue to rail against ‘over-complex code’, ‘over-engineering’, etc., while still continuing to pay crippling maintainability costs (and complaining about how hard the code base is to work in, all the time). For various reasons (including but not limited to this), those shops are death marches, etc., and it’s best to leave them to their fate.

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Thanks for your comment. I think a fair share of confusion comes from conflating simplicity with easiness. In order to learn OOP, we need to make some effort first, but it pays off by leading to simpler solutions in a long term. The same is true for any programming concepts that stood the test of time: functional programming, design patterns, etc.

      And I 100% agree that it all comes down to familiarity. A person can make a justified decision about a particular solution only if he is familiar with the technology he’s voting against. Otherwise, it’s often a matter of human stubbornness.