Hacker News new | ask | show | jobs
by Lewisham 3725 days ago
Disclaimer: I work at El Goog.

I recently moved teams so that I could use Go exclusively. It's often been said that Go solves the problems Google developers have, and it's 110% true. It's much easier to get things working, and it's much easier to write things like Protocol Buffers. But the key for me is that Go isn't fun in the sense of "wow, I'm so smart that I managed to one line this thing", it's fun in the "wow I read this code and I can understand what's happening and hack on it to do something else."

I've worked on teams with monolithic Java code, and it's nigh-on impossible to understand what's happening and where you are in the logic flow. I don't have that problem in Go. I even delve into the standard libraries to see how the original Go devs did something, and I both understand it and think it looks like code I would have written too. I never get that feeling from any other language, where I feel like the standard libs are written by hyper-intelligent aliens.

When I exclaimed this internally, I got the sarky comment "It's almost as if the things Go leaves out makes it easier". And that's the core of it. Would I like generics? Sure; I do miss map(). But that's it.

Go is opinionated. People that don't like Go don't share the opinion. That's OK. It's taken me a long time to realize there is no One True Language that can do everything (it was soon after steveklabnik said something along the lines of "I don't know why you'd want to write a web app in Rust"). Once you get there, you won't hate Go anymore, nor indeed any other language.

Apart from C++. Screw that ;)

10 comments

The reason Google's Java codebase is impossible to understand is not the language, it's mindless application of 'best practices' like dependency injection on an industrial scale. Guice is open source, anyone can go look at it. Just imagine a big complex server in which the "new" keyword wasn't used anywhere because everything was handled by the dependency injector. You get these things:

1. Things that should be compile time errors turning into runtime errors.

2. An IDE that can't navigate anywhere or usefully analyze the code because everything has interfaces between it.

3. Things being mocked out in unit tests just because they can be, meaning lots of superfluous code and tests that pass when they should fail.

There's nothing about Java that mandates this style of programming. It's the result of years of programmers trying to be clever and finding ways to do things better without being sufficiently cynical about new trends and fashions. Given that Java is 20 years old, there has been plenty of time for people to find ways of making simple things complicated.

Go will suffer this phenomenon too because it's not to do with the languages themselves, it's to do with programmers who have safe corporate jobs finding ways to make themselves stand out from the pack by inventing and spreading 'best practices'. If anything, Go will suffer it worse, because the language itself is so limited, so there's more potential for bizarre hacks disguised as cleverness. Like that Go profiler that worked by rewriting source code to insert stuff between every single line of code.

You are predicting the future (of Go) based on the past (of Java).

Non-idiomatic Go (or as you call it "bizarre hacks disguised as cleverness") is frowned upon. One example that comes to mind is the Martini web framework[1], which, once pointed out, resulted in a rewrite[2].

[1] https://github.com/go-martini/martini [2] https://codegangsta.io/blog/2014/05/19/my-thoughts-on-martin...

Non-idiomatic code is frowned on by all programmers working in every language.

The problems start when the "ma" is dropped from idiomatic. There's nothing in the design of any language that can stop people coming up with idioms and ideas that sound clever and become widely adopted but which actually cause problems down the line. Go is not immune to this. I'd argue Go is largely composed of such things to start with!

Very recently I have begun to work as programmer, and having to work with PHP I looked at phpunit and unit testing (first time I do it semi-seriously). The guidelines for mocking that I have found are about dependency injection (phpunit has some helpers, but I think that's it).

What is the alternative to it?

Python has an alternative: to use a technique called "patching". Basically, in Python, every module is just an object you can access, so in your unit-test, you can easily go into that module, replace a symbol inside a module (or a class) for something else, run unit-test, put thing back. Very nice feature which gives you incredible power to not have to code "with tests in mind".
The problem isn't with the idea of using mocks in testing. The problems start, as often the case, when people start religiously applying a design pattern and end up over using or abusing it.

The problematic thought process goes like this:

1. Gee, my tests are really slow and flaky and hard to run because they talk directly to a database. I know! I'll swap out my real database connection with an in memory database.

2. Things are better, but they're still slower than I'd like. Maybe I can just write a wrapper around my database code, make it implement an interface, and then provide a custom implementation just for my unit tests. Instead of having my class construct a database connection directly, I'll make it passed in as a parameter. Then they'll be nice and fast!

3. Whoa, now my tests run instantly! How great. This mocking pattern is awesome. I want to mock everything! But hmm, I have a lot of objects and they often depend on other objects, like services of some sort. And I was told in school that God Objects and global variables are bad. So I can't use those because I want to be a good programmer and good programmers don't use those. This is making my code kind of a mess because now every time I want to construct an object it takes lots of parameters when before it didn't.

4. <reads some article on hacker news> Hm, this dependency injection library sounds like what I want. Instead of constructing my objects directly, I ask an "injector" to do it for me. And it will then consult a table of bindings, and then find or construct the dependencies, and then build the object I want, recursively. All I have to do is [insert long complicated process here].

5. Meanwhile, some other programmers look at what's going on. Interesting! They say. Those programmers know what they're doing and they're converting their whole codebase to use dependency injection. They claim it's a best practice to make testable code. We want our code to be testable too, we should do the same thing. Plus, we can call this refactoring and code maintenance and get paid for doing essentially brainless work, instead of having to take risks on developing new features or fixing bugs.

The story ends like this: pretty quickly this newfound "best practice" is spreading throughout your codebase like a fire, and now when you deploy your app, you get an error whilst the app is running. You then try to figure out what's causing the error but 25% of the time you try to follow a method call you end up looking at an interface instead of code, and when you try to figure out what implementation of that interface your app called you have to read and decipher tons of binding definitions.

The correct place to stop in the above story was at point two. And the reason is not just code readability. Once you start over-using mock objects, you can start to discover that your mock is implementing what you think that service does, not what it actually does, and you can end up with code that's actually buggier than code written with traditional fat test dependencies would have been. Fail.

Thank you, very detailed explanation.
I will point out that the run time problems of guice are trying to be solved with dagger: http://google.github.io/dagger/
I agree with your take on this. Part of language arguments roots in the "assumptions of the Blub Paradox": that there exists some strict ordering among languages (and therefore some language "blub" that is superior to some and inferior to others).

I find this hypothesis questionable. It's likely that language "power" in the real world looks more like a loosely-ordered graph, with plenty of bidirectionals tagged with things like "If you are doing lots of matrix algebra" or "If you need the thing to run on a Raspberry Pi." Go solves a specific domain I operate in (data structure transformation and web services) really quite well---well enough that I can move from making the problem fit the language to solving the problem with small amounts of work, without worrying that I'll create a testing nightmare for myself in the future.

When I find a task too big for Go, I'll amend my feelings on the issue.

Huh, funny enough - Go was my first try to replace PHP, but as I don't share opinion "copy-paste is better than generics" and "executable comments are ok", I tried Rust, and now I have REST API with 10000+ LOC - it serves multiple web apps and works just awesome and I really feel fun of programming and I'm enjoying language. So "I don't know why you'd want to write a web app in Rust" sounds ridiculous for me. Really, "I don't know why you'd want to write a web app in Go".
"executable comments are ok"? Where did you hear that?
Go has many "special comments" that actually do stuff:

* build tags (https://golang.org/pkg/go/build/)

* code generation (https://blog.golang.org/generate)

This is the exact point where you start to see that the Go creators are ok with hackery as long as it works and doesn't make the overall design too complicated.

It looks like we agree Go has "special comments", not "executable comments".

I recognise that Go' special comments can be seen as a hack, but honestly they are not so different from Rust's attributes or Haskell's pragmas, which can look clunky too.

No way. Comments are comments, attributes are attributes. Previous discussion: https://news.ycombinator.com/item?id=9522973
Bingo.
When my primary platform moved from C#/ASP.NET to Objective-C, it took me a while to realise that one of my biggest reliefs was that I didn't have to deal with "software architects", and just to get on with coding. I began to realise that so much of that coding came down to "justify my existence" (as architect) -- needless complexity to check off a box from the gang of 4.

Mind you, having an architecture in mind is of primary importance, but dealing with such complexity and endless Factories made it impossible to follow the code.

And yes, I know that server-side implementations have an inherent complexity that most client-side apps lack.

I'm not sure that using C# inherently turns you into an architect. You can write the most direct and legible code in C#, just as you would in other languages. I think it is a mistake to conflate the two. In fact, I've seen it.

I am sure that as you work your way up the Objective-C chain, you will run into patterns and practices that have brought the best results over the years. They will feel burdensome after a while. At that point, you will be right back where you were with C#, except in Objective-C and will be happy to be a beginner at another language so that you are free of the burden of experience.

Agreed, that C# doesn't inherently turn you into an architect. It's more like C#/Java has a culture of 'architect'.

Objective-C is seriously in MVC-Land (or maybe MVVM-land too), and pretty much discourages the more convoluted design patterns that I saw back in my server dev days.

Indeed Go is a pretty good "Google Language". It almost google's C++ with it's C style guide baked into the language. For almost every feature in Go (or lack of thereof), I can find a line in the C style guide saying the very same thing. (ban of exceptions, rvalue references, don't include what you don't use, CHECK vs. panick... etc)

(Though yet many teams didn't adopt Go. Because if none of your teammembers has Go readability then you have to rely on someone in Go team to do CRs. Those guys are not very pleasant to deal with.)

"Those guys are not very pleasant to deal with."

I got Go readability more than 2 years ago, right before I left Google, but in my experience the Go team was very pleasant & professional. Has that changed?

FWIW, I have relaxed my opinion on web apps a bit ;) I do think that it's not a strong point, but there are places swhere it could make sense. Especially with some more libraries. I thought I made a comment more recently than https://news.ycombinator.com/item?id=10312354 , but whatever. :)

But the general point does stand: languages have different strengths, and that's totally okay.

> "wow I read this code and I can understand what's happening and hack on it to do something else."

I think that's the issue. For many things, that is perfectly fine. But for other stuff (finance, healthcare, safety critical, anything that loses real money/lives when it breaks), the last thing I want is somebody to just "hack on to it".

by "hack on it" he/she means being able to 1) easily understand it by looking at it, 2) easily figure out how to modify it (to add functionality or change it). You are reading "hack" as a bad thing when really it's a good thing.

Anything that evolves over time requires changes. All of the examples you give (finance, healthcare, safety, money/lives matter) are better served with people being able to read and maintain that code.

That's a relative term. Go will give you static type safety (until and unless you [inevitably ;)] break out into the 'reflect' package) and strong assumptions about what default values mean in data structures. These are very nice features to have in terms of trading off flexibility for expressiveness.

Python is not a language I would trust in healthcare, finance, etc. on the critical path of consuming and transforming user-supplied data. Go I'd be comfortable with.

  >  It's often been said that Go solves the problems Google developers have, and
  >  it's 110% true. 
I'm wondering how much of what you're experiencing is "Good Go" vs "Bad Java"? I feel like you can take any project in any language and screw it up pretty badly. I'm an iOS developer and I've seen developers bring questionable practices that aren't consistent with the Objective-C ecosystem in and cause all sorts of confusion. I'm sure there are a few other languages that would give you the same positive experience if you tried them out.
It's possible that Go hasn't been around long enough for anyone to screw it up badly. But that'd be a strength in the ecosystem of languages.
Many of the comments here evoke that feeling : that Go doesn't have compromises.

History teaches us that this is not in fact the case. Reality is that Go doesn't have too many compromises ... yet. I've run into more than a few already, and the cracks are visible, even if they're not yet too deep or expanding. (e.g. the logging struct, which prevents you from overriding logging destinations or adding logging contexts)

Every language gets a honeymoon phase. Once you program for a decade or so, you'll start remembering those. Java, for instance, had a great, and very long honeymoon phase when the concept of garbage collection and VM based languages was new, at least in mainstream languages. The cracks and compromises are what killed Java, and these days the cracks are regularly in the way, or at least visible.

Google has to statically link everything to produce a big binary with almost no dependency (besides syscalls), because it cannot promise the environment of the server, as you might get different boxes between deployments. So, it links all the jars and jvm into a huge binary, it's a pain to deal with that because it takes minutes to build and see your change.

And still, Java is almost used for front end only. When memory is not enough, you have to use C++.

Totally agree with your comment about monolithic code. I'm in a similar situation, at work I am primarily in a PHP pile of spaghetti, but in the past 2 months I've been lucky enough to develop micro services in Go.

Once I got over the initial learning curve and started to get the nuances I really enjoy working in the language. So much so I've started looking around for a full time Go position.

I have the feeling that golang is very focused on pulling code from github, but I've had a hard time packaging golang applications. I have succeeded with go-ipfs, which vendors all dependencies and includes them in their release tarballs, but I'm not sure how to do that with other applications which import dependencies from github.