When 'perfect' Code Fails
Key topics
The article discusses a bug caused by React's 'use server' directive implicitly converting a synchronous function to an asynchronous one, sparking a heated discussion about the pitfalls of JavaScript and the blurring of client-server boundaries.
Snapshot generated from the HN discussion
Discussion Activity
Very active discussionFirst comment
-63300s
Peak period
33
0-12h
Avg / period
10.6
Based on 53 loaded comments
Key moments
- 01Story posted
Oct 27, 2025 at 10:19 AM EDT
3 months ago
Step 01 - 02First comment
Oct 26, 2025 at 4:44 PM EDT
-63300s after posting
Step 02 - 03Peak activity
33 comments in 0-12h
Hottest window of the conversation
Step 03 - 04Latest activity
Nov 1, 2025 at 2:00 PM EDT
2 months ago
Step 04
Generating AI Summary...
Analyzing up to 500 comments to identify key contributors and discussion patterns
Want the full context?
Jump to the original sources
Read the primary article or dive into the live Hacker News thread when you're ready.
Since pages are, by default, SSR, you don't need to have the server call out to itself to run an endpoint to check permissions. Instead, the server should just run the function.
I'm pretty sure Next does some behind the scenes black magic optimizations and doesn't actually make an API request over the wire, but it's still running through some layer of abstractions (causing it to be async) to run the function when instead it could simply be a synchronous function if implemented properly.
These abstractions make sense if you know how to use them properly, but I honestly blame Nextjs for the whole server action confusion. I remember when they first came out and seeing how almost every single question on /r/nextjs was about being confused about server actions. All these footguns and confusion to avoid some boilerplate... I'm not sure if they've improved it since, but I've moved to Svelte and haven't looked back.
The documentation and tooling definitely got better and I don't think that such a situation is possible with the latest versions.
I just hope that some people who are still running the specific Next.js version won't fall into this as we did.
Tangential to the post, but mixing client-side and server-side code sounds like a recipe for disaster. There are already one too many services that perform authorization client-side, and I have a feeling making it harder to tell what runs where only makes the situation worse.
The problem without them in this day and age is how do you get your backend and frontend types in sync? You can manually update your types but this is tedious and prone to error (this is what we do at work), but having type errors immediately flagged on the client when you try to use a property wrong _is nice_.
The problem in this case is that it's so opaque that you have other issues like this one. Server Functions are unfortunately a low-level API in React and so their actual implementation is left up to frameworks.
- Write functional tests, not unit tests
- Not use compilers or other systems that do a lot of black magic (like changing the type signature of your functions (!))
But I concede that a thing that coerces your functions into async without adapting the corresponding calls is hilariously broken.
My general rule is to never mock or remove anything that has no side effects from the execution path of a test, even if it's some utility function that's already tested in isolation in its own test suite - trying to isolate every little bit of behaviour to be tested separately is just a bunch of extra work for questionable benefit.
I still call these tests "unit tests", and I think a lot of people do also. But there are the dogmatic people to whom only a test covering a single function with all dependencies mocked out is a true unit test.
But testing each class in the project with a Mockito-encrusted setup featuring cardboard bananas/gorillas/jungles—best practice in Java-land—seems tedious, and tedium is the enemy of a good testing strategy because programmers won't follow it. They'll bodge in a solution, get their teammates to lgtm it, and roll it out under the slightest bit of deadline pressure.
Oh God.
Never use JavaScript on the server side. The amount of bugs that can happen is insane. JavaScript is just a specification with varying implementations across various versions.
A proper language like Java, Go, or Rust ensures that your code will have fewer logical bugs of this type.
https://ashishb.net/tech/javascript/
I also support using whatever language you and your team prefer when you can. that's the glory of backend: no restrictions on what you can run. but sometimes you need to write client software, and those are strictly easier to manage in the platform's native tongue: Swift, Kotlin, JS, and so on
The function is unambiguously written, but the runtime functions differently, and this is not a language problem? This is incoherent; one of these statements is incorrect.
The language absolutely did something wrong, by trying to evaluate a non-boolean type as a boolean. That is a horrible footgun and JS is absolutely at fault for doing so.
Can JS succeed in the back end? I say yes, but not like this.
Under the hood it opens up an endpoint and the function calls it via a HTTP request.
You have to do your typical validations and auth logic on the server since anybody could call the endpoint without needing to use the function handle. There's a lot of magic and it seems to trip up a lot of people, including the people in the article.
A client side attack can just jump over that server call in a variety of ways, from mitm to running in a debugger to patching it out.
The answer is, it's not. This is terrible code and should never be written like this - the check can be bypassed client side, letting the user act as an owner no matter what the response claimed.
https://typescript-eslint.io/rules/no-misused-promises/
I also avoid technologies where the code I write is different from the code being executed. This is why I've been avoiding TypeScript. It performs code transformations which obfuscate my understanding of the logic which will be executed. With TS, the code I write is not the code which gets executed. This is scary to me. My code gets compiled into some messy junk-looking code and then that messy junk gets executed in ways I don't fully comprehend.
Not to be snarky, but as opposed to writing assembly? Where do you draw the line if you don't allow TypeScript (which is, with limited exceptions, only type erasure unless you specifically request polyfill), but allow other forms of compilation? Would you define JVM bytecode or IR as messy-looking junk code?
It's hard to see how a principled line could be drawn here.
The difference in the amount of value provided by a JS engine abstracting away from binary code vs TS transpiling to JavaScript is massive. The TS transpiler's value is extremely marginal by comparison, especially when considering the amount of trouble it brings.
Also, I'm familiar enough with the V8 engine and other ECMAScript-compliant engines that I have a good sense of how my code is being executed on there. I don't have to worry about TypeScript dropping support for a specific configuration setting in tsconfig and then having to spend hours trying to figure out why my code isn't working anymore.
And I agree with you on the second half :).
But the answer is actually batshit crazy.
C++ can also have a lot of tripwires, from overloaded operators (same with Swift, but people don't overload operators very often, in Swift. They compute properties, all the time, though).
The demonstration code illustrating the problem uses a file-level "use server" directive (and doesn't have other functions in the same file with the problem isOwner function); if they were using function level "use server" directives and it impacted a different function in the same file, I would say this is clearly surprising and unexpected (and even buggy) behavior, but this seems to be using a clearly documented feature and getting exactly what is advertised.
Title checks out, say no more.
> The snippet above was called as a _server_ function.
A semantic shift in what a function is.. lovely. Is this Javascript or Python?
https://en.wikipedia.org/wiki/The_Power_of_10:_Rules_for_Dev...
Generally wise advice. =3