Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

>(2) large yet maintainable systems

I have never seen anyone suggest Go for large systems or seen any open source code that even comes close to enterprise system size. I would argue that Go is inadequate for large systems compared to the JVM languages. The absence of operational tooling, exceptions, declarative annotations, runtime management etc all make it much harder to support and scale to large numbers of developers.

Go seems perfect for micro services, command line utilities and single purpose applications. Which is where it seems to have gained a lot of traction in companies to date.



>>(2) large yet maintainable systems

>>

>I have never seen anyone suggest Go for large systems or seen any open source code that even comes close to enterprise system size.

I might be moving the goal posts a bit here, but I think go's C heritage, and focus on message passing -- possibly coupled with something like protobuf or similar -- encourages breaking large systems into small services. So if you view a system as a "sum of functionality" -- I think one might still use go for "large, yet maintainable systems".

Now, it is of course possible to write micro services both in C++ and java -- but historically it appears at least in the java world, you end up throwing everything into a massive jboss container, exposing yourself to thousands upon thousands of lines of code.

With go, you deploy (relatively) small binaries, and a service can live as 20 binaries on one box, or as 20 binaries (along with some load balancers like haproxy or what not) across 300 machines. Or something in between.

I'd argue that some of the more sane java frameworks and projects also revolve around simplicity and separation of concerns -- typically leading to micro services. But a lot of people seem to end up working with large, poorly architectured beasts. That's probably more of a culture thing, than a language thing -- so I think people will make huge swats of unmaintainable go code as well...


> encourages breaking large systems into small services

That's as much of a curse as it is a blessing. To some extent, small services are handy for dev/ops type folks, as they can quickly see which specific part of an application is misbehaving with memory or cpu or diskspace, so they like it.

But small services means that you lock down the interface between parts of the system by using another language to specify the communications protocol (e.g. protobuf, json, ...) and 2 different codebases have to understand it. And even if you manage to get the code to change, now you have the problem of migrating the running program.

In other words, the interface is now set in stone. Nobody will ever touch it again. This is exactly what you do not want to happen. Small services are the enemy of large, flexible programs.

Contrast this to Java/C# (and, somewhat less perfectly, C++) and their refactoring tools. What a difference. Changing an interface is something that is mainly done by computer code, not by a programmer, and all parts are modified and all problems identified.

There are points where this is not a problem, like a file system interface, or a socket interface, that sort of thing (and even there you may change your mind ...). Places where flexibility is not needed or wanted (I would argue, looking at linux file systems, that the POSIX API is not, in fact, a good API for quite a few file systems, but looking at the kernel I can see why this is not going to change. Of course, half the distributed file systems are user space libraries, partly for this reason). This is exactly the sort of thing C programmers deal with.


One of go's strengths is how easy it is to refactor. Implicit interfaces means that you can change a function to take an interface, and the caller who is passing in a concrete type doesn't have to get updated at all.

also, there's a relatively recent tool created called gorename that does 100% type-safe renaming.

Plus there's been gofmt and gofix for forever which you can use to automatically rewrite your code.

Finally, because almost all go code is formatted with gofmt, you can often do simple find and replace changes because all the code is completely regular.


> One of go's strengths is how easy it is to refactor

Give me a minute to collect my jaw of the floor here. Nope ... need some more time. Unless you mean in the same way as C and pascal are easy to refactor, I disagree in the strongest possible way. I may conceed to a very limited extent. While Go and it's tools don't allow refactoring, due the static nature of Go, it's actually possible, through careful design and constantly putting extra effort in, to make sure that it's reasonably easy to refactor. As long as you stay away from using interfaces, use long and unique enough names for your variables, make sure no variable names are substrings of other variable names, have a convention for polymorphic method names (ie. Matrix.MakeWithFloatArray(), Matrix.MakeWithIntArray(), Matrix.MakeWithZeroes(), ...), ...

> Implicit interfaces means that you can change a function to take an interface, and the caller who is passing in a concrete type doesn't have to get updated at all.

Yes because that's what refactoring is ... what you're showing here is called "polymorphism", and Go "doesn't support it" (except when it does, like as you point out here, in interfaces, oh and in range, make, new, append, close, copy, delete, imag, len, print, println, real, go, defer, most of which are also generic and polymorphic in really, really bad ways (some have completely unrelated and surprising behavior when passing different types to them), and I doubt I've got all of them).

> also, there's a relatively recent tool created called gorename that does 100% type-safe renaming. > Plus there's been gofmt and gofix for forever which you can use to automatically rewrite your code. >Finally, because almost all go code is formatted with gofmt, you can often do simple find and replace changes because all the code is completely regular.

I have tried that tool. It only does a single file. Again that makes it not refactoring. Just so we're clear. Here's the definition of refactoring :

  Code refactoring is the process of restructuring existing computer code – changing the factoring – without changing its external behavior.
Which is not what those tools do. Change the name of a method ... boom 5 objects don't satisfy the interface they did 5 seconds ago anymore. Change the name of an interface ... doesn't change in all other parts of the code. Change an exported variable ... everything fails to compile.

Next major point of criticism of the go tools. When do you want to do refactoring ? Well, during development. Of course in order to refactor during development, when 2-3 of your program's files don't compile, you obviously cannot use a normal compiler to refactor, since it won't understand the program. While this is not technically part of the definition, it frustrated me to no end the first, and last, time I used gofix to attempt to refactor something. Me and vim are faster at refactoring a 10000 line Go codebases than gofix + cleaning up after it is. Gofix knows a cute trick with symbol tables that is 1% complete (because making it functional will require a full rework of the Go compiler), which is not refactoring (since it doesn't look at the full source tree), and it will require a rework of Go itself (I'm not yet positive, but I think that because Go works with implicit interfaces, it is not actually possible to refactor anything related to object methods or interfaces correctly).


> make sure no variable names are substrings of other variable names

Did you miss the point about gorename's type safe renaming? It understands that pkgfoo.Bar.Baz() is different than pkgbat.Bar.Baz(). So you can safely tell it to rename one, and it won't touch the other. And yes, it'll rename everywhere that was referencing the old name and fix that, too. Now I'll grant you that older refactoring tools were mostly text matching, but the fact that the std lib ships with a go parser and AST library means that anyone can write their own code parsing and refactoring tools.. and people are.

> what you're showing here is called "polymorphism"

I don't really want to argue about what counts as refactoring... what counts as "external behavior" depends a lot on how you look at a problem. To your customers, the CLI may be your external behavior, for partners, it may be your API, for the developer in the next cube, it may be the exported variables on your package/class/whatever.

I'm not sure what you would count as qualities that make code easy to refactor. I like Go's implicit interfaces, nice tooling, and static typing to help make sure I'm not shooting myself in the foot. I honestly would like to hear what language you think is easy to refactor and why.


According to the announcement [1], gorename is able to rename just about any identifier (function, method, exported variable, local variable) throughout your entire GOPATH, not just a file. I tried it out and it seemed to work fine. Additionally, it seems to detect at least some cases where the rename would cause the resulting code to not work, although you can -force it to apply the changes anyway.

I happen to agree that Go could use more refactoring tools, but I think it's in good shape considering how young it is.

[1] https://groups.google.com/forum/#!topic/golang-nuts/96hGPXYf...


A big problem in large-scale systems is dependency management. One of the significant decisions that Go makes in its design is its approach to package management. Its approach isn't perfect, but is simple, making it a breath of fresh air compared to C++ or JVM-based languages... e.g. how many hours have been spent tweaking Maven or debugging discrepancies between classpaths referencing different copies of commons-logging?

Speaking of Maven - `go build` normalizes build systems so there is less time twiddling builds. It's not a silver bullet, but it's specifically intended for large systems.

Go's strong decisions around formatting (`go fmt`) are another example of planning for enterprise-size. There is no need for a style guide for go programs - it's built into the language, and with an approach that feels less pedantic than Python.

Go isn't perfect, but it's certainly designed for large-scale systems.

I agree with you that the JVM languages have better large-scale system support currently (20 years does give you something) -- but Go's goal is not dissimilar to the one Java eventually settled on after the applet craze.

Contrary to what you've seen, Go seems (to me) to be a go-to language for new companies principally concerned with distributed systems / cloud infrastructure (particularly those that once might have hired a hybrid of C and scripting language devs). One of the trends in those communities is toward microservices / SOA / etc, but the total codebases managed are certainly enterprise-scale.

All that said I have absolutely no significant opinions on Go vs Rust :) Just had to take issue with the "go is not enterprise scale" idea.


Go's strong decisions around formatting (`go fmt`) are another example of planning for enterprise-size. There is no need for a style guide for go programs - it's built into the language, and with an approach that feels less pedantic than Python.

How is that different from the 40 years old Unix utility indent? Pretty much every single non-trivial programming language has its own code formatter. Auto code formatting is neither novel nor unique to Go.


I didn't mean to imply Go invented automatic formatting :) It's unique among the popular language that I know in that the formatter is built into the standard toolkit. It also goes well beyond indentation. I've never seen a discussion of the format of go code outside the discussion of how `go fmt` should work - this I consider a benefit.


If you consider C++'s standard toolkit to be Clang, Clang comes with clang-format.


indent goes well beyond indentations too, despite the name :)


You're certainly correct, but if you compare this:

  http://linux.die.net/man/1/indent
with this:

  https://golang.org/cmd/gofmt/
even just by line count, you'll see the difference in philosophy.


Unfortunately having a code formatter does not make up for basic deficiencies in the language itself when it comes to datatypes. Basic example :

http://stackoverflow.com/questions/19946992/sorting-a-map-of...

(shortest way to sort an array of a custom datatype in go is around 50 lines of code, and requires you to write a custom sorting class)


First example on that page shows a straightforward solution in under 10 lines of code.

Convert the map values to a slice and then use the stdlib package sort to organize the data.


You left out defining a sorting class, like in Turbo Pascal. So, it's 17 lines of code (wrong version provided on stackoverflow) or 21 lines, the correct version :

  type dataSlice []*data

  // Len is part of sort.Interface.
  func (d dataSlice) Len() int {
      return len(d)
  }

  // Swap is part of sort.Interface.
  func (d dataSlice) Swap(i, j int) {
      d[i], d[j] = d[j], d[i]
  }

  // Less is part of sort.Interface. We use count as the value to sort by
  func (d dataSlice) Less(i, j int) bool {
      // WRONG : will crash if there's a nil in the list ...
      return d[i].count < d[j].count

      // Correct version
      if d[i] == nil {
          return true // note : true is an exported name, with no capital ... yet another inconsistency
      }
      if d[j] == nil {
          return false
      }
      return d[i].count < d[j].count
  }

  func main() {
    // create s of type []*data

    sort.Sort(s)
  }
Python version:

  // create list of data in variable s
  s.sort(key=lambda x: x.count)
C++ version:

  // Create Vector<Data> in s
  sort(s.begin(), s.end(), [](const Data& d1, const Data& d2) { return d1.count < d2.count; });

  (bonus for the C++ version : it's behavior is defined and correct for nulls, just saying this because none of the other examples are, including the Go one)
Java version:

  // Create List<Data> in s
  Collections.sort(s, (Data d1, Data d2) -> Integer.compare(d1.count, d2.count));
Let's put it this way. If Java is 16 TIMES more concise than your language, you have a problem. A big problem.


Juju is ~300k lines of code, and that doesn't count 3rd party libraries (it's also like 6 months out of date, so it's probably significantly more than that now). There are many large Go projects out there - maybe you've heard of Docker or Kubernetes?

Saying it's not for large systems is just showing ignorance of what it's already being used for.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: