Fair comment -- thanks for the feedback. I've received similar feedback from a friend who reviewed the post, and I completely agree. To be honest, I had to go ahead and publish what I had or it would have remained in my drafts forever. However, I'd like to follow up in the future and address some of the points you raise.
Thank you for taking my criticism well. Writing is hard and exhausting. I struggle with writing all of the time, even when I make goals to do it. I hope I haven't discouraged you from writing more.
If you're taking feedback, I'd like to disagree on your examples of "accidental complexity".
To me, accidental complexities are quirks of design that everyone would agree to do over differently if history replayed itself. Examples like that would be:
-- Java calendar (and C Language ctime) months have index starting with 0 which means 0..11 for Jan..Dec but for days, it's index 1 which means 1..31
-- PHP inconsistencies such some functions being verb_obj while others are obj_verb and some have underscores and some do not
-- MS Excel has incorrect leap year calculation for 1900 which means all other spreadsheets must duplicate this "bug" to be compatible
Those are the types of "accidents" that arise out of ignorance or uncoordinated thinking. Andrei Alexandrescu (expert in C++ and D) has a similar concept he calls "unforced errors".
On the other hand, you compared a Tomcat servlet container as being "accidental complexity" and Go as "reduced complexity" because the http hosting is all built into the exe. To me this is an example of moving complexity around so you don't feel it for your particular use cases of the Go language.
In other words, if the Java community had a chance to do it again, I'm not convinced that Tomcat servlet would be baked into the standard Java distribution. If you bake it into the JVM, you've made the JVM Specification[1] more complex. If you leave it out of the JVM but include it as a standard class library, you've made the API library reference more complex[2] (Alternative universe: Why is the class library reference 3000 pages long?! Because it includes 200 pages to document a servlet container that most don't use.)
Complexity (in totality) wasn't reduced. It was only shifted around. I'm not saying moving complexity around isn't desirable because it is (hence we have "abstractions") but it's not an example of solving accidental complexity.
I think what happens is that we programmers find some language, or framework and if it matches our use cases, it's subjectively less complex. For the others who need generics to write algorithms, they write homegrown template code generators or resort to copy&paste which is more complex. So sure, Golang omits generics but it only makes it less complex from a language-specification perspective. However, it's actually more complex from a total project perspective. I think this explains all the contradictory posts about Django/Rails/etc being "simple" while other posts say it's "complex pulling teeth".
At this point, I don't buy that golang without qualifiers of use-cases is objectively less complex. I'm writing server-side web services with golang and for that use case, I think it's less complex than other alternatives such as C++ or Nodejs. For Windows GUI apps, using C++ with Qt is less complex than Golang.
Thanks for the feedback, jasode. Most of my piece was written from the perspective of Ruby, Python, and Node.js development, so your perspective on the state of affairs in Java is helpful. In this regard, Java sits in an interesting middle ground between Go and Ruby. Go completely relies on compile-time dependencies. Ruby completely relies on run-time dependencies.
Once piece of consistent feedback that I've gotten is that I didn't do a good enough job of clarifying that I was specifically dealing with the complexity of running applications. I'm pretty sure most people who have deployed Ruby or Python web applications would agree that deploying and running those applications is more complex that it needs to be. The examples of accidental complexity you gave are great examples of how it can manifest in code design.
I really liked your last point about the importance of use cases. I briefly alluded to that in the conclusion of my article, and I'm glad that you brought it up here. Use-case is extremely important.
I think you missed the author's point here. You have to have the webserver somewhere. That's inescapable complexity. It's in the language, or in the library, or in an external container like Tomcat. But if you're going to run a web app, you have to have a server somewhere.
Where Go made things simpler is in the dependencies. The app now has all the dependencies compiled in, and so it's completely isolated from what libraries some other app in the container needed. That is a removal of accidental complexity. (Right up until you need to update all the apps to fix a bug in some library, and now you have to update each app, not just update one library in the container...)
Now I'm building web services on top of Play Framework (and Scala), which comes with its own web server that has nothing to do with Servlets or Java EE and so it has nothing to do with application servers and WARs and containers. And it's been good for the Play framework to come with its own server, because Servlets is moving slowly (that's what happens to standards) and Play has been capable of WebSocket or asynchronous processing of requests long before Servlets was.
This is actually an area for which I love the JVM - Java's Servlets is an objectively good standard with multiple implementations, other platforms can only dream about such a popular and good standard (Java EE on the whole arguably less so, but the Servlets part is OK) and you can totally decide to not use it. I personally love having choice and when I'm faced with a problem, it's much easier for me to search and learn a new library than a new language - I'm saying this after I've been working with about 6 languages on the job and played with about a dozen others.
Also, here's another opinion - I personally started to hate languages coming with "batteries included". I like languages in which the standard library contains the core necessities. Like, immutable data-structures or abstractions for dealing with concurrency - completely fine to be in a standard library. JSON parsing, http handling - no freaking way.
Can you explain why you don't like languages with batteries included? Isn't it nice to have one JSON parser that you know is quality and that everyone can use? People new to the language don't have to go figure out which of the 8 libraries that are out there is the "good one". Standards like that also mean that you can jump into any random codebase and know exactly what's going on. So.... can you explain? I don't understand why they're a bad thing.
> Isn't it nice to have one JSON parser that you know is quality and that everyone can use?
Sure it is - but that's an utopia and never, ever happens.
The JSON parser in Scala's standard library sucks. The JSON parser in Python's standard library sucks. The JSON parser in Ruby's standard library sucks. I actually challenge you to give me an example of JSON functionality included in a standard library that doesn't suck.
In practice what happens is that one or two third-party libraries pop-up at some point that are so much better that people start using it as a de-facto standard and then the functionality in the standard library becomes legacy that has to be carried around because backwards compatibility.
And much worse than a small standard library is a standard library full of deprecated stuff.
>Where Go made things simpler is in the dependencies. The app now has all the dependencies compiled in, and so it's completely isolated from what libraries some other app in the container needed. That is a removal of accidental complexity.
I agree with all that Golang simplification except for one thing: I don't call the previous state of affairs "accidental complexity". It's certainly "more complex" with more moving parts but it doesn't feel "accidental" to me.
I maintain that the nature of complexity and location of it has changed. You yourself gave an example:
>(Right up until you need to update all the apps to fix a bug in some library, and now you have to update each app, not just update one library in the container...)
Complexity has now increased in deployment if there's a bug. If Tomcat has a security bug, you update it instead of all the servlets. If golang exe has a bug, you have to recompile all exes and redeploy.
It doesn't mean the Java situation is overall better than Golang, it's just different.
IMO, this is one area where Go shines. In a non-compiled language, you have to bear the burden of the upgrade process when writing code AND when running code. For example, if you update Python on you application servers and there is a breaking change, you break running applications that may not have been touched for weeks, months, or even years. With Go, you build a binary at the time the code was released and you never need to touch that binary again. Your binary can run forever even if breaking changes are introduced later.
As with Python, you still need to update code in subsequent releases to work with the breaking changes. However, in my experience, this isn't the hard part. The hard part is keeping your existing code running while staying on a recent version of your favorite language runtime. This is even more painful in an environment where multiple application run on the same server. If you write a new application using Ruby 1.9.x but an older application will only run on Ruby 1.8.x, you need to either split them across multiple servers or update the old application to run on 1.9.x. There are tools to handle this (rbenv, rvm, chruby, etc), but this is exactly the type of complexity I talk about reducing in my post.
I've had the same experience - very little need for generics in Go on a regular basis. I really appreciated the following snippet from http://robnapier.net/go-is-a-shop-built-jig:
"Probably the biggest complaint people have with Go is the lack of generics. And I did run into that in just the first couple of weeks of work on my project, and I wound up with a bunch of duplicated code to work around it. And then, when it was all working, I refactored out the duplicated code. And I refactored again. And in the end, the whole thing was simpler and shorter than what I would have done with generics. So again, in the end, Go turned out to be a language for solving real problems rather than a language filled with beautiful tools, and so you build real solutions rather than finding excuses to use your beautiful tools. Don’t try to make Go what it isn’t. If you’re trying to solve abstract CS problems in their most generalized forms, then you may find it frustrating. But if you’re trying to solve specific, practical problems in the forms professional developers typically encounter, Go is quite nice."
So ..... he ended up doing far more work than he would have done had Go been like any other modern language? Without detail about what these refactorings were and why he didn't write the code that way at first, what lessons he drew from it etc this anecdote is not very useful.
Per the blog post, Cloud Servers is no longer based upon an existing VPS product. Instead, it is based upon the OpenStack project that Rackspace started with NASA in 2010. http://en.wikipedia.org/wiki/OpenStack