I'm Sorry -- when I said "readability reviewer" I was referring to a very google-specific term about how they ensure the author of a change understands the language they're writing. Granted, it's been a while since I worked there but, at that time, each change request (aka MR, PR, etc...) required approval from (1) a code owner and (2) someone with *readability* for the language in question -- and, if the author covered both of those, an "LGTM" from someone else was still required (i.e. *Minimum Two Brains*). Regardless, no code change could be submitted without someone with *readability* being involved (either the author or a reviewer).
Each language had their own procedures about how *readability* would be granted. For Go, each and every CR from a non-readability SWE would get assigned to a random *readability reviewer* to ensure the code meets certain standards and is *idiomatically Go.* It took me ~15 months and just under 60 CRs to get Go *readability* (compared to 4 months and 3 CRs for Python). On Tuesday, August 9, 2022 at 1:08:42 PM UTC-7 bse...@computer.org wrote: > On Tue, Aug 9, 2022 at 1:52 PM Tim Peoples <t...@timpeoples.com> wrote: > >> Yeah, I'm with Burak on this one. The interface usage you're describing >> Henry is exactly the kind of thing I'm talking about. While on the surface >> it may seem advantageous -- in fact, I also tried writing Go that way when >> I first started -- my *readability* reviewers at Google did well to >> enlighten me about the many problems this can cause with Go -- some of >> which rog was kind enough to enumerate. > > > I think "readability" is not the right metric to use here. "Code > comprehension" (comprehensibility?) should be the right metric. Readability > does not always imply it can be easily comprehended. Java is readable, but > not necessarily comprehensible. I argue that Go code is more comprehensible > than code written in most other languages, because you can understand all > the implications of the code using mostly "local knowledge", that is, > knowledge you can gain by reading pieces of code "close" to the point of > interest. Wherever you have interfaces, you need non-local knowledge to > understand what's going on. > > > >> >> Also, since originally posting this yesterday, I've come to learn that my >> new shop is not only utilizing preemptive interface definitions but also a >> complete dependency injection framework and rather strict adherence to >> *Clean >> Architecture*™ >> <https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html> >> >> -- (likely from the scripts in this repo >> <https://github.com/thnkrn/go-gin-clean-arch>) which goes a long way >> towards explaining why so much of the code looks like Java written in Go >> syntax. >> >> On Tuesday, August 9, 2022 at 8:33:07 AM UTC-7 bse...@computer.org wrote: >> >>> On Mon, Aug 8, 2022 at 11:27 PM Henry <henry.ad...@gmail.com> wrote: >>> >>>> I am sure that many of us have been on that journey. After using Go for >>>> some time, we discover some practices that are not necessarily in >>>> agreement >>>> with the existing "adages" but effectively solve our problems. >>>> >>>> For me, if the data type is mutable, I prefer returning interfaces. It >>>> would be something like this: >>>> ``` >>>> type Student interface { >>>> //... >>>> } >>>> >>>> type studentImpl struct { >>>> //... >>>> } >>>> >>>> func NewStudent(id string) Student { >>>> return &studentImpl{ >>>> //... >>>> } >>>> } >>>> ``` >>>> There is a bit of history why I use this approach. For a struct with a >>>> mutex, I wanted to ensure that the user did not accidentally copy the >>>> struct. Nowadays we have *go vet* to give us a warning, but this was >>>> before *go vet* had this functionality. So, I return a pointer to the >>>> struct and hide it behind an interface. That way, it hides the >>>> implementation details from the user and the user can pass the object >>>> around without knowing whether it has a mutex or not. >>>> >>>> And then I ended up with some *constructors* returning structs and >>>> some returning interfaces. To ensure consistency, my colleagues and I >>>> decided to return interfaces for all mutable objects. For immutable >>>> objects, we return structs. >>>> >>>> The nice thing about this approach is that it makes the syntax a lot >>>> cleaner as you have to deal with fewer pointers. >>>> ``` >>>> //instead of this >>>> func Update(student *Student) { >>>> //... >>>> } >>>> func UpdateMany(students []*Student){ >>>> //... >>>> } >>>> >>>> //now you have this >>>> func Update(student Student) { >>>> //... >>>> } >>>> func UpdateMany(students []Student){ >>>> //... >>>> } >>>> ``` >>>> Some members in the team came from higher level languages and they >>>> found working with pointers a bit awkward, so we made some accommodation >>>> for them. >>>> >>> >>>> There are times when I need to *upgrade* some of these mutable >>>> objects, and this approach has proven to be quite flexible. It also plays >>>> nicely with code generators. >>>> >>>> Some people may disagree with this approach, but I have been using it >>>> ever since: return interface for mutable objects, return structs for >>>> immutable objects. >>>> >>> >>> I am one of those who disagrees. I have not seen any benefit from having >>> interfaces for data objects other than making other developers happy. In my >>> opinion, this amounts to emulating another language in Go. There are cases >>> where this might make sense, but as a general principle, I think it should >>> be avoided. Data is data. There are no implementation details to hide. >>> >>> >>> >>>> On Tuesday, August 9, 2022 at 3:09:10 AM UTC+7 t...@timpeoples.com >>>> wrote: >>>> >>>>> I can't speak to the *auto-generated swagger client* case but I >>>>> believe gRPC is still doing things the right way -- in that the framework >>>>> defines an interface I (the framework API consumer) then implements. >>>>> IOW: >>>>> I don't see that as a "java style interface" (where the interface defines >>>>> the API contract). >>>>> >>>>> I suspect you and I are saything the same thing. >>>>> >>>>> t. >>>>> >>>>> On Monday, August 8, 2022 at 12:51:29 PM UTC-7 bse...@computer.org >>>>> wrote: >>>>> >>>>>> On Mon, Aug 8, 2022 at 12:51 PM Tim Peoples <t...@timpeoples.com> >>>>>> wrote: >>>>>> >>>>>>> I don't necessarily consider the "multiple implementations" case as >>>>>>> being truly preemptive -- if there really are multiple implementations >>>>>>> (e.g. the "hash" package from the standard library). >>>>>>> >>>>>>> I'm much more concerned about interfaces that are defined by an API >>>>>>> producer -- for one and only one impl -- and then adding a bunch of >>>>>>> extra >>>>>>> (often autogenerated) code to deal with that. >>>>>>> >>>>>> >>>>>> Like a gRPC client/server, or auto-generated swagger client/server? >>>>>> >>>>>> I've had many instances where such an auto-generated client had to be >>>>>> passed down components that have no knowledge of those services. Writing >>>>>> such components using interfaces declaring only parts of those service >>>>>> implementations have benefits. An example that I can think of is an >>>>>> audit-trail service that deals with recording transaction metadata, >>>>>> looking >>>>>> them up, etc. It makes sense to write components that use only the >>>>>> writer >>>>>> part of that service, instead of requiring the whole thing. It makes >>>>>> writing tests easier. It lets you decouple services better, add >>>>>> adapters/interceptors etc. >>>>>> >>>>>> >>>>>>> >>>>>>> t. >>>>>>> >>>>>>> On Monday, August 8, 2022 at 11:02:31 AM UTC-7 bse...@computer.org >>>>>>> wrote: >>>>>>> >>>>>>>> On Mon, Aug 8, 2022 at 11:17 AM Tim Peoples <t...@timpeoples.com> >>>>>>>> wrote: >>>>>>>> >>>>>>>>> >>>>>>>>> For years I've read the old adage, "Accept interfaces, return >>>>>>>>> structs" and have spent years working to instill this understanding >>>>>>>>> among >>>>>>>>> my colleagues. I gathered a great many skills while learning Go (and >>>>>>>>> acquiring readability) back in the day -- and one of the strongest >>>>>>>>> of >>>>>>>>> those is the idea that interfaces should be defined by their consumer >>>>>>>>> instead of an API producer -- but I've now been away from Google >>>>>>>>> longer >>>>>>>>> than I was there and I'm beginning to suspect that the general >>>>>>>>> consensus >>>>>>>>> among the *Go Literati* may have shifted around some things -- >>>>>>>>> like preemptive interfaces. >>>>>>>>> >>>>>>>>> My arguments against preemptive interfaces have recently run into >>>>>>>>> more and more pushback -- especially among the influx of developers >>>>>>>>> coming >>>>>>>>> from the Java and/or C# world who seem to continually reject any >>>>>>>>> notion >>>>>>>>> that Go should be any different from the way they've always done >>>>>>>>> things. >>>>>>>>> >>>>>>>>> This has recently come to a head with a brand new job (I'm 3 weeks >>>>>>>>> in) where virtually all of their services are built atop a dependency >>>>>>>>> injection framework having a data model with dozens (if not hundreds) >>>>>>>>> of >>>>>>>>> preemptive interfaces and my initial, cursory review tells me the >>>>>>>>> codebase >>>>>>>>> is at least an order of magnitude more complex that it needs to be. >>>>>>>>> (Note, >>>>>>>>> I was told that none SWEs at this company (other than myself) knew >>>>>>>>> any Go >>>>>>>>> before they started). >>>>>>>>> >>>>>>>>> So, my questions to the group are thus, "Should I even care about >>>>>>>>> this at all? Are preemptive interfaces now considered the norm with >>>>>>>>> Go? >>>>>>>>> Or, should I just shut up and crawl back into my hole? >>>>>>>>> >>>>>>>> >>>>>>>> I believe both approaches have their uses. What you call preemptive >>>>>>>> interfaces can be effectively used to hide implementation details >>>>>>>> where >>>>>>>> multiple implementations can exist. This approach can coexist very >>>>>>>> well >>>>>>>> with interfaces defined by the consumer. For example we have services >>>>>>>> that >>>>>>>> are written to implement an interface, so it becomes a logical >>>>>>>> deployment >>>>>>>> unit. Then we have consumers of that service that define parts of the >>>>>>>> interface service implements, so the consumer is not dependent on the >>>>>>>> complete service, and we can add any interceptors/filters. >>>>>>>> >>>>>>>> However, I agree with your assessment that especially newcomers >>>>>>>> tend to choose the traditional "interface is a contract" approach. In >>>>>>>> addition to the effect of other languages, people seem to like "clean >>>>>>>> architecture". Nevertheless, even with people dedicated to clean >>>>>>>> architecture, the idea of "interface parts" seems to resonate, >>>>>>>> especially >>>>>>>> when you show that you can define an interface on the consumer side >>>>>>>> that >>>>>>>> combines parts of multiple "contracts". >>>>>>>> >>>>>>>> >>>>>>>>> >>>>>>>>> TIA, >>>>>>>>> Tim. >>>>>>>>> >>>>>>>>> -- >>>>>>>>> You received this message because you are subscribed to the Google >>>>>>>>> Groups "golang-nuts" group. >>>>>>>>> To unsubscribe from this group and stop receiving emails from it, >>>>>>>>> send an email to golang-nuts...@googlegroups.com. >>>>>>>>> To view this discussion on the web visit >>>>>>>>> https://groups.google.com/d/msgid/golang-nuts/f4777928-875d-4c0a-a4a7-9fb57bf9d51fn%40googlegroups.com >>>>>>>>> >>>>>>>>> <https://groups.google.com/d/msgid/golang-nuts/f4777928-875d-4c0a-a4a7-9fb57bf9d51fn%40googlegroups.com?utm_medium=email&utm_source=footer> >>>>>>>>> . >>>>>>>>> >>>>>>>> -- >>>>>>> You received this message because you are subscribed to the Google >>>>>>> Groups "golang-nuts" group. >>>>>>> To unsubscribe from this group and stop receiving emails from it, >>>>>>> send an email to golang-nuts...@googlegroups.com. >>>>>>> >>>>>> To view this discussion on the web visit >>>>>>> https://groups.google.com/d/msgid/golang-nuts/34469b98-c37e-4a1d-af13-729b639a71ben%40googlegroups.com >>>>>>> >>>>>>> <https://groups.google.com/d/msgid/golang-nuts/34469b98-c37e-4a1d-af13-729b639a71ben%40googlegroups.com?utm_medium=email&utm_source=footer> >>>>>>> . >>>>>>> >>>>>> -- >>>> You received this message because you are subscribed to the Google >>>> Groups "golang-nuts" group. >>>> To unsubscribe from this group and stop receiving emails from it, send >>>> an email to golang-nuts...@googlegroups.com. >>>> >>> To view this discussion on the web visit >>>> https://groups.google.com/d/msgid/golang-nuts/3774ae88-45ea-4181-909d-f18c788c7395n%40googlegroups.com >>>> >>>> <https://groups.google.com/d/msgid/golang-nuts/3774ae88-45ea-4181-909d-f18c788c7395n%40googlegroups.com?utm_medium=email&utm_source=footer> >>>> . >>>> >>> -- >> You received this message because you are subscribed to the Google Groups >> "golang-nuts" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to golang-nuts...@googlegroups.com. >> > To view this discussion on the web visit >> https://groups.google.com/d/msgid/golang-nuts/e8bc57a6-bcee-4904-82f6-f52cdf371b6bn%40googlegroups.com >> >> <https://groups.google.com/d/msgid/golang-nuts/e8bc57a6-bcee-4904-82f6-f52cdf371b6bn%40googlegroups.com?utm_medium=email&utm_source=footer> >> . >> > -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/3f44f55e-853c-4bbd-991f-e7abc339a2b0n%40googlegroups.com.