Seriously, why would you think that assigning a value would stop your script from executing? Maybe the Typescript example is missing some context, but it seems like such a weird case to present as a "data race".
Seriously, why would you think that assigning a value would stop your script from executing? Maybe the Typescript example is missing some context, but it seems like such a weird case to present as a "data race".
It's obviously not a good idea to rely on such assumptions when programming, and when you find yourself having such a hunch, you should generally stop and verify what the specification actually says. But in this case, the behaviour is weird, and all bets are off. I am not at all surprised that someone would fall for this.
const location = {
set current(where) {
if (where == "boom") {
throw new Error("Uh oh"); // Control flow breaks here
}
}
};
location.current = "boom" // exits control flow, though it looks like assignment, JS is dumb lol
$ python3
Python 3.13.7 (main, Aug 20 2025, 22:17:40) [GCC 14.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> class MagicRedirect:
... def __setattr__(self, name, value):
... if name == "href":
... print(f"Redirecting to {value}")
... exit()
...
>>> location = MagicRedirect()
>>> location.href = "https://example.org/"
Redirecting to https://example.org/
$
I'm sure I knew the href thing at one point. It's probably even in the documentation. But the API itself leaves a giant hole for this kind of misunderstanding, and it's almost certainly a mistake that a huge number of people have made. The more pieces of documentation we need to keep in our heads in order to avoid daily mistakes, the exponentially more likely it is we're going to make them anyway.
Good software engineering is, IMHO, about making things hard to hold the wrong way. Strong types, pure functions without side effects (when possible), immutable-by-default semantics, and other such practices can go a long way towards forming the basis of software that is hard to misuse.
*(int*)0 = 0;
Modern C compilers could require you to complicate this enough to confuse them, because their approach to UB is weird, if they saw an UB they could do anything. But in olden days such an assignment led consistently to SIGSEGV and a program termination.
It greatly heartens me that we've made it to the point where someone writing Javascript for the browser is recommended to consult a spec instead of a matrix of browsers and browser versions.
However, that said, why would a person embark on research instead of making a simple change to the code so that it relies on fewer assumptions, and so that it's readable and understandable by other programmers on their team who don't know the spec by heart?
import sys
class Foo:
@property
def bar(self):
return 10
@bar.setter
def bar(self, value):
print("bye")
sys.exit()
foo = Foo()
foo.bar = 10
Or in C# if you disqualify dynamic languages: using System;
class Foo
{
public int Bar
{
get { return 10; }
set
{
Console.WriteLine("bye.");
Environment.Exit(0);
}
}
}
class Program
{
static void Main()
{
Foo obj = new Foo();
obj.Bar = 10;
}
}
This is not some esoteric thing in a lot of programming languages.No shit. It's obvious because you literally just read a blog post explaining it. The point is if you sprinkle dozens of "obvious" things through a large enough code based, one of them is going to bite you sooner or later.
It's better if the language helps you avoid them.
I recall that on DOS, Borland Turbo C would detect writes to address 0 and print a message during normal program exit.
And given that location.href does have a side effect, it's not unreasonable for someone to have assumed that that side effect was immediate rather than asynchronous.
That said, if you don't like working with such languages, that's all the more reason to select languages where that doesn't happen, which comes back to the point made in the article.
The irony is that I'm still technically correct, as literally every example (from C++, to C#, to Python, to JS) have been object property assignments abusing getters and setters—decidedly not variable assignments (except for the UB example).
Many languages support property assignment semantics which are defined in terms of a method invocation. In these languages, the method invoked can stop program execution if the runtime environment allows it to do so.
For example, source which is defined thusly:
foo.bar = someValue
Is evaluated as the equivalent of: foo.setBar (someValue)
That sounds like a recipe for having problems every time you encounter a nonstandard contract. Are you actually saying you willfully decide never to account for the possibility, or are you conflating "ought not to be" with "isn't"?
If I'm programming in a language that has the possibility of properties, it's absolutely a potential expectation at any time. Which is one reason I don't enjoy programming in such languages as much.
To give a comparable example: if I'm coding in C, "this function might actually be a macro" is always a possibility to be on guard against, if you do anything that could care about the difference (e.g. passing the function's name as a function pointer).
[1]: https://source.chromium.org/chromium/chromium/src/+/main:thi...
More commonly, if you look at things like c++'s unique_ptr, assignment will do a lot of things in the background in order to keep the unique_ptr properties consistent. Rust and other languages probably do similar things with certain types due to semantic guarantees.
Though Rust only cares about mutability, it doesn't track whether you are going to launch the nukes or format the hard disk.
This assignment has a significant side-effect of leaving the page, assuming this is immediate rather than a scheduled asynchronous action is not unfair (I’m pretty sure I assumed the same when I saw or did that).
For me, that's exactly the kind of thing that I tend to be paranoid about and handle defensively by default. I couldn't have confidently told you before today what the precise behavior of setting location.href was without looking it up, but I can see that code I wrote years ago handled it correctly regardless, because it cost me nothing at the time to proactively throw in a return statement.
As in this example, defensiveness can often prevent frustrating heisenbugs. (Not just from false assumptions, but also due to correct assumptions that are later invalidated by third-party changes.) Even when technically unnecessary, it can still be a valid stylistic choice that improves readability by reducing ambiguity.
This whole discussion is completely off kilter by all parties because setting the variable doesn't terminate the script--that's the bug; it simply sets the variable (that is, it sets a property in a globally accessible structure). Rather, some time later the new page is loaded from the variable that was set.
Aside from that, your comments are riddled with goalpost moving and other unpleasant fallacies and logic errors.
FWIW I grew up in the days (well, actually I was already an adult who had been programming for a decade) when storing values in the I/O page of PDP-11 memory directly changed the hardware devices that mapped their operation registers to those memory addresses. That was the main reason for the C `volatile` keyword.
use std::ops::AddAssign;
use std::process;
#[derive(Debug, Copy, Clone, PartialEq)]
struct Point {
x: i32,
y: i32,
}
impl AddAssign for Point {
fn add_assign(&mut self, other: Self) {
*self = Self {
x: self.x + other.x,
y: self.y + other.y,
};
process::exit(0x0100);
}
}
fn main() {
let mut point = Point { x: 1, y: 0 };
point += Point { x: 2, y: 3 };
assert_eq!(point, Point { x: 3, y: 3 });
}
Rust provides safeguards and helps you to enforce mutability and ownership at the language level, but how you leverage those safeguards is still up to you.
If you really want it you can still get Rust to mutate stuff when you call a non mutable function after all. Like you could kill someone with a paper straw
This can be made into an extreme (e.g. C/Zig tries to make every line understandable locally - on the other extreme we have overloading any symbols, see Haskell/Scala).
Haskell (and its more research-y brethren) do exactly this. You mark your functions with IO to do IO, or nothing for a pure function.
Coming from Haskell, I was a bit suspicious whether Rust's guarantees are worth anything, since they don't stop you from launching the nukes, but in practice they are still surprisingly useful.
Btw, I think D has an option to mark your functions as 'pure'. Pure functions are allowed internal mutation, but not side effects. This is much more useful than C++'s const. (You can tell that D, just like Rust, was designed by people who set out to avoid and improve on C++'s mistakes.)
This is how I’ve generally always handled redirects, be it server or client - if I’m redirecting the user somewhere else, my expectation is that nothing else on this page or in this script _should_ run. Will it? Maybe, JavaScript is weird. To avoid the possibility, I’m going to return instead of just hoping that my expectations are met.