Remix.run Logo
wokwokwok a day ago

> I guess I feel bad for people who are particularly sensitive to the areas that Go does not succeed in, because they are probably going to be complaining about it for the rest of their lives.

Well, that’s a stellar endorsement of the article, because that’s literally the point they’re making.

You’ll use go.

…and then regret it.

…but by then it’ll be too late, and you’re stuck with it.

I think the author makes a compelling argument, which is very difficult to counter, that it is a bad choice and you will regret having it in production in many of the explicitly listed cases, and in many professional situations where companies that are not technically competent use unsuitable tech.

Companies should stick to boring tools.

…but, for personal projects? Sure, go for it.

jchw a day ago | parent | next [-]

There's no tool boring enough to prevent any chance of regret. At the end of the day, it's really, really difficult to anticipate where your pain points will actually wind up in the real world. In practice, I've had lots of really good production success with Go and not too much heartache about the choice. Since adopting it personally (in around 2014) almost every company I've gone to work since has used Go in some capacity and that capacity was usually growing because it was working for them.

Will you regret choosing Go? Maybe. Or, maybe not.

coldtea a day ago | parent [-]

>There's no tool boring enough to prevent any chance of regret.

I'm not so sure. I know C programmers that swear by it, warts and all, with absolutely zero regrets for using it e.g. in the embedded space.

jchw a day ago | parent [-]

If anyone tells you C is "boring", that's just plain and simple bullshit. C gives you undefined behavior, buggy compilers (yes even for simple C code, MSVC is especially bad at plain C but there are other offenders) and the world's worst standard library for manipulating strings. Using C in embedded development is probably OK, even if you have to suffer with whatever crappy vendor compiler you are stuck with, but that's only considering the pretty severe limitations that very resource-constrained embedded development typically has (e.g. no dynamic allocation.) C is only as boring as you force it to be, and you really have to force it to be.

That said... the thing about the embedded space is that most of it is C, always will be, and may continue to be for the foreseeable future. It's really hard to really know what you have to regret if all you've ever known is C.

coldtea a day ago | parent [-]

>If anyone tells you C is "boring", that's just plain and simple bullshit. C gives you undefined behavior, buggy compilers (yes even for simple C code, MSVC is especially bad at plain C but there are other offenders) and the world's worst standard library for manipulating strings.

Boring doesn't mean "has no issues" or "takes care of everything for you".

It means, stable syntax, lots of mature tooling and compilers, and any problems the language has are well known.

Of the various languages around, C is the least likely one to have "buggy compilers" (issues in MSVC because MS compilers focus on C++ are a different thing, also falling in the "known" category).

jchw a day ago | parent [-]

> It means, stable syntax, lots of mature tooling and compilers, and any problems the language has are well known.

In case of C what it really means is that the compiler codebases are extremely old and sometimes in rather bad shape.

Speaking of stable, C hasn't just stayed still: The C23 standard adds plenty of fun new things, like an annotation for unreachable code. Modern C has threads and synchronization primitives, #embed, complex numbers, and plenty more.

> Of the various languages around, C is the least likely one to have "buggy compilers"

C compilers are still routinely buggy, because the memory model is surprisingly tricky, there are many edge cases in the specification that are subtle and despite being fairly basic are not stressed often enough to come up, and because optimizing C while adhering to its rules about defined behavior is an unending arms race about what is still "technically" compliant with the standard.

Again, this is especially true if we're considering embedded development where the compiler you have might be a random hacked up old build of some vendor compiler rather than at least being recent GCC or Clang. In that case, even if you only consider a small subset of C99, there's still plenty of room for things to go wrong.

By any standard you can come up with, C is just plain-and-simple not a boring reliable solution. As it turns out, something being old and relatively simple isn't enough to make it boring; you can still have a pathological case that is just an absurd clusterfuck.

I will grant you one thing: it is the most boring embedded development solution. But come on. The best modern competition is Rust. Being more boring than Rust is not exactly an impressive accomplishment, especially when you consider how much practical value Rust has to offer.

coldtea 17 hours ago | parent [-]

>In case of C what it really means is that the compiler codebases are extremely old and sometimes in rather bad shape

Statistically nobody writing C code gets to worry about a compiler error.

>Speaking of stable, C hasn't just stayed still: The C23 standard adds plenty of fun new things, like an annotation for unreachable code. Modern C has threads and synchronization primitives, #embed, complex numbers, and plenty more.

Compared to any other modern language, this is so still that C could make a living as a living statue...

>By any standard you can come up with, C is just plain-and-simple not a boring reliable solution.

C is the de facto language that's considered a boring and reliable solution.

The points made are less substance and more pedantic nit picking ("yeah, it's a language with the most mature and relied upon compilers, but the code is old dawg", "yeah, it's one of the most convervative languages to change, and you can compile decades old code just fine, but they added some stuff in C99, C23, etc").

And Rust is still very niche, single compiler, quickly changing affair. Compared to C and C++ adoption (which is a big yardstick of a tech being "boring") it doesn't even register.

high_na_euv 5 hours ago | parent | next [-]

>C is the de facto language that's considered a boring and reliable solution.

Ive spent last year doing C and I disagree, it is not reliable

jchw 2 hours ago | parent | prev [-]

> Statistically nobody writing C code gets to worry about a compiler error.

lol. (Hello, it is me, I am the statistical nobody.)

> C is the de facto language that's considered a boring and reliable solution.

De facto? Yes. Boring? Citation needed.

> The points made are less substance and more pedantic nit picking ("yeah, it's a language with the most mature and relied upon compilers, but the code is old dawg", "yeah, it's one of the most convervative languages to change, and you can compile decades old code just fine, but they added some stuff in C99, C23, etc").

Incredible. You missed some of the most important points while adding new points that neither of us were talking about. Are you alright?

> yeah, it's a language with the most mature and relied upon compilers, but the code is old dawg

I'm not guessing that the code is bad. You go read GCC code.

> it's one of the most convervative [sic] languages to change, and you can compile decades old code just fine

I don't think you even made this point in the first place, but that just means the changes are all backwards compatible. They have to make almost every change backwards compatible because C doesn't have a versioning mechanism.

However, all of the changes were not backwards compatible. You may find that you actually can't compile code that uses variable length arrays. They were made optional and several compilers never implemented them or no longer support them. Granted, it's good they're dead, but it's something that real codebases really used, including for a long time the Linux kernel, only really removed from it to support compiling with LLVM.

In other cases, compiler updates break things that were never standard but were relied on anyways. For example, if you go and compile old code from the 90s and 2000s, you'll often find they either refuse to compile or crash because they were never actually compliant to standard C in the first place. Because the compilers are buggy. They still are, but they used to be, too.

> And Rust is still very niche, single compiler, quickly changing affair. Compared to C and C++ adoption (which is a big yardstick of a tech being "boring") it doesn't even register.

Something being commonplace doesn't make it boring. You missed the most "exciting" part of C, what I consider the magnum opus of why it is never boring and never will be: Undefined behavior.

(And the standard library. Seriously, fuck C's string manipulation functions.)

--

Addendum: Until recently, I actually had a C project where I was using the OpenWatcom 2 fork of OpenWatcom as the primary compiler, due to needing a weird target. While it definitely generally worked, it also gave me a brand new perspective on which to appreciate the stability and reliability of even GCC and MSVC, which I will openly admit rarely run into incorrect compilations.

That said, in my many years of doing C and C++ code, I've run into countless situations where the compiler was, in fact, wrong. Either it refused to compile valid code (MSVC usually, but all of them at times), or it ICE'd during compilation (GCC usually), or it did in fact compile, but did the wrong thing (GCC and LLVM, especially with -O3.) Every time I debugged these down enough to actually report them, someone else was already on top of it, and usually it was even already patched, if not released yet.

I understand that big projects like GCC and LLVM are tricky to maintain, especially since they're dealing with standards as broken and stupid as C. But still, it doesn't change the reality that it's not literally that hard to run into a new compiler bug. The truth is that a lot of us just run into a compiler bug, shrug and make a quick workaround, and then go on our merry way.

There are also some bugs that are kind of bugs and kind of not. For example, the old pointer provenance bug: a comparison of two pointers, which are in fact equal at runtime, can constant fold to being false; it's a valid compilation.

There are many write-ups talking about strange C behaviors, some of which may technically be standards-compliant but are anything but obvious (or boring.)

https://www.ralfj.de/blog/2020/12/14/provenance.html

And sure. It is possible that you never run into any of this fun stuff, of course, but it's extremely easy to experience the degrees to which C is not boring. Just accidentally store a pointer beyond its lifetime (especially a pointer to the stack.) Engage in some undefined behavior of some kind. Suddenly, C becomes... very exciting.

dgellow a day ago | parent | prev | next [-]

Go is as boring of a tool as it gets. Which is why I will happily use it

grey-area a day ago | parent | prev | next [-]

No, because it's an example of someone who chose Go and didn't regret it, and continues to choose Go, because the objections of the article are in practice just not very important or compelling.

mort96 a day ago | parent | prev | next [-]

There's plenty of software I work on from time to time that's written in Go which I'm happy are written in Go. Some of those were even projects which I started and chose the language for.

Then there's software I've been involved in where I believe Go was the wrong choice. And software not written in Go where I believe Go would have been the wrong choice.

My point is, Go is just another programming language, with a unique set of strengths and weaknesses, and there are plenty of cases which i have experienced myself where there are genuinely no regrets around picking Go. Not because there aren't shortcomings in Go, but because there are plenty of cases where those shortcomings don't matter.

In my professional life, I have seen significantly more regret associated with choosing JavaScript on the back-end than with choosing Go. But then again, there are services in production written in JavaScript where that just never has been an issue and there are no regrets.

guappa a day ago | parent | prev | next [-]

In my personal experience your average go developer that likes go, likes it because he doesn't really know anything else.

As a result most things written in go are, in my own experience, of lower quality compared to things written in other languages.

So using things written in go is usually my last resort, because I expect they won't be high quality before even downloading them.

Daegalus a day ago | parent [-]

That is an interesting experience, as I find the opposite true in most of my cases.

And I am a go developer that likes go and use it for 95% of my coding, including MMO servers. I love programming languages and have used and dabbled in many. I still go to go.

And many go tools I use tend to be pretty well written. But maybe this is just a sample size of 1 vs a sample size of 1 issue.

guappa a day ago | parent [-]

From go programs at the very least I can expect they won't respect normal GNU getopt command line options, and work terribly with signals and going background in the terminal and so on.

If it's a server, of course it won't support any way of communicating it's ready. Not the old fork way and certainly not the new sdnotify things.

Daegalus a day ago | parent [-]

why would it have to respect GNU getopt? When did that become the golden standard? I never respect getopt because I really don't care about it and it has no bearing on anything I build. As long as they are documented under `--help`. Almost everyone uses Cobra from command-line options. And it is capable of doing getopt if you want, but I don't see why it would be a requirement.

Signals and Backgrounding seem to be just developers that have little experience with that stuff. I can't remember the last time I did any sort of signal handling to do anything specific. And I haven't had issues with backgrounding, but that might be just the tools I use and the infrequency of backgrounding that I do.

Most servers I interact with or work on have some sort of `health` api endpoint to get that status because most servers in go are HTTP. What more are you expecting? I don't even know what you are referring to by the `old fork way` but I will agree most don't use sdnotify, as that makes the assumption you are running Go on something that handles systemd notifications.

I am fairly certain a majority of Go servers run in a container, specifically an Alpine, distroless, or scratch container. So communication would be over stdout or some kind of API endpoint, be it HTTP, GRPC, or any other TCP/UDP endpoint.

guappa 10 hours ago | parent [-]

> When did that become the golden standard?

30 years ago?

> Signals and Backgrounding seem to be just developers that have little experience with that stuff.

Yes, go developers have little experience. I see we agree.

> What more are you expecting?

Them to tell systemd they started.

> I don't even know what you are referring to by the `old fork way`

As I said, most go developers have little experience. I see you agree once again.

The old fork way is the portable way to do it with whatever init system (systemd included).

Daegalus 9 hours ago | parent [-]

> 30 years ago?

I have yet to find a developer, go or otherwise, that cares strongly enough about getopt outside the hardcore FOSS groups that pays attention to this. I know of getopt, and I don't follow it, because no one ever told me to was the golden standard, I was under the impression it was one of many ways, not the only way, especially if you work with systems like Windows at any point, that doesn't follow it either. I also personally don't like the getopt style

> Yes, go developers have little experience. I see we agree.

Or you know, they have no need for it, and don't implement it. You are conflating inexperience, with not caring about the thing you care about.

> Them to tell systemd they started.

Why? Very few services care about your init system these days. Like I said, containers have made it so you don't need to worry about it. I have dealt with many supervisors and init systems, and I have 0 interest in specifically adding code to notify them. At best I will add a network endpoint, and it is up to the unit file to poll for it, if it can.

> As I said, most go developers have little experience. I see you agree once again.

No, I am arguing that in modern day server dev, it is an unnecessary skill to learn, so they don't either learn it, while having plenty of other experience, or they specifically don't care enough about it for it to matter to them.

> The old fork way is the portable way to do it with whatever init system (systemd included).

This assumes you care about an init system at all. Like I mentioned previously, the majority of places Go servers run on are either in Containers, or microvms. The init system does not matter. So few go devs take the time to implement support. It is not lack of experience, it is priorities.

I work in DevOps, ever since we moved primarily to containers, I have stopped caring about adding signal handlers, init system supports, etc. I write far fewer init files, be it unit files for systemd, or old school SysV stuff. It is an unnecessary set of features to add for projects that are internal, small, or run in containers. I am sure if a Go project gets big enough, it gets support for init systems, but those kind of projects are few, and init system support is an after-thought.

If you are so big on FOSS standards, maybe open a PR and add the necessary sd_notify features. Should be only a few lines of code if you import https://github.com/coreos/go-systemd/blob/main/daemon/sdnoti... and call it.

Most Go projects won't add this by default, because the use-case is infrequent for it for most places Go is used. Just like the article said a pro and con of the Go ecosystem is we don't need to worry about the rest of the dev world to gain the benefits of Go. So GNU/linux standards are not a priority. Especially with how easy it is to build cross-platform, using a Linux standard for all platforms is not perfect.

Maybe take the time to be open minded instead of calling the entire Go dev ecosystem "inexperienced", it is simply the case of priorities and needs to the community, and when there is no need for doing the things you mention, they aren't added, even if the devs know about them.

And I disagree that everyone should follow getopt, it is not the only way, and it is not necessarily the best way.

guappa 7 hours ago | parent [-]

> I have yet to find a developer […]

Users care, because that's how all the other commands work. It's the standard whether you like it or not. Of course you can choose to do the snowflake CLI, but that doesn't really shout "experienced dev" does it?

> windows

oh lol… what % of go programs is used on windows? 1? 2?

> Why?

So systemd can start the other service that depends on that one?

You need to know when the docker daemon is up and running before starting your containers no?

You don't know how to do a thing that anyone writing a server process should know.

Yes you're further and further proving my point that go developers are on average less experienced. And you seem ok with that except when it's pointed out.

> maybe open a PR

I have far more FOSS contributions than you do. Mostly in C/C++ or Python.

> Maybe take the time to be open minded instead of calling the entire Go dev ecosystem "inexperienced"

Maybe take the criticism instead of getting angry.

Daegalus 7 hours ago | parent [-]

> Users care, because that's how all the other commands work. It's the standard whether you like it or not. Of course you can choose to do the snowflake CLI, but that doesn't really shout "experienced dev" does it?

I have never had a user complain about this. They just read the readme or the `help` and use it accordingly.

> oh lol… what % of go programs is used on windows? 1? 2?

I am sure it is quite a bit more than that, especially since I tend to see `exe` builds for most go tools I have used.

> You don't know how to do a thing that anyone writing a server process should know.

Oh I know it, I just don't think it is relevant for my day to day, or most of the go-made servers or tools I run.

Another thing that you seem to be assuming is that I am waiting for daemons to start. I used the term Containers instead of Docker specifically because the majority of them are in Kubernetes, or get converted to VMs for services like Fly.io. In those situations the daemon, or equivalent, is already running. I also use Podman, which doesn't have a daemon unless you need one.

And in the few cases where I would have needed to wait, I poll the docker unix socket for the /info path, and get the information that way.

> I have far more FOSS contributions than you do. Mostly in C/C++ or Python.

I never argued that, I have a paltry amount of FOSS contributions compared to most people interested in open source. I just said that if it matters so much to you that you are willing to label an entire dev community as inexperienced, and act all high and mighty about some standards that aren't seen as important these days, then you should teach us how to be better. Open that PR and show us the way. Obviously there is only 1 way to do things, and you are already well versed in it, guide us out of the dark hole we apparently live in.

> Maybe take the criticism instead of getting angry.

I am not in the least bit angry. I am discussing this, because I believe you are stuck in old ways and not adapting to the changes in the industry and development, especially in the areas where Go is used most, which is servers, devops, containers, and the like.

Also, I acknowledge there are industries, environments, etc that can't or won't use containers. But that is a small percentage of use-cases, and the engineers there are probably building things properly.

I will also never say no to implementing such a thing if I get an issue opened requesting it, a PR opened to add it for me, or the like. But I have not seen a need for it in years, both professionally or personally, to be added right away. And I feel like majority of Go devs have nothing against it, and would add it if requested. I know Miniflux for example, after their V2 rewrite into Go, added it 8 months later, due to a request. and have been maintaining/improving it since.

I had no clue how the sd_notify support was in Go in general, until this conversation with you. Not because I wasn't aware of sd_notify, I just never had to go looking for it or needed it. Took me 10 seconds to google it, find a library that saves me time, and I can add it to any server I need it in now. But I will do that if needed, not pre-emptively. Especially since 95% of the servers I make are for work, and dont need systemd. And personal stuff doesn't need it either. But if I opensource anything, and it is requested? I will add it without much fanfare.

underdeserver a day ago | parent | prev [-]

Uhh, maybe.

Where is the tradeoff analysis? Yeah, you might regret using Go when some zero value you forget to fill in somewhere pops up later and ruins your pipeline. But are you considering all the issues that didn't pop up because you chose Go?

Java's boilerplate code? Rust and C++'s lifetime analysis and memory management? C's lack of tooling? Python/Typescript's runtime errors? Functional languages' tiny employee pool? How much did trouble did you save by avoiding these?

guappa a day ago | parent | next [-]

Go is very boilerplate. It requires at least 3 lines of error checking every 1 line of actual code.

Also it doesn't have packed structs so it's completely incapable of doing low level networking or to handle binary files (you can of course do all the bitwise operations yourself… but is it sensible to use a language that is more error prone than C in 2024?).

Also due to its lack of A LOT of system calls, you will need to use modules written by someone on github, which will happily segfault if you look at them funny.

So now you have hidden memory management! Fun!

Of course if all you do with go is a glorified jq script… sure then it's kinda fine.

dfawcus 21 hours ago | parent | next [-]

How low level a networking use do you desire?

I certainly managed to use it to implement a protocol over UDP without any issues, that having byte and bit packed values.

Or do you wish to have something similar to C with structs and (endian dependent) bitfields overlaid on packet buffers?

guappa 10 hours ago | parent [-]

> Or do you wish to have something similar to C with structs and (endian dependent) bitfields overlaid on packet buffers?

endian dependent until you tell gcc which endianness you want :) Which you can't do in go.

mrbadguy a day ago | parent | prev | next [-]

I’m not sure I understand the packed structs complaint. I have used Go to read binary data and it’s quite easy. You just need to ensure that all of your struct fields have fixed sizes e.g. int32 or [4]int64 or whatever. Unless I’ve misunderstood what you mean?

guappa a day ago | parent [-]

Yes it works, but you can't state the endianness and you have no control to decide if the compiler will decide to insert padding. It's undefined.

You HOPE it works.

mrbadguy 12 hours ago | parent [-]

I don’t know about the padding (certainly it never inserted any when I’ve used it) but you can definitely state the byte order upon reading or writing. That would definitely be an oversight. Take a look at the encoding/binary package:

https://pkg.go.dev/encoding/binary

guappa 10 hours ago | parent [-]

I want a struct, not to having to write the code manually to do a struct every single time.

I know you can do that in go but as I already said: "more error prone than C".

mrbadguy 8 hours ago | parent [-]

Can you please give me an example of what you don’t like? I’m not sure I understand the “write the code manually to do a struct” bit.

You have to define the struct for sure, but beyond that you just pass it to binary.Read and it comes back with the fields populated. I don’t see how you’d avoid defining the struct.

dfawcus 3 hours ago | parent [-]

I believe what he wants, is the usual C trick of defining a struct which represents the wire format (with all the usual caveats). Then cast a char pointer to be an instance of a pointer to that struct. Sort of like this:

    https://github.com/danos/vyatta-dataplane/blob/master/src/ecmp.c#L108-L116
It sort of works on x86 chips, but is not so effective on MIPS, PPC, etc where misaligned access are either unavailable, or slow, or even trap and are slower still.

Once one has to handle that sort of situation, and actually copy the data, the lack of language support for such type-punning becomes immaterial.

underdeserver a day ago | parent | prev [-]

I worked on a project with gopacket. It was completely fine.

guappa a day ago | parent [-]

Try defining a new packet format and ping me.

dataflow a day ago | parent | prev [-]

Thoughts on C#?

underdeserver a day ago | parent | next [-]

When last I tried it, maybe around 2014? I found it a kinder, cleaner Java with better tooling. Visual Studio (not Code) is still the best IDE I've ever used.

Unfortunately it's not popular in the circles I hang around in and the places I've worked. Now that .NET core is the one true runtime I'd welcome an opportunity to try it again; alas, I doubt I'll have such an opportunity (at least not through work).

I remember the upsides but I'm sure there are downsides I'm not aware of. I'd love to read a critique from someone with real world experience.

jchw a day ago | parent | prev [-]

Not that you asked me but since Go is my goto language, my thought on C# is that it looks pretty cool. C# with hill-climbing thread pool and async seems rather compelling. I really see only two (major, obvious) downsides with C#:

- It has so much. language. design. This is both a strength and a weakness, of course, but C# really does take this to an extreme. I won't bother making a huge list of examples because I think you get the picture.

- Microsoft needs to stop doing things that harm confidence in .NET. Between the silliness of removing hot reloading from the CLI, the situation around debugging outside of Visual Studio products, and drama around the .NET organization... I feel cautious touching the .NET ecosystem. That's saying something considering the company that backs the Go programming language.

(Note: I have not used C# in production, so I can't speak to what it's like in that case. Seems like it's at least fairly "boring" in a good way for a lot of organizations though.)

neonsunset a day ago | parent [-]

Is there a specific aspect of language design that you see as problematic? I agree that it can be perceived as "way too many things to keep track of". I think most of it are small, atomic changes designed to reduce boilerplate that can be intuitively understood (like collection literals, additions to pattern matching and null-coalescing operators). You don't have to spend mental effort on these and if there is a scenario where more idiomatic syntax is available - the analyzer has a good chance of catching it and providing an autofix suggestion.