[HN Gopher] Go Style
       ___________________________________________________________________
        
       Go Style
        
       Author : tomcam
       Score  : 310 points
       Date   : 2022-11-18 09:28 UTC (13 hours ago)
        
 (HTM) web link (google.github.io)
 (TXT) w3m dump (google.github.io)
        
       | ltbarcly3 wrote:
       | I think on average google stopped being a technical leader years
       | ago. They produce a fair amount of technology, but they also
       | employ nearly 1% of the software engineers in the country. That's
       | a long way to say I would gladly wager that there are dozens of
       | people who read this thread that could produce by themselves and
       | in one session a document which would serve as good or probably
       | better than this.
        
       | mroche wrote:
       | I found the following statement in the Maintainability section
       | interesting:
       | 
       | > _Maintainable code minimizes its dependencies (both implicit
       | and explicit). Depending on fewer packages means fewer lines of
       | code that can affect behavior. Avoiding dependencies on internal
       | or undocumented behavior makes code less likely to impose a
       | maintenance burden when those behaviors change in the future._
       | 
       | Obviously this guide was written for internal Google use, but
       | it's interesting to see a recommendation that appears to run a
       | bit counter to how ecosystems tend develop for languages that
       | have easily consumable packaging solutions. It's not uncommon to
       | look at a Go, Rust, Node, Python, etc project and see _many_
       | dependencies (direct and indirect) in use, most of which you have
       | no idea what they 're being used for outside of some of the most
       | popular packages. Not that I think the suggestion is wrong, I
       | fully support fewer dependencies and using external packages
       | where they make sense. I start to get uneasy when you see a tree
       | of dependencies of unknown code, but I don't have the time myself
       | to read through all of those packages.
        
         | xavdid wrote:
         | There's definitely a balance to strike.
         | 
         | On the one hand, you don't want to be constantly re-inventing
         | the wheel. If someone has made a great package for doing a
         | specific thing you need (printing data in tables, making
         | ergonomic http requests that cover edge cases, reading an epub
         | file, etc), then IMO you're better off using theirs. They care
         | about it, they've tested it, and the implementation of it is
         | one less thing you have to think about it. If you consider that
         | every line of code that you write/maintain is a liability, then
         | offloading that to others is very convenient.
         | 
         | On the other hand, external packages typically come without
         | guarantees. Their owner/maintainer could lose interest, get hit
         | by a bus, get hacked, anything. You're running their code in
         | your projects and suddenly, all this uncontrolled code becomes
         | the liability. You'd do best to do _everything_ in house in
         | order to limit your external risk.
         | 
         | Ultimately, I think there's no single right answer here. Sure,
         | you probably shouldn't add dependencies for things like "left-
         | pad", but the line is a little blurrier for things like npm's
         | "is-promise". It sounds simple enough (and the implementation
         | [0] is only a 3-line function), but I'm unlikely to have
         | written it correctly if I did it from scratch. Plus, it's
         | tested! And lastly, I think your risk is lower the larger an
         | external dependency is. Large projects, like React or Django,
         | are much more upside than liability; it's everything in the
         | medium range that you really need to consider.
         | 
         | [0]: https://github.com/then/is-
         | promise/blob/ec9bd8a3f576324a1343...
        
         | gilgad13 wrote:
         | I imagine that opinions such as this have influenced this
         | recommendation: https://research.swtch.com/deps . I think there
         | is some spirit in Go of not taking on a ton of small
         | dependencies, but that may be a hold-over from before Go had a
         | built-in package manager.
         | 
         | I think as an organization grows to some combination of
         | available resources and severity of an outage this view becomes
         | more and more common.
        
         | CSSer wrote:
         | A lot of this comes down to available resources too. I used to
         | hate how many third party things we used on principle and
         | because I directly felt all the pain of tying them together.
         | Now I'm a lead and I still dislike it but because I have a
         | realistic read on how much bandwidth my team has I think this
         | has balanced my view. I also still feel the pain but sometimes
         | more indirectly (code reviews, for example).
         | 
         | I think where I still struggle is "rules for thee but not for
         | me". At some point, someone on the team (usually the person in
         | charge) is just going to be better at picking which external
         | libs to let in. I find this really hard to teach without it
         | seeming arbitrary. I find it a lot easier to teach good coding
         | habits.
        
           | kevmo314 wrote:
           | This works with high quality or high familiarity libraries,
           | which usually stem out of doing something very simple, but as
           | I've gotten more experienced I've learned that most libraries
           | it's faster and clearer to write the one or two functions I
           | need myself instead.
        
       | frou_dh wrote:
       | Kinda amusing how the following section is complicated by the
       | language's casing-based visibility feature
       | 
       | https://google.github.io/styleguide/go/decisions#initialisms
        
         | toastal wrote:
         | I like this. Generally speaking, information is not lost in
         | this form--as in form does not change for initialisms. We have
         | the casing on purpose in English to communicate that you are
         | looking at an initialism or abbreviation so why would you lose
         | that to change in variable names? Obviously there are some
         | cases this doesn't work as many languages variable names need
         | to start with a lowercase letter, hence the need for the
         | section.
        
         | watt wrote:
         | Indeed, this is one Go convention that I strongly dislike. Java
         | did have that approach once (HTMLDOMURIReference, XMLIDREF and
         | the likes) but they learned their lesson.
         | 
         | And what's the basis of capitalizing D in ID?
        
           | benji-york wrote:
           | I also dislike "ID" vs "id" (as short for "identity"), but
           | there is some case for it. The below are from Webster's Third
           | New International Dictionary Unabridged.
           | 
           | First, the case for "id":                   id abbreviation
           | 1 [Latin idem] the same
           | 
           | Now for "ID"                   ID noun, plural ID's or IDs
           | [REVISED] [identification]         1 a : documentation
           | bearing identifying information
        
           | Joker_vD wrote:
           | Because "ID" is short for "Identification Datum".
        
             | benji-york wrote:
             | citation needed
        
           | icedchai wrote:
           | It is commonly abbreviated that way in normal life (see
           | https://en.wikipedia.org/wiki/Identity_document ) When I see
           | "Id" in code, I wince.
        
             | euroderf wrote:
             | Rage. Raging Id.
        
       | throwaway2203 wrote:
       | Go is absurd. It's opinionated in all the wrong ways.
       | 
       | > Functions that return something are given noun-like names.
       | 
       | > // Good: > func (c _Config) JobName(key string) (value string,
       | ok bool)
       | 
       | > A corollary of this is that function and method names should
       | avoid the prefix Get.
       | 
       | > // Bad: > func (c _Config) GetJobName(key string) (value
       | string, ok bool)
       | 
       | That's dumb. I'd like a function to be GetJobName to indicate
       | that it doesn't mutate anything. Maybe CreateJobName to indicate
       | mutation. Just JobName is useless.
       | 
       | The other day, I spent a whole day trying to figure out the
       | "idiomatic" way to return a an object not found case from my db.
       | Do you return a nil pointer (don't, passing pointers leads to
       | bugs), or an empty struct (then how do you reliably "test" its
       | emptiness?) or an error? And of course, there's no real hierarchy
       | of errors, no built-in extensible handling of errors that is
       | semantic and makes sense, so every project just goes and
       | reinvents their wheel.
        
         | Zamicol wrote:
         | I mostly avoid getters/setters in Go code, unless I have a lot
         | of functions all related to the same thing similarly named.
         | It's the return type that denotes the return. It's already in
         | the function signature.
         | 
         | That advice is a consequence of Go expecting function
         | signatures to be read and understood. That's not the right
         | choice for all programming languages, but for Go it works.
        
           | throwaway2203 wrote:
           | I mean, you can't get around database CRUDs. I'm not talking
           | about setting a private variable and getting or setting it
           | (that doesn't seem like idiomatic Go at all), but using the
           | logic described above, you can't know whether a method with a
           | noun-based name is a Read or a Create.
        
         | jrockway wrote:
         | > That's dumb. I'd like a function to be GetJobName to indicate
         | that it doesn't mutate anything. Maybe CreateJobName to
         | indicate mutation. Just JobName is useless.
         | 
         | It can't mutate anything with a non-pointer receiver. Mostly.
         | 
         | > how do you reliably "test" its emptiness
         | 
         | See time.IsZero() for an example.
        
         | Thaxll wrote:
         | If you rely on naming function to know if things can be mutated
         | you're doing things wrong. The only source of truth is the type
         | received.
        
           | throwaway2203 wrote:
           | Why? Let's say I have a table full of cars and have a method
           | 
           | > func Car(id string) Car
           | 
           | How do I know whether it fetched that from the database or
           | created a new one and returned that to me?
        
             | Thaxll wrote:
             | Only the type Car matters, why would the function name
             | tells you anything about mutation? It does not tells you if
             | you receive *Car or Car or if it's a Car but pointers
             | inside.
        
               | notpushkin wrote:
               | Because I want to know what function does?
        
               | Thaxll wrote:
               | I think it's not possible to achieve with func name on
               | its own, the closest thing I see would be comments on the
               | function but again not reliable imo.
        
               | throwaway2203 wrote:
               | Absolutely not reliable. Comments get stale faster than
               | anything else in the codebase.
        
         | Beltalowda wrote:
         | Just assume that "Noun()" gets the noun and it's effectively
         | the same as "GetNoun()". Of course, nothing in the language
         | prevents you from using "GetNoun()" if you strongly prefer
         | that.
         | 
         | > The other day, I spent a whole day trying to figure out the
         | "idiomatic" way to return a an object not found case from my
         | db. Do you return a nil pointer (don't, passing pointers leads
         | to bugs), or an empty struct (then how do you reliably "test"
         | its emptiness?) or an error?
         | 
         | sql.ErrNoRows is often used for this.
        
       | sidlls wrote:
       | > A little copying is better than a little dependency.
       | 
       | Go takes this to an extreme, though. Generics helps, but the
       | implementation is so limiting that it doesn't help much.
       | 
       | > The general rule of thumb is that the length of a name should
       | be proportional to the size of its scope and inversely
       | proportional to the number of times that it is used within that
       | scope.
       | 
       | Some (most) of the go code I've seen where I work suffers from
       | Enterprise Java style identifiers. That is,
       | TheyAreGenerallyTooLongAndIHateThemAll.
        
         | thomascgalvin wrote:
         | This is probably because Go doesn't allow function overriding.
         | You can't have `streamFromUrl(foo)` and `streamFromUrl(foo,
         | bar)`, so you end up with `streamFromUrlWithFoo(foo)` and
         | streamFromUrlWithFooAndBar(foo, bar)`.
        
           | [deleted]
        
           | sidlls wrote:
           | No doubt: and that is generally the pattern I've seen. It's
           | really an eyesore.
        
           | everybodyknows wrote:
           | Consider variadic arguments for such cases.
        
             | sidlls wrote:
             | There is a group of developers where I work who would take
             | a perfectly fine `New(...*Options)` constructor and write a
             | whole suite of "option wrapper" "utilities" around it,
             | rather than expose that function. It makes me tear up a
             | little (not in a good way).
        
       | leetrout wrote:
       | > Go interfaces generally belong in the package that consumes
       | values of the interface type, not a package that implements the
       | interface type. The implementing package should return concrete
       | (usually pointer or struct) types.
       | 
       | I like this rule. Most companies violate it everywhere. There are
       | good times to ignore it but I always push for
       | func NewThing
       | 
       | To return something other than the interface type. The last Go
       | interview I was in I brought this up and the argument was that
       | you cant test things if you are not returning the interface and I
       | disagree entirely.
       | 
       | I love the power of duck typing with interfaces but give me
       | concrete things that quack.
        
         | robmccoll wrote:
         | Yep - return concrete types, but also make sure you have an
         | assertion along the lines of:                   var _ IfaceType
         | = &ConcreteType{}
         | 
         | Somewhere, else you risk not knowing that a change to
         | ConcreteType broke is implementation of IfaceType in a way that
         | you might not know about until a rare runtime code path is
         | executed.
        
           | masklinn wrote:
           | > Somewhere, else you risk not knowing that a change to
           | ConcreteType broke is implementation of IfaceType in a way
           | that you might not know about until a rare runtime code path
           | is executed.
           | 
           | Why would runtime be involved? Surely if ConcreteType doesn't
           | satisfy the interface anymore then the compiler will catch
           | that at any site where a ConcreteType is being used as / cast
           | to an IfaceType?
           | 
           | Or are you talking about iface->iface side-cast?
        
             | jrockway wrote:
             | The compiler will catch it at the point of use, but it's
             | clearer if the error is at the point of declaration.
             | Consider:                  type FooReader struct{}
             | var _ io.Reader = FooReader{}             func (r
             | FooReader) Raed(p []byte) (int, error) { return 0, nil }
             | 
             | Before you even use FooReader as an io.Reader, you can find
             | that you typo'd "Read". This is useful in practice because
             | you probably wrote FooReader to have a bunch of other
             | methods that aren't implementing io.Reader, and it's
             | possible that your tests for this object don't actually
             | ever use it as an io.Reader. So you won't notice this
             | mistake until someone tries it elsewhere in the codebase.
             | 
             | It also acts as documentation that you intend for this
             | thing to be an io.Reader. But I also recommend that you
             | document Read as "// Read implements io.Reader." to be
             | extra explicit.
        
               | masklinn wrote:
               | Hah, how's that for an endorsement of structurally typed
               | interfaces.
        
         | masklinn wrote:
         | That rule is not without its issues when pointers get involved,
         | due to the typed nil problem.
        
           | gregwebs wrote:
           | Yeah, this is a setup for hard to understand bugs unless you
           | have a corresponding rule (imposed by a linter) that a
           | concrete type cannot be cast into an interface and then
           | checked against nil (mostly this means when returning a
           | concrete type from a function it should be assigned to a
           | freshly declared variable rather than re-using a variable
           | that could already have an interface type.
        
         | dpifke wrote:
         | I prefer to make the implementation package-private and hide it
         | behind an interface if there's no way to make the
         | implementation's zero value useful. Doing so prevents someone
         | from trying to use it without calling the (mandatory)
         | constructor.
        
         | jrockway wrote:
         | Interfaces are the most misused feature of Go. I think people
         | like to prototype their API by typing in all the functions they
         | intend to implement, so the interface acts as a template for
         | their program that they can fill out. This, unfortunately, is
         | not what they're for.
         | 
         | I have a longer rant about this here:
         | https://jrock.us/posts/go-interfaces/
        
         | gouggoug wrote:
         | Slightly unrelated to Parent:
         | 
         | I struggle very hard creating my own interfaces for my own
         | programs. I haven't found any literature online teaching the
         | _right way_ of coming up with your interface.
         | 
         | For example, I very often struggle with function parameters and
         | return types: should my interface functions only take basic
         | type and return basic types? Can my interface function take
         | more concrete types? Should my interface function take
         | interface types?
         | 
         | I'd love to be directed to a guide on how to create a good
         | interface.
         | 
         | As an example, here's a recent interface declaration I wrote:
         | type Checker interface {             Check(context.Context,
         | *object.Commit, bool) (bool, []string, error)
         | Name() string             Describe() string         }
         | 
         | My program runs through a list of commits and runs "Checks" on
         | them. These checks "pass (true)" or "fail (false)".
         | 
         | I want the user of my package to be in charge of the
         | implementation details of a "Check", so I created the interface
         | above.
         | 
         | Is it OK for the Check() function to take a *object.Commit?
         | Should I instead pass a `SHA string` and let the implementer
         | figure out how to get a *object.Commit from that string?
        
       | whalesalad wrote:
       | golang driving me nuts these days. enjoying the performance, but
       | time.Parse put me into a ragequit mode last night and I really
       | wish it was possible to return a thing OR nil
        
         | is_taken wrote:
         | Why not writing a small wrapper around time.Parse?
         | func ParsedTimeOrNil(s string) *time.Time {         t1, err :=
         | time.Parse(time.RFC3339, s)        if err != nil {
         | return nil        }        return &t1         }
        
           | twblalock wrote:
           | Hiding errors like that is going to make it hard to figure
           | out what is going on as more and more wrappers like this pile
           | up.
           | 
           | If time.Parse is failing, you either have bad input or a bug,
           | right? If you are ok with this failing without even logging
           | the error, something might be wrong with the design.
        
             | jrockway wrote:
             | Hiding errors saves less than a second of typing, at the
             | cost of making every five minute bugfix a one day bugfix.
             | Don't do it.
             | 
             | You can't build your own programming language inside of Go.
             | If you absolutely cannot mentally handle functions
             | returning an error and you having to type "if err != nil {
             | fix the problem }", you really need to find a different
             | programming language. But, errors happen all the time, and
             | handling them correctly is the difference between an
             | unreliable piece of garbage that randomly fails and
             | software you and your users can trust. There is,
             | unfortunately, no automation around making software
             | reliable.
        
               | twblalock wrote:
               | Plus, even if you just return an error/bubble up an
               | exception, that is probably better than bubbling up a
               | nil/null value for some of the consuming code to try to
               | dereference.
        
               | jrockway wrote:
               | Bubbling up is a great option. It is such a good
               | opportunity to capture relevant context that the caller
               | doesn't know about; like the internal state of the
               | method, and the "why" behind making the call. If everyone
               | does this, you can end up with an error message like
               | "handle /ping: health check servers: ping server 1.2.3.4
               | (3/42): i/o timeout" instead of just "i/o timeout" (which
               | you get if you just lazily "return err"). You see the
               | first error message, and you know you need to adjust the
               | ping function to treat a timeout as a part of the
               | response. You see the second one, and all you can do is
               | scratch your head. It could be anywhere.
               | 
               | (BTW, stack trace proponents... stack traces don't
               | capture loop iterator variables, or "why" you're calling
               | a particular function. But when you write a quick error
               | message with fmt.Errorf, you can include all of those
               | things.)
               | 
               | Software engineering is a job of continuous improvement.
               | Make it really easy to find the right place to target
               | improvements!
        
           | Cthulhu_ wrote:
           | This is the answer. Write a utility instead of get frustrated
           | with the language; be pragmatic. Same with looking for
           | libraries that do something trivial.
        
             | whalesalad wrote:
             | I'm used to parsing a date time with things like %Y-%m-%d
             | not the asinine way Go does it.
        
               | abdusco wrote:
               | If you're using JetBrains Goland, hit Ctrl+Space when
               | you're writing the date layout in `time.Parse`, and it'll
               | suggest you the regular YYYY, MM, dd etc. placeholders.
               | When you pick one, it types in 2006, 01, 02 magic
               | numbers. Here's a GIF:
               | 
               | https://imgur.com/FyCJZvh
               | 
               | That said, I hate this, all the ways it's opinionated and
               | praised zealously for it as the best thing since sliced
               | bread.
        
         | charlieflowers wrote:
         | You can make your own Maybe with generics.
        
           | still_grokking wrote:
           | And why isn't this build-in since the beginning like in any
           | other sane language?
           | 
           | A language where you need to constantly write wrappers around
           | everything just to get basic functionality is quite a
           | miserable language, imho. That's like C...
        
             | masklinn wrote:
             | > A language where you need to constantly write wrappers
             | around everything just to get basic functionality is quite
             | a miserable language, imho. That's like C...
             | 
             | Which makes complete sense, since Go was designed as
             | Google's C for Dummies.
        
               | still_grokking wrote:
               | I know. Even by the same people that like to repeat their
               | mistakes.
        
             | charlieflowers wrote:
             | Completely agree.
        
       | encryptluks2 wrote:
       | Interesting that Google uses GitHub still. With Microsoft owning
       | it and it becoming less free than alternatives, you'd think for
       | documentation like this they'd use an externally hosted GitLab
       | instance for their external projects or that they'd have acquired
       | something to compete with GitHub by now like Gitea.
        
         | ErikCorry wrote:
         | They could call it "Google Code" /s
        
         | metaltyphoon wrote:
         | Less free? Microsoft didn't change a damn thing on GH, in fact
         | its only getting better after acquisition.
        
           | jen20 wrote:
           | Another case of the lack of distinction in English between
           | "libre" and "gratis" causing problems, I see.
        
         | jefftk wrote:
         | https://gerrit.googlesource.com/
        
           | jeffbee wrote:
           | It is nevertheless the case that the Go Style Guide and all
           | the other style guides were officially hosted in the past at
           | https://google-styleguide.googlecode.com/ and now that's a
           | 404.
        
       | cytzol wrote:
       | I found this "best practice" curious to read:
       | 
       | > The standard net/http server violates this advice and recovers
       | panics from request handlers. Consensus among experienced Go
       | engineers is that this was a historical mistake. If you sample
       | server logs from application servers in other languages, it is
       | common to find large stacktraces that are left unhandled. Avoid
       | this pitfall in your servers.
       | 
       | I don't think I've ever seen a server library -- HTTP or
       | otherwise -- that _didn 't_ have a top-level "catch all
       | exceptions" or "recover from panic" step in place, so that if
       | there's a problem, it can return 500 (or the Internal Server
       | Error equivalent) to the user and then _carry on serving other
       | requests_.
       | 
       | My reasoning is that any panic-worthy programming error is almost
       | certainly going to be in the "business logic" part of the server,
       | rather than the protocol-parsing "deal with the network" part,
       | and thus, recoving from a panic caused by processing a request is
       | "safe". One incoming request could cause a panic, but the next
       | request may touch completely unrelated parts of the program, and
       | still be processed as normal. Furthermore, returning a 500 error
       | but having nobody read the stacktrace is bad, yes, but it's way,
       | way, _way_ better than having your server crash meaning nobody
       | can use it ever.
       | 
       | Oh wait, is the assumption here that your service is being run
       | under Borg and has another 1000 instances running ready to jump
       | in and take the crashed one's place? Is this another case of
       | Google forgetting that people use Go outside of Google, or am I
       | reading too much into this?
        
         | kevincox wrote:
         | I've always thought that the ideal is somewhere in between the
         | two.
         | 
         | 1. Catch the panic/exception.
         | 
         | 2. Track the rate of these panics or exceptions. If it is too
         | high some data structure has probably been corrupted or some
         | lock has been poisoned. If a lot of requests are failing abort.
         | 
         | And ideally: 3 signal that you are in a degraded state so that
         | some external process can gracefully drain your traffic and
         | restart you. Although very few people have this level of self-
         | healing infrastructure set up.
        
           | llimllib wrote:
           | I wrote a web server that handled a lot of requests, and my
           | solution to 2. was to have it notify me via our alert chat
           | channel every time it panicked. This was rare enough that I
           | could investigate them individually, and if it got
           | overwhelming (it never did) I could choose to do something
           | fancier.
        
         | otabdeveloper4 wrote:
         | Presumably you have a load balancer in front of your Go code
         | that's written using saner languages and assumptions that
         | _does_ handle errors correctly.
        
         | marcus_holmes wrote:
         | If your program fails in testing, and no-one reads the logs,
         | does it ever get fixed?
         | 
         | There is one advantage to not having a top-level catch: if it
         | fails in testing, it's very obvious immediately.
         | 
         | Though I do agree with you - and this can be done with a
         | feature flag for dev/prod servers.
        
         | zerr wrote:
         | No, that's about error handling: panic/recover in the standard
         | net/http server is used in lieu of exception handling, which is
         | absent in Go. Instead, they (and official Go docs as well)
         | recommend to use "if-hell".
        
         | andrew_eu wrote:
         | The Echo web framework is an interesting example of this [0].
         | By default it doesn't handle panics, but there is a
         | configurable `Recover` middleware which does so. I agree though
         | it's unusual, and this stuck out to me as an exception that
         | proves the rule.
         | 
         | [0] https://echo.labstack.com/middleware/recover/
        
         | enneff wrote:
         | The question: what is the state of your server after a handler
         | panics? The answer: you have no idea. It is not wise to
         | continue serving requests when you may have serious issues in
         | the state of your server. Maybe some central data structure is
         | now corrupt. You have no way of knowing. Fail fast and fail
         | hard.
         | 
         | OTOH, maybe your priorities are different and you would prefer
         | to be more available than correct. In that case by all means
         | add a recover to the top level of your request handlers. But it
         | was a mistake to have made this decision for all users of
         | net/http ahead of time.
        
           | Zach_the_Lizard wrote:
           | In my experience, the panic is most likely because someone
           | accessed a nil field when adapting some data. Nothing is
           | corrupt, we just threw an exception in a mundane way.
           | 
           | The reality is this is far more common than something truly
           | fatal. Mistake for someone or not, it probably is correct for
           | most use cases
        
             | masklinn wrote:
             | > In my experience, the panic is most likely because
             | someone accessed a nil field when adapting some data.
             | Nothing is corrupt, we just threw an exception in a mundane
             | way.
             | 
             | Even more so if a database is involved (which is generally
             | the case), because odds are the transaction just gets
             | rolled back and there's basically nothing that could be
             | corrupted.
        
             | Cthulhu_ wrote:
             | But that implies your request checking is off; if your
             | server expects a field to be filled in and a client doesn't
             | send it, that should be a HTTP 400 Bad Request error, not
             | an internal server error.
             | 
             | C/C++ based servers running into an error like that would
             | possibly be open for attacks. Go will be a bit more
             | resilient, but it's still better to avoid situations like
             | that.
             | 
             | That said, logging the error and going on serving responses
             | is fine I think (pragmatic), as long as the error is
             | analyzed. But an error that doesn't trigger immediate
             | action is a warning, and warnings are noise [0].
             | 
             | [0] https://dave.cheney.net/2015/11/05/lets-talk-about-
             | logging#:...
        
             | throw827474737 wrote:
             | Yes, why they made that decision back then. However in the
             | end the lib cannot and shouldn't know, and this decision
             | needs to be intentionally done by the middleware. Correct
             | for most use cases is not good enough here, should be
             | always correct for such fundamental thing.
        
             | knorker wrote:
             | You're assuming that the code was written exception safely.
             | E.g.                   mu.Lock()         foo := bar[baz]
             | // <- throws exception / panics         mu.Unlock()
             | 
             | Go is sold as a language without exceptions, so people
             | don't write exception-safe code. Which is fine, except when
             | exceptions are actually caught.
        
               | Vendan wrote:
               | That wouldn't pass a code review where I work... Use a
               | defer to do the unlock
        
               | knorker wrote:
               | I would also not allow it. I'm saying the problem is that
               | core Go developers say "Go doesn't have exceptions",
               | which is manifestly false, but causes people to not write
               | exception safe code.
               | 
               | But despite you and me, I'm saying there's a lot of
               | broken code out there because of this doesn't-but-
               | actually-does misinformation.
               | 
               | And it's very annoying that you have to tell people to
               | do:                   var i int         func() {
               | mu.Lock()           defer mu.Unlock()           i =
               | foo[bar]         }()
               | 
               | Clean code, that is not. (even if you simplify it by
               | having the lambda return the int)
        
               | adamwk wrote:
               | Does defer get called on panic? I thought panic cancels
               | all further execution
        
               | HALtheWise wrote:
               | Yes it does, which is why recovering from a panic can be
               | done in a deferred function. The go runtime maintains
               | enough metadata to track what deferred functions need to
               | be run while unwinding the stack.
        
             | seritools wrote:
             | > most likely
             | 
             | > probably is correct
             | 
             | Yeah I don't know...
        
             | nasretdinov wrote:
             | The main issues that usually arise as a result of catching
             | panics in handlers like HTTP is that unless code it written
             | very deliberately to be able to recover from being
             | interrupted in the middle of any function call, there is a
             | high risk of e.g. mutexes left locked, which in turn leads
             | to a (silent) program deadlock in general.
             | 
             | This has happened during my couple years at Google at least
             | once, even though it wasn't in an HTTP handler, but the
             | issue was very similar.
        
         | sakras wrote:
         | > Is this another case of Google forgetting that people use Go
         | outside of Google, or am I reading too much into this?
         | 
         | Whether or not they are forgetting aside, this is Google's
         | style guide for code bases in Google. I don't think non-Google
         | Go programmers were a consideration for them.
         | 
         | On a broader note, it seems as though anytime Google publishes
         | something people interpret it as "industry standard" (see their
         | C++ style guide) and apply it to their non-Google projects. I
         | personally don't see this as healthy.
        
         | [deleted]
        
         | evercast wrote:
         | I believe the idea is that 'panic' is considered something
         | fatal. If it happens, the application should die unless you
         | have a strong reason for it not to. If you can recover e.g. by
         | returning HTTP 500 for a single request, it should be handled
         | by returning errors up throughout the call stack, i.e., by
         | error handling instead of panicking.
         | 
         | It's definitely an opinionated approach though.
        
           | sarthak-ag wrote:
           | Nil pointer panics happen. When a DB column that was supposed
           | to be non-null is null and the code unexpectedly accesses it.
           | Oh schema won't help because legacy. There are rare though
           | and we fix such issues as they are discovered, but just
           | pointing out a benign case when crashing the whole server on
           | panic may be a wrong approach. Our approach is to always
           | recover from panics so the server keeps chugging, but raise
           | an alarm so someone is paged and fixes the issue right away.
        
           | xienze wrote:
           | > I believe the idea is that 'panic' is considered something
           | fatal.
           | 
           | I think opinions in 3rd party Go code on what "fatal" means
           | vary, that's the issue. Sure it was intended to mean "this
           | error is so bad the entire program needs to die right now"
           | but in practice there's cases where it's treated more like an
           | unchecked exception in Java, i.e., "I can't recover from this
           | so _I'm_ going to give up but _you_ can keep going." Or put
           | another way, what a library might consider fatal the caller
           | doesn't. It can be argued whether or not that's the right
           | thing to do, but the fact is it happens in the wild, so for
           | something like a server you probably should trap it.
        
             | throw827474737 wrote:
             | > It can be argued whether or not that's the right thing to
             | do
             | 
             | No, because both approaches could be right at a higher
             | level, to pretake that decision at this level is definitely
             | wrong here?!
        
             | fn1 wrote:
             | > but in practice there's cases where it's treated more
             | like an unchecked exception in Java, i.e., "I can't recover
             | from this so _I'm_ going to give up but _you_ can keep
             | going."
             | 
             | Java has a supertype for unrecoverable problems like out-
             | of-memory-errors. It's called "Error", a subtype from
             | Throwable. Exceptions are also Throwables, but they are NOT
             | errors.
        
               | vbezhenar wrote:
               | There's nothing unrecoverable about Error exceptions. If
               | one thread hit a bug and threw StackOverflowError, it's
               | not a reason to kill the entire application or even
               | thread itself, just unwind stack, show some error and
               | continue processing next task. The only tricky situation
               | I'm aware of is OutOfMemoryError, because it can affect
               | other threads. I'd prefer to restart the server. But
               | again it's just because typical code allocated heap all
               | the time. One can write code carefully without heap
               | allocations and this code will work just fine with OOM
               | errors thrown around.
        
         | Thaxll wrote:
         | How I understand it is that it's up to the user to create a
         | panic handler middleware, which is perfectly valid and what
         | most people are doing anyway.
         | 
         | Something like: https://github.com/go-
         | chi/chi/blob/master/middleware/recover...
        
       | gregwebs wrote:
       | Best practices should differentiate between libraries and
       | applications. Otherwise, like this guide you assume every piece
       | of code is a library and slow down application development.
       | 
       | For example they recommend not using %w for errors- because
       | library authors need to be careful about what information is
       | exposed. However, as an application code developer, %w should be
       | used by default- this avoids an accidental bug where annotating
       | an error could cause an upstream error check to fail- which they
       | allude to in the guide- but now their rule is getting complicated
       | whereas "default to %w" would be safer and improve velocity in
       | application code.
        
       | keybored wrote:
       | > Go source code uses MixedCaps or mixedCaps (camel case) rather
       | than underscores (snake case) when writing multi-word names.
       | 
       | > This applies even when it breaks conventions in other
       | languages. For example, a constant is MaxLength (not MAX_LENGTH)
       | if exported and maxLength (not max_length) if unexported.
       | 
       | Good. All-caps for constants would make no sense in a language
       | like Go.
        
       | everybodyknows wrote:
       | > If the receiver is a "large" struct or array, a pointer
       | receiver may be more efficient. Passing a struct is equivalent to
       | passing all of its fields or elements as arguments to the method.
       | If that seems too large to pass by value, a pointer is a good
       | choice.
       | 
       | For an app that has medium-sized structs in hot paths, this
       | tradeoff introduces considerable tension into the development
       | flow.
       | 
       | However, there is one takeaway for us devs: Don't count on help
       | from the inlining optimization pass of the compiler to evaporate
       | away calling overhead of structs, neither when passed as the
       | receiver nor as an ordinary arg.
        
       | matttproud wrote:
       | My favorite Easter egg when helping to write the style guide was
       | including a reference to Full Metal Jacket within it. See if you
       | can find it.
       | 
       | In all seriousness, I am very proud of the team's work in both
       | writing and publishing this externally. It was a lot of work.
        
       | mseepgood wrote:
       | > Go tips - stay tuned
       | 
       | I'm looking forward to this document.
        
       | gopher_1234 wrote:
       | The kubernetes ecosystem has a lot of go code which
       | consequentially suffer from nil panics. Thankfully they recover
       | otherwise we'd see an absolute shit ton of pod restarts.
       | 
       | In general, please stop panicking in library Go and Rust code.
       | It's rude.
        
         | jrockway wrote:
         | nil panics are hard bugs, not stylistic concerns. There is
         | never an occasion where correct code dereferences a nil
         | pointer.
         | 
         | The style point that people are missing here is that every type
         | should have a usable zero value. If (&SomeStruct{}).Method()
         | panics, that's an API design flaw.
        
         | rand0m4r wrote:
         | Totally agree, panic in a library is a smell
        
         | j-krieger wrote:
         | Yes! This is one of the tiny few things I don't like about
         | rust. In a language so focused on safety, why can _anything_
         | panic? Every single failable function should return result. No
         | ifs or buts.
        
       | adamgordonbell wrote:
       | > The general rule of thumb is that the length of a name should
       | be proportional to the size of its scope and inversely
       | proportional to the number of times that it is used within that
       | scope.               > A variable created at file scope may
       | require multiple words, whereas a variable scoped to a single
       | inner block may be a single word or even just a character or two,
       | to keep the code clear and avoid extraneous information.
       | 
       | I love how it covers both short iterators and long descriptive
       | names under a single principle.
        
         | nebiros wrote:
        
         | hoosieree wrote:
         | The more I think about this rule the more I like it.
         | 
         | One thing I would add is if you're writing a library, think of
         | the people using it. Are they going to curse you every time
         | they have to write
         | YourModule.ExtremelyVerboseMethodNameWhyOhWhyIsItSoLong, even
         | if it only appears once in your module?
         | 
         | As soon as you start sharing code, you can't know how many
         | times a name will appear. So err on the side of brevity, for
         | our sakes.
        
         | raverbashing wrote:
         | Good
         | 
         | Apple APIs are infamous for their long names. Not sure what
         | they gain by it
        
           | Cthulhu_ wrote:
           | I never minded it so much in Apple code; with the syntax for
           | calling methods, it made method invocations sound like
           | sentences, more like literal programming. Plus, intrinsic
           | named parameters, so many languages - including Go IMO -
           | would benefit from named parameters.
        
           | masklinn wrote:
           | > Apple APIs are infamous for their long names. Not sure what
           | they gain by it
           | 
           | Clarity.
           | 
           | It's also a factor of Objective-C's compound methods
           | (inherited from Smalltalk), as each parameter names appears
           | in the method, and which leads to a very phrase-like style.
           | 
           | By osmosis, the C APIs use a similar style.
        
           | wingerlang wrote:
           | Never having to figure out what a function actually does is
           | nice.
           | 
           | builder.makeThing("a", true, true, 46)
           | 
           | [builder makeThingNamed:"a" isRound:YES isRed:YES size:46]
           | 
           | or lately
           | 
           | builder.makeThing(named: "a", isRound:true, isRed:true,
           | size:46)
        
             | speedyapoc wrote:
             | Right? I have never understood the frustration around named
             | parameters like this. It helps reduce cognitive load quite
             | a bit.
        
               | Jiro wrote:
               | It increases cognitive load when writing the function
               | call, since you now need to remember what kind of true to
               | use. It also increases cognitive load for people who
               | either do already understand the parameters or who are
               | doing something for which the parameters are not
               | relevant; it's harder to ignore a long thing than to
               | ignore a plain "true".
        
               | masklinn wrote:
               | > It increases cognitive load when writing the function
               | call, since you now need to remember what kind of true to
               | use.
               | 
               | There is no "kind of true".
               | 
               | > It also increases cognitive load for people who either
               | do already understand the parameters
               | 
               | By letting them see what the parameter they remember is?
               | There's no cognitive load to seeing what you expect.
               | 
               | > or who are doing something for which the parameters are
               | not relevant;
               | 
               | If the language doesn't have optional parameters, all
               | parameters are relevant. By making parameters named, you
               | avoid having to count positional parameters as in MS APIs
               | to ensure you didn't get one in the wrong slot.
               | 
               | > it's harder to ignore a long thing than to ignore a
               | plain "true".
               | 
               | Which is very much valuable. It's much easier to notice
               | mistakes than when you've got 11 positional parameters of
               | which 2/3rds are usually set to 0/null.
        
         | don_albrecht wrote:
         | This advice should be universal in coding. When I first started
         | programming, I had a manager that hated 1-2 character
         | variables. But they make sense for loop iterators.
        
           | SEJeff wrote:
           | Yeah i for for loops (in python) x for comprehensions, etc,
           | make sense. But in general, single character variables make
           | code really hard to read.
        
             | ehsankia wrote:
             | Yep, you should just use the best judgement, for example
             | for distance, time in driving_metrics:           speed =
             | distance / time
             | 
             | is a lot clearer than                   for d, t in
             | driving_metrics:           s = d / t
             | 
             | even if it's used in a tiny scope once. But some things
             | don't have a valuable meaning, or the meaning can trivially
             | be inferred.
        
           | thunky wrote:
           | For loop iterations it's fine, but I think Go code often
           | takes this too far.
           | 
           | It often takes longer to read a single character than a word,
           | because I have to mentally map the character to the word
           | anyway.
           | 
           | Reminds me of when people go nuts aliasing table names in SQL
           | queries, which IMO makes it harder to read as well.
        
             | philote wrote:
             | I agree. Plus, with IDEs and auto-completion, why not use
             | longer names?
        
               | vbezhenar wrote:
               | One reason so use short names is to keep lines short and
               | reduce line wrapping. This helps to read code, especially
               | when you do 3-way merges.
        
               | mikepurvis wrote:
               | If you have to use that name a bunch of times close
               | together, it bloats line lengths which also affects
               | readability.
        
               | GuB-42 wrote:
               | - Longer names mean longer lines, and lines that are
               | longer than your viewport are terrible. Less of a problem
               | with modern ultrawide screens, but passed ~100
               | characters, it starts becoming a problem in some cases
               | (ex: diffing). Personally, I have a soft limit at 80,
               | hard limit at 120, because that's what works best for me.
               | 
               | - With such rules, variable length is a hint of its
               | scope. For example, I tend to use "i" when the loop body
               | is small, "idx" or "index" when it is a bit larger, and a
               | more descriptive name when it exceeds one screen. This
               | way, just by looking at a single line, I already have a
               | hint about its context.
               | 
               | - Just because a variable name is short doesn't mean it
               | is meaningless. For example "i", "j", "k", are loop
               | indices, "x", "y", "z" are point coordinates and "dx",
               | "dy", and "dz" are differences, "a" and "b" are both
               | sides of a comparison function, "t" can be a temporary
               | variable or a time depending on context, etc... The
               | corollary is to make sure you use your single letter
               | variables consistently. If I see an "x" in a place where
               | a coordinate can be used and it does not refer to a
               | coordinate, or an "i" that is not a loop index, I will be
               | confused.
        
             | david422 wrote:
             | Or in languages that you can import a resource but give it
             | a custom name. So whenever you are reading someone else's
             | code you have to double check to understand what is going
             | on. For instance, import DiskUtils as DU, and now all the
             | code references DU.diskSpace()
        
             | madeofpalk wrote:
             | There's also a bit of Fitt's Law in here as well - I find
             | it a lot harder to select, or put my cursor in, a single
             | letter variable. So if I want to rename `i` to something
             | else, I must carefully select `i`, whereas with a longer
             | variable I find it easier to put my cursor in the middle of
             | `index` and hit F2.
        
               | SnooSux wrote:
               | I started using ii as my iterator variable name for
               | similar reasons. Easier to highlight and Ctrl-F for. Also
               | removing ambiguity when using i as the imaginary
               | constant.
        
               | ErikCorry wrote:
               | If you are using a mouse for things like this you already
               | lost though.
        
               | shepherdjerred wrote:
               | Most developers use their mouse quite often.
        
             | Xeoncross wrote:
             | The constancy in Go makes this better. I have come to
             | expect `r` to be an io.Reader or http.Request depending on
             | context.
             | 
             | There are a few interfaces in Go that are used heavily and
             | I don't mind that people often use a single character for
             | them.
             | 
             | It's the same thing as everyone using `i` for iterators.
        
               | bpicolo wrote:
               | I just use reader//req. Code is instantly easier to read
               | for me personally and no time has been wasted.
               | 
               | It's all preference, of course.
        
               | shepherdjerred wrote:
               | I don't believe so.
               | 
               | Imagine you're new to Go and you haven't mentally mapped
               | all of these abbreviations. What would you rather read,
               | `r` or `reader`?
        
               | jrockway wrote:
               | Designing an API that "stutters" is a very common mistake
               | that many programmers make. reader.Read() is pretty
               | jarring.
               | 
               | There is also nothing wrong with naming variables after
               | what they're for instead of what they are. Consider
               | io.Copy(dst, src). That's nicer than io.Copy(reader1,
               | reader2).
        
               | pindab0ter wrote:
               | But not necessarily nicer than io.Copy(destination,
               | source).
        
               | kevmo314 wrote:
               | Sometimes you run into packages called reader which makes
               | for a less fun time using variable names that are also
               | words.
        
               | rlabrecque wrote:
               | This is much nicer for other people coming into this.
               | It's the same problem with spoken language and slang,
               | slang is better if you know it, worse if you don't.
        
               | dimitrios1 wrote:
               | Sorry to be pedantic (well, not really it's M.O. for a
               | hacker news commentor after all, isn't it?) , but as
               | someone who knows a few languages, slang is not an issue
               | with spoken languages at all. You learn a language by
               | full immersion with other speakers, who impart that
               | knowledge unto you. Unless all you are doing is
               | conversing with grammaticians and newscasters, you will
               | pick up on regional and local differences, euphemisms,
               | and slang.
        
               | jawzz wrote:
               | This makes me wonder: what if there was a language where
               | variable names are determined according to the type, with
               | the option of overriding with a custom name. So a
               | variable of type http.Request would automatically be
               | named "req", the next one in scope would be "req2", etc.
               | 
               | If you think about it, when you solve a physics problem,
               | for instance, you call every mass "m1", "m2", etc. Maybe
               | this would be another step in Go's direction of
               | conforming style to make code more standard and readable.
        
               | vlunkr wrote:
               | What if I introduce a new http.Request between 1 and 2?
               | Now all the references are broken.
        
               | notpushkin wrote:
               | https://en.wikipedia.org/wiki/Hungarian_notation
        
               | jawzz wrote:
               | Oh cool, thanks for that.
        
               | leetrout wrote:
               | This is interesting.
               | 
               | The idea of having a standard library of types related to
               | what are effectively program keywords... could be really
               | good. Or hideous like global vars.
               | 
               | I wonder if any language has tried this?
        
               | gbalduzzi wrote:
               | I agree that most types come with a natural variable
               | name.
               | 
               | However, in many cases a more descriptive name is way
               | appropriate. On top of my mind:
               | 
               | - Multiple variables of the same type. How are you
               | supposed to distinguish between req and req2? Compare it
               | to something like "apiReq" and "cdnReq"
               | 
               | - Primitive types, that does not inherently carry a
               | domain value. An integer called "seconds" or "max_offset"
               | has a lot more meaning that one named "num"
               | 
               | Forcing such a notation into the language would make
               | impossibile to represent all these cases
        
               | gogogogogogo wrote:
               | The real question from this example is: why are you
               | creating two http requests in the same scope before using
               | them?
               | 
               | I've been writing Go cloud stuff for the better part of a
               | decade and "req" for a request has never been ambiguous
               | because I go ahead and send the request and process the
               | response before sending another one, at which point I can
               | just reassign the variable and let the first one fall out
               | of scope.
        
               | jawzz wrote:
               | You're right. Going back to my physics example, all the
               | variables in a physics problem are actually of the same
               | type, float. So you'd need more specific types, but that
               | would be infeasible-the whole point of types in a general
               | purpose language is that they're general enough for any
               | use case.
               | 
               | If you were just coding physics problems you could have
               | types restricted to the physics domain, that is, physical
               | units. Which it looks like MATLAB does now support:
               | https://www.mathworks.com/help/symbolic/units-of-
               | measurement...
        
             | monocasa wrote:
             | Exactly.
             | 
             | I think a better heuristic rather than tying it to scope is
             | to sort of semantically huffman encode. Using an iterator
             | is probably the most used variable name that starts with an
             | 'i', so it gets just plain 'i'. As a _concept_ gets more
             | specific, then it gets a longer name.
        
           | stevekemp wrote:
           | When I started coding I'd type                  10 FOR F = 1
           | TO 10         20   PRINT F, F*F        30 NEXT F
           | 
           | Because "FOR" and "F" shared the same key, on my ZX Spectrum
           | keyboard.
        
             | antod wrote:
             | And for those that don't know - entering Basic code on the
             | Spectrum (well the original one at least) was ALL
             | single/shifted key shortcuts for keywords rather than
             | typing them out.
             | 
             | eg https://www.old-computers.com/museum/photos/sinclair_zx-
             | spec...
             | 
             | The low cost and quirky masochism was mainly why we loved
             | it.
        
         | everybodyknows wrote:
         | > A variable created at file scope
         | 
         | How does one declare a variable to be of file scope in Go? An
         | ordinary "var x int" has _package_ scope, visible from any file
         | in the directory (aside from the special cases around _test.go
         | files).
         | 
         | Scope of package import declarations is narrowed to a single
         | .go file, but these of course are not _variables_.
        
           | avianlyric wrote:
           | I think you're reading this too literally. They likely just
           | mean file scope from a purely usage perspective. I.E. you
           | only use the variable in one file, it doesn't matter how the
           | language processes it into a computational scope
        
             | everybodyknows wrote:
             | It absolutely does matter. If I'm editing the file in which
             | the package-scope variable (or type, or const) is declared
             | (and also referenced) and decide on the fly to subtly
             | change its semantics, forgetting that the same is also
             | referenced from some other file within the package, I have
             | likely just created a bug. Worse, the bug will likely
             | manifest itself only at run time.
        
               | chrsig wrote:
               | You're still taking things too literally.
               | 
               | A human typed in something less precise than you'd like
               | in a style guide. Find a bug tracker to request a
               | verbiage change. It's not that big of a deal.
               | 
               | No one is asserting that variables are in fact file
               | scoped.
        
       | oracardo wrote:
       | In my opinion the hardest style rules to accept when trying to
       | use this guide are:                 1. Do not create "assertion
       | libraries" like `assertEqual(x, y)` [1]       2. Leave testing to
       | the Test function [2]       3. Intialisms (HTTPURL, IOS, gRPC)
       | [3]       4. Function formatting [4]
       | 
       | For the record I'm not saying I disagree with these. I just think
       | that folks coming from other languages have a lot of built in
       | muscle memory to do it other ways.                 [1]
       | https://google.github.io/styleguide/go/decisions#assertion-
       | libraries       [2] https://google.github.io/styleguide/go/best-
       | practices#leave-testing-to-the-test-function       [3]
       | https://google.github.io/styleguide/go/decisions#initialisms
       | [4] https://google.github.io/styleguide/go/decisions#function-
       | formatting
        
         | marwatk wrote:
         | I've read the assertions section a few times and I still don't
         | understand the argument. How is:                 if got == nil
         | {         t.Errorf("blog post was nil, want not-nil")       }
         | 
         | Better than                 assert.NotNil(t, got, "blog post")
         | 
         | ? They seem to suggest that you lose context, but their "Good"
         | examples are similarly devoid of context.
        
           | jrockway wrote:
           | NotNil is fine, but as a library author you need to implement
           | every comparison operator for every type. You also have to
           | implement each assertion twice; once for t.Error and once for
           | t.Fatal. We have a homegrown assertion library in our
           | codebase at work, and every time I write a test I have to
           | settle for an inaccurate assertion, or add two more methods
           | to the library. I grew up on Go at Google where I would not
           | have been allowed to check in assertion helpers, and I think
           | they made the right call there.
           | 
           | I've seen a lot of gore in code that uses assertion libraries
           | like assert.Equals(t, int64(math.Round(got)), int64(42)).
           | Consider the error message in that case when got is NaN or
           | Inf.
           | 
           | It is so easy to write your own assertions. I can type them
           | in my sleep and in seconds:                   if got, want :=
           | f(), 42; got != want {              t.Errorf("f:\n  got: %v\n
           | want: %v", got, want)         }              if got, want :=
           | g(), 42.123; math.Abs(got - want) > 0.0001 {
           | t.Fatalf("g:\n  got: %v\n want: %v", got, want)         }
           | 
           | Why implement a NotEquals function when != is built into the
           | language?
           | 
           | (The most popular assertion library also inverts the order of
           | got and want, breaking the stylistic convention. It's so bad.
           | Beware of libraries from people that wrote them as their
           | first Go project after switching to Go from Java. There are a
           | lot of them out there, and they are popular, and they are
           | bad.)
           | 
           | Finally, cmp.Diff is the way to go for complex comparisons.
           | Most use of assertion libraries can be replaced by cmp.Diff.
           | I wouldn't use it for simple primitive type equality, but for
           | complex data structures, it's great. And very configurable,
           | so you never have to "settle" for too much strictness or
           | looseness.
        
           | adamwk wrote:
           | Coming from Swift development I first missed having a
           | collection of assertion functions for testing, but I've come
           | around to the go testing patterns. I do think test assertion
           | libraries usually result in less useful messages, and you end
           | up having to implement a new function for every type of
           | comparison under the sun.
           | 
           | (One thing I absolutely abhor is those assertion DSLs similar
           | to rspec)
        
           | dgunay wrote:
           | Drawing inspiration from:
           | 
           | > Complex assertion functions often do not provide useful
           | failure messages and context that exists within the test
           | function.
           | 
           | I think the best compromise is to avoid combining individual
           | logical units into one assertion. For example:
           | 
           | Bad:                 assert.True(t, myStr == "expected" &&
           | myInt == 5)
           | 
           | Good:                 assert.Equal(t, "expected", myStr)
           | assert.Equal(t, 5, myInt)
           | 
           | Real world example: at my workplace we have some code that
           | tests HTTP request formation for correctness (right URL,
           | body, headers, etc). Replacing big all-or-nothing booleans
           | with individual assertions on each property of the request
           | provides much more useful test failure messages.
           | 
           | As with any published "best practices" like this, have an
           | open mind but don't just cargo cult whatever Google does.
           | Best to be selective about what does and doesn't work for
           | your situation.
        
           | Beltalowda wrote:
           | The main reason I dislike the assert libraries is that you
           | lose the ability to format things nicely; being able to see
           | what's going on quickly is pretty nice when tests fail.
           | Sometimes you want %s, sometimes %q, sometimes maybe a diff
           | or formatted as something else. Testify "solves" this by just
           | dumping a lot of stuff to your terminal, which is a crude
           | brute-force solution. Plus with table-driven test you're
           | saving what, 2 or 3 lines of code?
           | 
           | For example for a simple string comparison it's 12 lines:
           | --- FAIL: TestA (0.00s)            --- FAIL:
           | TestA/some_message (0.00s)                main_test.go:11:
           | Error Trace:    /tmp/go/main_test.go:11
           | Error:          Not equal:
           | expected: "as\"d"
           | actual  : "a\"sf"
           | Diff:                                        --- Expected
           | +++ Actual                                        @@ -1 +1 @@
           | -as"d                                        +a"sf
           | Test:           TestA/some_message
           | 
           | vs. 2 lines with a "normal" test:                  --- FAIL:
           | TestA (0.00s)            --- FAIL: TestA/some_message (0.00s)
           | main_test.go:11:                    have: "as\"d"
           | want: "a\"sf"
           | 
           | With your NotNil() example it's 4 lines, which seems about 3
           | lines more than needed.
           | 
           | This kind of stuff really adds up if you have maybe 3 or 4
           | test failures.
        
         | matttproud wrote:
         | If you want context on why _some_ testing guidance is the way
         | it is, this external discussion may be helpful: https://www.red
         | dit.com/r/golang/comments/yy8edb/googles_inte....
        
         | nasretdinov wrote:
         | Yeah IMO assertion libraries are ok if you actually agree with
         | your colleagues to provide context for every check in the
         | assertion message. Obviously that means that you have to
         | actually have that conversation, but it's no different from the
         | advice to just never use those libraries either.
        
       | marcus_holmes wrote:
       | > Name constants based on their role, not their values. If a
       | constant does not have a role apart from its value, then it is
       | unnecessary to define it as a constant.
       | 
       | This breaks the linter in most cases because "magic numbers".
       | Like having to declare a constant for the number of cents in a
       | euro/dollar.
       | 
       | I think Google are right in this case, and linters need to be
       | smarter.
        
         | xiaq wrote:
         | > Like having to declare a constant for the number of cents in
         | a euro/dollar.
         | 
         | I agree with your point, but this is a bad example. Naming
         | these constant CentsInEuro and CentsInUSDollar is consistent
         | with the style guide.
         | 
         | As silly these examples are in isolation ( _of course_ a cent
         | is 1 /100 of a euro or a dollar), if you are writing code that
         | processes currencies beyond euro and dollars, you will quickly
         | end up with (using Chinese Yuan as an example):
         | const (             JiaoInCNYuan = 10             FenInCNYuan =
         | 100             ...         )
         | 
         | At which point adding                   const (
         | CentsInUSDollar = 100             CentsInEuro = 100
         | ...         )
         | 
         | is not just reasonable but a good idea.
         | 
         | Additionally, suppose your code needs to deal with historical
         | currencies, you'll most likely need:                   const (
         | ShillingsInOldBritishPound = 20
         | PenceInOldBritishShilling = 12             ...         )
         | 
         | This is also a good example that the knowledge that a cent is
         | 1/100 of a dollar or euro is highly culture-dependent. A
         | British programmer from as late as 1970 will tell you that it's
         | silly to create constants for these: _of course_ there are 20
         | shillings in a pound and 12 pence in a shilling, everyone knows
         | that! (I also like to think that they 'd assume that a "cent"
         | is 100 dollars because that's what centum means in Latin, but
         | it's probably highly unlikely for them to never know US dollars
         | and cents.)
        
           | tragomaskhalos wrote:
           | Yeah maybe. I've worked on code where there were constants
           | defined for minutes-in-hour, hours-in-day etc though, and
           | that was just annoying; it's conceivable that our culture
           | will one day decide that the Babylonians were idiots and we
           | should use the French revolutionary clock instead, but I'll
           | take my chances
        
             | [deleted]
        
       | panabee wrote:
       | has anyone compiled a comprehensive list of style guides from
       | leading companies?
       | 
       | google has published a style guide, but it doesn't seem like
       | facebook and netflix do.
       | 
       | it would be awesome to have one central list of best practices
       | from leading tech companies.
       | 
       | we started one on github, but it's woefully limited:
       | https://github.com/HotpotDesign/Developer-Style-Guides
       | 
       | could anyone kindly recommend better ones?
        
         | akshayshah wrote:
         | https://github.com/uber-go/guide/blob/master/style.md
        
       | BrainVirus wrote:
       | _> Concise Go code has a high signal-to-noise ratio._
       | 
       | A few lines later:                 // Good:       if err :=
       | doSomething(); err != nil {           // ...       }
       | 
       | "Tell me, how many lights you see?"
        
         | B-Con wrote:
         | I think that it is fair to say Go considers error handling to
         | be signal, not noise.
         | 
         | This position has obviously prompted a philosophical war and
         | not everyone agrees, but the two statements are consistent
         | under Go's philosophy.
        
       | bushbaba wrote:
       | Isn't the point of go is that go fmt follows the languages global
       | style guide?
        
         | roessland wrote:
         | Go's fmt enforces some parts of formatting, like indentation,
         | spaces vs. tabs and spacing around brackets and parentheses.
         | But it is by no means a complete style guide. Fmt doesn't give
         | your packages, structs or methods reasonable names
         | automatically. Fmt doesn't tell you where to use values and
         | where to use structs, what your interfaces should be, and how
         | large your functions should be.
         | 
         | There is still room for a style guide.
        
         | ainar-g wrote:
         | Formatting [?] Style
         | 
         | Style may also include naming, documentation requirements,
         | recommendations regarding function length, etc.
        
         | marcyb5st wrote:
         | It includes things that are not formatting specific like naming
         | (https://google.github.io/styleguide/go/best-practices)
        
         | liampulles wrote:
         | Yes - I always thought that Go took an opinionated position
         | that the style is whatever gofmt outputs. This is a very
         | limited set of style "rules" - that is on purpose. gofmt is
         | meant to allow one to spend near zero time manually styling
         | code or picking people up on styling in reviews.
        
         | sethammons wrote:
         | At first glance, that question makes sense; however, fmt
         | doesn't cover all code format issues. Ex from the post:
         | 
         | > Line length
         | 
         | > There is no fixed line length for Go source code. If a line
         | feels too long, it should be refactored instead of broken. If
         | it is already as short as it is practical for it to be, the
         | line should be allowed to remain long.
         | 
         | > Do not split a line:
         | 
         | > Before an indentation change (e.g., function declaration,
         | conditional)
         | 
         | >To make a long string (e.g., a URL) fit into multiple shorter
         | lines
         | 
         | fmt doesn't force arbitrarily short lines in lieu of
         | readability (looking at you python + pep8; I can't recall how
         | many lines of code were a few characters long and pep8
         | formatting made the multiple lines a mess to visually parse).
         | The Google Team decided that it was important to not break up
         | perfectly readable lines and gave guidance on how to do that.
        
           | Beltalowda wrote:
           | I hate autoformatters/linters that frob with line length with
           | a passion. I'm an 80-column kind of guy, but 81 columns can
           | be just fine, and in some cases 90 or even 100 can be more
           | readable than obsessively wrapping at 80 (or some other
           | arbitrary value).
           | 
           | There are a few (rare) cases where gofmt will wrap lines, but
           | it mostly leaves it alone which is one of the better
           | "features" IMHO. Many *fmt tools really got this wrong.
           | 
           | Automated formatters are nice, but in the end there is no
           | substitute for human eyes and common sense.
        
         | shp0ngle wrote:
         | That is just a formatting guide. How to write spaces, newlines,
         | semicolons.
         | 
         | Go fmt doesn't change anything that is not formatting.
        
           | hoosieree wrote:
           | Yet.
        
         | rob74 wrote:
         | Yeah, and that allows them to just write "all Go source files
         | must conform to the format outputted by the gofmt tool" under
         | "Formatting" and move on to the stuff for which a style guide
         | is still needed...
        
       ___________________________________________________________________
       (page generated 2022-11-18 23:01 UTC)