![](/static/253f0d9b/assets/icons/icon-96x96.png)
![](https://programming.dev/pictrs/image/8140dda6-9512-4297-ac17-d303638c90a6.png)
That’s great, but surely, from time to time, you have to deal with code that other people have written?
That’s great, but surely, from time to time, you have to deal with code that other people have written?
It’s not a strawman, though, because Martin’s actual example code in the book is like this, including a full module he rewrites toward the end.
The whole book is like this, though, and these are specifically supposed to be examples of “good” code. The rewritten time class toward the end, a fully rewritten Java module, is a nightmare by the time Martin finishes with it. And I’m pretty sure it has a bug, though I couldn’t be bothered to type the whole thing into an editor to test it myself.
Because abstractions leak. Heck, abstractions are practically lies most of the time.
What’s the most time-consuming thing in programming? Writing new features? No, that’s easy. It’s figuring out where a bug is in existing code.
How do abstractions help with that? Can you tell, from the symptoms, which “level of abstraction” contains the bug? Or do you need to read through all six (or however many) “levels”, across multiple modules and functions, to find the error? Far more commonly, it’s the latter.
And, arguably worse, program misbehavior is often due to unexpected interactions between components that appear to work in isolation. This means that there isn’t a single “level of abstraction” at which the bug manifests, and also that no amount of unit testing would have prevented the bug.
The fairly unique thing about the web is that the tech stack is pretty much entirely dependent on what browsers are “winning” at any given time. There are web standards, but Chrome steamrolls them regularly (either by ignoring them or by pressuring the committee to standardize what they want). This is why browser monoculture is bad, and why people recommend Firefox and other non-Chrome (or really, non-WebKit) browsers, as a matter of principle.
So right now, with Chrome’s dominance, Mozilla’s struggles, and the extremely slow progress of WebAssembly and WASI, it definitely feels like JavaScript will remain dominant for a long while. But since Chrome does support WebAssembly and Google participates in WASI, and since there’s no fundamental reason why WASI can’t eventually provide everything that JS does today, there’s good reason to expect the JS stranglehold not to last forever.
And the great thing about WASM/WASI is that, since it’s designed as a compilation target rather than a language, there won’t be any remaining reason for a single language (such as Rust) to dominate. Rust got an early lead in WASM because they put the effort into making it a viable target platform for the compiler, and because it’s the kind of language that attracts people who dislike JavaScript (such as myself). But there’s no reason a different language couldn’t rapidly become the most commonly used WASM language if, say, a web framework in a previously-niche language becomes popular. (After all, Rails, a back-end framework, is what popularized Ruby.)
Edit to add: I say “fairly unique”, but in fact there’s a very analogous situation with C: https://faultlore.com/blah/c-isnt-a-language/
In this context, stabilization refers to the adoption growth curve flattening out.
The second one!
The bit in Big Hero 6 with the video records of Tadashi inventing Baymax are about as close to this as I’ve ever seen in a sci fi action movie.
Why do you think most early adopters use Windows exclusively?
In both cases, Go and Python, the operator will assign a variable a value and also use it as an expression.
That is absolutely not true. foo := <expr>
is a statement in Go, full stop. Just try something trivial like assigning to the output of :=
: https://go.dev/play/p/nPINGc7LO8B
It’s true that if
and for
let you use :=
but don’t let you use var
, but you still can’t use the result of the assignment directly. So for instance you need if foo := <expr>; foo { ... }
rather than just if foo := <expr> { ... }
.
I was curious about the Python connection because multiple comments mentioned it, but I’ve worked on multiple Python projects over the past dozen-ish years and never seen that operator.
Turns out it was introduced in 3.8, released in 2019, so it was much too late to inspire Go, and most of the projects I’ve worked on were written to target an earlier Python version. It also has a substantially different meaning than in Go.
I don’t know if there’s an “official” rationale for the Go syntax, but :=
is a fairly common (but not ubiquitous) math notation meaning “define the thing on the left to be equal to the expression on the right”, i.e. to distinguish it from the other use of =
, i.e. “the expression on the left must be equal to the expression on the right.” Go’s usage matches this mathematical meaning of introducing a new variable definition pretty well.
I read it a few hours ago. Was it substantially longer before?
This “full on rage essay” is nine sentences, including the tl;dr and the sentence fragments. There’s really not a big difference between telling your coworkers a story like this and posting about it on social media.
If only there were a good statically typed database, as opposed to thin wrappers over SQL.
What…? That is a terrible idea.
How is await
like comefrom
, any more than threading is like comefrom
? The variable context is preserved and you have no control over what is executed before the await
returns.
Does the catchphrase “blazing fast” ring any bells? Some people care.
(Arguably that’s just the pendulum swinging the other way; Ruby, Python, and Java ruled the software world for a while, and I think that’s a large part of why the Go and Rust communities make such a big deal about speed.)
Honestly, wouldn’t it be great if POSIX eventually specified a WASM runtime?
It’s relevant because there are still platforms that don’t have actual Bash (e.g. containers using Busybox).
sh
is not just a symlink: when invoked using the symlink, the target binary must run in POSIX compliant mode. So it’s effectively a sub-dialect.
Amber compiles to a language, not to a binary. So “why doesn’t it compile to sh
” is a perfectly reasonable question, and refers to the POSIX shell dialect, not to the /bin/sh
symlink itself.
I hear you, but here’s my experience:
I’ve had one coworker whose personal coding style actually somewhat resembled that in the Clean Code examples. He wrote functions as small as possible, used many layers of abstraction, and named everything very verbosely and explicitly.
Now, to be fair, I don’t think he did that because of Clean Code, and he also didn’t follow most of the good practices that Martin recommends. Most egregiously, he almost never tested things, even manually (!!). He once worked an entire weekend to finish something that I needed for my part of the project, and when he was done, it didn’t work, because he hadn’t actually run it at any point (!!!).
But even when his software did work, it was horrendous to navigate and modify, specifically because of that style of writing code. I know, because when he retired, I was the only person on the team who could deal with it, so his part of the project fell entirely on me.
Now, I’ve also had to work with code that had the opposite problem: short names, no abstraction. And a sort of “worst of both” codebase where the functions were exceedingly long and full of near-duplicate functionality, but overall there was a fair amount of modularity and abstraction.
But in my opinion, it was much harder to deal with the code that hid all of its weirdness behind layers and layers of abstractions, despite those abstractions being carefully documented and explicitly named.