>I tried to explain how review workflows work in a PR-based setup, and gave concrete suggestions for how we could improve the process. But they didn’t want to try it. That might sound like a small thing, but at that stage, all I wanted was a smooth and efficient collaboration process. The easier it was for me to track changes, the better.
I'm surprised the copy editor was more comfortable using git than using a web-based review tool to leave comments, especially given that she was reviewing a Go book and didn't seem to know what Go was.
How does that even happen? It seems bizarre that Manning had this copy editor at all.
I recently had a negative experience with Manning. I sent them an email saying that I'm in the process of writing a book, and I'm self-publishing it, but I was curious about the possibility of applying to Manning for a second edition. I asked whether they accept second editions after a self-published first edition and what document formats they accept.
I got back a form letter telling me that they'd rejected my proposal. When I followed up and said I didn't send a proposal but was asking preliminary questions, they told me that they understood I hadn't sent a proposal, but they were going off of the table of contents on my book's website. I guess they decided to pre-emptively reject me?
They also only said Google Docs as a document format, but based on this blog post, clearly they accept AsciiDoc.
I must compliment your ability to keep the reader hooked, I had to see what chapters they saw, stalked your website and ended up reading the whole post about your pre sale.
This is pretty off topic but did you test how your book works on an E-Reader? I check a sample chapter and there where a lot of pictures and colors to distinguish information, this will probably not work very well on my Kindle.
The first few chapters, I've been primarily targeting web and not testing on e-readers. I figured that until I knew whether people actually wanted to read it, I should just focus on making the web excerpts look decent and try to avoid over-optimizing for web.
Now that the book is officially a go, the PDF version is a first-class citizen, and I'll be testing e-reader experience on my rm2.
That kinda blew my mind too, I'd expect the complaint to be about needing to use some online review tool. Editing the asciidoc source directly sounds archaic, and I was expecting the authoring / editing world to have had 'code review' style software years before the software development world did.
I mean all mainstream word processing applications have a 'commentary' / 'review' mode where someone can leave comments and suggest edits.
If you browse around Go's stdlib use of sync.Pool, you'll see a variety of tiered pools with fixed sizes, and many drop anything over a large enough size (sometimes gigantic! as much as 16KB!): https://cs.opensource.google/go/go/+/refs/tags/go1.24.0:src/...
Maybe somewhat, but it's not hard to get pathologically bad GC behavior out of generational GCs either. Pools / manual memory management will always have some reasonable use cases.
On the topic of copy editing you raise: I wrote a book in DocBook for Manning in 2010. DocBook is XML, so I structured it with opening / end tags on their own line, content in the middle. As you would with a HTML document.
After copy editing multiple chapters, they sent it back to me with all the content on a single line. I was so incredibly upset that they ditched all my painstaking format that I almost abandoned the project there + then.
Not needing to care in the slightest is one of the major value propositions of that sort of syntax, at least IMO. If it does happen to matter for whatever reason that's what formatting tools are for.
This book format lends itself very nicely to a book club. I was in one with both new and experienced gophers. The mistakes are great discussion points. The veterans got to share stories when they've made whatever mistake or how they've avoided it over the years. The noobs often made comparisons with other languages.
A little secret about the book is a lot of the "mistakes" are introductions to some aspect of Go worded as a mistake. "Not using fuzzing" and "Not using errgroup" are a couple of examples.
O’Reilly author here. Seems the author stumbled over pitching them the book. You can almost certainly start with an email. Our initial contact with O’Reilly was only an email. We filled out a small form later with the details of the proposal, but it was not laborious. I can also attest that their tooling is great. From any git commit I can generate a full version of the book in any of its supported formats. I wrote all of my part of the book in vim.
They host their own repos, and builds are done on their system, but the layout is pretty simple. Numbered, named files in asciidoc, markdown, etc. your choice.
Hello fellow Manning author! This book is fantastic. I remember reading through a lot of it a couple years back. Super helpful and it's one of those books you can pick up, grab a nugget or two of good info and come back later.
Now that I'm starting another big Go project I'm going to look at it again.
What I like most about this book is it feels like it's all "real world" stuff. You can tell the author has built a lot with Go and can save you time by telling you were the potholes are. Great stuff!
I got a question about the example shown, the goroutine one marked as #63, I'd copypaste but it's an image.
Is there a reason the common mistake is about goroutines specifically? If I instead just made function closures without launching off goroutines, would they all refer to the same 'i' variable? (I assume it's maybe just that the mistake tends to go hand in hand with goroutine-launching code in a pattern like that).
I'd presume the book would say right after the example :)
But otherwise: the author gets serious respect from me for going through that process, taking feedback and learning how to communicate, i.e. taking "how do I make a good book?" very seriously and trying their best. And also things like for putting their foot down when the problematic copyeditor. I'm almost interested in the book, not for learning about Go but learning about what it looks like when I know the writing has some serious intent behind it to communicate clearly.
So not only do you write a full book, but you keep the content online, up to date by making sure readers are informed of new developments that might make advice irrelevant? And you are able on the spot to say "one of three mistakes that are not relevant anymore"? You impress me, random book-writing Internet person.
You give me a feeling you really care about the craft and just making a good useful resource, which what I respect. I looked around the site and bookmarked it as a technical writing example I might go to read around now and then.
I sometimes teach coding or general computing things (but hands-on, less about writing) and I've come to appreciate that sometimes it is extraordinarily difficult to educate on or communicate complicated ideas.
Quoting you: To give you a sense of what I mean by improving the book “over and over“, keep in mind that between feedback from my DE, external reviewers, and others, there are parts of the book that I rewrote more than ten times.
I also do rewriting especially with content I intend to be a resource or education piece. Obsessively rewrite. Make it shorter. Clearer. Oops that reads like crap let's start over. IMO having an urge to do this and address all feedback or your own itches means you care about your work. Just have to remind myself that that perfect is the enemy of good enough (or something like that I forgot if the expression went exactly like that).
I think #633 must be a typo, or just a fumbled explanation.
"We might expect this code to print 123 in no particular order" should really say "exactly" or "in order", since it's proved in the next paragraph to be inconsistent.
And that would be the layman's explanation of concurrency resulting in things added sequentially happening out of order.
And assuming FIFO on async execution, akin to running everything in series, is probably the first mistake anyone will make when they encounter concurrency for the first time.
The problem isn’t that they might be out-of-order. The problem is expecting that they merely might be out-of-order and actually getting missed and duplicated values due to the timing of shared memory access. This was enough of a problem that they [changed the behavior][1] in Go 1.22.
Yes, that was the crux of my question (and was answered by that link when I checked teivah-given link, which linked https://go.dev/blog/loopvar-preview right there as well). Basically I wondered if the example given was really about:
1) In Go, the 'i' variable in the for loop is the same 'i' for each round of the iteration, meaning closures created inside the loop all refer to that same 'i' variable, instead of getting their own copy of it. Very easy to accidentally think the all closures have their own copy of 'i'. Goroutines are only mentioned because in Golang this mistake tends to come up with Goroutines because of a common code pattern.
OR
2) Goroutines themselves either behave or have some weird lexical scope rules in a way I don't know and it doesn't really have to do with closures but an entirely Golang-foreign-alien concept to me I cannot see, and this is why the book example had Goroutines mentioned with the mistake.
I rarely write Go myself so I was curious :) It looks like it was 1) unless I am bad at reading, and I think the Go 1.22 change is good. I could easily imagine myself and others making that mistake even with the knowledge to be careful around this sort of code (the link shows a more complicated example when scrolling down that IMO is a good motivating example).
It was definitely 1. There were ways to demonstrate the issue without involving goroutines, such as by creating a list of closures in a loop, one at each iteration, and then invoking them synchronously after the loop exits. They would all have the same (in this case, final) value of i.
#63 isn't about the lack of execution guarantees when you execute multiple goroutines without proper synchronization; it was related to loop variables and goroutines.
> I was working in Switzerland, refactoring a C++ legacy codebase...
Such a nice place to work, where you can just decide "Let's implement thing A in a completely new stack for us that shows promise" and then, after some time, say, "Ah... this is too hard, bad decision though. Let's try another one"
Per author, this book started the "100 _ Mistakes and How to Avoid Them" series. That opened a whole new avenue of books based on programming language.
> I learned a ton from my DE. Like, really, a ton. Before that, I had been writing on various blogs for about a decade, but writing online is all about being direct because most people don’t have time. With a book, it’s different. People made a deliberate decision to buy your book. Now, it’s your job to bring them somewhere valuable. And if that takes time (meaning more words), so be it.
I have a hard time with this point. It feels to me like a lot of books have A LOT of unecassery padding all over the place.
The example of taking 28 words and turning it to 120 is pretty good at showing this. The first paragraph is totally pointless - we are reading a book about 100 most common mistakes, obviously this mistake is very common, how did this increased the value?
Then we have another line that explaining what happens in the code, which is totally useless because the code is super trivial.
Then the code, with more explanations on the side as if the previous line was not clear.
And only after that we get to the crux of the issue.
I understand that book publishers feel they need to justify the price of a book by reaching the 300p mark in some or other way, but in my way this only makes the book worse.
It's your opinion, nothing wrong with it. Let me try to see if I can make you change it at least a bit.
> The first paragraph is totally pointless - we are reading a book about 100 most common mistakes, obviously this mistake is very common, how did this increased the value?
There are different levels in terms of common mistakes, and this one was probably one that all the devs did at some point. So I think highlighting the fact it's a frequent one does make sense.
> Then we have another line that explaining what happens in the code, which is totally useless because the code is super trivial.
I have a rule: always explain the intention of the code. Even if it's 5 lines of code, it helps the reader to better understand what we will want to highlight.
> Then the code, with more explanations on the side as if the previous line was not clear.
The explanations on the side do not impact the size of the book so the argument doesn't hold. I did it in many code snippets to highlight where the reader needs to focus.
> I understand that book publishers feel they need to justify the price of a book by reaching the 300p mark in some or other way
This is more about guiding the readers, making sure the expectations are crystal clear and that they can follow me throughout an explanation. You judge it as a criteria to justify the price of the book, but it's not the real reason. At least not for my book and I'm sure it's the case for many others :)
> This is more about guiding the readers, making sure the expectations are crystal clear and that they can follow me throughout an explanation.
Sure, but this holds true for the blog version as well, right?
To be clear, I'm not advocating for The Little Schemer version, and am not arguing that the blog version is the best it can be, but surely we can agree that book padding phenomenon does exist.
By the way, I have read parts of your book over at O'Reilly Learning, and I do think it is a good book. So I'm not trying to take a dump on your work. My criticism is aimed at publishers.
No worries I didn't take it as a criticism. I understand your point. I mean when we sign a contract there's a minimum number of pages to write. But personally, I never felt the pressure of having to add more stuff.
Instead, my DE multiple times told me that it's better to favor just-in-time teaching over just-in-case teaching. Meaning multiple times, he made me drop certain section because they weren't really serving the chapter. They were "perhaps helpful" and he made me drop all of those.
I guess it also depends on who you're working with and which publisher. On this aspect, Manning was fair, imo.
The author's categorization of Go mistakes into four areas - syntax, standard libraries, interfaces, and concurrency - is quite helpful. As someone who's wrestled with incorrect error handling and goroutine leaks, I appreciate the practical examples that highlight both common pitfalls and idiomatic solutions.
"Yet I think Tim’s single review was possibly as valuable as all the others combined".... and yet you don't even tell us anything about his review.... disappointing esp. considering the screenshots/level of details on other input.
On a different note, def. feel you pain regarding the copyeditor.
His review contains comments annotated on a book of 300+ pages. I don't think it would have made this section any better by deep diving into his review.
The go mistakes that tend to bother me the most are things like gc pauses, lack of first-class named and default arguments, lack of exceptions, lack of subrange types, etc.
I usually avoid them by not using go. Or waiting until missing features (generics) are added.
That was an issue in 2023? Quite sure C# fixed something similar back in 2012. Is Go still proudly reinventing the wheel, wooden and filled with termites?
I purchased this book on audible (because it said PDF version as well) but PDF version had only some code and not the entire book. Sorry if this is how Audible works and if its not related to this book per se. Looking forward to listen to it.
If you’re interested in publishing a book, please reach out and let me know what’s blocking you. My email address is in my profile. I’m not selling anything here, I want to understand the issues you’re facing.
Not that, but I did think he wrote a second book about writing the 100 mistakes book. I don’t claim that’s a reasonable reading of the title, but it’s how I read it.
Did you know there exists languages that don't have run time errors? Languages that you can never crash outside of an OOM.
That means you can't find a language with no mistakes. But across all languages there are languages with astronomically more gotchas and mistakes then others.
Golang is one of these languages that is astronomical. I mean just compare it to the language with zero runtime errors.
It "succeeded" in that it delivers on its promise and some people still use it, but it is largely dead now due to the lack of development and the community's inability to push the language itself forward. There are forks that address that, but Elm itself seems unlikely to Lazarus any time soon.
It's definitely proof that software can be written in such a regime, though, and I hope we see something similarly dogmatic some day.
Gleam / Lustre are fantastic, and I hope to use them in anger at some point, but it's my understanding that they don't aim to be as "total" as Elm (quotation marks because I don't think Elm was total in the formal sense). That is, while Lustre is very much following in Elm's footsteps, both it and the language have not been designed around avoiding runtime failure at all costs. (Which makes sense, given the Erlang/BEAM heritage.)
It depends on what you consider failure. If you feel popularity is the main metric then golang is successful. Outside of that metric golang is pretty bad.
The proof is in the pudding. Here’s a quote from rob pike the creator of golang:
“The key point here is our programmers are Googlers, they’re not researchers. They’re typically, fairly young, fresh out of school, probably learned Java, maybe learned C or C++, probably learned Python. They’re not capable of understanding a brilliant language but we want to use them to build good software. So, the language that we give them has to be easy for them to understand and easy to adopt.”
Basically in a nutshell he’s saying they dumbed down golang so it’s useable by beginners. Golang is a step backwards. A failure in language development but a success in popularity.
You dumb down a language to a point where the language is so dumbed down it hits the largest demographic. You are part of that demographic. It’s similar to the demographic that voted for trump because he’s not fakeish like all the other candidates.
I understand what you are saying here but I think you've missed the point of what Pike was getting at.
I think Pike is acknowledging the practical realities of engineering at scale, and intentionally designed Go with simplicity in mind, which leads to more maintainable code and faster onboarding for new devs.
I'll also add that outside of the popularity metric, Go is not all bad. Fast compile times, readability, excellent standard library and toolchain, backward compatibility, to name a few things.
>I think Pike is acknowledging the practical realities of engineering at scale, and intentionally designed Go with simplicity in mind, which leads to more maintainable code and faster onboarding for new devs.
Doubt it. Read what he wrote. He's literally referring to people without much experience in programming. The stuff you said is literally NOT what he said.
>I'll also add that outside of the popularity metric, Go is not all bad. Fast compile times, readability, excellent standard library and toolchain, backward compatibility, to name a few things.
Pike didn't write it, he said it. (1) It is a talk about how Go was created to make concurrency simple. The "brilliant" language he refers to here is C++, which I'm sure you're aware has many of its own downsides.
Your argument that Go is a step backward because it was intentionally designed to be simple for novice programmers seems flawed. It's design was a deliberate tradeoff to address a specific problem. While I don't think it is a language that should be used for everything, it is good at the things it is good at.
What is it about Go that you have a problem with, specifically?
I understand he said it. But I can’t transmit voice over HN so I need you to read the transcript I made oof what he said rather then listen to it. I’m actually really confused why you couldn’t figure out this is what I meant. I could make a link and point you to the time like you did, but it’s easier to refer to it with an actual written version. Make sense? I hope so.
> The "brilliant" language he refers to here is C++, which I'm sure you're aware has many of its own downsides.
No. The brilliant language he’s referring to is a hypothetical one he could have created. Instead he created golang because he needed to create a language catered to people with less experience. That is what he is saying. I find it strange that you can literally read what is written and also reference the video and literally not understand what was literally said/written.
Maybe you’re just making up meaning subconsciously to cater to your own biases rather then facing the cold hard truth that pike created go to be not “brilliant”.
> What is it about Go that you have a problem with, specifically?
Oh there’s tons of stuff. One is that errors don’t have stack traces. You create an error that can’t be handled and so it bubbles up the stack until the only way to handle it is with a panic. You see the panic in your logs but now you have no idea where the error came from because no stack trace. You get the trace of the panic but no trace of the error. The whole thing is just poorly thought out.
> the language is so dumbed down it hits the largest demographic. You are part of that demographic. It’s similar to the demographic that voted for trump because he’s not fakeish like all the other candidates.
I've been a lot of places and done a lot of things but I've never had somebody liken me to a Trump voter because I like a programming language. Is this the new Godwin's law? Did panic() and nil kill your grandpappy?
> Basically in a nutshell he’s saying they dumbed down golang so it’s useable by beginners.
He's saying that developers can't handle Coq (the brilliant language), so they had to build a language that is like every other commonly used language, for better or worse.
> A failure in language development
As brilliant as Coq truly is, he's not wrong, is he? It is no coincidence that nobody is using Coq to build web servers. Which is, after all, what Pike said Go was designed for – that it was not intended to be a general purpose programming language. The vast majority of developers, even outside of Google, truly can't grasp it... And of even the scant few developers who can, they will tell you that the tradeoffs aren't worth it for something like a run-of-the-mill CRUD web server.
Your, being a researcher who does understand Coq, perspective is interesting from an academic angle, but Pike's point is that you don't understand the realities of engineering. This "Use Coq or you are no better than a Trump supporter" shows he was exactly on point. Cry as you might, nobody is going to be using Coq to build web servers, and for good reason.
No he's saying go is designed for people like you. You're obviously joking about COQ, but I'm not joking about you. This is literally what he's talking about. Take a step back out of your sarcasm and look at reality. You are the demographic Pike is talking about. And we both know Pike is not even referring to coq.
> No he's saying go is designed for people like you.
Stands to reason. It is true that I do spend my time on the engineering side of the industry. While I have great appreciation for the brilliant languages, they don't offer a whole lot for practical production work after you've weighed the tradeoffs. Especially in the particular niche Go is designed for. You are going to use a blub language like Rust for those types of problems, and for good reason.
> And we both know Pike is not even referring to coq.
Lean, then? The brilliant list isn't terribly long. We do know he isn't talking about Scala and Haskell, at least. He lumps them in with C++ and Java – albeit he has expressed that they are more beautiful. Not that anyone would consider them brilliant anyway. Well, maybe if you consider Trump to be also brilliant... There is always that guy.
It’s designed for people who don’t have much experience with programming. He literally said this. There is nothing said about coq or lean... that’s just trolling.
> It’s designed for people who don’t have much experience with programming.
Exactly. It follows the same basic "loop, variable, function" programming model as Java, C++, Haskell, PHP, Ruby, Rust, Python, LISP, Smalltalk, basically every language you've seen production code written in, that is familiar to early career beginners who have come out of traditional learning paths (e.g. college). Where once you understand one of them, you can jump into another with minimal overhead. None of these languages brilliant, but they are useful. Which is where he said they wanted Go to fit as well: A language that is useful and familiar.
That isn't what researchers and language theory enthusiasts want. They are enthralled by languages that think about programming in an entirely different way. The key point here is that it wasn't built for them. That is what he said.
That’s a stretch. Your saying he called golang not brilliant then you say basically every language from Haskell to Java is also not brilliant. Let’s be real here.
You also talk about looping for languages with no loops. And additionally pike never brought up looping at all. You just made this part up out of thin air. Your evidence is made up. He never said or referenced any of things you said.
> The key point here is that it wasn't built for them. That is what he said.
No read what he said again. He didn’t say golang was not designed for language experts. He did say it was designed for programmers just out of school with barely any experience with programming languages. He did not say he designed golang for an average engineeer who is not a PL expert he said he designed golang for literally people just out of school.
I don’t know how you can make stuff up out of thin air like this. Read what he literally said.
Most of the mistakes in this book and general mistakes you make when programming aren't about crashes or other runtime errors. They are about writing code that is overly complex or otherwise difficult to maintain.
I don't think Go is particularly esoteric to where these "gotchas" are a big problem.
In fact, I would argue that the gotchas are an intentional part of Go's design philosophy. I think it is strange to work in Go when coming from another language because of this, which leads people to think Go sucks.
That may be part of why they think Go sucks, but it is mostly because Go doesn't have many features to paper over poor design decisions. "Advanced" languages let you throw any old gobbledygook at them, and when you finally realize you screwed up, you can just monkey patch (or whatever feature of the day the language has) your way out of it and call it a day. Go is far less forgiving. You have to get the design right or pain will ensue. And pain does ensue for those who try it after being accustomed to having leeway in haphazard design, and thus they conclude Go sucks instead of acknowledging that their design sucks.
I mean, Go does suck. Maybe a language should be accommodating to bad designs. But still, those shouting "Go sucks" from the rooftops never seem to be willing to bring introspection into where they might have failed. It's always someone else's fault. (Something not limited to the case of Go, of course)
Go sucks not because it's accomodating to poor designs. It sucks because it is in itself poorly designed and that leaks over to the design of entire applications.
Why have functions return err, nil? Why even allow for a runtime error here? It's a really simple fix. You don't even have to make the language complex to support this. Instead the entire program is littered with holes and if statements you have to check in order to prevent a actual crash
Why not? It doesn't make any difference in practice. Without a complete type system you must write tests to ensure that error conditions (to stay with your example, although this also applies broadly) do what you need of them. If you somehow introduced a runtime error there, your tests would be unable to not notice. Whether your compiler cries or your test suite cries when you screw up is not a meaningful difference.
> You don't even have to make the language complex to support this.
A complete type system is insanely complex to implement and even harder to write against.
Without a complete type system, all you can have is silly half-measures. Maybe the error becomes an optional/result type with forced unwrapping, for example, but you still haven't asserted in the types what needs to happen with the error. So you still need to write the same tests that you had to write anyway. So, other than moving where you discover the problem – from your tests to the compiler – nothing has changed.
The half-measures are a cute party trick, I'll give you that, but makes no real difference when actual engineering is taking place. They might, however, give a false sense of security. They might even convince you that you don't need to write tests (you do). Maybe those make for desirable traits?
> Doesn't make any difference in practice meaning you never had a runtime error while running go?
I am not sure I have written enough Go to comment there, but I have worked extensively in other languages where runtime errors are possible, similar to Go in that regard. I have encountered runtime errors in said tests now and then, sure, but then you know about it and deal with it... So, in practice, no different than if the compiler told you that there is a possible runtime error.
> Who says you need a complex type system?
It is needed if you want to avoid the need for said tests. With a complete type system the type system can become your test suite, so to speak. But the languages people normally use, even those with "advanced" type systems, are nowhere near expressive enough for that. Meaning that you have to write the tests anyway. And then you'll know if there are any runtime errors as soon as you run your tests because how could the tests run without encountering the runtime error too? It is not like a CPU magically changes how it works if it detects that a test is being run. So, in practice, the type system doesn't change the outcome. But it is a cool party trick. I'll give you that.
That said, aside from these hand-wavy, make-believe stories, you are still very right that Go would benefit from sum types. For the reason that they map to the human model of the world very well, succinctly communicating structures that are often needed to be expressed. Languages are decidedly for humans. You can sort of work in the same basic idea in Go using interfaces, but it is far more confusing to read and understand than sum types would be. For a language that claims to value readability...
> So, in practice, no different than if the compiler told you that there is a possible runtime error.
If your program has runtime errors then that means you can deploy it to production and catch your errors in production.
If your compiler catches all possible runtime errors and refuses to compile. Then you will have no runtime errors in production guaranteed by proof. The program cannot even exist with runtime errors. It can only exist with no runtime errors.
So no difference catching errors in production vs. compile time? I beg to differ. Big fucking difference imho.
> It is needed if you want to avoid the need for said tests.
I’m referring to the fact that you don’t need a complex type system to design a language that will absolutely never have any runtime errors. You’re going off on a tangent here about how you need a type system to have less tests which is completely different from what I’m talking about. This entire paragraph you wrote here is like you’re responding to an irrelevant topic.
> I am not sure I have written enough Go to comment there
Honestly it seems that you haven’t just not written enough go. But basically any programming language . It seems that you’re not clear about runtime errors and you seem to have only encountered these types of errors during tests. So yes you don’t have much experience imho and rob pike deliberately targeted the language towards people like you.
> If your program has runtime errors then that means you can deploy it to production and catch your errors in production.
That questions: Why are you allowing your programs to be deployed when tests are failing? This is not a realistic scenario in the real world. Yes, you can invent contrived hypotheticals all day long, but it is meaningless. We've been clear that we are referring to practical settings.
But, but, what if there is a bug in your compiler that sees the runtime error slip through??? Who gives a shit? In some imaged world it may be possible, but it is not realistic. Not worth talking about.
> I’m referring to the fact that you don’t need a complex type system to design a language that will absolutely never have any runtime errors. You’re going off on a tangent here
What you are referring to is clear, but it cannot be considered in a vacuum. The alternative is to see the program keep trodding along, but do the wrong thing. In that case who cares if the program crashes instead? You're getting incorrect behaviour either way.
What you actually need is assurances that the program won't do the wrong thing top to bottom. That requires either a complete type system or, more realistically in the real world, testing. If you go the testing route, you'll know about any runtime errors when you run your tests.
> Honestly it seems that you haven’t just not written enough go.
There is nothing unique to Go here. Many popular languages suffer the same problem. But, if we want to place extensive Go experience as a requirement to speak to this then we have to defer to your experience. Perhaps you can choose an example of where you wrote code in Go that produced a runtime error, show us your tests, and explain how the condition evaded your checks and balances? – I'm fascinated to learn how your code ran perfectly while under test but then blew up in production.
> That questions: Why are you allowing your programs to be deployed when tests are failing? This is not a realistic scenario in the real world. Yes, you can invent contrived hypotheticals all day long, but it is meaningless. We've been clear that we are referring to practical settings.
Tests don’t catch everything. You can have a billion tests and there can still be uncaught runtime errors.
If you had a language that probably does not have runtime errors you don’t even need one test. Your program cannot fail in that way.
I honestly don’t think you know what you’re talking about. I didn’t make up a single hypothetical. This is real. Production errors can happen in spite of tests. Are you not familiar with this happening? It just means this: no experience.
Your compiler having a bug or not is orthogonal to the topic. Again you don’t know what you are talking about. If our compiler allows for runtime errors but is fully correct then no amount of tests can guarantee a runtime error will never happen. Golang as a fully correct compiler cannot be gauranteed to have no runtime errors with tests ever.
> What you are referring to is clear, but it cannot be considered in a vacuum. The alternative is to see the program keep trodding along, but do the wrong thing. In that case who cares if the program crashes instead? You're getting incorrect behaviour either way.
You’re writing this because you don’t have experience with programs that can never crash. A program that doesn’t crash doesn’t mean you never exit the program. The program can exit if you want it to. You just need to deliberately tell the program to exit. In golang if you do a division by zero, the program crashes. If you had sum types all divisions return an optional. Both paths of the optional must be handled by exhaustive matching so you must handle the case where the division yields a number or its undefined. If you want the program to exit when it is undefined you can do so. In golang the compiler doesn’t force you to handle both outcomes It just crashes. It’s the same with out of bounds access of an array.
Again real world testing doesn’t guarantee shit. A “complete type system” can be as extensive as dependent types like COQ or much simpler like rusts where you just have sum types and exhaustive pattern matching.
> Perhaps you can choose an example of where you wrote code in Go that produced a runtime error, show us your tests, and explain how the condition evaded your checks and balances?
Oh easy. we had a function that calculates velocity from a stream of input data. That’s (p2 - p1 / t2 - t1). Our integration tests and unit tests have dozens of tests that never yielded an error and we never saw an error in production for years. We switched to a new iot device that sometimes sent identical measurements to our system. Division by zero. We had a crash in production.
> I'm fascinated to learn how your code ran perfectly while under test but then blew up in production.
You’re inexperienced that’s why you’re fascinated. If you have formula involving velocity there an almost infinite amount of combinations that will never produce a runtime error and an infinite amount of parameter combinations that do. True full coverage that completely proves the function works with tests involves infinite tests. Better to prove the function works via proof with a simple extension to the type system. Sum types.
Again, they will catch your runtime errors if your behaviour is covered. If your behaviour isn't covered, then you're just shifting the problem to the program doing the wrong thing instead of crashing. That is not a win. It might even be worse! So, this doesn't matter in practice. Your purely academic view of the world doesn't work with the discussion taking place, I'm afraid.
> If you had a language that probably does not have runtime errors you don’t even need one test.
Go on.
Here, let's use your example:
> That’s (p2 - p1 / t2 - t1).
Traditionally, the calculation is (p2 - p1) / (t2 - t1). I'll assume you had a non-standard situation that necessitated a different formula, but this could have equally been a mistake. It wouldn't be too hard to forget the inner parentheses. We'll assume that divide by zero was already eliminated by the type system, but now show us how sum types would avoid someone from making that mistake.
Maybe you did need tests after all...
> You’re writing this because you don’t have experience with programs that can never crash.
Not so. I spend most of my days writing code in programming languages that do provide such guarantees. It is a cool party trick, but doesn't really matter at the end of the day because they still don't offer the expressiveness to ensure that the program does what is expected of it. I still have to write tests, and once I've written the necessary tests to ensure all the behaviour is correct, there is no practical way you can miss a crash situation.
> Our integration tests and unit tests have dozens of tests that never yielded an error and we never saw an error in production for years.
And had you avoided the crash you'd get erroneous results from the function instead. You're not really any farther ahead. You still need assurances that the function actually behaves correctly. And if you had those assurances, you'd have caught the divide by zero condition.
You're not going to convince me that a complete type system is academically better. I already agree with that. But absent a complete type system, you're going to have to resort to tests. Once you've written those tests, you're going to uncover the runtime errors anyway.
> Division by zero. We had a crash in production.
Your fuzz tests never tried passing in values that would lead to division by zero? For such a simple function that has many possible states that can lead to that condition, that seems completely inconceivable. Hell, I just tried it for fun and it found the issue in less than 100 tries! This must have been the time you were talking about where you deployed to production without running the tests?
But let me be try to be clear: The compiler warning you that you haven't considered a division by zero case does not mean you've handled it correctly. Absent a complete type system, you still need tests to ensure that the behaviour is consistent with expectations even in those edge cases. But with those tests, runtime errors can't go unnoticed anyway, so you didn't really need the type system.
> Better to prove the function works via proof
Agreed. Complete type systems are unquestionably better theoretically. Writing tests is tedious. But it remains that with the languages people actually use, even those with "advanced" type systems, you can't prove much. You have to fall back to testing, and at that point you're going to uncover the runtime errors too.
>But, again, they will catch your runtime errors if your behaviour is covered. If your behaviour isn't covered, then you're just shifting the problem to the program doing the wrong thing instead of crashing. That is not a win. It might even be worse! So, this doesn't matter in practice. Your purely academic view of the world doesn't work with the discussion taking place, I'm afraid.
So you're saying write tests that cover every possible behavior. Makes sense right? It's like saying write code without any bugs. Simple! You're not getting it. You can go run around telling people to write tests that eliminates 100% of bugs and that if you think that will eliminate all bugs from the world, well you're just not experienced.
>Go on. To continue with the original example, I have a function that tries to write to a file. If that fails, the caller is to try to write to a file on a different device. If the caller does anything else the program is broken with serious consequences and should not be shipped to production. Express that expectation using sum types. Hell, express it using any type construct available in popular languages. Good luck!
You can do this on rust. Literally it's the core of the rust sum type system. Good luck? Have you done basic programming with rust? Here's some psuedo code:
match getFile(fileName) {
Some(file) => do someghing
Error => match getFile2(fileName2) {
Some(file) => do something
Error => exit()
}
}
The above is psuedo code. The thing with the match operator is that the program DOES not compile if you do not handle both SOME and ERROR. For golang you can handle the Some and then it crashes if there's a problem. You aren't required to explicitly handle it.
>Not so. I spend most of my days writing code in programming languages that do provide such guarantees. It is a cool party trick, but doesn't really matter at the end of the day because they still don't offer the expressiveness to ensure that the program does what is expected of it. I still have to write tests, and once I've written the necessary tests to ensure all the behaviour is correct, there is no way you can miss a crash situation.
There is a way. You're just not getting it. there's about infinite ways to crash a program that has runtime errors.
>Your fuzz tests never tried passing in values that would lead to division by zero? For such a simple function that has many possible states that can lead to that condition, that seems inconceivable. Hell, I just tried it for fun and it found the issue in less than 100 tries! This must have been the time you were talking about where you deployed to production without running the tests?
Hell I used a programming language with no run time errors and I didn't write a single test. Amazing! There are tons of functions complex enough such that your fuzz test will miss it. Again we had this code working for years because we implictly assumed said devices will never pass duplicate data.
Also we don't write fuzz tests. We just do basic testing. Fuzz testing is something our start up doesn't have time for. We would prefer guarantees without the need of extra testing/work/time in this area.
>Traditionally, the calculation is (p2 - p1) / (t2 - t1). I'll assume you had a non-standard situation that necessitated a different formula, but does serve as a great example of how behaviour is your real concern. One could easily input the formula you gave where they expected (p2 - p1) / (t2 - t1) and sum types wouldn't care one bit.
Nope your formula is correct. I just assumed you were intelligent enough to know what I meant even though I didn't put in all the parenthesis (I'm typing on my phone afte rall). I thought wrong.
>Agreed. Complete type systems are cool. Writing tests is tedious. But we await your proof to my case for a realistic setting where one uses a typical production programming language. If all you have is silly half-measures that only cover a small number of cases, you're not really proving much. All you are doing is giving yourself a false sense of security.
Yes rust. Jesus. You're so inexperienced you don't even know when it's standing in front of your face. You don't need the borrow checker from rust. You only need the sum type. Then take the sum type apply it to division by zero and out of bounds array access and all IO calls. Boom that's it. No more runtime errors.
>There's a good way to change that. Let's see your code!
just look at elm man. Yuo don't even know what I'm talking about because you literally don't have experience. You want to see code that never crashes? Get some experience with Elm and you'll see why it never crashes and you'll see it doesn't take a "complete" type system to make it that way. Elms type system is woefully simplistic.
Rust is like 80% of the way there... the reason why people don't use it is lifetimes and the borrow checker and the complexity associated with it. Additionally rust left some holes so it can crash (like division by zero), but it has all the primitives needed to prevent it.
> Good luck? Have you done basic programming with rust?
Have you? I am sorry that the good luck did not shine upon you. With the types remaining intact, I modified the (pseudo)code:
match getFile(fileName) {
Some(file) => do someghing
Error => do something else unintended
}
It still compiles. You failed.
> The thing with the match operator is that the program DOES not compile if you do not handle both SOME and ERROR.
But as you come off your hubris, you can now see above it will compile even when you screw up the error handling. So you haven't gained anything. You still need to write tests to ensure that you are actually doing the right thing. And once you've ensured you are doing the right thing, how do you think crashes are going to go beyond that? Right. Not going to happen.
> Fuzz testing is something our start up doesn't have time for.
Testing, is testing, is testing. If you have time to write tests, you have time to write fuzz tests where they are appropriate. To throw a test out the window just because it has a slightly different execution model (as provided by the language; not something you have to build yourself) is bizarre. In fact, in your case it seems it could have supplanted the other tests you wrote, actually saving you time not only while writing the code, but also later when you had to waste time dealing with the issue. Time clearly isn't as constrained as you let on.
> For golang you can handle the Some and then it crashes if there's a problem. You aren't required to explicitly handle it.
Technically, in Go 'Some' should always be valid, regardless of whether or not there is an error. That is a fundamental feature of the Go language. Given (T, error), the values are not dependent. You don't need to explicitly handle the error. That is a huge misunderstanding. The same would not be true in Rust, which does consider them to be dependent by design, but Go is a completely different language. You can't think Go as being Rust with different syntax. There is a lot more to languages than the superficial.
>Have you? I am sorry that the good luck did not shine upon you. With the types remaining intact, I modified the (pseudo)code:
Your modification makes no sense. "do something else unintended" Wtf does that even mean? What are you doing? Why don't you spell it out? Because in golang you can do this:
v, err := getFile(fileName)
v.read()
And that's a fucking crash. You understand examples are used to illustrate a point right? And that your example shows you missed the point. Hey why don't I insert some psuedo code called "blow up the earth" in my program and that disproves every point ever made by anyone and I'm right. Genius.
>But as you come off your hubris, you can now see above it will compile even when you screw up the error handling.
Think of it like this. The point I'm illustrating is that in rust, you have to handle an error or the program won't compile. In go, you can forget to handle an error and your program will compile. You're going to have to write a bunch of tests to only POSSIBLY catch a missed error handling case. Understand? I don't think you do.
>Testing, is testing, is testing. If you have time to write tests, you have time to write fuzz tests where they are appropriate. To throw a test out the window just because it has a slightly different execution model (as provided by the language; not something you have to build yourself) is bizarre. In fact, in your case it seems it could have supplanted the other tests you wrote, actually saving you time not only while writing the code, but also later when you had to waste time dealing with the issue. Time clearly isn't as constrained as you let on.
It's not bizarre. It's again, lack of experience from your end. Why do I want to spend time writing generic test code that executes off of fuzzed input? I can write test specific code for specific use cases and that's much faster to write then attempting to write tests that work for generic cases.
Also how about not writing tests all together? I mean that's the best solution right? Honestly not to be insulting here, but it's not at all bizarre that you're not seeing how a better type system is better then tests that check for runtime errors. The root of it is that you're just stupid. Like why jump through a bunch of hoops and just call what I'm saying "bizarre" and just be straight with me. We're both mature right? If I think you're truthfully stupid and you think of me the same, just say it. We can take it. Why dance around it by calling my points "bizarre". No your points aren't "bizarre". They are stupid and wrong.
>Technically, in Go 'Some' should always be valid, regardless of whether or not there is an error.
That's why go is bad. You don't need to handle an error if err is not nil. V will be a nil here. And you know what's the only thing you can do with a nil besides check if it's a nil? Crash the program. Literally.
With rust, you can do this:
match getFile(fileName) {
Some(file) => do someghing
Error => {}
}
and do nothing. Which is the same effect as golang. But rust at least tells you to explicitly watch for it.
>You can't think Go as being Rust with different syntax. There is a lot more to languages than the superficial.
It's not about what I think of the language. It's about the intention of the designers. Go was made for people with not much experience. Straight from the horses mouth. Pike is saying he designed it for you.
>That is a fundamental feature of the Go language.
I think you're kind of not getting it. Seriously like the feature of golang is to allow you to unintentionally crash the program and you think that's a good thing?
v, err := getFile(fileName)
doSomething(v)
Take some time to think here. I know you think you're smart, but you need to hit the brakes for a second. Think: What is the purpose of the above code? If err actually is not a nil, and v ends up being a nil. What is the purpose of this type of logic to even exist? Is it for v to crash somewhere in doSomething? Are you saying that a fundamental feature for golang to crash somewhere inside doSomething?
Really think about this. You literally said it's a feature for golang to not handle an actual error and for v to still be "valid." So if err is not nil, v is a nil. What happens here? You think this is a feature? Or are you just not thinking straight? Just pause for a second.
Another thing to help you along: You know the inventor of the null/nil value called it his billion dollar mistake right? Have you thought about why it's a huge mistake? Here's a hint: You can't do anything with a null/nil except check if it's a null or crash the program by using it improperly. The existence of a nil/null signifies the existence of feature that you can only use to crash your program unintentionally.
Why doesn't elm crash? Why is rust safer then most languages? A big part of the reason is both languages don't have nulls or nils. And that doesn't have anything to do with a "complete" type system.
Hopefully you get it now. If not I can't help you.
The earlier comment already spelled it out. Hence why it wished you luck as we knew you would not be able to deliver. Yet right on cue you hilariously tried anyway.
I take that you haven't actually read anything in this discussion? If you have read it, you haven't understood it. Slow down, comprehend before posting. It seems you've become so fixated on telling us how sum types work – something we understood decades ago – that you have missed the forest for the trees.
> Why do I want to spend time writing generic test code that executes off of fuzzed input?
Remember when you mistakenly wrote (p2 - p1 / t2 - t1)? That's why. A complete type system would negate the need for testing there, but if all you have is sum types... There is no difference between a fuzz test and any other test other than the tooling will feed it 'arbitrary' inputs. It is not like they take more time to write or something. Testing is testing is testing.
In fact, it took me like ten seconds to write the fuzz test for your function when I tried it out earlier. This time argument is disconnected from reality. You have 10 seconds to spare to ensure correctness, especially when you admitted to writing a bunch of useless tests instead. One good test would have gone further and saved you time.
> I think you're kind of not getting it.
Let's change that. Consider your code:
v, err := getFile(fileName)
doSomething(v)
Presumably your requirements dictate that your code must do something when getFile fails, so you are obviously going to write a test for that scenario. How do you envision that potential crash condition evading the test? The CPU steps through until getFile fails, then checks if you are running a test and if it so it invents a valid file handle, but if it notices you are in production it returns a corrupt file handle and then crashes? That doesn't make any sense. What does make sense to you that explains how your code will pass tests but fail in production?
If what you are struggling to say is that you have no such requirement to deal with the failure so it is unspecified behaviour, and thus you didn't feel the need to write a test for it, all you can do with sum types, to keep the compiler happy, is purposefully panic. It is unspecified behaviour. Anything else but crashing would be nonsensical – something that is true even if it were written in Rust. Is the source of the panic significant?
> Seriously like the feature of golang is to allow you to unintentionally crash the program and you think that's a good thing?
It doesn't matter in practice. When the program is going to do the wrong thing, it makes little difference how it goes about doing it wrong. But if you really have to choose, crashing is better than silently corrupting data, yes. The latter is far more scary.
However, better is to ensure that your program doesn't do the wrong thing in the first place. If you have a complete type system, that's you best bet. But that isn't realistic. No language you are going to encounter in the real world has that. Meaning, prepare to learn how to write tests. They aren't easy to do right, but you're going to have to do it anyway because without a complete type system you have no other choice.
Anything else is running on hopes and prayers. If you honestly believe that hope and prayer is sufficient, then why are you so worried about crashing?
>Presumably your requirements dictate that your code must do something when getFile fails, so you are obviously going to write a test for that scenario. How do you envision that potential crash condition evading the test? The CPU steps through until getFile fails, then checks if you are running a test and if it so it invents a valid file handle, but if it notices you are in production it returns a corrupt file handle and then crashes? That doesn't make any sense. What does make sense to you that explains how your code will pass tests but fail in production?
Sigh. You said it was a feature. You said v being nil was a feature lol. Now you want to test it to see if it crashes? I'm saying it should be tested, but YOU said it was a feature for golang to always return a valid v. Now I know you're just not aware of what you're talking about.
>If what you are struggling to say is that you have no such requirement to deal with the failure so it is unspecified behaviour, and thus you didn't feel the need to write a test for it, all you can do with sum types, to keep the compiler happy, is purposefully panic. It is unspecified behaviour. Anything else but crashing would be nonsensical – something that is true even if it were written in Rust. Is the source of the panic significant?
I don't need to specify the difference here. You're just being stubborn. You already know that in rust you need to explictly panic vs. golang you can panic mistakenly with a hidden nil. This discussion is over.
Maybe because you're stuck with it because a bunch of software has already been written in it, in need of maintenance and extension. Or maybe because the browser only runs javascript.
It sounds like the opposite, a language popular and practical enough to the point where people actually use it across a wide range of cases and skillsets.
The reason why the books don't exist for <highbrow language of choice> is because there's only 50 programs written in it, 49 of which are tooling for the language.
That's just one dimension of "good" though, every language has its own pitfalls, some more than others. Go has pitfalls, sure, but the language it was designed to replace (C/C++) has more of them.
And languages with no room for mistakes have their own issues, like readability or productivity, but I don't have any experience with those; what language(s) are you thinking of? I don't know it myself but Rust seems more "bolted down" when it comes to that aspect.
I said I was a source of inspiration for the mistakes in my book. Said differently, I've done a lot of mistakes myself which ended up being a section in the book.
>I tried to explain how review workflows work in a PR-based setup, and gave concrete suggestions for how we could improve the process. But they didn’t want to try it. That might sound like a small thing, but at that stage, all I wanted was a smooth and efficient collaboration process. The easier it was for me to track changes, the better.
I'm surprised the copy editor was more comfortable using git than using a web-based review tool to leave comments, especially given that she was reviewing a Go book and didn't seem to know what Go was.
How does that even happen? It seems bizarre that Manning had this copy editor at all.
I recently had a negative experience with Manning. I sent them an email saying that I'm in the process of writing a book, and I'm self-publishing it, but I was curious about the possibility of applying to Manning for a second edition. I asked whether they accept second editions after a self-published first edition and what document formats they accept.
I got back a form letter telling me that they'd rejected my proposal. When I followed up and said I didn't send a proposal but was asking preliminary questions, they told me that they understood I hadn't sent a proposal, but they were going off of the table of contents on my book's website. I guess they decided to pre-emptively reject me?
They also only said Google Docs as a document format, but based on this blog post, clearly they accept AsciiDoc.
I must compliment your ability to keep the reader hooked, I had to see what chapters they saw, stalked your website and ended up reading the whole post about your pre sale.
This is pretty off topic but did you test how your book works on an E-Reader? I check a sample chapter and there where a lot of pictures and colors to distinguish information, this will probably not work very well on my Kindle.
Oh, thanks!
The first few chapters, I've been primarily targeting web and not testing on e-readers. I figured that until I knew whether people actually wanted to read it, I should just focus on making the web excerpts look decent and try to avoid over-optimizing for web.
Now that the book is officially a go, the PDF version is a first-class citizen, and I'll be testing e-reader experience on my rm2.
That kinda blew my mind too, I'd expect the complaint to be about needing to use some online review tool. Editing the asciidoc source directly sounds archaic, and I was expecting the authoring / editing world to have had 'code review' style software years before the software development world did.
I mean all mainstream word processing applications have a 'commentary' / 'review' mode where someone can leave comments and suggest edits.
Since you're in here, if I can recommend: sync.Pool with non-fixed-size objects is a source of many problems. https://wundergraph.com/blog/golang-sync-pool as a decent overview, and https://github.com/yarpc/yarpc-go/issues/2355 has been my recent plague.
If you browse around Go's stdlib use of sync.Pool, you'll see a variety of tiered pools with fixed sizes, and many drop anything over a large enough size (sometimes gigantic! as much as 16KB!): https://cs.opensource.google/go/go/+/refs/tags/go1.24.0:src/...
It's a pretty well-established gotcha, sadly, and https://github.com/teivah/100-go-mistakes/blob/master/src/12... falls right into it.
Wish Go had introduced memory arenas. Most of the use cases for pools could have been cleanly solved by arenas.
Go has had experimental arena api since 1.22 or so
The experiment failed, though. It is dead at this point.
I think they removed arenas again.
Maybe I'm wrong, but when reading the first article, it seems like the entire thing exists mainly (not completely) because Go's GC isn't generational.
Maybe somewhat, but it's not hard to get pathologically bad GC behavior out of generational GCs either. Pools / manual memory management will always have some reasonable use cases.
On the topic of copy editing you raise: I wrote a book in DocBook for Manning in 2010. DocBook is XML, so I structured it with opening / end tags on their own line, content in the middle. As you would with a HTML document.
After copy editing multiple chapters, they sent it back to me with all the content on a single line. I was so incredibly upset that they ditched all my painstaking format that I almost abandoned the project there + then.
It sounds like from your experience that it has barely changed. I ended up moving to self-publishing so I have a greater control over the whole process. I wrote it up long-form here: https://ryanbigg.com/2015/08/my-self-publishing-success-stor...
Maybe I'm misunderstanding, but you're saying you formatted XML by hand?
Someone likely loaded that into some tool, made changes and saved and likely never even looked at the XML.
Why would anyone care what the XML looks like?
Not needing to care in the slightest is one of the major value propositions of that sort of syntax, at least IMO. If it does happen to matter for whatever reason that's what formatting tools are for.
Because in this case it was used for its combination of both human and machine readability, but the reviewers disregarded the former.
Might as well write it in Word if that's the case.
This book format lends itself very nicely to a book club. I was in one with both new and experienced gophers. The mistakes are great discussion points. The veterans got to share stories when they've made whatever mistake or how they've avoided it over the years. The noobs often made comparisons with other languages.
A little secret about the book is a lot of the "mistakes" are introductions to some aspect of Go worded as a mistake. "Not using fuzzing" and "Not using errgroup" are a couple of examples.
O’Reilly author here. Seems the author stumbled over pitching them the book. You can almost certainly start with an email. Our initial contact with O’Reilly was only an email. We filled out a small form later with the details of the proposal, but it was not laborious. I can also attest that their tooling is great. From any git commit I can generate a full version of the book in any of its supported formats. I wrote all of my part of the book in vim.
Is there a good example repository to see how it's done?
They host their own repos, and builds are done on their system, but the layout is pretty simple. Numbered, named files in asciidoc, markdown, etc. your choice.
Hello fellow Manning author! This book is fantastic. I remember reading through a lot of it a couple years back. Super helpful and it's one of those books you can pick up, grab a nugget or two of good info and come back later.
Now that I'm starting another big Go project I'm going to look at it again.
What I like most about this book is it feels like it's all "real world" stuff. You can tell the author has built a lot with Go and can save you time by telling you were the potholes are. Great stuff!
> You can tell the author has built a lot with Go
That was the funny part from the intro of the article - the author had not, in fact, build a lot with Go at the time.
But, it proves you don't actually need to in order to become knowledgeable about a subject and / or write a book.
>the author had not, in fact, build a lot with Go at the time
So many programming books are like that, and usually it shows.
I got a question about the example shown, the goroutine one marked as #63, I'd copypaste but it's an image.
Is there a reason the common mistake is about goroutines specifically? If I instead just made function closures without launching off goroutines, would they all refer to the same 'i' variable? (I assume it's maybe just that the mistake tends to go hand in hand with goroutine-launching code in a pattern like that).
I'd presume the book would say right after the example :)
But otherwise: the author gets serious respect from me for going through that process, taking feedback and learning how to communicate, i.e. taking "how do I make a good book?" very seriously and trying their best. And also things like for putting their foot down when the problematic copyeditor. I'm almost interested in the book, not for learning about Go but learning about what it looks like when I know the writing has some serious intent behind it to communicate clearly.
More details here: https://100go.co/#not-being-careful-with-goroutines-and-loop.... My example was probably terrible as it's one of the three mistakes in the book that aren't relevant anymore, thanks to Go's recent updates.
Thank you very much for your comment, though. It means a lot.
So not only do you write a full book, but you keep the content online, up to date by making sure readers are informed of new developments that might make advice irrelevant? And you are able on the spot to say "one of three mistakes that are not relevant anymore"? You impress me, random book-writing Internet person.
You give me a feeling you really care about the craft and just making a good useful resource, which what I respect. I looked around the site and bookmarked it as a technical writing example I might go to read around now and then.
I sometimes teach coding or general computing things (but hands-on, less about writing) and I've come to appreciate that sometimes it is extraordinarily difficult to educate on or communicate complicated ideas.
Quoting you: To give you a sense of what I mean by improving the book “over and over“, keep in mind that between feedback from my DE, external reviewers, and others, there are parts of the book that I rewrote more than ten times.
I also do rewriting especially with content I intend to be a resource or education piece. Obsessively rewrite. Make it shorter. Clearer. Oops that reads like crap let's start over. IMO having an urge to do this and address all feedback or your own itches means you care about your work. Just have to remind myself that that perfect is the enemy of good enough (or something like that I forgot if the expression went exactly like that).
If you'd ever write a follow-up, would you just remove "mistakes" and end up with the book "86 Go mistakes and how to avoid them", or find more?
I think #633 must be a typo, or just a fumbled explanation.
"We might expect this code to print 123 in no particular order" should really say "exactly" or "in order", since it's proved in the next paragraph to be inconsistent.
And that would be the layman's explanation of concurrency resulting in things added sequentially happening out of order.
And assuming FIFO on async execution, akin to running everything in series, is probably the first mistake anyone will make when they encounter concurrency for the first time.
The problem isn’t that they might be out-of-order. The problem is expecting that they merely might be out-of-order and actually getting missed and duplicated values due to the timing of shared memory access. This was enough of a problem that they [changed the behavior][1] in Go 1.22.
[1]: https://go.dev/blog/loopvar-preview
Yes, that was the crux of my question (and was answered by that link when I checked teivah-given link, which linked https://go.dev/blog/loopvar-preview right there as well). Basically I wondered if the example given was really about:
1) In Go, the 'i' variable in the for loop is the same 'i' for each round of the iteration, meaning closures created inside the loop all refer to that same 'i' variable, instead of getting their own copy of it. Very easy to accidentally think the all closures have their own copy of 'i'. Goroutines are only mentioned because in Golang this mistake tends to come up with Goroutines because of a common code pattern.
OR
2) Goroutines themselves either behave or have some weird lexical scope rules in a way I don't know and it doesn't really have to do with closures but an entirely Golang-foreign-alien concept to me I cannot see, and this is why the book example had Goroutines mentioned with the mistake.
I rarely write Go myself so I was curious :) It looks like it was 1) unless I am bad at reading, and I think the Go 1.22 change is good. I could easily imagine myself and others making that mistake even with the knowledge to be careful around this sort of code (the link shows a more complicated example when scrolling down that IMO is a good motivating example).
It's literally the same problem with closures and scope that JS has.
You think you're working with a value but you're actually working with a reference to a value under the hood.
It was definitely 1. There were ways to demonstrate the issue without involving goroutines, such as by creating a list of closures in a loop, one at each iteration, and then invoking them synchronously after the loop exits. They would all have the same (in this case, final) value of i.
#63 isn't about the lack of execution guarantees when you execute multiple goroutines without proper synchronization; it was related to loop variables and goroutines.
This is a lovely write up. I don't have anything real to add in this comment, but somehow just clicking the upvote button doesn't feel like enough.
I just noticed my post was on HN. Thank you very much :)
Just came here to write exactly the same!
Thank you!
> I was working in Switzerland, refactoring a C++ legacy codebase...
Such a nice place to work, where you can just decide "Let's implement thing A in a completely new stack for us that shows promise" and then, after some time, say, "Ah... this is too hard, bad decision though. Let's try another one"
Sensei's Library has a collection of pages about mistakes made in Go: <https://senseis.xmp.net/?Mistakes>
This is about the boardgame, not the language?
Per author, this book started the "100 _ Mistakes and How to Avoid Them" series. That opened a whole new avenue of books based on programming language.
> I learned a ton from my DE. Like, really, a ton. Before that, I had been writing on various blogs for about a decade, but writing online is all about being direct because most people don’t have time. With a book, it’s different. People made a deliberate decision to buy your book. Now, it’s your job to bring them somewhere valuable. And if that takes time (meaning more words), so be it.
I have a hard time with this point. It feels to me like a lot of books have A LOT of unecassery padding all over the place.
The example of taking 28 words and turning it to 120 is pretty good at showing this. The first paragraph is totally pointless - we are reading a book about 100 most common mistakes, obviously this mistake is very common, how did this increased the value?
Then we have another line that explaining what happens in the code, which is totally useless because the code is super trivial.
Then the code, with more explanations on the side as if the previous line was not clear.
And only after that we get to the crux of the issue.
I understand that book publishers feel they need to justify the price of a book by reaching the 300p mark in some or other way, but in my way this only makes the book worse.
It's your opinion, nothing wrong with it. Let me try to see if I can make you change it at least a bit.
> The first paragraph is totally pointless - we are reading a book about 100 most common mistakes, obviously this mistake is very common, how did this increased the value?
There are different levels in terms of common mistakes, and this one was probably one that all the devs did at some point. So I think highlighting the fact it's a frequent one does make sense.
> Then we have another line that explaining what happens in the code, which is totally useless because the code is super trivial.
I have a rule: always explain the intention of the code. Even if it's 5 lines of code, it helps the reader to better understand what we will want to highlight.
> Then the code, with more explanations on the side as if the previous line was not clear.
The explanations on the side do not impact the size of the book so the argument doesn't hold. I did it in many code snippets to highlight where the reader needs to focus.
> I understand that book publishers feel they need to justify the price of a book by reaching the 300p mark in some or other way
This is more about guiding the readers, making sure the expectations are crystal clear and that they can follow me throughout an explanation. You judge it as a criteria to justify the price of the book, but it's not the real reason. At least not for my book and I'm sure it's the case for many others :)
> This is more about guiding the readers, making sure the expectations are crystal clear and that they can follow me throughout an explanation.
Sure, but this holds true for the blog version as well, right?
To be clear, I'm not advocating for The Little Schemer version, and am not arguing that the blog version is the best it can be, but surely we can agree that book padding phenomenon does exist.
By the way, I have read parts of your book over at O'Reilly Learning, and I do think it is a good book. So I'm not trying to take a dump on your work. My criticism is aimed at publishers.
No worries I didn't take it as a criticism. I understand your point. I mean when we sign a contract there's a minimum number of pages to write. But personally, I never felt the pressure of having to add more stuff.
Instead, my DE multiple times told me that it's better to favor just-in-time teaching over just-in-case teaching. Meaning multiple times, he made me drop certain section because they weren't really serving the chapter. They were "perhaps helpful" and he made me drop all of those.
I guess it also depends on who you're working with and which publisher. On this aspect, Manning was fair, imo.
> Adding comments in the source code itself
I've worked with editorial teams and I'd rather have that than PDFs and/or Word files without version control.
The author's categorization of Go mistakes into four areas - syntax, standard libraries, interfaces, and concurrency - is quite helpful. As someone who's wrestled with incorrect error handling and goroutine leaks, I appreciate the practical examples that highlight both common pitfalls and idiomatic solutions.
"Yet I think Tim’s single review was possibly as valuable as all the others combined".... and yet you don't even tell us anything about his review.... disappointing esp. considering the screenshots/level of details on other input.
On a different note, def. feel you pain regarding the copyeditor.
His review contains comments annotated on a book of 300+ pages. I don't think it would have made this section any better by deep diving into his review.
The go mistakes that tend to bother me the most are things like gc pauses, lack of first-class named and default arguments, lack of exceptions, lack of subrange types, etc.
I usually avoid them by not using go. Or waiting until missing features (generics) are added.
Generics are already there.
Indeed. And we only had to wait ten years for them, from Go 1.0 to 1.18.
The single example given here is incorrect. Since Go 1.22 loop variables have per-iteration scope, the "broken" code is deterministic and correct.
https://go.dev/blog/loopvar-preview
Outdated I'd rather say :) I documented here https://100go.co/#not-being-careful-with-goroutines-and-loop... but you're right, it was fixed (alongside 2 other mistakes in the 100).
I still need to support Windows 7 in my industry. The last supported version is 1.20. Outdated is relative.
I wrote some code today and I relied on the new behaviour. I felt a little dirty.
That was an issue in 2023? Quite sure C# fixed something similar back in 2012. Is Go still proudly reinventing the wheel, wooden and filled with termites?
I read it about two years ago, and would definitely recommend it. It has a similar vibe to Josh Bloch’s Effective Java.
I purchased this book on audible (because it said PDF version as well) but PDF version had only some code and not the entire book. Sorry if this is how Audible works and if its not related to this book per se. Looking forward to listen to it.
If you’re interested in publishing a book, please reach out and let me know what’s blocking you. My email address is in my profile. I’m not selling anything here, I want to understand the issues you’re facing.
As someone interested in learning Go is this great, thank you!
[dead]
[flagged]
Not that, but I did think he wrote a second book about writing the 100 mistakes book. I don’t claim that’s a reasonable reading of the title, but it’s how I read it.
[flagged]
I'm not a big fan of Go, and I think you could write this about nearly any language.
I'd love to try a language that you couldn't write a similar book about. Any suggestions?
Could you let me know what language doesn't have at least 100 common mistakes across a broad range of developers with varying skill level?
Did you know there exists languages that don't have run time errors? Languages that you can never crash outside of an OOM.
That means you can't find a language with no mistakes. But across all languages there are languages with astronomically more gotchas and mistakes then others.
Golang is one of these languages that is astronomical. I mean just compare it to the language with zero runtime errors.
> Did you know there exists languages that don't have run time errors?
Probably not. All those languages failed.
Elm.
It "succeeded" in that it delivers on its promise and some people still use it, but it is largely dead now due to the lack of development and the community's inability to push the language itself forward. There are forks that address that, but Elm itself seems unlikely to Lazarus any time soon.
It's definitely proof that software can be written in such a regime, though, and I hope we see something similarly dogmatic some day.
Gleam has potential to fill the niche and has an active and growing community.
Gleam / Lustre are fantastic, and I hope to use them in anger at some point, but it's my understanding that they don't aim to be as "total" as Elm (quotation marks because I don't think Elm was total in the formal sense). That is, while Lustre is very much following in Elm's footsteps, both it and the language have not been designed around avoiding runtime failure at all costs. (Which makes sense, given the Erlang/BEAM heritage.)
Failed language. And for that reason few know it exists.
It depends on what you consider failure. If you feel popularity is the main metric then golang is successful. Outside of that metric golang is pretty bad.
The proof is in the pudding. Here’s a quote from rob pike the creator of golang:
“The key point here is our programmers are Googlers, they’re not researchers. They’re typically, fairly young, fresh out of school, probably learned Java, maybe learned C or C++, probably learned Python. They’re not capable of understanding a brilliant language but we want to use them to build good software. So, the language that we give them has to be easy for them to understand and easy to adopt.”
Basically in a nutshell he’s saying they dumbed down golang so it’s useable by beginners. Golang is a step backwards. A failure in language development but a success in popularity.
You dumb down a language to a point where the language is so dumbed down it hits the largest demographic. You are part of that demographic. It’s similar to the demographic that voted for trump because he’s not fakeish like all the other candidates.
I understand what you are saying here but I think you've missed the point of what Pike was getting at.
I think Pike is acknowledging the practical realities of engineering at scale, and intentionally designed Go with simplicity in mind, which leads to more maintainable code and faster onboarding for new devs.
I'll also add that outside of the popularity metric, Go is not all bad. Fast compile times, readability, excellent standard library and toolchain, backward compatibility, to name a few things.
>I think Pike is acknowledging the practical realities of engineering at scale, and intentionally designed Go with simplicity in mind, which leads to more maintainable code and faster onboarding for new devs.
Doubt it. Read what he wrote. He's literally referring to people without much experience in programming. The stuff you said is literally NOT what he said.
>I'll also add that outside of the popularity metric, Go is not all bad. Fast compile times, readability, excellent standard library and toolchain, backward compatibility, to name a few things.
I agree with readability and fast compile times.
Pike didn't write it, he said it. (1) It is a talk about how Go was created to make concurrency simple. The "brilliant" language he refers to here is C++, which I'm sure you're aware has many of its own downsides.
Your argument that Go is a step backward because it was intentionally designed to be simple for novice programmers seems flawed. It's design was a deliberate tradeoff to address a specific problem. While I don't think it is a language that should be used for everything, it is good at the things it is good at.
What is it about Go that you have a problem with, specifically?
1. Around the 20 minute mark: https://learn.microsoft.com/en-us/shows/lang-next-2014/from-...
I understand he said it. But I can’t transmit voice over HN so I need you to read the transcript I made oof what he said rather then listen to it. I’m actually really confused why you couldn’t figure out this is what I meant. I could make a link and point you to the time like you did, but it’s easier to refer to it with an actual written version. Make sense? I hope so.
> The "brilliant" language he refers to here is C++, which I'm sure you're aware has many of its own downsides.
No. The brilliant language he’s referring to is a hypothetical one he could have created. Instead he created golang because he needed to create a language catered to people with less experience. That is what he is saying. I find it strange that you can literally read what is written and also reference the video and literally not understand what was literally said/written.
Maybe you’re just making up meaning subconsciously to cater to your own biases rather then facing the cold hard truth that pike created go to be not “brilliant”.
> What is it about Go that you have a problem with, specifically?
Oh there’s tons of stuff. One is that errors don’t have stack traces. You create an error that can’t be handled and so it bubbles up the stack until the only way to handle it is with a panic. You see the panic in your logs but now you have no idea where the error came from because no stack trace. You get the trace of the panic but no trace of the error. The whole thing is just poorly thought out.
> the language is so dumbed down it hits the largest demographic. You are part of that demographic. It’s similar to the demographic that voted for trump because he’s not fakeish like all the other candidates.
I've been a lot of places and done a lot of things but I've never had somebody liken me to a Trump voter because I like a programming language. Is this the new Godwin's law? Did panic() and nil kill your grandpappy?
[flagged]
> Basically in a nutshell he’s saying they dumbed down golang so it’s useable by beginners.
He's saying that developers can't handle Coq (the brilliant language), so they had to build a language that is like every other commonly used language, for better or worse.
> A failure in language development
As brilliant as Coq truly is, he's not wrong, is he? It is no coincidence that nobody is using Coq to build web servers. Which is, after all, what Pike said Go was designed for – that it was not intended to be a general purpose programming language. The vast majority of developers, even outside of Google, truly can't grasp it... And of even the scant few developers who can, they will tell you that the tradeoffs aren't worth it for something like a run-of-the-mill CRUD web server.
Your, being a researcher who does understand Coq, perspective is interesting from an academic angle, but Pike's point is that you don't understand the realities of engineering. This "Use Coq or you are no better than a Trump supporter" shows he was exactly on point. Cry as you might, nobody is going to be using Coq to build web servers, and for good reason.
No he's saying go is designed for people like you. You're obviously joking about COQ, but I'm not joking about you. This is literally what he's talking about. Take a step back out of your sarcasm and look at reality. You are the demographic Pike is talking about. And we both know Pike is not even referring to coq.
> No he's saying go is designed for people like you.
Stands to reason. It is true that I do spend my time on the engineering side of the industry. While I have great appreciation for the brilliant languages, they don't offer a whole lot for practical production work after you've weighed the tradeoffs. Especially in the particular niche Go is designed for. You are going to use a blub language like Rust for those types of problems, and for good reason.
> And we both know Pike is not even referring to coq.
Lean, then? The brilliant list isn't terribly long. We do know he isn't talking about Scala and Haskell, at least. He lumps them in with C++ and Java – albeit he has expressed that they are more beautiful. Not that anyone would consider them brilliant anyway. Well, maybe if you consider Trump to be also brilliant... There is always that guy.
It’s designed for people who don’t have much experience with programming. He literally said this. There is nothing said about coq or lean... that’s just trolling.
> It’s designed for people who don’t have much experience with programming.
Exactly. It follows the same basic "loop, variable, function" programming model as Java, C++, Haskell, PHP, Ruby, Rust, Python, LISP, Smalltalk, basically every language you've seen production code written in, that is familiar to early career beginners who have come out of traditional learning paths (e.g. college). Where once you understand one of them, you can jump into another with minimal overhead. None of these languages brilliant, but they are useful. Which is where he said they wanted Go to fit as well: A language that is useful and familiar.
That isn't what researchers and language theory enthusiasts want. They are enthralled by languages that think about programming in an entirely different way. The key point here is that it wasn't built for them. That is what he said.
That’s a stretch. Your saying he called golang not brilliant then you say basically every language from Haskell to Java is also not brilliant. Let’s be real here.
You also talk about looping for languages with no loops. And additionally pike never brought up looping at all. You just made this part up out of thin air. Your evidence is made up. He never said or referenced any of things you said.
> The key point here is that it wasn't built for them. That is what he said.
No read what he said again. He didn’t say golang was not designed for language experts. He did say it was designed for programmers just out of school with barely any experience with programming languages. He did not say he designed golang for an average engineeer who is not a PL expert he said he designed golang for literally people just out of school.
I don’t know how you can make stuff up out of thin air like this. Read what he literally said.
There is no such language, even Rust has many footguns.
There is such languages, but they actually failed, so you've probably never heard of them.
That's why Manning will consider a 100 Rust Mistakes edition.
Most of the mistakes in this book and general mistakes you make when programming aren't about crashes or other runtime errors. They are about writing code that is overly complex or otherwise difficult to maintain.
I don't think Go is particularly esoteric to where these "gotchas" are a big problem.
In fact, I would argue that the gotchas are an intentional part of Go's design philosophy. I think it is strange to work in Go when coming from another language because of this, which leads people to think Go sucks.
That may be part of why they think Go sucks, but it is mostly because Go doesn't have many features to paper over poor design decisions. "Advanced" languages let you throw any old gobbledygook at them, and when you finally realize you screwed up, you can just monkey patch (or whatever feature of the day the language has) your way out of it and call it a day. Go is far less forgiving. You have to get the design right or pain will ensue. And pain does ensue for those who try it after being accustomed to having leeway in haphazard design, and thus they conclude Go sucks instead of acknowledging that their design sucks.
I mean, Go does suck. Maybe a language should be accommodating to bad designs. But still, those shouting "Go sucks" from the rooftops never seem to be willing to bring introspection into where they might have failed. It's always someone else's fault. (Something not limited to the case of Go, of course)
Go sucks not because it's accomodating to poor designs. It sucks because it is in itself poorly designed and that leaks over to the design of entire applications.
Why have functions return err, nil? Why even allow for a runtime error here? It's a really simple fix. You don't even have to make the language complex to support this. Instead the entire program is littered with holes and if statements you have to check in order to prevent a actual crash
> Why even allow for a runtime error here?
Why not? It doesn't make any difference in practice. Without a complete type system you must write tests to ensure that error conditions (to stay with your example, although this also applies broadly) do what you need of them. If you somehow introduced a runtime error there, your tests would be unable to not notice. Whether your compiler cries or your test suite cries when you screw up is not a meaningful difference.
> You don't even have to make the language complex to support this.
A complete type system is insanely complex to implement and even harder to write against.
Without a complete type system, all you can have is silly half-measures. Maybe the error becomes an optional/result type with forced unwrapping, for example, but you still haven't asserted in the types what needs to happen with the error. So you still need to write the same tests that you had to write anyway. So, other than moving where you discover the problem – from your tests to the compiler – nothing has changed.
The half-measures are a cute party trick, I'll give you that, but makes no real difference when actual engineering is taking place. They might, however, give a false sense of security. They might even convince you that you don't need to write tests (you do). Maybe those make for desirable traits?
>Why not?
TO prevent a runtime error. You say it Doesn't make any difference in practice meaning you never had a runtime error while running go? Impossible.
>A complete type system is insanely complex to implement and even harder to write against.
Who says you need a complex type system? You just need exhaustive evaluation of sum types. That's one feature that's it.
Removing run times errors doesn't mean building the most complex type system in the world.
> Doesn't make any difference in practice meaning you never had a runtime error while running go?
I am not sure I have written enough Go to comment there, but I have worked extensively in other languages where runtime errors are possible, similar to Go in that regard. I have encountered runtime errors in said tests now and then, sure, but then you know about it and deal with it... So, in practice, no different than if the compiler told you that there is a possible runtime error.
> Who says you need a complex type system?
It is needed if you want to avoid the need for said tests. With a complete type system the type system can become your test suite, so to speak. But the languages people normally use, even those with "advanced" type systems, are nowhere near expressive enough for that. Meaning that you have to write the tests anyway. And then you'll know if there are any runtime errors as soon as you run your tests because how could the tests run without encountering the runtime error too? It is not like a CPU magically changes how it works if it detects that a test is being run. So, in practice, the type system doesn't change the outcome. But it is a cool party trick. I'll give you that.
That said, aside from these hand-wavy, make-believe stories, you are still very right that Go would benefit from sum types. For the reason that they map to the human model of the world very well, succinctly communicating structures that are often needed to be expressed. Languages are decidedly for humans. You can sort of work in the same basic idea in Go using interfaces, but it is far more confusing to read and understand than sum types would be. For a language that claims to value readability...
> So, in practice, no different than if the compiler told you that there is a possible runtime error.
If your program has runtime errors then that means you can deploy it to production and catch your errors in production.
If your compiler catches all possible runtime errors and refuses to compile. Then you will have no runtime errors in production guaranteed by proof. The program cannot even exist with runtime errors. It can only exist with no runtime errors.
So no difference catching errors in production vs. compile time? I beg to differ. Big fucking difference imho.
> It is needed if you want to avoid the need for said tests.
I’m referring to the fact that you don’t need a complex type system to design a language that will absolutely never have any runtime errors. You’re going off on a tangent here about how you need a type system to have less tests which is completely different from what I’m talking about. This entire paragraph you wrote here is like you’re responding to an irrelevant topic.
> I am not sure I have written enough Go to comment there
Honestly it seems that you haven’t just not written enough go. But basically any programming language . It seems that you’re not clear about runtime errors and you seem to have only encountered these types of errors during tests. So yes you don’t have much experience imho and rob pike deliberately targeted the language towards people like you.
> If your program has runtime errors then that means you can deploy it to production and catch your errors in production.
That questions: Why are you allowing your programs to be deployed when tests are failing? This is not a realistic scenario in the real world. Yes, you can invent contrived hypotheticals all day long, but it is meaningless. We've been clear that we are referring to practical settings.
But, but, what if there is a bug in your compiler that sees the runtime error slip through??? Who gives a shit? In some imaged world it may be possible, but it is not realistic. Not worth talking about.
> I’m referring to the fact that you don’t need a complex type system to design a language that will absolutely never have any runtime errors. You’re going off on a tangent here
What you are referring to is clear, but it cannot be considered in a vacuum. The alternative is to see the program keep trodding along, but do the wrong thing. In that case who cares if the program crashes instead? You're getting incorrect behaviour either way.
What you actually need is assurances that the program won't do the wrong thing top to bottom. That requires either a complete type system or, more realistically in the real world, testing. If you go the testing route, you'll know about any runtime errors when you run your tests.
> Honestly it seems that you haven’t just not written enough go.
There is nothing unique to Go here. Many popular languages suffer the same problem. But, if we want to place extensive Go experience as a requirement to speak to this then we have to defer to your experience. Perhaps you can choose an example of where you wrote code in Go that produced a runtime error, show us your tests, and explain how the condition evaded your checks and balances? – I'm fascinated to learn how your code ran perfectly while under test but then blew up in production.
> That questions: Why are you allowing your programs to be deployed when tests are failing? This is not a realistic scenario in the real world. Yes, you can invent contrived hypotheticals all day long, but it is meaningless. We've been clear that we are referring to practical settings.
Tests don’t catch everything. You can have a billion tests and there can still be uncaught runtime errors.
If you had a language that probably does not have runtime errors you don’t even need one test. Your program cannot fail in that way.
I honestly don’t think you know what you’re talking about. I didn’t make up a single hypothetical. This is real. Production errors can happen in spite of tests. Are you not familiar with this happening? It just means this: no experience.
Your compiler having a bug or not is orthogonal to the topic. Again you don’t know what you are talking about. If our compiler allows for runtime errors but is fully correct then no amount of tests can guarantee a runtime error will never happen. Golang as a fully correct compiler cannot be gauranteed to have no runtime errors with tests ever.
> What you are referring to is clear, but it cannot be considered in a vacuum. The alternative is to see the program keep trodding along, but do the wrong thing. In that case who cares if the program crashes instead? You're getting incorrect behaviour either way.
You’re writing this because you don’t have experience with programs that can never crash. A program that doesn’t crash doesn’t mean you never exit the program. The program can exit if you want it to. You just need to deliberately tell the program to exit. In golang if you do a division by zero, the program crashes. If you had sum types all divisions return an optional. Both paths of the optional must be handled by exhaustive matching so you must handle the case where the division yields a number or its undefined. If you want the program to exit when it is undefined you can do so. In golang the compiler doesn’t force you to handle both outcomes It just crashes. It’s the same with out of bounds access of an array.
Again real world testing doesn’t guarantee shit. A “complete type system” can be as extensive as dependent types like COQ or much simpler like rusts where you just have sum types and exhaustive pattern matching.
> Perhaps you can choose an example of where you wrote code in Go that produced a runtime error, show us your tests, and explain how the condition evaded your checks and balances?
Oh easy. we had a function that calculates velocity from a stream of input data. That’s (p2 - p1 / t2 - t1). Our integration tests and unit tests have dozens of tests that never yielded an error and we never saw an error in production for years. We switched to a new iot device that sometimes sent identical measurements to our system. Division by zero. We had a crash in production.
> I'm fascinated to learn how your code ran perfectly while under test but then blew up in production.
You’re inexperienced that’s why you’re fascinated. If you have formula involving velocity there an almost infinite amount of combinations that will never produce a runtime error and an infinite amount of parameter combinations that do. True full coverage that completely proves the function works with tests involves infinite tests. Better to prove the function works via proof with a simple extension to the type system. Sum types.
> Tests don’t catch everything.
Again, they will catch your runtime errors if your behaviour is covered. If your behaviour isn't covered, then you're just shifting the problem to the program doing the wrong thing instead of crashing. That is not a win. It might even be worse! So, this doesn't matter in practice. Your purely academic view of the world doesn't work with the discussion taking place, I'm afraid.
> If you had a language that probably does not have runtime errors you don’t even need one test.
Go on.
Here, let's use your example:
> That’s (p2 - p1 / t2 - t1).
Traditionally, the calculation is (p2 - p1) / (t2 - t1). I'll assume you had a non-standard situation that necessitated a different formula, but this could have equally been a mistake. It wouldn't be too hard to forget the inner parentheses. We'll assume that divide by zero was already eliminated by the type system, but now show us how sum types would avoid someone from making that mistake.
Maybe you did need tests after all...
> You’re writing this because you don’t have experience with programs that can never crash.
Not so. I spend most of my days writing code in programming languages that do provide such guarantees. It is a cool party trick, but doesn't really matter at the end of the day because they still don't offer the expressiveness to ensure that the program does what is expected of it. I still have to write tests, and once I've written the necessary tests to ensure all the behaviour is correct, there is no practical way you can miss a crash situation.
> Our integration tests and unit tests have dozens of tests that never yielded an error and we never saw an error in production for years.
And had you avoided the crash you'd get erroneous results from the function instead. You're not really any farther ahead. You still need assurances that the function actually behaves correctly. And if you had those assurances, you'd have caught the divide by zero condition.
You're not going to convince me that a complete type system is academically better. I already agree with that. But absent a complete type system, you're going to have to resort to tests. Once you've written those tests, you're going to uncover the runtime errors anyway.
> Division by zero. We had a crash in production.
Your fuzz tests never tried passing in values that would lead to division by zero? For such a simple function that has many possible states that can lead to that condition, that seems completely inconceivable. Hell, I just tried it for fun and it found the issue in less than 100 tries! This must have been the time you were talking about where you deployed to production without running the tests?
But let me be try to be clear: The compiler warning you that you haven't considered a division by zero case does not mean you've handled it correctly. Absent a complete type system, you still need tests to ensure that the behaviour is consistent with expectations even in those edge cases. But with those tests, runtime errors can't go unnoticed anyway, so you didn't really need the type system.
> Better to prove the function works via proof
Agreed. Complete type systems are unquestionably better theoretically. Writing tests is tedious. But it remains that with the languages people actually use, even those with "advanced" type systems, you can't prove much. You have to fall back to testing, and at that point you're going to uncover the runtime errors too.
> You’re inexperienced that’s why you’re fascinated.
There's a good way to change that. Let's see your code!
>But, again, they will catch your runtime errors if your behaviour is covered. If your behaviour isn't covered, then you're just shifting the problem to the program doing the wrong thing instead of crashing. That is not a win. It might even be worse! So, this doesn't matter in practice. Your purely academic view of the world doesn't work with the discussion taking place, I'm afraid.
So you're saying write tests that cover every possible behavior. Makes sense right? It's like saying write code without any bugs. Simple! You're not getting it. You can go run around telling people to write tests that eliminates 100% of bugs and that if you think that will eliminate all bugs from the world, well you're just not experienced.
>Go on. To continue with the original example, I have a function that tries to write to a file. If that fails, the caller is to try to write to a file on a different device. If the caller does anything else the program is broken with serious consequences and should not be shipped to production. Express that expectation using sum types. Hell, express it using any type construct available in popular languages. Good luck!
You can do this on rust. Literally it's the core of the rust sum type system. Good luck? Have you done basic programming with rust? Here's some psuedo code:
The above is psuedo code. The thing with the match operator is that the program DOES not compile if you do not handle both SOME and ERROR. For golang you can handle the Some and then it crashes if there's a problem. You aren't required to explicitly handle it.>Not so. I spend most of my days writing code in programming languages that do provide such guarantees. It is a cool party trick, but doesn't really matter at the end of the day because they still don't offer the expressiveness to ensure that the program does what is expected of it. I still have to write tests, and once I've written the necessary tests to ensure all the behaviour is correct, there is no way you can miss a crash situation.
There is a way. You're just not getting it. there's about infinite ways to crash a program that has runtime errors.
>Your fuzz tests never tried passing in values that would lead to division by zero? For such a simple function that has many possible states that can lead to that condition, that seems inconceivable. Hell, I just tried it for fun and it found the issue in less than 100 tries! This must have been the time you were talking about where you deployed to production without running the tests?
Hell I used a programming language with no run time errors and I didn't write a single test. Amazing! There are tons of functions complex enough such that your fuzz test will miss it. Again we had this code working for years because we implictly assumed said devices will never pass duplicate data.
Also we don't write fuzz tests. We just do basic testing. Fuzz testing is something our start up doesn't have time for. We would prefer guarantees without the need of extra testing/work/time in this area.
>Traditionally, the calculation is (p2 - p1) / (t2 - t1). I'll assume you had a non-standard situation that necessitated a different formula, but does serve as a great example of how behaviour is your real concern. One could easily input the formula you gave where they expected (p2 - p1) / (t2 - t1) and sum types wouldn't care one bit.
Nope your formula is correct. I just assumed you were intelligent enough to know what I meant even though I didn't put in all the parenthesis (I'm typing on my phone afte rall). I thought wrong.
>Agreed. Complete type systems are cool. Writing tests is tedious. But we await your proof to my case for a realistic setting where one uses a typical production programming language. If all you have is silly half-measures that only cover a small number of cases, you're not really proving much. All you are doing is giving yourself a false sense of security.
Yes rust. Jesus. You're so inexperienced you don't even know when it's standing in front of your face. You don't need the borrow checker from rust. You only need the sum type. Then take the sum type apply it to division by zero and out of bounds array access and all IO calls. Boom that's it. No more runtime errors.
>There's a good way to change that. Let's see your code!
just look at elm man. Yuo don't even know what I'm talking about because you literally don't have experience. You want to see code that never crashes? Get some experience with Elm and you'll see why it never crashes and you'll see it doesn't take a "complete" type system to make it that way. Elms type system is woefully simplistic.
Rust is like 80% of the way there... the reason why people don't use it is lifetimes and the borrow checker and the complexity associated with it. Additionally rust left some holes so it can crash (like division by zero), but it has all the primitives needed to prevent it.
> Good luck? Have you done basic programming with rust?
Have you? I am sorry that the good luck did not shine upon you. With the types remaining intact, I modified the (pseudo)code:
It still compiles. You failed.> The thing with the match operator is that the program DOES not compile if you do not handle both SOME and ERROR.
But as you come off your hubris, you can now see above it will compile even when you screw up the error handling. So you haven't gained anything. You still need to write tests to ensure that you are actually doing the right thing. And once you've ensured you are doing the right thing, how do you think crashes are going to go beyond that? Right. Not going to happen.
> Fuzz testing is something our start up doesn't have time for.
Testing, is testing, is testing. If you have time to write tests, you have time to write fuzz tests where they are appropriate. To throw a test out the window just because it has a slightly different execution model (as provided by the language; not something you have to build yourself) is bizarre. In fact, in your case it seems it could have supplanted the other tests you wrote, actually saving you time not only while writing the code, but also later when you had to waste time dealing with the issue. Time clearly isn't as constrained as you let on.
> For golang you can handle the Some and then it crashes if there's a problem. You aren't required to explicitly handle it.
Technically, in Go 'Some' should always be valid, regardless of whether or not there is an error. That is a fundamental feature of the Go language. Given (T, error), the values are not dependent. You don't need to explicitly handle the error. That is a huge misunderstanding. The same would not be true in Rust, which does consider them to be dependent by design, but Go is a completely different language. You can't think Go as being Rust with different syntax. There is a lot more to languages than the superficial.
>Have you? I am sorry that the good luck did not shine upon you. With the types remaining intact, I modified the (pseudo)code:
Your modification makes no sense. "do something else unintended" Wtf does that even mean? What are you doing? Why don't you spell it out? Because in golang you can do this:
And that's a fucking crash. You understand examples are used to illustrate a point right? And that your example shows you missed the point. Hey why don't I insert some psuedo code called "blow up the earth" in my program and that disproves every point ever made by anyone and I'm right. Genius.>But as you come off your hubris, you can now see above it will compile even when you screw up the error handling.
Think of it like this. The point I'm illustrating is that in rust, you have to handle an error or the program won't compile. In go, you can forget to handle an error and your program will compile. You're going to have to write a bunch of tests to only POSSIBLY catch a missed error handling case. Understand? I don't think you do.
>Testing, is testing, is testing. If you have time to write tests, you have time to write fuzz tests where they are appropriate. To throw a test out the window just because it has a slightly different execution model (as provided by the language; not something you have to build yourself) is bizarre. In fact, in your case it seems it could have supplanted the other tests you wrote, actually saving you time not only while writing the code, but also later when you had to waste time dealing with the issue. Time clearly isn't as constrained as you let on.
It's not bizarre. It's again, lack of experience from your end. Why do I want to spend time writing generic test code that executes off of fuzzed input? I can write test specific code for specific use cases and that's much faster to write then attempting to write tests that work for generic cases.
Also how about not writing tests all together? I mean that's the best solution right? Honestly not to be insulting here, but it's not at all bizarre that you're not seeing how a better type system is better then tests that check for runtime errors. The root of it is that you're just stupid. Like why jump through a bunch of hoops and just call what I'm saying "bizarre" and just be straight with me. We're both mature right? If I think you're truthfully stupid and you think of me the same, just say it. We can take it. Why dance around it by calling my points "bizarre". No your points aren't "bizarre". They are stupid and wrong.
>Technically, in Go 'Some' should always be valid, regardless of whether or not there is an error.
That's why go is bad. You don't need to handle an error if err is not nil. V will be a nil here. And you know what's the only thing you can do with a nil besides check if it's a nil? Crash the program. Literally. With rust, you can do this:
and do nothing. Which is the same effect as golang. But rust at least tells you to explicitly watch for it.>You can't think Go as being Rust with different syntax. There is a lot more to languages than the superficial.
It's not about what I think of the language. It's about the intention of the designers. Go was made for people with not much experience. Straight from the horses mouth. Pike is saying he designed it for you.
>That is a fundamental feature of the Go language.
I think you're kind of not getting it. Seriously like the feature of golang is to allow you to unintentionally crash the program and you think that's a good thing?
Take some time to think here. I know you think you're smart, but you need to hit the brakes for a second. Think: What is the purpose of the above code? If err actually is not a nil, and v ends up being a nil. What is the purpose of this type of logic to even exist? Is it for v to crash somewhere in doSomething? Are you saying that a fundamental feature for golang to crash somewhere inside doSomething?Really think about this. You literally said it's a feature for golang to not handle an actual error and for v to still be "valid." So if err is not nil, v is a nil. What happens here? You think this is a feature? Or are you just not thinking straight? Just pause for a second.
Another thing to help you along: You know the inventor of the null/nil value called it his billion dollar mistake right? Have you thought about why it's a huge mistake? Here's a hint: You can't do anything with a null/nil except check if it's a null or crash the program by using it improperly. The existence of a nil/null signifies the existence of feature that you can only use to crash your program unintentionally.
Why doesn't elm crash? Why is rust safer then most languages? A big part of the reason is both languages don't have nulls or nils. And that doesn't have anything to do with a "complete" type system.
Hopefully you get it now. If not I can't help you.
> Why don't you spell it out?
The earlier comment already spelled it out. Hence why it wished you luck as we knew you would not be able to deliver. Yet right on cue you hilariously tried anyway.
I take that you haven't actually read anything in this discussion? If you have read it, you haven't understood it. Slow down, comprehend before posting. It seems you've become so fixated on telling us how sum types work – something we understood decades ago – that you have missed the forest for the trees.
> Why do I want to spend time writing generic test code that executes off of fuzzed input?
Remember when you mistakenly wrote (p2 - p1 / t2 - t1)? That's why. A complete type system would negate the need for testing there, but if all you have is sum types... There is no difference between a fuzz test and any other test other than the tooling will feed it 'arbitrary' inputs. It is not like they take more time to write or something. Testing is testing is testing.
In fact, it took me like ten seconds to write the fuzz test for your function when I tried it out earlier. This time argument is disconnected from reality. You have 10 seconds to spare to ensure correctness, especially when you admitted to writing a bunch of useless tests instead. One good test would have gone further and saved you time.
> I think you're kind of not getting it.
Let's change that. Consider your code:
Presumably your requirements dictate that your code must do something when getFile fails, so you are obviously going to write a test for that scenario. How do you envision that potential crash condition evading the test? The CPU steps through until getFile fails, then checks if you are running a test and if it so it invents a valid file handle, but if it notices you are in production it returns a corrupt file handle and then crashes? That doesn't make any sense. What does make sense to you that explains how your code will pass tests but fail in production?If what you are struggling to say is that you have no such requirement to deal with the failure so it is unspecified behaviour, and thus you didn't feel the need to write a test for it, all you can do with sum types, to keep the compiler happy, is purposefully panic. It is unspecified behaviour. Anything else but crashing would be nonsensical – something that is true even if it were written in Rust. Is the source of the panic significant?
> Seriously like the feature of golang is to allow you to unintentionally crash the program and you think that's a good thing?
It doesn't matter in practice. When the program is going to do the wrong thing, it makes little difference how it goes about doing it wrong. But if you really have to choose, crashing is better than silently corrupting data, yes. The latter is far more scary.
However, better is to ensure that your program doesn't do the wrong thing in the first place. If you have a complete type system, that's you best bet. But that isn't realistic. No language you are going to encounter in the real world has that. Meaning, prepare to learn how to write tests. They aren't easy to do right, but you're going to have to do it anyway because without a complete type system you have no other choice.
Anything else is running on hopes and prayers. If you honestly believe that hope and prayer is sufficient, then why are you so worried about crashing?
>Presumably your requirements dictate that your code must do something when getFile fails, so you are obviously going to write a test for that scenario. How do you envision that potential crash condition evading the test? The CPU steps through until getFile fails, then checks if you are running a test and if it so it invents a valid file handle, but if it notices you are in production it returns a corrupt file handle and then crashes? That doesn't make any sense. What does make sense to you that explains how your code will pass tests but fail in production?
Sigh. You said it was a feature. You said v being nil was a feature lol. Now you want to test it to see if it crashes? I'm saying it should be tested, but YOU said it was a feature for golang to always return a valid v. Now I know you're just not aware of what you're talking about.
>If what you are struggling to say is that you have no such requirement to deal with the failure so it is unspecified behaviour, and thus you didn't feel the need to write a test for it, all you can do with sum types, to keep the compiler happy, is purposefully panic. It is unspecified behaviour. Anything else but crashing would be nonsensical – something that is true even if it were written in Rust. Is the source of the panic significant?
I don't need to specify the difference here. You're just being stubborn. You already know that in rust you need to explictly panic vs. golang you can panic mistakenly with a hidden nil. This discussion is over.
This guy writes Malbolge and has zero runtime errors because he can't compile it.
Whatever the hell that is. And no. Zero runtime errors and compiles to run.
Did you know that this book is about mistakes that humans make?
But the comment is about languages that prevent mistakes that humans make.
Disagree. Every language has quirks, and best practices.
You can make 100 mistakes in literally every language.
Agreed. That was also my point when I mentioned the book started the "100 ${LANGUAGE} Mistakes and How to Avoid Them" series at Manning.
There's an infinite amount of mistakes in every language. Obviously the meaning behind this is mistakes only makeable in golang.
Why would anyone take time to write a book like that for a failed language?
Maybe because you're stuck with it because a bunch of software has already been written in it, in need of maintenance and extension. Or maybe because the browser only runs javascript.
If a bunch of software is written in a language, that doesn't suggest failure. More like resounding success.
Popularity and vendor lock are not merit.
Success/failure isn't a function of merit.
Because they don't know it's failed.
A lot of replies to your comment conflate possibility with necessity. Could and need are different things.
It sounds like the opposite, a language popular and practical enough to the point where people actually use it across a wide range of cases and skillsets.
The reason why the books don't exist for <highbrow language of choice> is because there's only 50 programs written in it, 49 of which are tooling for the language.
Every language has mistakes. But it's also 100% true that many languages have more mistakes then others.
A good language should leave no room for mistakes.
But a good language should also be useful, and you can't have something both useful and without room for mistakes. A curious contradiction.
The best applications have no bugs, and you can only make sure of that if you don’t write any code.
The language that does not have room for mistakes cannot be used by humans.
We need it for llm agent.
That's just one dimension of "good" though, every language has its own pitfalls, some more than others. Go has pitfalls, sure, but the language it was designed to replace (C/C++) has more of them.
And languages with no room for mistakes have their own issues, like readability or productivity, but I don't have any experience with those; what language(s) are you thinking of? I don't know it myself but Rust seems more "bolted down" when it comes to that aspect.
I agree. It's sad that the only languages that exist are the bad ones.
> (to be honest, I was also a significant source of inspiration!)
There is no honesty in praising yourself, no matter what you did and achieved.
Honesty is when you pay tribute to someone else by saying he is a source of inspiration.
In this case the use seems appropriate since he was the source of inspiration for mistakes, so he wasn't praising himself.
I said I was a source of inspiration for the mistakes in my book. Said differently, I've done a lot of mistakes myself which ended up being a section in the book.
To be honest, i doubt whether you get what he implied