This was interesting, thanks. Was hoping to see a bit more about type hinting, but there's already a lot here.
A question about efficiency: IIUC, in your initial bitmap rastering implementation, you process a row of target bitmap pixels at once, accumulating a winding number count to know whether the pen should be up or down at each x position. It sounds like you are solving for t given the known x and y positions on every curve segment at every target pixel, and then checking whether t is in the valid range [0, 1). Is that right?
Because if so, I think you could avoid doing most of this computation by using an active edge list. Basically, in an initial step, compute bounds on the y extents of each curve segment -- upper bounds for the max y, lower bounds for the min y. (The max and min y values of all 3 points work fine for these, since a quadratic Bezier curve is fully inside the triangle they form.) For each of the two extents of each curve segment, add a (y position, reference to curve segment, isMin) triple to an array -- so twice as many array elements as curve segments. Then sort the array by y position. Now during the outer rendering loop that steps through increasing y positions, you can maintain an index in this list that steps forward whenever the next element crosses the new y value: Whenever this new element has isMin=true, add the corresponding curve segment to the set of "active segments" that you will solve for; whenever it's false, remove it from this set. This way, you never need to solve for t on the "inactive segments" that you know are bounded out on the y axis, which is probably most of them.
Thanks, I've bookmarked an article recently that I thought was about that, but haven't read it yet. Your explanation lays a very good foundation to understand that technique.
If I understood you correctly, this might be an issue if you have multiple strokes (so multiple mins and maxes that you need to stay within) on a row of pixels (think all strokes of an N).
What I'm suggesting is just a way to do less computation to get the same result as before, it doesn't change the correctness of the algorithm (if implemented correctly!). Instead of testing every curve segment at each (x, y) pixel location in the target bitmap, you only need to test those curve segments that overlap (or, more precisely, aren't known not to overlap) that y location, and what I described is a way to do that efficiently.
Gemini tells me that for thousands of years, the swastika was used as "a symbol of positivity, luck and cosmic order". Try drawing it on something now and showing it to people. Is this an effective way to fight Nazism?
I think it's brave to keep using em dashes, but I don't think it's smart, because we human writers who like using them (myself very much included) will never have the mindshare to displace the culturally dominant meaning. At least, not until the dominant forces in AI decide of their own accord that they don't want their LLMs emitting so many of them.
I think it's safe to assume they meant it within their specific cultural context. They the symbol has different connotations in other cultures doesn't really change the point being made.
My point is just: if a test for what a symbol ‘really means’ depends on choosing an audience that conveniently erases everyone who uses it differently, that’s not describing intrinsic meaning, that’s describing the author’s cultural bubble and bias.
And on em dashes—most people outside tech circles see no “AI fingerprint,” and designers like myself have loved them since early Mac DTP, so the suspicion feels hilariously retroactive and very knee-jerk. So what if somebody thinks my text here is written by a bot?
> So what if somebody thinks my text here is written by a bot?
Then they might not read it at all. I often zone out as soon as I expect I'm reading slop and that's the reason try to ensure my own writing isn't slop adjacent.
I'm also not sure there is an "AI bubble." Everyone I know is using it in every industry. Museum education, municipal health services, vehicle engineering, publishing, logistics, I'm seeing it everywhere.
As mentioned elsewhere I've seen non-tech people refer to them as "AI dashes."
> if a test for what a symbol ‘really means’
There was no suggestion of such a test. No symbol has an intrinsic meaning. The point GP was about considering how your output will be received.
That point was very obviously made within a specific cultural context, at the very least limited to the world of the Latin alphabet. I'm sure there are other LLM signifiers outside of that bubble.
> I often zone out as soon as I expect I'm reading slop and that's the reason try to ensure my own writing isn't slop adjacent.
And how is this a problem someone else has to address? Some people zone out when they see a text is too long: are we supposed to only publish short form then? I have 10 years of writing on my site, if someone in 2026 sees my use of em dashes and suddenly starts thinking that my content is AI generated that's their problem, not mine.
Too many people are willingly bending to adapt to what AI companies are doing. I'm personally not gonna do it. Because again, now it's em dashes, tomorrow it could be a set of words, or a way to structure text. I say fuck that.
> And how is this a problem someone else has to address?
Where has anyone made the claim that it is?
> Some people zone out when they see a text is too long: are we supposed to only publish short form then?
No, but a good writer will generally consider if their text is needlessly verbose and try to make it palatable to their audience.
> starts thinking that my content is AI generated that's their problem, not mine.
If you want to reach them with your writing then it might become a problem. Obviously the focus on em dashes alone isn't enough but it's undoubtedly one of the flags.
> Too many people are willingly bending to adapt to what AI companies are doing.
It's bending rather to what readers are feeling. It's not following the top down orders of a corporation, it's being aware of how technology shapes readers' expectations and adapting your writing to that.
I'm not confident that the average person is aware of an em dash nor that it is widely associated with AI; I think the current culturally dominant meaning is just a fat hyphen (which most people just call a dash anyway).
My wife was working from home recently and I overheard a meeting she was having. It's a very non technical field. She and her team were working on a presentation and her boss said "let's use one of those little AI dashes here."
> Gemini tells me that for thousands of years, the swastika was used as "a symbol of positivity, luck and cosmic order". Try drawing it on something now and showing it to people. Is this an effective way to fight Nazism?
I'm happy to change my position when some 13 million people are killed by lunatics that used the em dash as the symbol of their ideology. Until then, I'll keep using it everywhere it's appropriate.
Also, if we don't have the guts to resist even when the stakes are this low and the consequences for our resistance are basically non existent, then society is doomed. We might as well roll on our side and die.
> At least, not until the dominant forces in AI decide of their own accord that they don't want their LLMs emitting so many of them.
It's not a power I'm willing to give them. What if tomorrow they tweak something and those tool start to use a specific word more often? Or a different punctuation sign? What do we do then? Do we constantly adapt, playing whack-a-mole? What if AI starts asking a lot more questions in their writing? Do we stop asking them as a result?
You feel free to adapt and bend. I'm personally not going to do it and if someone starts thinking that I'm using AI to write my thoughts and as a result that's on them.
> First put all your services in a monorepo have it all build as one under CI. That’s a static check across the entire system.
That helps but is insufficient, since the set of concurrently deployed artifact versions can be different than any set of artifact versions seen by CI -- most obviously when two versions of the same artifact are actively deployed at the same time. It also appears to rule out the possibility of ever integrating with other systems (e.g., now you need to build your own Stripe-equivalent in a subdir of your monorepo).
> Second don’t use databases.
So you want to reimplement your own PostgreSQL-equivalent in another monorepo subdir too? I don't understand how opting not to use modern RDBMSes is practical. IIUC you're proposing implementing a DB using compiled queries that use the same types as the consuming application -- I can see the type-safety benefits, but this immediately restricts consumers to the same language or environment (e.g., JVM) that the DB was implemented in.
>That helps but is insufficient, since the set of concurrently deployed artifact versions can be different than any set of artifact versions seen by CI
Simple, although I only mentioned repos should be mono, I should've also said deployment should be mono as well. I thought that was a given.
>So you want to reimplement your own PostgreSQL-equivalent in another monorepo subdir too?
I'm too lazy to do this but in general I want an artifact that is built. All sql queries need to be compiled and built and the database runs that artifact. And of course all of this is part of a monorepo that is monodeployed.
>I don't understand how opting not to use modern RDBMSes is practical.
It's not practical. My point is there's some really stupid obvious flaws we live with because it's the only practical solution.
> Simple, although I only mentioned repos should be mono, I should've also said deployment should be mono as well. I thought that was a given.
Deploying your service graph as one atomic unit is not a given, and not necessarily even the best idea - you need to be able to roll back an individual service unless you have very small changes between versions, which means that even if they were rolled out atomically, you still run the risk of mixed versions sets.
>Deploying your service graph as one atomic unit is not a given,
It's not a given because you didn't make it a given.
>and not necessarily even the best idea - you need to be able to roll back an individual service unless you have very small changes between versions, which means that even if they were rolled out atomically, you still run the risk of mixed versions sets.
It is the best idea. This should be the standard. And nothing prevents you from rolling back an individual service. You can still do that. And you can still do individual deploys too. But these are just for patch ups.
When you roll back an individual service your entire system is no longer in a valid state. It's in an interim state of repair. You need to fix your changes in the monorepo and monodeploy again. A successful monodeploy ensures that the finished deploy is devoid of a common class of errors.
Monodeploy should be the gold standard, and individual deploys and roll backs are reserved for emergencies.
> It is the best idea. This should be the standard. And nothing prevents you from rolling back an individual service. You can still do that. And you can still do individual deploys too. But these are just for patch ups.
There are a ton of reasons it's not the best idea. This flies in the face of a lot of _better_ ideas.
Keeping changesets small so that it's easier to debug when something goes wrong? Blown out of the water by deploying everything at once.
Bringing every service up at once is a great way to create the coldest version of your entire product.
Requiring a monodeployment turns canarying or A/B testing entire classes of changes into a blocking rollout where any other feature work has to move at the pace of the slowest change.
> When you roll back an individual service your entire system is no longer in a valid state. It's in an interim state of repair.
The gold standard is that each version of your service can work with each other version of your service, because in The Real World your service will spend time in those states.
> Monodeploy should be the gold standard, and individual deploys and roll backs are reserved for emergencies.
No, because if it's still possible to mix versions in your services, then a monodeploy doesn't actually solve any issues.
I actually am a big fan of fewer services and deploying bigger artifacts, but if you have multiple services, you have to act like you have multiple services.
>Keeping changesets small so that it's easier to debug when something goes wrong? Blown out of the water by deploying everything at once.
The size of your monodeploy is orthoganol to the concept of monodeploy. You can make a large change or a small change.
In fact your deploy can be smart. For a specific service in a full system monodeploy when upgrading from v2 to v3 it can do some sort of diff on the source of a specific service and if there's no difference it goes from v2 -> v3 without a new build and uses the same artifact from v2 to v3. The entire point is though that this service (or the entire system) still goes from v2 to v3 and it tagged this way. This is an optional optimization for speed.
In fact, your compiler when building artifacts ALREADY does this. It caches huge parts of the build and reuses it. A deploy can do the same.
This is the important concept of a monodeploy: The static check; The integration testing. The verification of the ENTIRE system as a whole. Your monodeploy determines what new artifacts need to be recreated, what artifacts need to be reused... verifies everything, and deploys.
>Requiring a monodeployment turns canarying or A/B testing entire classes of changes into a blocking rollout where any other feature work has to move at the pace of the slowest change.
Again orthoganol. Your complaining that a monodeploy is slow. Integration testing and unit testing are also slow and take time. The monodeploy is for safety. If you're saying speed > safety, here's an idea: throw all testing out the window as well. That's a big speed up right there.
If your monodeploy is slow, work on speeding it up. Work on it being smarter and faster. Do you throw testing out the window because it's slow or do you work on speeding it up? Make the smart choice.
>The gold standard is that each version of your service can work with each other version of your service, because in The Real World your service will spend time in those states.
And that gold standard is stupid. We can do better. We can go to a state where different versions between different services don't exist. Only one monoversion. You throw that concept of different versions out the window then you also throw the possibility of a mismatch out the window as well.
You're trying to deal with an error. I'm saying make the error not exist.
>No, because if it's still possible to mix versions in your services, then a monodeploy doesn't actually solve any issues.
It's not possible to mix versions in a monodeploy because the whole concept of it is to have ONE version of everything. Let me be clear I'm talking about a MONOREPO + MONOBUILD + MONODEPLOY. If there's only one version of everything and it's all deployed than issues are solved under this model. At this point I think you just don't like being wrong.
>I actually am a big fan of fewer services and deploying bigger artifacts, but if you have multiple services, you have to act like you have multiple services.
A monodeploy doesn't preclude multiple services. You can still act like it's different services. A monodeploy + monorepo just makes sure there's ONE version of the entire system.
You're solution here is just saying you want to be able to deploy different versions of different services in a staggered way. You want different repos so different modules of the system can move out of step with everything else. Service A is at v23, Service B is at v32.
The only way to deal with this mismatch is to have complicated "versioning" system on top of that where API contracts between services only accept "backward compatible" changes. This works but it's also extra complication and extra restriction. You can no longer radically change an API because it can break a number of systems in different repos. You're stuck. Or if you're willing to deal with the fallout you can make breaking changes and accept the risk whilst under my system the risk doesn't even exist.
You are advocating for an idea that's definitively worse. But you'll never admit it, not right now anyway because basically you've dug your heals into the ground. At this point I've never seen a human who is so unbiased they are capable of proper reasoning to flip their stance. If we continue talking, you will continue to build logical scaffolding to support YOUR point rather then to support A point and it's pointless (punintended) to keep going.
I'm ok to keep going, but I think it's completely obvious to any neutral arbiter that the conversation is over and that your perspective is rationally worse.
Outstanding piece. It articulates the actually fundamental correctness problem of most software development:
> “given the actual set of deployments that exist right now, is it safe to add this one?”
This problem has been right in front of our faces for decades, but I haven't ever seen it laid out like this, or fully realised it myself. Which is surprising, given the obvious utility of types for program-level correctness.
I think the next big leap forward in software reliability will be the development/integration of both the tooling to accomplish this, and the cultural norms to use them. Automating this (as far as possible -- they do note that semantic drift is immune to automatic detection) will be as big a deal as the concept of version control for code, or CI/CD.
There's lots here that I need to dig into further.
I don't think that's happening in practice, but 1) it may not be specified and 2) What you say could well be the persisted state after a machine crash or power loss. In particular if those files live in different directories.
You can remedy 2) by doing fsync() on the parent directory in between. I just asked ChatGPT which directory you need to fsync. It says it's both, the source and the target directory. Which "makes sense" and simplifies implementations, but it means the rename operation is atomic only at runtime, not if there's a crash in between. It think you might end up with 0 or 2 entries after a crash if you're unlucky.
If that's true, then for safety maybe one should never rename across directories, but instead do a coordinated link(source, target), fsync(target_dir), unlink(source), fsync(source_dir)
Depending on metadata cache behavior configuration, if the system is powered off immediately after the first command, then that could indeed happen I think.
As to whether it’s technically possible for it to happen on a system that stays on, I’m not sure, but it’s certainly vanishingly rare and likely requires very specific circumstances—not just a random race condition.
Uhh, if the system powers off immediately after the first command (mv a b), the second command (mv c d) would never run. So where would d come from if the command that created it never executed?
Sure, but splitting "atomic" operations across a reboot is an interesting design choice. Surely upon reboot you would re-try the first `mv a b` before doing other things.
All you need for this to occur is the window where both renames occurs overlap. A system polling to check if a, b, c, and d exist while the renames are happening might find all four of them.
Assuming that the two `mv` commands are run in sequence, there shouldn't be any possibility for a and d to be observed "at once" (i.e. first d and then afterwards still a, by a single process).
With regards to Linux kernel implementation, I could map you out a list of sequence points caused in the kernel by above sequential "shell script", proving that no other process could observe d and then a using two subsequent "stat" calls. I could imagine that a single directory listing (getdents) could happen to first visit a and then d, though -- I don't know the internals of that.
With regards to POSIX specification, I don't know the details but anything less than that guarantee would be very a huge robustness problem.
I'm almost certain what the OP meant was if the commands were run synchronously (ie: from 2 different shells or as `mv a b &; mv c d`) yes there is a possibility that a and d exist (eg: On a busy system where neither of the 2 commands can be immediately scheduled and eventually the second one ends up being scheduled before the first)
Or to go a level deeper, if you have 2 occurrences of rename(2) from the stdlibc ...
rename('a', 'b');
rename('c', 'd');
...and the compiler decides on out of order execution or optimizing by scheduling on different cpus, you can get a and d existing at the same time.
The reason it won't happen in the example you posted is the shell ensures the atomicity (by not forking the second mv until the wait() on the first returns)
nitpick, it should be `touch a c & mv a b & mv c d` as `&;` returns `bash: syntax error near unexpected token `;'`. I always find this oddly weird, but that would not be the first pattern in BASH that is.
`inotifywait` actually sees them in order, but nothing ensure that it's that way.
$ inotifywait -m /tmp
/tmp/ MOVED_FROM a
/tmp/ MOVED_TO b
/tmp/ MOVED_FROM c
/tmp/ MOVED_TO d
`stat` tells us that the timestamps are equal as well.
$ stat b d | grep '^Change'
Change: 2026-02-06 12:22:55.394932841 +0100
Change: 2026-02-06 12:22:55.394932841 +0100
However, speeding things up changes it a bit.
Given
$ (
set -eo pipefail
for i in {1..10000}
do
printf '%d ' "$i"
touch a c
mv a b &
mv c d &
wait
rm b d
done
)
1 2 3 4 5 6 .....
And with `inotifywait` I saw this when running it for a while.
$ inotifywait -m -e MOVED_FROM,MOVED_TO /tmp > /tmp/output
cat /tmp/output | xargs -l4 | sort | uniq -c
9104 /tmp/ MOVED_FROM a /tmp/ MOVED_TO b /tmp/ MOVED_FROM c /tmp/ MOVED_TO d
896 /tmp/ MOVED_FROM c /tmp/ MOVED_TO d /tmp/ MOVED_FROM a /tmp/ MOVED_TO b
The Poisson distribution is well approximated by the binomial distribution when n is high and p is low, which is exactly the case here. Despite the high variance in the sample mean, we can still make high-confidence statements about what range of incident rates are likely -- basically, dramatically higher rates are extremely unlikely. (Not sure, but I think it will turn out that confidence in statements about the true incident rate being lower than observed will be much lower.)
reply