Note: While I am using Go in my example, most programming languages and their developer communities apply.

One of the things I instantly liked about Go was its focus on simplicity. A lot has been said and written about how the language - by being small and reduced of wiggle room, can lead to code that is equally easy (or difficult) to understand by all team members.

I agree that while working with the language, I found many of these to be true. The compact syntax, coupled with the handy standard library, works in favor of focusing on the problem at hand. Ultimately, one of the best things about using Go felt the lack of need to prove you're smart by coming up with clever but convoluted solutions for easy problems. In my days of writing Java code for a living, it was common to judge someone's work by the patterns and constructs one used to solve a problem.

And yet, the language offers far more room for creative exploration than many so-called" purists" would like to admit. I believe that many of us bringing our baggage and experience bonded prematurely around the idea of using Go as our emotional outlet. A counter-reaction against the building of "sandcastles" that happens everywhere else. Hiding behind terms like "idiomatic", we are quick to dismiss any solution that feels like it's going to destroy the perfect shelter we built. Often, to the point that we intentionally shoot ourselves in the foot or by discouraging newcomers who need more time to adapt.

Be pragmatic, not dogmatic.

I've read somewhere that it is not the smartest or the strongest species who survive, but those who adapt best to the changing environment. That applies to software too. In my career as a developer, I claim to have seen it all - from perfectly designed behemoths that failed to catch up with the changing nature of the problem they were solving to million-dollar apps built out of a single PHP file.  Admittedly, both had to be re-written at some point, but I guess which side you'd rather be sitting on.

My point is, whether starting from scratch or maintaining a 20-year-old monster, you should move forward by making pragmatic decisions, not ones infused with dogmatism. It's good to question why things are being done a certain way. That is where knowing the fundamentals and core principles is essential. Most of the time, there is a pretty logical explanation.

Idioms don't fall from the sky. They evolve from the ground up.

There are, however, that 20% of the time, when being dogmatic, will cost you 80% of the development efforts. Honestly, for more than ten years roaming around teams, I have not been on a single one where they'd meticulously followed everything that an "idiomatic" codebase should have. They'd follow most of the guidelines but would leave out the ones that bring little value to the product or the culture of the team. As for team newcomers, with enough time and patience, anyone proficient enough in any language can get up to speed into the "lingo" of the particular team.

Case in point

One particular point of tension in the Go community is error handling. While no one denies that error handling is essential, one might argue whether the excessive use of:

val, err := somethingThatMightFail()
if err != nil {
	// handle error or return it

otherVal, err := somethingElseThatMightFail()
if err != nil {
	// handle error or return it

makes code easier to comprehend. Feel free to challenge me on that, but I think it wasn't the original intention of the Go team either. It just naturally arose based on the fact that returnVal, err and err turned into language "idioms." Since everyone started using them, everyone else had to comply, which naturally led to situations like the one above - complex logic requiring multiple error checks per function/method. In a widely quoted but little understood (in my opinion) post titled "Errors are values", Rob Pike tried to let people open up the imagination, pointing out that errors are not some magical language constructs. Rather, errors are plain values - they can be passed around, but also stored, transformed, or even ignored.

Take the example above. What if we used a simple pipe instead:

var err error
var val Something
var otherVal OtherThing

err := P().
	Next(func() error { val, err = somethingThatMightFail(); return err }).
	Next(func() error { otherVal, err = somethingElseThatMightFail(); return err }).
	Next(func() error { return yetAnotherThingThatMightFail(val, otherVal) }).
	// ... and so on

if err != nil {
	// now, we handle the error once

Is it "idiomatic"? Hell, no. Have I seen it work in real projects? You bet. And it's much more efficient than anyone would assume by just looking at the multiple function calls. Remember that compilers have one or two tricks in their sleeves. Will I use it in every situation? Probably not, but I won't blindly deny the possibility. Depending on the case, it might be easier to glance at the logic and maintain/modify it in the future.

The same kind of thing can be said about pretty much any aspect of the language that is considered "non-idiomatic".  Ask yourself and your team what idioms apply to your style of work; what helps your team deliver better products—those you can allow yourselves to be dogmatic about (most of the time).

Focus on shipping things instead.

Errors are values - The Go Blog
Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.