
I Want Off Mr. Golang's Wild Ride - whatever_dude
https://fasterthanli.me/blog/2020/i-want-off-mr-golangs-wild-ride/
======
fhood
The author spent a lot of time dwelling on Window's filesystems, at which
point many of the readers got bored and started commenting. There are actually
a couple of excellent points in here, the majority of which relate to Go's
tendency to just be silently completely wrong in its behaviors from time to
time, and is absolutely packed with hidden gotchas.

~~~
klodolph
That said… I feel that Rust’s use of WTF-8 for OsString on Windows has
resulted in some really nasty problems, especially since OsString doesn’t
expose any useful methods for string manipulation. As far as I can tell,
Rust’s approach fails to hide any of the complexity, and then adds the
additional complexity of a new encoding and conversions on top. I can see that
there’s some end goal of being able to work with OsString in Rust code but at
the moment the API is missing _everything_ except a couple functions to
convert it into something else.

It’s a truly cursed problem that we have three separate notions of strings. We
have Unicode strings, we have bytestrings, and we have wchar_t strings on
Windows. No two of these are completely interoperable. This has a ton of
direct consequences which cannot be completely avoided. For example, if I want
to make a version of “ls” that gives a result in JSON, I’m already fucked and
I have to change my requirements.

~~~
steveklabnik
I have felt this pain for sure, but only really once. This is because, in
languages with strings as paths I tend to use string manipulation to do
operations on paths, but given that virtually all of my OsString usage is
paths, which have specific manipulation functions already it’s lesser.

This is also why the interface isn’t so rich, there just hasn’t been a lot of
demand. That said I think some things are in the pipeline?

~~~
ChrisSD
Do you have any more info about what's in the pipeline?

~~~
edflsafoiewq
[https://github.com/rust-lang/rust/issues/49802](https://github.com/rust-
lang/rust/issues/49802)

------
whateveracct
Here's an example of why Go's simplicity is complicated:

Say I want to take a uuid.UUID [1] and use it as my id type for some database
structs.

At first, I just use naked UUIDs as the struct field types, but as my project
grows, I find that it would be nice to give them all unique types to both
avoid mixups and to make all my query functions clearer as to which id they
are using.

    
    
        type DogId uuid.UUID
        type CatId uuid.UUID
    

I go to run my tests (thank goodness I have tests for my queries) and
everything breaks! Postgres is complaining that I'm trying to use bytes as a
UUID. What gives? When I remove the type definition and use naked UUIDs, it
works fine!

The issue is Go encourages reflection for this use-case. The Scan() and
Value() methods of a type tell the sql driver how to (de)serialize the type.
uuid.UUID has those methods, but when I use a type definition around UUID, it
loses those methods.

So the correct way to wrap a UUID to use in your DB is this:

    
    
        type DogId struct { uuid.UUID }
        type CatId struct { uuid.UUID }
    

Go promised me that I wouldn't have to deal with such weird specific knowledge
of its semantics. But alas I always do.

[1] [https://github.com/google/uuid](https://github.com/google/uuid)

EDIT: This issue also affects encoding/json. You can see it in this playground
for yourself!
[https://play.golang.org/p/erfcSIe-Z7b](https://play.golang.org/p/erfcSIe-Z7b)

EDIT: I wrongly used type aliases in the original example, but my issue is
with type definitions (`type X Y` instead of `type X = Y`). So all you
commenters saying that I did the wrong thing, have another look!

~~~
uhoh-itsmaciek
Not to mention that sql.Result (the return value of Exec) has a LastInsertId()
that's an int64, so if you're using uuids, you can't use that at all and have
to call Query instead and manage generated IDs yourself.

~~~
HelloNurse
This is a more ridiculous symptom of bad library design than the filesystem
trouble mentioned by the article.

In the real world, executing most SQL statement could be made to return a
semi-useful integer according to simple and consistent rules (e.g. affected
row count, -1 if there's no meaningful integer).

But the official Go documentation

[https://golang.org/pkg/database/sql/#Result](https://golang.org/pkg/database/sql/#Result)

makes it quite clear that the Go design committee decided to imitate a
remarkably limited and inelegant MySQL function that returns the value of an
auto-increment column, not even realizing that only a few statements have
auto-increment columns to begin with. I'd call this a negative amount of
design effort.

    
    
      LastInsertId returns the integer generated by the database
      in response to a command. Typically this will be from an
      "auto increment" column when inserting a new row. Not all
      databases support this feature, and the syntax of such
      statements varies.
    

(Of course, MySQL's LAST_INSERT_ID() is only bad as a building block and
inspiration for a general API; in SQL queries assumptions aren't a problem and
overspecialized tools can be occasionally very useful)

------
dilap
If you imagine a spectrum of languages from sloppy-but-"easy" to precise-
but-"hard", with something like Python or Ruby way off on the left and
something like Rust way off on the right, Go is sitting somewhere in the
middle.

And so if what you're craving is absolute precision and maximal avoidance of
errors or incorrect behavior, then Go is not going to be your jam. I
sympathize w/ that.

That said, these specific complaints don't strike me as that bad.

\- Filesystem perms exposed on windows, which just no-op. This seems pretty
reasonable, though!

\- Filesystem paths represented as str type, which is assumed to be utf8, but
doesn't have to be. This also seems reasonable! If you want to check for
invalid utf8 and specifically print out something special in that case,
nothing in Go is stopping you from doing that. This is a classic "easy but
sloppy" vs "hard but precise" tradeoff.

\- Timeout thing -- I'm a little confused here, or maybe not up-to-date. He
says let's do things the "modern" way and pass a context to do HTTP timeouts,
which apparently doesn't work, and then goes off on a 3rd party package to
then fix this which has an insane dependency graph. But...if you just set the
Timeout field on the http client, everything works correctly. So what's the
problem? Or am I missing something?

~~~
fhood
The http client isn't always directly exposed. I agree with the author.
Context is a per request object and timeout should be able to function on a
per request basis. Client is often shared and reused, and thus not always
exposed in certain design patterns. If context has a timeout why doesn't it
work as you would expect?

Also, now that I think about it, why does the basic http.get call mentioned in
every go networking tutorial not have a default timeout?

~~~
dilap
(I have never personally used context, so I'm not so sure what the
expectations are with that.)

Looking at the http docs, I don't see any reason to believe setting a context
for a request would control timeouts.

If the complaint is, "the http library API does not provide a way to set
timeouts on a per-request basis," then OK, I guess, that's true, but I don't
see why that should be a huge issue (just use different clients for the
different timeout values you need).

But if you _really_ don't want to do that, it should be easy enough to access
the underlying network connection and set the timeout before reading the body,
though I've never done this.

What Go is doing here still seems very reasonable from my perspective...

~~~
fasterthanlime
Author here, the article was actually wrong - I meant to expose yet _another_
timeout you can set on HTTP requests, I've updated it to include that one, and
be clearer on what `idletiming`'s purpose is.

------
gameswithgo
A lot of people seem to be missing an overarching point, which is the benefits
of a language having Sum types, so that edge cases can be represented clearly,
and in a way where the consumer of the api can't fail to know they exist, and
can't fail to handle them. Anyone thinking of making a new language today,
should really get some familiarity with Option and Result types. They make so
many things not only safer, but also nicer to use.

~~~
fhood
I surprises me that most people here aren't up in arms in agreement with this
point. Code that is silently incorrect is an absolute disaster on an
enterprise level. I spend a lot of time writing seemingly redundant double and
triple error checking into my code, only to have the designers of the LANGUAGE
say, "yeah, most filepaths are utf-8 so seems good enough to me".

~~~
physicles
I’ve written go full-time for the last 3.5 years and it still amazes me that
by default the linter doesn’t at least warn about unused/uncaptured return
error values.

~~~
nif2ee
Golang is such a joke of a language. The compiler won't even compile if there
is an unused variable but won't warn you if there is an unchecked error! This
language is meant to produce buggy incorrect code that can only be mitigated
with writing excessive repetitive tests that have nothing to do with business
logic itself.

Golang is probably the biggest embarrassment of a modern programming language
ever conceived. Again, if you don't believe me, just start writing your first
Kubernetes controller.

~~~
api
This doesn't match my experience. Go apps tend to be extremely clean and
reliable. It's of course possible to write crap code in Go, but you can write
crap code in any language.

The unused variable thing is mildly annoying but fits with the cleanliness
philosophy. Not checking errors is very easily detected by a LINTer such as
the one built into the JetBrains GoLand IDE. It highlights failure to check
errors and requires that you explicitly ignore the error return with something
like "_, foo = bar.baz()".

Go is spectacularly productive when used properly. It's a very nice language.

------
_bxg1
I think the mistake may be assuming that Go is meant to be a general-purpose
language. From what I can tell, it's purpose-built to be a "web services"
language, and its design-decisions center around that. What does that mean?

\- It's expected to be run on Linux servers (not Windows) and developer
workstations (probably not Windows).

\- It needs to be fast but not blisteringly fast. Micro-performance concerns
like the Time object thing are devalued.

\- Embedded use-cases are probably not given too much attention.

\- Agility in working with dynamic data (because that data is often foreign)
is valued over flawlessly safe types.

By deciding not to worry about certain use-cases, the language can be more
developer-efficient for its intended use-cases. In this light, for better or
worse, I think the decisions made make a lot more sense.

~~~
alexandercrohde
Except docker is written in go. Guess they never got the memo to not use go
for non-webservices...

~~~
lwb
Docker’s primary use case is also web services.

~~~
g_delgado14
Ubuntu is used (mostly?) for web services. Is Ubuntu suitable to be written in
Go?

~~~
eudoxus
Umm..what? Ubuntus primary use case was not web services, it was a user-
friendly PC OS compared to the Linux variants at the time.

It's picked up a lot in the server space because of the familiarity of it,
with respect to package management et all.

------
Liru
My most popular project on Github is currently a program I slapped together in
Go a long time ago. The `sync/atomic` issue mentioned at the end of the
article is THE issue that made me stop considering Go for anything other than
trivial things. Lack of decent error handling, a terrible builtin json
library, constant `interface{}` to poorly substitute for generics, the package
management issues that made Node.js look well-thought-out by comparison,
struct field tags, and generators provided by the core team that set off
linters provided by the core team with no good way to silence them kind of
piled on before that, but the `atomic` issue is the one that made me avoid it.
The author is right, all the little things add up.

Note that a bunch of these may have been fixed since I last used it, but
honestly, I haven't checked because it was frustrating working in it and
debugging it. It's a shame, `pprof` and the race detector are pretty cool.

------
gavinray
Holy shit, the entire explanation of the absurd reasoning behind needing to
use the _getlantern /idletiming_ lib, and the debacle behind unraveling it's
dependencies is pure gold. When the breadcrumb trail to dependency-hell stems
from a file whose contents are:

    
    
        // This file is intentionally empty.
        // It's a workaround for https://github.com/golang/go/issues/15006
    

I about fell out of my chair. Pure gold.

------
madhadron
> The Go way is to half-ass things.

This used to be known as the New Jersey school, and is the underlying
philosophy of Unix: build a bunch of little pieces that work a lot of the time
and kind of fit together if you remember the gotchas, then call it a day.
There is an essay on this that I am unable to locate right now which mentions
the horror of someone working on ITS when they asked how Unix solved a
rollback on error case in a system call and were told, "Oh, we just leave it
inconsistent, and the application programmer has to deal with it."

Does anyone else remember this citation? I truly am failing to find it this
morning.

~~~
skrebbel
Sounds like it might be straight from the original "worse is better" essay:

[http://dreamsongs.com/RiseOfWorseIsBetter.html](http://dreamsongs.com/RiseOfWorseIsBetter.html)

> _The MIT guy did not see any code that handled this case and asked the New
> Jersey guy how the problem was handled. The New Jersey guy said that the
> Unix folks were aware of the problem, but the solution was for the system
> routine to always finish, but sometimes an error code would be returned that
> signaled that the system routine had failed to complete its action. A
> correct user program, then, had to check the error code to determine whether
> to simply try the system routine again. The MIT guy did not like this
> solution because it was not the right thing._

~~~
wrs
Ironically, that is referring to the EINTR error code that I predict is about
to cause a bunch of unexpected failures when people switch to Go 1.14. [0]

[0]
[https://golang.org/doc/go1.14#runtime](https://golang.org/doc/go1.14#runtime)

------
typon
I don't understand why you would create a statically typed language but not
actually take advantage of types, instead typing everything with generic types
like string. Why make the user pay for complexity in types but not actually
deliver their promise? This is the problem with C and Go doesn't really solve
it either

~~~
terminaljunkid
There is a sweet spot and for different people, that spot lies in different
places.

Having a proliferation of types is bad for everyone but highest order FP
Weenies among us.

------
reggieband
Classic: “There are only two kinds of languages: the ones people complain
about and the ones nobody uses.”[1]

The thing that bugs me is the comparison to Rust. I mean, the author did
caveat that he chose it because Rust provided the best available counter
examples to his specific gripes. But my issue is that comparison seems to make
a false conclusion: Rust is better. My intuition says if the author used Rust
(or any other language) as much as they have used Go, and in the same
environments solving similar sized problems, they would have a completely
different 1000+ word rant on all the things they hate about that language.

We have an expression "use in anger". It describes a particular kind of
understanding that only becomes available when we face the real problems and
not just idealized ones. I even see smaller rants within this comment section
showing how the very systems he lauds in Rust have sharp corners when used in
anger.

I thought this rant had many good points and highlights many shortcomings of
Go. I would have preferred that it did not contain the comparison which draws
an implicit conclusion that IMO is likely incorrect.

1\. [https://www.goodreads.com/quotes/226225-there-are-only-
two-k...](https://www.goodreads.com/quotes/226225-there-are-only-two-kinds-of-
languages-the-ones-people)

~~~
eximius
Rust is better _at the problem presented_.

Rust not being perfect does not mean other languages can learn from its
successes.

~~~
reggieband
> Rust is better _at the problem presented_.

What I'm suggesting is that wasn't demonstrated. Go had a real-world used-in-
anger problem. That was compared to an idealized solution in Rust. It seems to
me that this is an unfair comparison.

Fair enough, it is hard to demand anyone who wishes to make a comparison
between two programming languages to have built equivalent massive systems
that stretch each language to their limits. But the point of the article
wasn't to compare languages, it was to show the kinds of problems exposed in
Go when it is used in massive real-world systems. So maybe it would have been
better to leave the comparison out.

~~~
eximius
> What I'm suggesting is that wasn't demonstrated.

An in-depth analysis of the respective languages APIs for a particular
targeted problem isn't enough?

I won't disagree that readers might make a leap to intuit the author thinks
Rust is overall better. But that extra leap doesn't mean he failed to show
Rust was better at a particular problem. In fact, that is WHY people would
make that un-warranted leap.

> But the point of the article wasn't to compare languages, it was to show the
> kinds of problems exposed in Go when it is used in massive real-world
> systems.

I mean, not really. Cross platform file manipulation is, maybe not common, but
not obscure. And making a web request _reliably_ is also not something you'd
expect to be only needed in massive systems.

~~~
reggieband
> An in-depth analysis of the respective languages APIs for a particular
> targeted problem isn't enough?

It isn't the _same_. There is another cheeky quote I can paraphrase: Everyone
has a plan until they get punched in the face. He is comparing a Go
implementation that has been punched in the face in a real-world use case
against a Rust implementation that was sitting on the sidelines.

If the point of the article (and the title) was "Go file system API vs Rust
API, an in-depth analysis" I would not have made my comment. The thesis of the
article appeared to be "pains I felt in Go when I used it on hard real-world
problems". All of his points seem to stand completely fine when you remove the
comparisons to Rust. For that reason I would have preferred to remove them.

~~~
eximius
You mean the Rust language that has dozens of cross platform implementations
of coreutils binaries? Go might be larger, but I hardly think Rust qualifies
as sitting on the sidelines.

That's a fair opinion. I think the article is richer for having shown what a
better API can look like for contrast.

------
umvi
Maybe I'm a zealot, but I don't really consider "doesn't work as well on
windows" a con of a language.

C# is (or at least used to be) utter garbage on Linux compared to Windows. I
don't hold that against C#, but rather recognize that Linux/Windows are very
different, and that compiler maintenance and development is non-trivial (and
obviously Microsoft is going to prioritize Windows).

This article is basically a rant that Go was designed with *nix in mind and
that Windows is a second-class citizen by comparison.

~~~
jjuel
But you knew C# was Windows only. Go was always touted as a cross platform
solution due to statically compiled binaries. If said binaries have issues on
Windows due to design decisions it seems like a language fault.

~~~
eudoxus
The binaries themselves don't have issues. But Go's descision to make the
standard library Unix focused isn't a flawed design decision.

If you need OS/platform-specific precision, you're free to create or use an
alternative library. The standard lib was never designed to be the magic
bullet for cross-platform, but the language internals, compilation, and
execution do a good job for _many_ platforms.

We need to keep in focus what the goals of each part of the language are
intended for.

------
bsimpson
> Nine out of ten software engineers agree: it's a miracle anything works at
> all

There was a beautiful rant about a decade ago called something like
"everything's broken all the time and nobody cares." The gist of it is that
all software is written by people. Anyone who's written software knows that
it's usually riddled with hidden corner cases, unfortunate tradeoffs, rushed
deadlines, etc. Software is also moving into critical spaces like aerospace,
medicine, banking, etc. The thrust of the article is that we're trusting more-
and-more critical infrastructure to a discipline that anyone who's worked in
knows is untrustworthy.

Does anyone remember the link to the article? I've often wanted to re-read it
and share it with people, but I've never been able to find it.

~~~
temac
> Software is also moving into critical spaces like aerospace, medicine,
> banking, etc. The thrust of the article is that we're trusting more-and-more
> critical infrastructure to a discipline that anyone who's worked in knows is
> untrustworthy.

"Anyone" who's worked in those industries knows SW _can_ be done in a
trustworthy way.

At least not less than other engineering _disciplines_.

"hidden corner cases, unfortunate tradeoffs, rushed deadlines" in uncontrolled
proportions are a symptom of _lack_ of discipline, either originating directly
at low level (even if maybe mainly because of cultural influences, but I mean,
what is not?), or under pressure from the hierarchy. The same conditions can
led to critical failures of other kind of engineering realisations. One key
point of critical failures resulting from hierarchy pressure is that it does
not absolves the engineers doing the work, and some engineering culture
actually recognize and teach that. Other cultures mixe everything in the same
pot without even an once of ethics nor serious reliability thinking, and you
get people maintaining the myth that software just can't be reliable, that the
whole industry - without exception - is in an eternal crisis, and that that's
even normal because the field is "young". None of that is true; you even have
plenty examples around you, and decades of history to study. And of course, we
must remain exigent so that the quality does not decline just because of a
kind of self prophecy.

~~~
stjohnswarts
*self fulfilling prophecy.

------
flohofwoe
Isn't basically all of this shortcomings of Go's standard library, not "Go the
language"?

The Go standard seems to be heavily geared towards doing work on the server-
side, and "server-side" essentially means "Linux" today.

If I'd need to write "client-side" cross-platform code that also needs to run
on Windows, Go wouldn't be my first choice, also not my second or third.

And TBH, most other languages are not that much better (Python might be the
only notable exception, and even this requires different code paths for
"Windows vs the rest of the world" here and there).

For this type of cross-platform code, it's almost always better to talk
directly to the underlying OS APIs and put those under a thin custom wrapper
library instead of relying on the language's standard library.

~~~
fasterthanlime
A standard library says a lot about the language. Even if someone made a much
better path/file handling library for Go, another one of its strengths are the
ubiquitous interfaces you can rely on across libraries. Unless the superior
library gained a lot of adoption really quickly, it would remain largely
irrelevant in the face of the standard that was set years ago by the Go
authors.

~~~
throwaway894345
Look at Go’s HTTP library. It’s much lauded for striking a good balance
between performance and ease of use, but it’s not as performant as it could
be. For that, fasthttp exists and is quite popular although not nearly as
popular as the standard HTTP library.

Your comment gives the impression that this is a failure because the library
for niche performance cases hasn’t become the go-to library for the general
case. I disagree—it’s ideal that we have a canonical general purpose library
and another for high performance cases.

Perhaps you would argue that we should have interfaces that allow for a
pluggable performant implementation and an easy-to-use general purpose
implementation? This is all well and good, but it’s inherently not possible,
because the interface is about ease-of-use and the performance is achieved by
trading off on friendliness. You might offer Rust as a counterpoint since many
of its standard libraries use an interface that is suitable for the general
case and the high performance cases; however, this is a lie: these interfaces
(and the core language) are manifold harder to use than their Go equivalents.
In other words, Rust’s “general purpose” interfaces trade ease of use for the
ability to support high performance implementations. This tradeoff isn’t
inherently bad, but it is bad to pretend as though it’s _inherently good_ or
that there is no tradeoff at all.

------
DLA
Windows-focused rant. Plus a few reasonable points. Every language is complex
at some level and in their own ways-Rust included. Every language hides some
of the complexity of layers below it like assembly and thus hides hardware
details. Computers are complex. Point granted.

Fact is Go is a very reasonable set of compromises that let's real enterprise-
scale work get done and run with solid performance. I've done work on mostly
Nix systems but have cross-compiled for Windows when needed. These are wildly
different OSes and some adjustments are needed thusly in the code.

Go has faults. The "OMG Go has no generics so it's total trash" argument is
just silly. Generics are coming.

Personally, Go has never let me down with anything I've asked it to do -- ETL
flows, servers, streaming data processing, CLI programs, networking tools,
etc. Use whatever tool fits your needs.

~~~
HideousKojima
>Go has faults. The "OMG Go has no generics so it's total trash" argument is
just silly. Generics are coming.

Until it has them it's a valid complaint. And the fact that they're finally
coming _11 years_ after the language's creation is another matter

~~~
throwaway894345
This is silly. Lots of people are very productive in Go without generics. Even
more productive than many languages that have generics (including Rust). Lots
of people are very productive in _languages without static typing at all_.
Generics will significantly improve a relatively small proportion of use
cases.

------
physicles
Having used go full-time for the last 3.5 years, this article didn’t feel like
a twist of the knife. Yet all the language evolution efforts I’ve seen in the
last two years make me think that early Go was, mixaphorically speaking,
lightning in a bottle that won’t strike twice.

\- I’ve never hit the file system stuff. We all use Linux; all our code runs
on Linux. I’m curious who the people are who are using Go on Windows.

\- Network timeouts are a stupid gotcha I first hit about six months into my
go tenure. You can set read/write timeouts on the Transport that’s used by the
connection though; not sure why that isn’t covered.

\- The wall clock time thing is new to me and looks crazy complicated; I’m
angry that it’s something I have to know about now. It’s bad enough that
time.Time operator == and .Equals() behave mostly but not quite the same.

Something that’s not in the article: the tooling situation (autocomplete,
source navigation, and so forth) IS STILL WORSE THAN IT WAS TWO YEARS AGO. The
old tools were perfect but were never updated for module support. gopls is
still an unfinished mess; last week I had to write a script that auto-kills it
if it uses more than 3GB of memory.

------
weego
_It constantly lies about how complicated real-world systems are, and optimize
for the 90% case, ignoring correctness_

I don't know how this comment appears to come as a new thought after them
using Go in production. I don't use it at all for work but that is literally
my understanding of the point of Go; granular "correctness" as a trade off for
the productivity it provides if you're doing things that are just on the "good
path"

------
fortran77
> So, no errors. Chmod just silently does… nothing. Which is reasonably -
> there's no equivalent to the “executable bit” for files on Windows.

It's simply not true that Windows doesn't have "execute permissions" for
files. It does:

[https://docs.microsoft.com/en-
us/windows/win32/fileio/file-s...](https://docs.microsoft.com/en-
us/windows/win32/fileio/file-security-and-access-rights)

It's just that the people who wrote the go library couldn't be bothered to
abstract this interface across all platforms.

~~~
mehrdadn
+1 came here to say the same thing. This seems more like a problem with the
standard library authors not putting in enough effort than anything else.

------
kodablah
2/3rds about platform FS incompat, last third about only a couple of things
like time comparisons being monotonic now (does more good than bad IMO).

I suspect if issues of such importance are enough to make you want off the
"wild ride", you will not find a ride suitable.

~~~
jjoonathan
He presented them as typical examples, not as issues of such importance as to
independently make him off the "wild ride."

He also compared them to alternatives that he found favorable, which
specifically addresses the idea that alternatives are worse.

It could be reasonable to disagree with the content of his argument, but it
didn't have either of these structural problems.

~~~
kodablah
> it didn't have either of these structural problems.

Disagree...the volume/importance of grievances should be directly proportional
to willingness to abandon. That a few examples can be provided isn't an
indictment of the ecosystem anymore than it would be if I did the same to
those the OP found favorable.

~~~
jjoonathan
He chose to go deep instead of broad. That was an editorial tradeoff to keep
the length this side of an encyclopedia, and I don't think it's fair to
criticize him for it unless you're also going to argue that the generalization
he asked us to take on faith doesn't hold -- in other words, that the example
he gave in which a simplifying API decision backfired is atypical.

I haven't used much Go, but the bit that I've played with gave me the distinct
impression that "opinionated simplification" wasn't just common, it was the
defining quality of the entire language, which would strongly suggest that
OP's complaint would easily generalize to a hundred other APIs. Is that not
the case?

------
dickeytk
> _(Note that path /filepath violates Go naming conventions - “don't stutter”
> - as it includes “path” twice)._

That guideline is for package/content name, not package directory names.
[https://blog.golang.org/package-names](https://blog.golang.org/package-names)

~~~
skywhopper
Yeah, the author misunderstands the naming scheme which actually suggests this
sort of repetition. See also, io/ioutil.

~~~
fasterthanlime
My mistake, I removed the relevant paragraph in the article.

~~~
iudqnolq
I still see it, maybe cached?

------
7532yahoogmail
I'm c/c++ over 20 years, go 1 year, Python 5+ years. I work at a company with
a guy on the c++ standards committee with the internal sdlc and engineering
training for large scale, commercial systems to boot.

This article is a rant. Not an engineering take down of go. There's just not
much of substance here. Were I to care about windows (I don't) for serious
cross platform os interaction, go isn't your hammer of choice.

I've turned to go recently for some I/O heavy apps of a micro-service type
which it is fine for. I also turned to go because of God awful c++ build times
and bad build systems in the sense that they assume all code is in a single
branch. By switching to go I also prevent less experienced programmers from
linking in legacy c++ libraries and the evil that comes with them.

Go has delivered. My needs are such that protobuf/flatbuffer are good enough
for types and go's lack of generics is irrelevant. I'm pushing bytes across a
network pipe in which each message admits simple transforms/operations.

Now I am keeping my eye on three things that I think go could burn me on:

\- garbage collection

\- channels ... cool but slow

\- something unixy/multicore/close to the bare metal ... Like kv store

Those things I'd be reticent about doing in go.

Folks, we need 2-4 languages with their connections to libraries and tool
chains in our toolbox.

While we remain dominated by c++ (a complex beast of a language) I am looking
to add a functional language to my kit (ocaml/Haskell). Btw good engineers
need a formal language too. I recommend tla+ and there's a guy in hacker news
here that's got good books on it. Recommended! Highly concurrent code ought to
modeled in tla+ first before leaving your app language gun and taking the
canolli.

Cheers

------
Thaxll
"With a Go function, if you ignore the returned error, you still get the
result - most probably a null pointer."

Well you should handle the error in the first place.

~~~
hota_mazi
The language should make you handle the error and the compiler should refuse
to compile your code until you have done so.

~~~
philwelch
So checked exceptions, then?

~~~
hota_mazi
That's one way, yes.

------
bfrog
I felt the same way after writing a large (100kloc) project in Go, this is
back when go was 1.0 or so as well. It started off well enough, but eventually
started to fail in helping me create the software I needed to make.

~~~
martinni
No matter what, after 100k loc, you'll encounter language quirks that
irritates you. It's a matter of how complicated it was to find and what the
work around is.

------
madmax96
I think its worth pointing out that the Rust code is more broken than the Go
code in this example. Because Rust is trying to come up with a sane way to
display the filename (a) it prevents users from using encodings the language
designers did not anticipate and (b) it prevents the solution from integrating
with other system tools. For instance, you can't run `rm "$(rust_program)"`,
but you can with the Go solution.

But discussing _any_ of this means you've missed the point of languages like
Go. Instead of arguing about the best way to to represent pathnames that
aren't a valid byte sequence under $PREFERRED_LOCALE, we should be talking to
our customers and solving their problems.

------
totalperspectiv
My main takeaway is the quote from scottlamb:

> ... these sorts of statements contribute to my belief that Go is an
> opinionated language that I should hesitate to choose for anything that the
> language's authors haven't specifically considered in depth.

------
marcus_holmes
Can we just stop with the "Rust vs Go" shit?

If you want me to take a critique of Go seriously these days, pick another
language to compare it to. Any other language.

And yeah, I'm aware that 5 years ago there were a ton of "Go vs Java"
articles. I didn't think much of them then, either.

~~~
fasterthanlime
Author here - I apologize for pulling Rust into this, but for the life of me
couldn't find any comparable language that solves those problems "the right
way".

I tried really hard. I knew a lot of people would instantly have that
reaction, but I couldn't find another way to show that _there is another way_
, short of pulling it out of thin air (which would've made for an even longer,
less accessible article).

~~~
marcus_holmes
LISP, surely? all HN knows that LISP is the perfect programming language... ;)

~~~
msla
If only because Lisp is a moving target, so practically anything you say about
it will be true by someone's definition.

------
lanius
For those unaware, the title is a reference to a hilariously long user-created
ride in Roller Coaster Tycoon 2 titled "MR BONES WILD RIDE" [1]. The ride's
exit connected to its entrance, so passengers were forced to repeatedly ride
the roller coaster forever.

[1] [https://knowyourmeme.com/memes/mr-bones-wild-
ride](https://knowyourmeme.com/memes/mr-bones-wild-ride)

~~~
CameronNemo
Have you not heard of Mr. Toad's Wild Ride?

------
carapace
> Computers, operating systems, networks are a hot mess. They're barely
> manageable, even if you know a decent amount about what you're doing. Nine
> out of ten software engineers agree: it's a miracle anything works at all.

I like that he identified the real problem right at the start.

~~~
0xdeadbeefbabe
Yeah so do I. It would have made a nice tweet.

------
mushufasa
main gripe seems to be that go will "optimize for the 90% case, ignoring
correctness" \-- particularly leading to issues on non-unix systems like
windows.

That fits Go's stated goals afaik. While I understand the author ran into
problems for their use-case, I did not find this rant compelling as a general
criticism.

~~~
defnotashton2
Same because if you try to create the % case you end up with things like ASP.
NET and entity framework. Working with those for 5 years I was constantly
annoyed with how far I could add super complex features only to have to
unravel them to implement a simple lower level edge case.

And in my experience this too easily reflects poorly on the devs "well I found
a blog post for ef that does what we need in 30 seconds.." which just isn't
the case. I migrated to golang and find its nuances much easier to swallow. No
generics? True - write a generator for your use case. It's really not that
hard..

------
dellinspiron
(Comparing a function in Rust's sdtlib to Go's:) > Of course there's a
learning curve. Of course there's more concepts involved than just throwing
for loops at byte slices and seeing what sticks, like the Go library does. >
But the result is a high-performance, reliable and type-safe library. > It's
worth it.

When I first saw Go, I was blown away. Not by its features, but rather the
lack thereof. It seemed like one last "Hail Mary!" from the C programming
community to get "back to basics". But, as the author showcases, the time when
programming was about manipulating arrays with pointers is, if not behind us,
hopefully on its way out.

------
klodolph
With the path example… just try to combine this with flags, so we do something
like:

    
    
        $ ./my_program --file="$(printf "\xbd\xb2\x3d\xbc\x20\xe2\x8c\x98")"
    

Well, just try to write the program that does that in Rust, without using some
option-parsing library that hides all the details, and then try to figure out
how to get it to work equally on Windows.

To spoil the answer, it turns out that OsString only exposes a couple
conversion routines and can’t be manipulated, and people have been trying to
figure out a way to add a string-like API to it for years. Rust’s “do it the
right way even if that exposes lots of complexity” approach here has its
drawbacks.

~~~
dpc_pw
If you want to half-ass it like Go you go [https://doc.rust-
lang.org/std/ffi/struct.OsString.html#metho...](https://doc.rust-
lang.org/std/ffi/struct.OsString.html#method.to_string_lossy) or
[https://doc.rust-
lang.org/std/ffi/struct.OsString.html#metho...](https://doc.rust-
lang.org/std/ffi/struct.OsString.html#method.into_string) if you want to
potentially get an error.

If you want to deal with bytes / invalid Unicode, you go [https://doc.rust-
lang.org/std/ffi/index.html#conversions](https://doc.rust-
lang.org/std/ffi/index.html#conversions)

~~~
klodolph
I'm aware of the conversions, unfortunately, you can’t really do any
processing before you convert and you can’t (unlike C++) write generic code
that works on both types of converted values.

On Unix you get Vec<u8> and on Windows you get an iterator over u16. This is
hot garbage, to say the least, if you want to do any kind of processing. I can
go into more details, but in C++ you would just be working with std::string
and std::wstring, depending on platform, and at least in that case you can
hide everything away like this:

    
    
        #if defined WIN32
        using OsChar = wchar_t;
        #else
        using OsChar = char;
        #endif
    
        using OsString = std::basic_string<OsChar>;
    

This is only the beginning, but you can see how the C++ version is much easier
to work with, even though it doesn’t hide the problem from you.

Note that I’m not advocating that you make everything in your code into
OsString, just that it’s common to need to do some small amount of
manipulation of OsString and Rust makes this much harder than it should be.

~~~
dpc_pw
With Rust you can also convert to `Vec<T>` where T is either `u8` or `u16` and
use generics to work on any. ️

And there are probably handful of libraries that would help with all that too.
Also - whatever convenient functionality you might want, can be added in the
future without issues. Hardly a language flaw - just a minor unimplemented
functionality.

~~~
klodolph
> With Rust you can also convert to `Vec<T>` where T is either `u8` or `u16`
> and use generics to work on any.

That’s a very cumbersome way of doing things. I would love to see an
illustration. It also involves a ton of conversions: if want to parse a
command-line flag which contains a file path, it would go: wchar_t -> OsString
-> Vec<u16> -> OsString -> wchar_t. It also makes it difficult to use Rust
APIs in a more or less idiomatic way.

> Hardly a language flaw - just a minor unimplemented functionality.

It’s a flaw in the standard library, not the language. When you say that it’s
minor, all you’re doing is saying, “I don’t care about the things you care
about.” That’s not really an argument, just a statement of your own personal
opinion.

------
irrational
>when you make something simple, you move complexity elsewhere.

This applies to so many things. I wish I could get non-technical people to
understand that making something simple moves the complexity elsewhere.

------
sitzkrieg
i wrote go professionally on a project for a year in a single very intense
push, and i was burned by every single thing listed in the article. felt like
uphill impedance mismatch the whole way. its nice to see it articulated well

~~~
donatj
> burned by every single thing listed in the article

Really? That seems absolutely bizarre to me, I've been writing it
professionally for ~8 years now and never hit… any of these.

I mean I basically never interact with Windows on any level, but none of this
has ever bit me.

~~~
sitzkrieg
this was cross platform forensics software ripe with edge cases

------
SpaceManNabs
I have become annoyed at go for completely different reasons than OP. I wrote
my blog using go as the backend a few years ago. Deployed it on Google App
Engine. Every time Go updates or the App Engine SDK updates, it is a super
pain to update my site. I almost want to throw it all away now that Go is
handling dependencies in a completely new matter.

------
altmind
50 Shades of Go: Traps, Gotchas, and Common Mistakes for New Golang Devs, a
good read about go quirks and unexpected behaviors
[http://devs.cloudimmunity.com/gotchas-and-common-mistakes-
in...](http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-
golang/)

------
luord
I kept waiting for practical examples that showed how these shortcomings made
go a non-starter, and ultimately all I got was a mention, right at the end,
about how he hit a particular bug multiple times.

I mean, currently I work in a go shop and I hate nearly everything about it,
all just from what he calls "the bad", which is enough to make me not feel
precisely happy about writing it. The content of this article, what he calls
"the ugly", comes across as a bit nitpicky in comparison.

Nonetheless, it is a good article about string and path handling, time, and
being irresponsible with what one is depending on.

------
donatj
I'm late to the show here, but I think the whole argument around filepaths
needing to potentially be encoded before being presented to end users is a
non-issue / how most languages I've worked in have handled it?

Does rust have some fancy handling? Sure? Is it syntactic sugar? Absolutely.

Maybe I've been working in Web too long, but encoding a value before handing
it to the user seems second nature.

------
kkredit
This is an excellent example of Waterbed Theory: "This is a theory which says
that if you push down the complexity in one part of a language or tool, there
is a compensation which increases the complexity of another part of the
language or tool."
[http://wiki.c2.com/?WaterbedTheory](http://wiki.c2.com/?WaterbedTheory)

------
time0ut
Based on the title, I was expecting a post about some sort of production
horror story or some difficult edge case upgrading to 1.14.

------
dellinspiron
Comparing Rust to Go: > Of course there's a learning curve. Of course there's
more concepts involved than just throwing for loops at byte slices and seeing
what sticks, like the Go library does. > But the result is a high-performance,
reliable and type-safe library. > It's worth it.

------
GiantSully
Go is an opinionated language, but opinion is not just right or wrong, and
it’s getting complicated with time passing by. Opinion might be prejudice.
Anyway, I like the multiplexing and go routine in go.

------
ungerik
I wanted a little bit more from FS department, so I rolled my own:
[https://github.com/ungerik/go-fs](https://github.com/ungerik/go-fs)

------
ggm
Scuolo di Michaelangelo "god, this Carrera marble is so hard to work in why
can't we just pour concrete into rubber moulds like the garden gnome factory
next door"

Michelangelo "fine, I thought you wanted to learn how to sculpt perfect
buttocks but whatever"

Jeff Koons "that garden gnome idea, how about now I know how to carve Carrera
marble I make one in marble"

Scuolo ..."Jeff.. we hate you"

The GO authors are gifted. They make tools gifted people understand. If you
aren't gifted, they are difficult tools to use.

(I'm not gifted btw)

------
philpearl1
getlantern/idletiming has 9 stars and 2 forks. This is not something the
general Go community uses

------
pmarreck
Go needs good criticisms like this.

I will never understand why this language is so popular.

~~~
bsdubernerd
It's due to the same reason Rust is: it's backed by a large popular company
investing in the language and being loud about it, which leads to a rapidly
growing mindshare and ecosystem around it, which is essential for adoption.

This is not meant as a criticism toward go or rust: the history shows several
cases where this happened before irregardless of the technical merits.

A language still needs to become popular, it's not like we're lacking great
languages nowdays. It's certainly easier if you're big and can provide the
founding around it.

~~~
AnimalMuppet
The backing can get you publicity. If the language is lousy, though, the
publicity won't help it. But publicity can turn an obscure good language into
a well-known good language.

I think the bigger thing that corporate support gets you, though, is a better
library (more complete, more debugged, and more polished). _That_ is an
essential ingredient for language popularity. Up through Java, it was enough.

But these days, I think that there's one more ingredient needed: Solve some
problem that isn't well-solved in other existing popular languages. Go has
pretty good answers on multiple threads and network services. Rust has the
borrow checker. Those are useful enough pieces to gain traction for those
languages.

------
pcj-github
Got really tired and bored with this. Maybe structure the article with some
sort of meaningful abstract so that you can summarize the points you want to
make up front without having to subject the reader to 9/10ths of this article.

------
tschellenbach
2+ years on Go, 13 years on Python and JS. Some Kotlin and Java as well. Go is
by far the best programming language you can find to build scalable
microservices. Hands down, years ahead of anything else.

------
tyrankh
I think this post summarizes to,

\- I don't like the file-related packages

\- What's up with this random 7 star library having a lot of transitive
dependencies

\- Rust for life

\- In summation, Go is the worst

~~~
fasterthanlime
Not that this is a good faith summary, but I've updated the article to point
out that it's not just "this random 7-star library", but in fact, 266
publicly-available Go packages.

~~~
cdelsolar
Your rant largely has to do with a library someone wrote that did not handle
dependencies well. A few years ago you would have complained about lack of
module support at all. I have a toy project that's relatively simple, and the
Javascript frontend has a lockfile that is literally over 10000 lines long.

~~~
fasterthanlime
I was already shipping Go code a few years ago, and the various vendoring
tools gave me a lot less grief the new module system has. Besides, it still
had all the same limitations, the same standard library choices, the same
sloppy abstractions. The rant applied then and it applies now - and focuses
not on any specific problem outlined in the article, but the general
philosophy of the language, its standard library, and its ecosystem.

------
miguelmota
Go is my favorite language but I do agree that Windows support has always felt
like an afterthought.

------
shadowgovt
It's a pretty good article. The tl;dr is that golang is a POSIX-focused
application programming language that is incorrectly advertised as a platform-
agnostic systems programming language.

------
crimsonalucard
Rust is great it follows modern programming techniques and theory but it
focuses a little too much on zero cost abstractions and because of that the
abstractions are a bit complicated.

Go is easy to learn but poorly designed with an incomplete type system hence
all these strange issues.

There is a vacuum that exists between Rust and Go. A language that utilizes
modern Algebraic Data Types (like rust) but does not necessarily need to
create abstractions just to make everything zero cost (like Go).

~~~
hajile
It's not a void. StandardML file the niche well and Ocaml is getting close
(just waiting for multicore support). The issue is a company that wants to put
in resources.

~~~
jhoechtl
Came here to say OCaml wojld be the sweet spot.

Sorry about multicore. It's like Perl6. A dream which will never come true or
your accept F#.

~~~
lizmat
Note that Perl 6 has been very much a thing since December 2015 (first
official release). However, last October it got renamed to Raku
([https://raku.org](https://raku.org) using the #rakulang tag on social
media). And it is still very much a thing. If you want to keep up to date, you
should check the Rakudo Weekly News
([https://rakudoweekly.blog](https://rakudoweekly.blog)).

------
jbverschoor
Haha

------
terminaljunkid
TL;DR I am on Windows and Go doesn't play well with its weird filesystem. It
is all Go's fault for relying on highly used server OS semantics and therefore
Go's simplicity is lie.

------
ptah
i think your problem is with windows, not go

------
_wldu
Articles like this are evidence of Go's huge success.

------
sagichmal
What a ridiculous and narrow thing to get so upset about.

~~~
crimsonalucard
It's not ridiculous. Functions should not be returning garbage values when an
error occurs.

This is the worst part of javascript and certainly it's not pleasant to
uncover this in Go.

------
ainiriand
If somehow we had a way of checking Golang's source and submit our desired
changes...

~~~
klohto
I seriously hate this argument.

Go and have a look at the issue the golang is discussing currently. Do you
seriously think that everything can be fixed by a simple request?

Most of the time it wouldn't fit with the way Golang is going. It's not a
critique of some bugs in Golang source code but the mentality and flow
surrounding changes.

Do you except the author that submitted change overhauling the whole way
Golang handles Unix vs Windows would be accepted?

I do not agree with the author, but that is fine. It's fine for me, understand
it's not good for his use cases. Saying "duh, just submit your request" is
stupid as it gets.

~~~
ainiriand
Obviously what I mentioned is just an oversimplification. I expect that when
some particular piece of software (open source in this case) is causing major
trouble to a big chunk of its users, they get together to fix it.

In the particular case of this user, some of the problems are are really
related so I can imagine that if they were widespread it would´ve been taken
care of.

I am sorry for using sarcasm to take a detour from my real point and I was
just making some light-hearted fun about op's problem.

~~~
klohto
Apologizes for not getting the sarcasm, it seemed real enough.

------
kissgyorgy
If you don't care about Windows, half of the rant is just not interesting for
you. So even if you accept that he is right in every single point, excluding
those parts there these are not a lot of problems. EVERY language has
problems, even Rust.

------
cheese4242
Clickbait title.

Should be titled "Golang doesn't work well with Windows".

~~~
jackbravo
Granted, the article is pretty long, and spends a lot of time talking about
this windows pitfall that I was also about to abandon it. Then it speaks of
other examples like the monotime issue which I think is a better example of
what he is advocating.

------
zemnmez
i cant help but feel Go is the new Javascript. Everyone wants to complain
about how its semantics as a language do not align with their favorite
programming paradigm. In this case, having complex, algebraic type-based
abstractions that attempt to accurately reflect subtleties that are rarely
important.

Yes, Go, as Javascript has unique failure cases and subtleties, but they are
(as of 2020) very productive languages within their particular paradigms.
That's not to say either language is beyond criticism, of course. But it's a
little silly to think that a language that supports the 99.9% of writing a
service well, but does the .1% badly as a tradeoff for simplicity is a
fundamentally broken language because it doesn't share those aspirations. We
might as well be complaining about the lack of pointer arithmetic in Python.

------
zxcvbn4038
What is the point of a ten page rant like this? If the guy doesn’t like coding
in Go, just stop using Go, problem solved. How many more times are we have to
have the language X is different then language Y and I hate feature Z
discussion? These are popping up almost daily. We could probably automate
generating a daily rant with commentary, and let all the Joe Nobody coders get
back to whatever they are trying to accomplish.

~~~
krallja
Hardly anybody writes a retraction after three years of “mongodb is great in
production” — they silently switch to a new product, and maybe say something
positive about it too. These kind of rants are hard-earned battle scars of
former zealots learning their lesson, and should not be discarded.

------
sudhirj
Most of this rant is a disagreement of how Go handles file system differences
between Unix and Windows, most of the rest is complaining about some badly
written library.

May be good to know if you’re dealing with any of that, but this much effort
would be much better served submitting a proposal to change whatever the
author is so worked up about. Either the proposal is accepted, or the Go
community will provide a response if the proposal is written with due
consideration.

------
earwetr
i went through the pain points of Go myself but that was at the beginning when
i was expecting behavior i was used to from previous language(s) and from
trying to force the previously learnt norms onto Go.

Reading the blog post(i wrote something similar that got a ton of views here
few years ago) it sounds more like the issue is between the keyboard and the
armchair, not with the language itself.

As with anything else, if you don't like it, don't use it. If you like Rust,
Rust away.

------
h2odragon
I'm sure there's real issues; but this reads as an extended whinge on "Windows
and Unix are __Different __and languages wrap those differently whaaa! "

If you want OS interfaces that look the same wherever; then choose a
portability layer that abstracts that for you.

------
sascha_sl
Uh okay, lots of "but windows" and a few misinformed takes about the http lib
(using contexts over using a client instance with a timeout set) alongside
ripping apart a random package I've never used or heard of for having a huge
dependency graph.

Such a long article, for this?

------
alharith
> which makes a lot of problems impossible to model accurately

 _Impossible?_

> (instead, you have to fall back to reflection, which is extremely unsafe,
> and the API is very error-prone),

 _Extremely_ unsafe?

> when you make something simple, you move complexity elsewhere.

Does it? Or did you, in reality, not really make it simpler?

> Go says “don't worry about encodings! things are probably utf-8”

Does it? [https://blog.golang.org/strings](https://blog.golang.org/strings)

It just sounds like the author is very frustrated at some seemingly minor
inconsistencies (from their perspective), and the extreme language used for
things that are not that extreme are evidence in my opinion. Blogging can be a
good exercise to shed some frustration, I definitely understand that aspect.
Not sure this needs to be shared as a good example of anything or taken in any
light, other than "someone is venting."

~~~
sascha_sl
It's the fairest point, modeling some issues is a pain in go, particularly
dynamic data types (think ActivityStreams).

~~~
alharith
Debating whether certain domains are more difficult/painful in Go than other
languages is valid. Saying they are _impossible_ is extreme.

