This is because nobody can say how many people are needed to develop or maintain some body of software. So companies hire however many employees they can afford. When the number they can afford changes they hire more or fire some. The number of staff has pretty much nothing to do with the actual work. AI introduces another factor to the equation: it allows one person to perform N times as much softy-work as before. So even if you have no idea how many people you need (as always, see above) you do know that you can get by with 1/N.
Seriously? Lawyers. Doctors. Civil engineering. Accountants. And that's just the fields I know of directly being significantly impacted that I could think of in 10 seconds. Oh, also: education.
A difference is the output of software development can be immediately applied in an automatic way - production leads to delivery as a matter of fact. The whole flow can actually be automated.
Producing legal documents on the other hand is only part of the process, a lawyer or similar must still apply the produced text in some human driven context.
According to Claude when I asked it to audit one of our Python applications, there are scenarios where Brolti can be used only when some Python library detects that the C library is installed on the system. So your Brolti-ness can depend on which machine your code runs on. Ymmv, it didn't look to be a practical possibility in our code so I didn't probe further.
These chat bots always hedge like that. They will never give you a straight answer, it's always "depending on version and build configuration and system configuration".
Investigate yourself. Read the documentation. Grep for brotli in the source. Don't blindly trust a chat bot when it says it "might happen in certain configurations".
Chat bot lol. It did all of those things including scanning the binary. I ran it after doing my own (albeit more limited, but maximal-human) investigation.
I've noticed the newer frontier models are a lot better about this and are willing to give concrete answers, where before even in 90/10 scenarios they would always be wishy-washy espousing the "it depends on the circumstances" BS.
The only reply to this criticizes the hedging, but I think it's actually true and it does make it harder to tell if you're vulnerable to this. And on top of that, quite a few systems have brotli installed by necessity. On Debian 12, for example, I have the following dependency chain on a VPS I run:
It worked out pretty well for Go. Nothing else besides Erlang cracked the parallelism UX/runtime-performance code as well as Go did, and it didn't have the drawbacks of Erlang.
> rule broken by almost every language in common use
I don't think so. Go/Erlang/Java do break it (Java very recently). C/C++/Rust/C#/JavaScript/Ruby/Python/Perl/TCL don't break it. PHP ... sort of breaks it, (IMO they just hide cooperative nonparallel concurrency in a nice API, but I'm not well-informed on php)?
I think you're confusing userland threading (also known as green threads) with a regular thread pool implementation (that uses regular OS threads).
Because most web servers use OS threads. Nginx, Apache, litespeed, they all spin up `core count` threads but they can all work single threaded just fine as well.
No, I mean that Tokio, Go, etc., were built with general-purpose web servers in mind, and they decided that lightweight threads were necessary for that purpose because OS threads had too much overhead to be used per-request.
NGINX uses a state machine per task, which as far as I know is kind of what Rust's async functions end up desugaring to.
Tokio's not a lightweight thread/green thread system. It's an event loop, just like Javascript's, optionally with a fixed number of threads running work underneath.
For Go: you're likely right that OS thread overhead was the main reason for Goroutines, but I don't think it was the only one. There is also a ton of ergonomic efficiency to be had via green threads; like async/await, they're kind of a syntactic unlock that allows you to very liberally use goroutines/channels as a means of semantically modelling actors/state/data flow in your program, where doing that with regular OS threads is both dangerous and (given the start/stop overhead of threads on lots of other platforms) feels like kind of using a howitzer for a flyswatter. Golang saying "yeah, create a million of these, we don't care, it's 4 machine instructions per to start one" was pretty liberating for a lot of people.
The goroutine architecture also unlocked some nice performance at the OS level: GC latency improvements became easier, and some IO multiplexing efficiency a la epoll became almost free to implement due to the ability to cheaply move and suspend goroutines.
> Tokio's not a lightweight thread/green thread system
It's not green threads (and maybe not lightweight threads; there are so many names for things that may or may not be considered the same...) in the sense of stackful coroutines, but either way, it's a userspace implementation of multitasking on top of a smaller number of OS threads, which is the part that's relevant to my question.
Reasonable clarification. My response to your original "never do this" is a is a bit more specific then. There are two broad userspace concurrency models: cooperative and preemptive. Cooperative is a "sometimes the right call, sometimes more trouble than it's worth" situation; I can see what you're getting at there. But you're flat wrong about preemptive.
Cooperative concurrency: it depends. It's sometimes the right move, but has drawbacks. It's usually implemented to enable efficiency or partial parallelism of kernel-space code (IO). There are plenty of platforms that absolutely need it for that purpose: c10k, nodejs's rise to fame, all that. It behaves poorly when mixed with non-cooperative (blocking) code, and is poorly understood by many folks who expect userspace code to be parallelized as well. A common claim is that cooperative concurrency is harder to use than pre-emptive, but that's not true: some common things are harder, but some less common, but more dangerous things like race conditions tend to be much easier. Tokio and other cooperative systems that schedule onto fixed kernel threads can help with the hard parts a bit, but it's a partial mitigation at best.
Preemptive userspace concurrency: firmly disagree with your original point. Green threads are usually combined with userspace-code parallelism (e.g. goroutines scheduled onto multiple threads) and efficient kernelspace interactions (IO multiplexing). In the popular implementations of green threads I know well (Erlang and Go), this is an unalloyed blessing, in that it both removes many of the dangerous edges of threaded programming, facilitates increased efficiency, and lets users semantically model concurrency/parallelism with much less fear of e.g. creating too many threads, torn reads, etc. That's a big benefit: now we can model things like actors or CSP as subprograms written in procedural, synchronous, ordinary-looking code without getting Ph.Ds in operating system internals or having to carefully reason about complex interlocking layers of callbacks and futures to minimize risk of blocking or deadlock. In many ways, this brings the good parts of single-responsibility/what folks call "the UNIX philosophy" into a single process with minimal pain.
Rare cases where the userspace-concurrent runtime fights with your ability to interact directly with the OS, as with Docker's fork/thread/process-global-state challenges, do emerge. But I don't think those are primarily because of userspace concurrency; rather, they're because it's hard to do low-level things in a high-level, managed runtime. You can fork in the JVM, too, and you'll have problems. Different ones, because the runtime's different (e.g. endless RSS burn at idle due to the child GC moving parent objects), but problems all the same. Same for Python: forking in the presence of regular OS threads and/or a running event loop is equivalent to arming a minefield; doable but troublesome, just like it was for the Docker folks with Go.
To be fair, some of the same pains as the cooperative model appear with pre-emptive, too, when userspace concurrency systems are retrofitted onto runtimes that do a lot of other kinds of concurrency as well--but that's because of the hybrid/retrofit nature of the product, and not because of green threading as a concept. Java's rocky adoption of virtual threads is an example of that.
This advice comes from threads on Linux being second-class citizens. As TFA itself states, there are some syscalls that really should only be made in a single-threaded process which is frankly insane... but that's the legacy of starting with the processes-only model and patching it years later to have threads as well.
reply