frontpage.
newsnewestaskshowjobs

Made with ♥ by @iamnishanth

Open Source @Github

fp.

Start all of your commands with a comma

https://rhodesmill.org/brandon/2009/commands-with-comma/
193•theblazehen•2d ago•56 comments

OpenCiv3: Open-source, cross-platform reimagining of Civilization III

https://openciv3.org/
679•klaussilveira•14h ago•203 comments

The Waymo World Model

https://waymo.com/blog/2026/02/the-waymo-world-model-a-new-frontier-for-autonomous-driving-simula...
954•xnx•20h ago•552 comments

How we made geo joins 400× faster with H3 indexes

https://floedb.ai/blog/how-we-made-geo-joins-400-faster-with-h3-indexes
125•matheusalmeida•2d ago•33 comments

Jeffrey Snover: "Welcome to the Room"

https://www.jsnover.com/blog/2026/02/01/welcome-to-the-room/
25•kaonwarb•3d ago•21 comments

Unseen Footage of Atari Battlezone Arcade Cabinet Production

https://arcadeblogger.com/2026/02/02/unseen-footage-of-atari-battlezone-cabinet-production/
62•videotopia•4d ago•2 comments

Show HN: Look Ma, No Linux: Shell, App Installer, Vi, Cc on ESP32-S3 / BreezyBox

https://github.com/valdanylchuk/breezydemo
235•isitcontent•15h ago•25 comments

Vocal Guide – belt sing without killing yourself

https://jesperordrup.github.io/vocal-guide/
39•jesperordrup•5h ago•17 comments

Monty: A minimal, secure Python interpreter written in Rust for use by AI

https://github.com/pydantic/monty
227•dmpetrov•15h ago•121 comments

Show HN: I spent 4 years building a UI design tool with only the features I use

https://vecti.com
332•vecti•17h ago•145 comments

Hackers (1995) Animated Experience

https://hackers-1995.vercel.app/
499•todsacerdoti•22h ago•243 comments

Sheldon Brown's Bicycle Technical Info

https://www.sheldonbrown.com/
384•ostacke•21h ago•96 comments

Microsoft open-sources LiteBox, a security-focused library OS

https://github.com/microsoft/litebox
360•aktau•21h ago•183 comments

Show HN: If you lose your memory, how to regain access to your computer?

https://eljojo.github.io/rememory/
292•eljojo•17h ago•182 comments

Where did all the starships go?

https://www.datawrapper.de/blog/science-fiction-decline
21•speckx•3d ago•10 comments

An Update on Heroku

https://www.heroku.com/blog/an-update-on-heroku/
413•lstoll•21h ago•279 comments

ga68, the GNU Algol 68 Compiler – FOSDEM 2026 [video]

https://fosdem.org/2026/schedule/event/PEXRTN-ga68-intro/
6•matt_d•3d ago•1 comments

Was Benoit Mandelbrot a hedgehog or a fox?

https://arxiv.org/abs/2602.01122
20•bikenaga•3d ago•10 comments

PC Floppy Copy Protection: Vault Prolok

https://martypc.blogspot.com/2024/09/pc-floppy-copy-protection-vault-prolok.html
66•kmm•5d ago•9 comments

Dark Alley Mathematics

https://blog.szczepan.org/blog/three-points/
93•quibono•4d ago•22 comments

How to effectively write quality code with AI

https://heidenstedt.org/posts/2026/how-to-effectively-write-quality-code-with-ai/
260•i5heu•17h ago•202 comments

Delimited Continuations vs. Lwt for Threads

https://mirageos.org/blog/delimcc-vs-lwt
33•romes•4d ago•3 comments

Female Asian Elephant Calf Born at the Smithsonian National Zoo

https://www.si.edu/newsdesk/releases/female-asian-elephant-calf-born-smithsonians-national-zoo-an...
38•gmays•10h ago•13 comments

I now assume that all ads on Apple news are scams

https://kirkville.com/i-now-assume-that-all-ads-on-apple-news-are-scams/
1073•cdrnsf•1d ago•459 comments

Introducing the Developer Knowledge API and MCP Server

https://developers.googleblog.com/introducing-the-developer-knowledge-api-and-mcp-server/
60•gfortaine•12h ago•26 comments

Understanding Neural Network, Visually

https://visualrambling.space/neural-network/
291•surprisetalk•3d ago•43 comments

I spent 5 years in DevOps – Solutions engineering gave me what I was missing

https://infisical.com/blog/devops-to-solutions-engineering
150•vmatsiiako•19h ago•71 comments

The AI boom is causing shortages everywhere else

https://www.washingtonpost.com/technology/2026/02/07/ai-spending-economy-shortages/
8•1vuio0pswjnm7•1h ago•0 comments

Why I Joined OpenAI

https://www.brendangregg.com/blog/2026-02-07/why-i-joined-openai.html
154•SerCe•10h ago•144 comments

Learning from context is harder than we thought

https://hy.tencent.com/research/100025?langVersion=en
187•limoce•3d ago•102 comments
Open in hackernews

The bloat of edge-case first libraries

https://43081j.com/2025/09/bloat-of-edge-case-libraries
134•PaulHoule•4mo ago

Comments

01HNNWZ0MV43FF•4mo ago
3 of those 4 examples wouldn't exist in a statically-typed language like TypeScript
ants_everywhere•4mo ago
Yes this is just ad hoc runtime type checking
mewpmewp2•4mo ago
Alternative -> could just use something like Zod or have native stdlib Zod like type of thing to improve a lot on this type of edge case handling. Although if performance is key, then might question how much this constant parsing and validation would add on top, but in 99% cases probably wouldn't matter.
b_e_n_t_o_n•4mo ago
JavaScript had to implement these checks because you could pass a function literally anything and it was on the implementation to deal with it. Typescript has this same problem tbh.
Spivak•4mo ago
I mean they clearly don't have to, Python has the same problem of being able to pass anything—you don't see this kind of incredibly defensive programming. But Python set a culture of being for "consenting adults" where it's expected that callers will read the docs and ensure preconditions are satisfied, and if they don't whatever happens, happens.

It leads to less code, and more generalizable code. Maybe the caller does know what they're doing, who am I to say they can't.

pcwelder•4mo ago
>if they don't whatever happens, happens

What happens is you get an error. So you immediately know something is wrong.

Javascript goes the extra mile to avoid throwing errors.

So you've 3>"2" succeeding in Javascript but it's an exception in python. This behavior leads to hard to catch bugs in the former.

Standard operators and methods have runtime type checks in python and that's what examples in the article are replicating.

recursivecaveat•4mo ago
It's so strange to me that JS goes so far to avoid errors, mashing random stuff into strings or NaNs, misspelled variables just become undefined, etc. Then when your off-the rails program reaches `undefined()` it's "woah woah woah, that's clearly nonsense, stop program execution right this instant". I feel like I could respect it a little more if they committed to "a webpage should never crash under any circumstances" and there were just no exceptions in the language whatsoever.
watwut•4mo ago
It has nothing to do with the wish to not crash page.

Javascript was a prototype that was supposed to be refined and cleaned. Then management said "we will ship it now" and it was shipped as it was.

At the time, they thought it will be used for small things, minor snippets on web page. Webapps as we have them were not a thing yet.

umanwizard•4mo ago
> Typescript has this same problem

Don’t most languages have the same problem? Even C or Rust have escape hatches that let you override compile-time type checking and pass whatever gibberish you want to a function. How is Typescript any worse?

b_e_n_t_o_n•4mo ago
node main.js just runs the code. you could simply not run tsc, which plenty of people do. can you ignore all type errors in Rust?
nevir•4mo ago
The Rust equivalent is more like using `unsafe` and derefing raw pointers
b_e_n_t_o_n•4mo ago
Yeah and you can explicitly assert a null is a string in TS, but it's explicit. You can't build a Rust program without those asserts but it's trivial to skip the type checking for TS which is more of a linter than a type system.
chamomeal•4mo ago
I disagree that TS is more of a linter. But I definitely feel sympathetic to that perspective.

I’ll pull off the cleanest, nicest generic constraints on some component that infers everything perfectly and doesn’t allow invalid inputs, just for a coworker to throw @ts-nocheck on the entire file. It hurts

IshKebab•4mo ago
You can ignore the borrow checker at least by using mrustc. And some type checks at least could be optional. The fact that they aren't isn't really a fundamental property of Rust, it's just because it's easy to make them not optional.

In my experience nobody writes Typescript without checking the types. Unlike Python for example where it's not uncommon to have broken types. (And it's way more of a mess in general.)

umanwizard•4mo ago
If you don’t run tsc you are not really using typescript.
quotemstr•4mo ago
> you could pass a function literally anything and it was on the implementation to deal with it

So? Language is irrelevant here. What matter is the contract, the agreement that operates through time and space between caller and callee on behavior and expectations. Contracts exist outside of the type system. All a type system can do is enforce a subset of the contractual requirements.

For example, log(0) is undefined. If you write a function to compute log(x), you can (and should) document the function as having unspecified behavior when x is zero.

That's the contract.

The contract holds whether the function is spelled like this

  // x must be positive number
  function log(x) { return ... }
or

  // x must be positive
  function log(x: number): number { ... }
In the second example, we have the type system enforce the "is a number" rule, but at least in TypeScript, you can't use the type system to check that x is positive. In some languages you can, but even in those languages, programs have invariants the type system can't express.

Likewise, you could define a version of log that accepted, e.g. spelled numbers, like log("three"). You can express this weird contract with static or dynamic typing.

The nasty thing is that if early in the log library's history, before the author wrote down the contract or even had TypeScript, someone had written log("three") and gotten a type error, the log library's author might have "fixed" the "bug" by checking for spelled numbers as inputs. That would have been an expansion of the function's contract.

The log library author probably wasn't thinking about the long-term downsides of this expansion. He could have, even when our log() was a pure-JavaScript, just said "The contract is the input is a positive number. Your code is buggy."

But our hypothetical log library author didn't have the experience or good judgement to say no, so we ended up with the kind of "corner case bloat" the article's author describes.

0x20cowboy•4mo ago
I agree with the spirit of this, but I’d go one more step with the example:

export function clamp(value: number, min: number, max: number): number { return }

That is just adding an extra jump and entry on the callstack where you could just have done:

Math.min(Math.max(value, min), max);

Where you need it.

(The example used was probably just for illustration though)

chii•4mo ago
> you could just have done ...

i disagree with this particular example - it's actually a good use case for being a utility maths function in a library.

I mean, the very `min()` and `max()` function you used in the illustration could also have been used as an example, if you use the same logic!

mewpmewp2•4mo ago
I'm not native English, but I've done coding for way more than 10+ years, yet I would be able to understand and read quicker Math.min(Math.max(value, min), max) compared to registering in my brain what exactly "clamp" does.

In terms of DRY and cleanliness, yes "clamp" sounds awesome. But in terms of practicality and quick understanding? Math.min and Math.max there is such a frequent pattern that brain immediately understands what is being tried to do there, as opposed to exact meaning of "clamp" to me.

It may be just me though, clamp is not a word I frequently hear in English, and I see it in code sometimes, but not frequently enough to consciously register what it does as fast in my brain. Despite seeing code for probably 8h+ a day for the past 10 years.

If it was in std lib of JS, maybe then it would be better.

Like there is some sort of balance line on how frequently something is used and whether it's REALLY worth to abstract it. If it's in stdlib, use it, if it's not use it if truly it's pretty much always used.

xmprt•4mo ago
Maybe it's different since you're not native English but clamp is as intuitive for me as something like ceil or floor methods. It might not be as simple for someone who is ESL, probably because no major programming languages that I know of include clamp in their standard library like they do with min, max, ceil, floor.

I'm also coming from being a Go programmer where the native library is very barebones so a lot of custom utility methods like this are inevitable.

aero_code•4mo ago
FYI, some major programming languages have clamp:

C++: https://en.cppreference.com/w/cpp/algorithm/clamp.html

.NET/C#: https://learn.microsoft.com/en-us/dotnet/api/system.math.cla...

Java: https://docs.oracle.com/en/java/javase/24/docs/api/java.base...

Ruby: https://ruby-doc.org/core-2.4.0/Comparable.html#method-i-cla...

Rust: https://doc.rust-lang.org/stable/std/primitive.f64.html#meth...

webstrand•4mo ago
Even CSS has clamp. I have to periodically remind myself that Math.clamp is missing from the stdlib, it's much easier to read when you need to use a bunch. Math.min(lower, Math.max(value, upper)) is fine for a one-off, but when you need multiple instances of clamp in the same expression the extra parentheses become troublesome.
zahlman•4mo ago
I do find it strange how Python's min and max are builtin and don't require an import, but 'clamp' isn't provided at all. When I look at https://stackoverflow.com/questions/4092528 I feel the lack of an "obvious way to do it", since the simple approach has variant spellings and there are always people trying to be clever about it.
TRiG_Ireland•4mo ago
I am a native English speaker, and I've never heard of "clamp" before. Admittedly, most of my programming experience is in PHP, and I've rarely done anything with much maths.
pigbearpig•4mo ago
The author does acknowledge that in the "How it Should Be" section.
BobbyTables2•4mo ago
How are the download rates of these things so high?

Are there 1000 people running 100 CI pipelines/day where downloads aren’t cached?

GenerocUsername•4mo ago
Yes
kingstnap•4mo ago
Yep. True webscale is astronomical levels of inefficient and wasteful usage of bandwidth and CPU.
nerdponx•4mo ago
It doesn't help that container images typically require you to "opt in" to sharing a cache with the host system. Docker even has a special file mount type for it. I find that people on average don't have a lot of interest in making such things work, they see that it's not available right in front of them and shrug and say "oh well" because ultimately it doesn't hurt them much.
phito•4mo ago
Yeah I'd say most people view it as such, until they hit (docker hub) rate limits.
BobbyTables2•4mo ago
Local caching is also a royal pain when one has multiple CI runners, besides the hassle of Docker related configuration.

Do they cache to shared storage and worry about concurrency? Do they only have local storage in various differing states?

zahlman•4mo ago
It seems to happen in the Python world, too. Check out the stats for the most commonly downloaded packages from PyPI (https://pypistats.org/top), and also consider which packages are on the list.

(Pip used to be right up there next to setuptools. It's still pretty popular — https://pypistats.org/packages/pip — but uv has clearly changed the equation.)

swiftcoder•4mo ago
Yep, every single transitive downstream consumer pulls a fresh copy on every CI run. It's kind of ridiculous, and it's like this in every modern ecosystem.

For example, this is a completely empty rust crate, which I started years ago and never released, and it's still downloaded multiple times per day... https://crates.io/crates/ruble

fainpul•4mo ago
I'm confused. Why did you publish this? Who "uses" it?
swiftcoder•4mo ago
I published it purely to placeholder the name (and then life got in the way, and I never released the actual library). Nobody uses it at all, but some CI system is still polling at regular intervals...
qludes•4mo ago
So someone could reserve all the pronounceable crate names?
swiftcoder•4mo ago
Yeah, it’s a known problem in the (non-namespaced) crates.io ecosystem. If someone else wants this name for their Bluetooth LE crate, I’ll happily hand over the keys, but obviously not every crate-squatter is so principled
fainpul•4mo ago
But if no other crate depends on your crate and nobody directly uses it, why would any CI system download it?
swiftcoder•4mo ago
I think some of the CI systems just monitor all published crates. For example, docs.io potentially pulls it to see if there is any new documentation
BobbyTables2•4mo ago
Wonder if someone has an internal crate named the same, but yours is getting pulled down for some tertiary use (license check, etc).

Stuff like Docker that defaults to downloading things from Internet repos is kinda scary. There isn’t always a clear dividing line between the thing as a “tool” and the thing as a “repo”.

At least with “git”, I know they I’m not going to end up cloning an artifact from an Internet repo just because I made a small typo…

dwoxctbvgq•4mo ago
Could it be caused by Crater runs?

https://github.com/rust-lang/crater

ModernMech•4mo ago
Oh so you're the reason we got btleplug!:P

https://github.com/deviceplug/btleplug

fwiw the downloads are probably automated crates.io indexers.

mrweasel•4mo ago
Have you noticed how much stuff breaks when Github or NPM is down? People do not cache, they do not mirror, modern development for large parts are just people YOLO-ing it.
khaledh•4mo ago
This is why every language needs a good standard library, which unfortunately JavaScript never had.
franciscop•4mo ago
I've tried to optimize to avoid these before. The problem is that these libraries are normally not used directly, they are usually pretty low in the dependency tree (root?). The farther you go down in the dependency tree, the older the packages are, where rightfully there's some reluctance to update things for the sake of updating things, risking breaking a stable library. It's a tricky thing and not so easy as to just say "don't use npm's `is-array`, use `Array.isArray()` instead", since every JS/TS package author writing code in 2025 knows that already.

It'd be a lot more productive to ask the authors of the (normally bigger, user-facing) libraries that you use nowadays to avoid using these libraries with deep and outdated dependencies. e.g. I stopped using Storybook because of this, but I'm happy to see that they've been working on cleaning up their dependency tree and nowadays it's much better:

https://storybook.js.org/blog/storybook-bloat-fixed/

tobr•4mo ago
I would be a little upset if a clamp function could throw a validation error when I passed only numbers. I’ve always written clamp to work even if min is larger than max. Basically, clamp comes down to sorting the three numbers and returning the middle one. I’ve never thought of that as an edge case, it just seems like a more general way to think of the operation that gives you less surprises since every possible input has a well-defined output.
nerdponx•4mo ago
That's clever, but it's not a common perspective so the behavior you propose might be quite surprising indeed. And moreover it only applies to this one particular function. The general principle still applies in general.
IshKebab•4mo ago
> clamp comes down to sorting the three numbers and returning the middle one

That is a really weird definition of clamp. I would expect to consistently get either min, max, or an error.

I bet Rust doesn't use your weird idea... Yep. Fully sane implementation:

https://docs.rs/num/latest/num/fn.clamp.html

tobr•4mo ago
It’s not a definition, it’s an implementation. A definition would be something like ”constrain a number to an interval defined by two other numbers”. That happens to be identical to sorting and picking the middle value. The only difference vs OP’s clamp is that it gives a consistent and predictable result for ”inverted” intervals where min > max, instead of arbitrarily deciding how to collapse the interval.
IshKebab•4mo ago
It is a definition. You've defined the behaviour when min>max and you've chosen a weird way to define it.
pash•4mo ago
> We should be able to define our functions to accept the inputs they are designed for, and not try to handle every possible edge case.

Oh, look, somebody just re-discovered static typing.

whilenot-dev•4mo ago
How do you type the min > max constraint though?
kuruczgy•4mo ago
Well, if your language has a sufficiently strong type system (namely, dependent types), you can take proofs of some properties as arguments. Example in Lean:

  def clamp (value min max : Float) {H : min < max} : Float := ...
whilenot-dev•4mo ago
Sure, but the author picked TypeScript nonetheless. TypeScript is not a runtime, but a mere type checker - JavaScript is the runtime and a highly dynamic language. This detail got somehow completely lost in the article, but is IMHO the main culprit why such validations aren't bad, or sometimes even preferred.

The article also skipped over the following related topics:

  - When would you wrap errors from lower levels as your own?
  - What does "parse don't validate" mean when a TypeScript library gets transpiled to JavaScript?
chongli•4mo ago
You don’t need a runtime for dependent types. After type checking the types get erased during compilation.
whilenot-dev•4mo ago
Nobody would question that, but publishing a JavaScript library means that anyone using plain JavaScript can make use of it. Even though you aren't ever in control of the toolchain of your library's users, it's still your responsibility - as library author - to take that differences into account. If you'd transpile your library from Idris to JavaScript and publish it, these validations just can't be neglected at runtime. Type systems are just another model of the world at runtime.
thomasmg•4mo ago
Many libraries throw an exception, panic, or silently swap the parameters at runtime.

To detect this at compile time, you would need either min and max to be known at compile time, or a type system that supports value-dependent types. None of the popular language support this. (My language named 'Bau', which is not popular of course, support value-dependent types to avoid array-bound checks.)

Animats•4mo ago
In a compiled language, it takes one or two machine instructions to test

    assert!(b >= a);
Works in C, C++, Go, Rust...

Amusingly, nowhere in the original article is it mentioned that the article is only about Javascript.

Languages should have compile time strong typing for at least the machine types: integers, floats, characters, strings, and booleans. If user defined types are handled as an "any" type resolved at run time, performance is OK, because there's enough overhead dealing with user defined structures that the run time check won't kill performance.

(This is why Python needs NumPy to get decent numeric performance.)

whilenot-dev•4mo ago
Sure, use macros in function bodies. That won't affect the function signature in any meaningful way for the type checker and remains a check at runtime only, doesn't it?

It seems like the point of the article was to not do that though, contrary to my own opinion, and I just wonder why...

IshKebab•4mo ago
Some languages can do it, but most can't do you either throw an error or do something reasonable. In this case just returning min would be reasonable.
roenxi•4mo ago
If we're trying to solve problems with good design, use endpoint1 and endpoint2 and then the function sorts them. Having max and min is itself a bad design choice, the function doesn't need the caller to work that out. Why should the caller have to order the ends of the interval? It adds nothing but the possibility of calling the function wrong. So in this this case:

    export function clamp(value: number, endpoint1: number, endpoint2: number): number {
      return Math.min(Math.max(value, Math.min(endpoint1, endpoint2)), Math.max(endpoint1, endpoint2));
    }
whilenot-dev•4mo ago
So an implicit fallback, but make it explicit through good design. Haven't even thought about this as a principle, since type checking persuades me to avoid anything implicit, thank you!
vanviegen•4mo ago
That would lead to unpleasant surprises. When calling the function from some loop and when the bounds are inclusive, it's pretty common for (correct) edge cases to exist where you'd call the function with end===start-1. The function would do the right thing by returning an empty set. You'd get duplicate/unexpected records in some cases, that may be hard to debug.

It seems like your approach is just trying to ignore programmer errors, which is rarely a good idea.

roenxi•4mo ago
I have no horse in the race and would usually just implement my clamp function the way the article does. However, if the clamp function clamping a number is an unpleasant surprise, I'm not going to accept that it is the fault of the clamp function. This hypothetical loop is buggy code and should be rewritten to expect clamp to clamp.

It is a special type of madness if we're supporting a reliance on implementation specific failure modes of the clamp function when someone calls it with incoherent arguments.

RHSeeger•4mo ago
> This hypothetical loop is buggy code and should be rewritten to expect clamp to clamp.

But it makes it harder for the developer to recognize that the code is buggy. More feedback to the developer allows them to write better code, with less bugs.

Your argument could be made in the same way to claim that static typing is bad; because the caller should be calling it with the right types of values in the first place.

roenxi•4mo ago
> But it makes it harder for the developer to recognize that the code is buggy. More feedback to the developer allows them to write better code, with less bugs.

But the feedback is unrelated to the bug, the bug here is that the programmer doesn't understand what the word "clamp" means and is trying to use the function in an incorrect way. Randomly throwing an exception on around 50% of intervals doesn't help them understand that, and the other 50% of the time they're still coding wrong and not getting any feedback. I'm not against the clamp function doing whatever if people want it to, it can make coffee and cook pancakes when we call it for all I care. But if it just clamps that is probably better. It isn't a bug if I call clamp and don't get pancakes. It also isn't a bug if I call clamp and it remains silent on the fact that one argument is larger than another one.

Feedback has to be relevant. It'd be like having a type system that blocks and argument that isn't set to a value. If the programmer provides code that has bugs, it'll give them lots of feedback. But the bug and the error won't be related and it is effectively noise.

atombender•4mo ago
This maps poorly to the mathematical concept of a closed interval [a, b], which can be written a ≤ x ≤ b for a set of x. An interval where a > b is usually a programming error.

To ensure only valid intervals are supported at the type system level, the function could perhaps be redefined as:

    function clamp(n: number, i: Interval<number>): number
Of course, you need to deal with the distinction between closed and open intervals. Clamping really only makes sense for closed ones.
roenxi•4mo ago
It maps very well onto the mathematical concept of a closed interval [a, b] where a and b are endpoints of the interval though. You're adding a constraint for no logical reason and it happens to be very hard to represent in a basic type system.

> An interval where a > b is usually a programming error.

If you want it to be, sure. Anything can be a programming error if the library author feels like it. We may as well put all sorts of constraints on clamp, it is probably an error if the caller uses a large number or a negative too. It is still bad design in a theoretical sense - the clamp function throws an error despite there being an obvious non-error return value. It isn't hard to meaningfully clamp 2 between 4 and 3.

Smaug123•4mo ago
Don't send `start` and `end`; send `start` and `lengthOfInterval`. (Whether that's a good idea in a given API is another question.)
amavect•4mo ago
That trades "min <= max" with "min + interval <= MAXINTEGER":

  if(number < min) return min;
  else if(number < min + interval) return number; // "if(number < max)"
  else return min + interval; // "return max"
croes•4mo ago
If min and max aren’t user inputs maybe we should trust the developer that they know what they are doing.
fph•4mo ago
You define an Interval type, and check the constraint in its constructor.
sfn42•4mo ago
You don't need to. One if statement to check that is not a problem. The problem occurs when you have a bunch of other ifs as well to check all kinds of other stuff that a type system would handle for you like nullability, incorrect types etc.

Personally I just write JS like a typed language. I follow all the same rules as I would in Java or C# or whatever. It's not a perfect solution and I still don't like JS but it works.

quotemstr•4mo ago
> Oh, look, somebody just re-discovered static typing.

If you're going to smug, at least do it when you're on the right side of the technology. The problem the article describes has nothing to do with the degree of static typing a language might have. You can make narrow, tight, clean interfaces in dynamic languages; you can make sprawling and unfocused ones in statically-typed languages.

The problem is one of mindset --- the way I'd do it, an insufficient appreciation of the beauty of parsimony. Nothing to do with any specific type system or language.

Rumudiez•4mo ago
Yep, I’ve seen this in Swift with a dozen overloads for functions and class initializers to support umpteen similar, but different, types as input. Sloppy schema design reveals itself in combinatorial explosions of type conversions
quotemstr•4mo ago
Yes. I don't understand why HN reacts with such froth to the suggestion that this problem runs deeper than type systems.
xg15•4mo ago
I think there is an important observation in it though: That dynamic, loosely-typed languages will let you create code that "works" faster, but over the long run will lead to more ecosystem bloat - because there are more unexpected edge cases that the language drops onto the programmer for deciding how to handle.

Untyped languages force developers into a tradeoff between readability and safety that exists only to a much lesser degree in typed languages. Different authors in the ecosystem will make that tradeoff in a different way.

renmillar•4mo ago
In my experience, this only holds true for small scripts. When you're doing scientific computing or deep learning with data flowing between different libraries, the lack of type safety makes development much slower if you don't maintain strict discipline around your interfaces.
jmull•4mo ago
Static and runtime type checks are each specified in similar code. The bloat's the same.
ModernMech•4mo ago
For this particular example where they have to do a runtime parse to do the string to number conversion, yes. But in general static type checks are resolved at compile time, so they incur neither runtime cost nor do they increase the size of the resulting code. This is the primary benefit of doing static type checking.
DarkNova6•4mo ago
Yep…

‘’’ export function clamp(value: number | string, min: number | string, max: number | string): number { if (typeof value === 'string' && Number.isNaN(Number(value))) { throw new Error('value must be a number or a number-like string'); } if (typeof min === 'string' && Number.isNaN(Number(min))) { throw new Error('min must be a number or a number-like string'); } if (typeof max === 'string' && Number.isNaN(Number(max))) { throw new Error('max must be a number or a number-like string'); } if (Number(min) > Number(max)) { throw new Error('min must be less than or equal to max'); } return Math.min(Math.max(value, min), max); } ‘’’

WhyNotHugo•4mo ago
I’ve seen something similar happen in Rust as well (and I do consider it an antipattern).

Some libraries take a `TryFrom<RealType>` as input, instead of RealType. Their return value is now polluted with the Error type of the potential failure.

This is a pain to work with when you’re passing the exact type, since you basically need to handle an unreachable error case.

Functions should take the raw types which they need, and leave conversation to the call site.

_bent•4mo ago
It's definitely pretty annoying, though not because of the errors. Actually the errors might be the biggest benefit even. If the conversion fails I can't continue with the function call.
_bent•4mo ago
It's annoying, but not for the error handling. To the contrary, I think the error handling is actually improved by this pattern. If you manually convert beforehand you easily run into working with a Result<Result<T, E>, E>.

What I find annoying about the pattern is that it hinders API exploration through intellisense ("okay, it seems I need a XY, how do I get one of them"), because the TryFrom (sort of) obscures all the types that would be valid. This problem isn't exclusive to Rust though, very OO APIs that only have a base class in the signature, but really expect some concrete implementation are similarly annoying.

Of course you can look up "who implements X"; it's just an inconvenient extra step.

And there is merit to APIs designed like this - stuff like Axum in Rust would be much more significantly more annoying to use if you had to convert everything by hand. Though often this kind of design feels like a band aid for the lack of union types in the language.

quotemstr•4mo ago
Why not teach rust-analyzer this pattern as an ad hoc heuristic to use when finding completions?
phanimahesh•4mo ago
The errors in the result might be different types and need different handling, so nested result might not be undesirable
jerf•4mo ago
They've discovered how to write dynamically-typed code correctly, or at least, a philosophy of it. It's not "discovering static typing" because that doesn't come up in static type languages. (Typescript is, for this particular purpose, still effective a dynamically typed language.)

I remember writing Python and Perl where functions largely just aimed you passed them the correct types (with isolated exceptions where it may have made sense) years before JavaScript was anything but a browser language for little functionality snippets. It's a dynamic language antipattern for every function to be constantly defensively checking all of it's input for type correctness, because despite being written for nominal "correctness", it's fragile, inconsistent between definitions, often wrong anyhow, slow, and complicates every function it touches, to the point it essentially eliminates the advantages of dynamic language in the first place.

Dynamic languages have to move some responsibility for being called with correct arguments to the caller, because checking the correctness of the arguments correctly is difficult and at times simply impossible. If the function is called with the wrong arguments and blows up, you need to be blaming the caller, not the called function.

I observe that in general this seems to be something that requires a certain degree of programming maturity to internalize: Just because the compiler or stack trace says the problem is on line 123 of program file X, does not mean the problem is actually there or that the correct fix will go there.

iwontberude•4mo ago
I thought parent commenter was making a joke but thanks to you I am not sure anymore.
eduction•4mo ago
Incredibly shallow post. The level of validation and defensiveness in any piece of code will generally have to do with how close to the edge of your application it is — that is, how close to the input.

Functions near the core can assume more and code less defensively than those near the edge.

Libraries shouldnt be edge case first? What? /What kind/ of libraries? You really can’t imagine a library where it makes sense to code very defensively?

Also the post doesn’t even use “edge case” correctly. Definitionally you can’t have an edge case before you’ve defined the acceptable input. In their clamp function the edge case would be something like a large number on the border of graduating from long to BigInt or something. Edge case does not mean invalid input. Edge case is a rare/extreme input that is valid.

MathMonkeyMan•4mo ago
This is what undefined behavior is for. You specify a contract for your function, and then the implementation of the function assumes that the contract is satisfied. It does not check, and does not try to diagnose errors.

It's not about performance, it's about separating concerns: the caller handles the not-a-number case, so the callee doesn't have to.

Then you add optional runtime checking of the contract, preferably in a different build mode (or a different version, e.g. "my-lib1.2-debug"), to get sensible diagnostics quickly in tests and canary deployments. The checks are redundant by definition. Defensive programming.

sesm•4mo ago
Also known as "garbage in - garbage out" approach. That's how Clojure standard library is designed.
pwdisswordfishz•4mo ago
And C/C++.
chowells•4mo ago
Now, consider that that the program might be rejected for being incorrect, without being run. Wouldn't that be even better?
MathMonkeyMan•4mo ago
All other things being equal, yes. Static types work well for constraints on function parameters. "Parse, don't validate" and all that.

Some contracts are more difficult to express, though. The worst kind involve sequences of behavior, e.g. "start() must have been called," or "must have been obtained from a previous call to 'allocate()' on the same object."

Even value parameter constraints can be tricky. "The input must be sorted."

I haven't studied type systems, but I like the idea of values picking up attributes as they pass through functions. Imagine a "sort" function that bestows its output with the "sorted" type attribute. But then what happens when you append to that value? Is it still sorted?

At some point it's convenient to be permissive with the parameter's static type and to constrain the caller with a contract.

sebtron•4mo ago
> At this point, it seems clear to me we’ve just poorly designed our function. It solely exists to clamp numbers, so why would we accept strings?

To me it sounds like you are using a poorly-designed language

nenenejej•4mo ago
Yes. Go for example doesnt need an is-nunber, nor do you need to worry about if a validation throws an exception and unwinds your call stack (or not)
quotemstr•4mo ago
> To me it sounds like you are using a poorly-designed language

Not language specific. A C example that comes to mind is checking every non-nullable pointer parameter in every function for NULL and reporting an error instead of just letting the program crash. (Please, don't do that: just let contract violations produce crashes.)

The article's author describes a problem of experience and spirit, not a language wart.

Turskarama•4mo ago
Let me blow your mind for a second: this problem is not insurmountable in language design, C is not a perfect language, and nulls are bad design.
hnlmorg•4mo ago
But the problem is literally a direct result of language wart.

The nullable pointer you’ve given is another great example of language wart. And one that’s often highlighted as a specific criticism against Go lang.

quotemstr•4mo ago
No, the problem is a result of sloppy thinking --- or no thinking --- about function contracts.
hnlmorg•4mo ago
Humans error. The point of programming languages is to provide an abstraction to make development faster and less error prone than writing stuff in lower level languages or even assembly.

If your language requires you to manually track function contracts then that’s a problem with the language itself. Not the developer.

ModernMech•4mo ago
Indeed, you've pointed out a second language in the class of "poorly-designed languages"
whatevaa•4mo ago
C crashes with worst possible error, equivalent of "Something went wrong". Good luck debugging. Only core dumps reveal more information.
quotemstr•4mo ago
Nothing beats the old Unix tools. From https://www.gnu.org/fun/jokes/ed-msg.en.html

"

  golem$ ed

  ?
  help
  ?
  ?
  ?
  quit
  ?
  exit
  ?
  bye
  ?
  hello?
  ?
  eat flaming death
  ?
  ^C
  ?
  ^C
  ?
  ^D
  ?
Note the consistent user interface and error reportage. Ed is generous enough to flag errors, yet prudent enough not to overwhelm the novice with verbosity."
mrweasel•4mo ago
Can you point to a language that's doesn't have poorly designed aspects to it?
afc•4mo ago
I wrote a somewhat related article, after running just this Friday into two blocks of code that were deliberately causing invariant violations (akin to the "max < min" situation) to be ignored silently: https://alejo.ch/3gk - Fail loudly: a plea to stop hiding bugs
rrauenza•4mo ago
I think of this as fail fast. Fail immediately so its easy to root cause the failure and not have it be hidden and cause more obscure side effects later.
moomin•4mo ago
Problem is, there’s a well known principle of library design: any behaviour you exhibit eventually becomes something someone relies upon. You can choose to live with silent breakage or you can validate your inputs within an inch of their lives. Next, with the best will in the world, even with a small library, you quickly discover a wide range of use cases that should reasonably be supported by your library. So all libraries end up complex, not just because of validation.

Yes, static typing would help with some of this, but very much not all.

goranmoomin•4mo ago
The post is complaining about a library for a problem that javascript had 12 years ago, was not a thing for 7 years, and the ecosystem moved on. Typescript was not a thing back then. (or more exactly, it was a small thing out of the all too many transpile-to-js languages, at least)

Yes, having a library named is-number looks very stupid until you look at the state of javascript in 2014. Look at issue is-number#1[0] if you’re interested.

The library is-arrayish exists because array-like objects are actually a thing in javascript.

About is-regexp: the author mentions that their library supports cross-realm values because it’s useful, but then says that it’s an edge case that most libraries don’t need to care about? The whole reason that the library exists is to cover the edge cases. If not needed, yes the consumers of the library would have just been using the simple instanceof RegExp check.

If you’re arguing that there are consumers of those libraries that are wrong, the post might at least make sense – the presented case here is that the writer of the clamp function is stupid, not the other way around. Having a function that determines if a string is a number is not stupid; it’s importing that function and creating a clamp function with the wrong type signature part that’s stupid. Especially when it’s 2025 and typescript is universal.

All of the libraries that are mentioned are like 10 years old at this point. I don’t think we have to beat the dead horse one more time.

[0]: https://github.com/jonschlinkert/is-number/issues/1

throwaway290•4mo ago
> the presented case here is that the writer of the clamp function is stupid, not the other way around

So are you saying the author updated the implementation and added deprecation warning?))

And author is not "stupid". More like "strategic". popular npm package = money. this is why everybody falls over to write leftpads and stuff.

goranmoomin•4mo ago
> So are you saying the author updated the implementation and added deprecation warning?

As far as I understand, the author wrote the clamp function by themselves. There is no clamp library that the author is arguing against.

In fact, it seems the library ‘clamp’ in npm is a library that does exactly what the author wants – no validation, assuming that the value is a number, and just returning a number.[0]

[0]: https://github.com/hughsk/clamp/blob/master/index.js

throwaway290•4mo ago
I mean isnumber, isarray, that sort of stuff you were talking about. Maybe clamp is the exception if there is no builtin way to do it in js
whatevaa•4mo ago
What money did leftpad get?
phito•4mo ago
I've only seen these kind of libraries in JavaScript. They are a direct result of poor language design.
procaryote•4mo ago
Nothing in javascript makes you have tiny pointless libraries that check if something is odd or if something is an array(ish). This is just a quirk of javascript developer culture

At some point someone went "let's decouple as much as we can! A library should be just a single function!" and we've spent a lot of time since then showing why that's quite a bad idea

The lack of types perhaps inspires some of these functions masquerading as libraries, but they're often trivial checks that you could (and probably should) do inline in your less pointless functions, if needed.

pornel•4mo ago
Properties of a language shape the tooling and culture that develops around it.

JS has exploded in popularity when Internet Explorer was still around, before ES6 cleanup of the language. JS had lots of gotchas where seemingly obvious code wasn't working correctly, and devs weren't keeping up with all the dumb hacks needed for even basic things. Working around IE6's problems used to be a whole profession (quirksmode.org).

Browsers didn't have support for JS modules yet, and HTTP/1.1 couldn't handle many small files, so devs needed a way to "bundle" their JS anyway. Node.js happened to have a solution, while also enabled reusing code between client and server, and the micro libraries saved developers from having to deal with JS engine differences and memorize all the quirks.

procaryote•4mo ago
In other languages people build abstraction libraries for that, like apache portable runtime that gives you a consistent api for most things you need to build a web-server, using just one dependency. That would also save you needing to memorise all the micro libraries needed to work around the relevant quirks.

Splitting it into a library per quirk seems like an unforced error in that context.

One could have excused it as being a way to keep the code size down, but if you use npm you also usually use a build step which could drop the unused parts, so it doesn't really hold water

pornel•4mo ago
All of the differences are attributable to the language.

The Apache runtime isn't sent over the network every time it's used, but JS in the browser is.

JS ecosystem has got several fix-everything-at-once libraries. However, JS is very dynamic, so even when "compiled", it's very hard to remove dead code. JS compiling JS is also much slower than C compiling C. Both of these factors favor tiny libraries.

Abstractions in JS have a higher cost. Even trivial wrappers add overhead before JIT kicks in, and even then very few things can be optimized out, and many abstractions even prevent JIT from working well. It's much cheaper to patch a few gaps only where they're needed than to add a foundational abstraction layer for the whole app.

procaryote•4mo ago
I have a vague memory of using dead code removing javascript toolchains pretty long ago, like the closure compiler

I'm not sure the dependency tree madness actually translates to smaller code in the end either, given the bloat in the average web app... but to be fair it would be perfectly plausible that javascript developers opted for micro-libraries motivated by performance, even if it wasn't the effect of that decision.

larusso•4mo ago
Over 10 years ago I was a bit more invested in the ruby environment and the whole duck typing thing. I really liked dynamically typed languages. One thing I remember was when learning rsync that other libraries loved to write specs where all kinds of types and values gets thrown at a function to see how it behaved. Since these languages have no compile step one needs to check how they do at runtime. I remember I picked up a pattern to check if something is looking like a string or number or can be turned into Omer etc. It definitely blows up your test specs but also makes your awesome function even more flexible for the future. Hey you never know what you throw at it tomorrow. Maybe you need to call it with an a number array in string format. I stopped doing that and concentrated to test for the types I expect. Which really breaks with the typeless nature of a language but that is more philosophical. Needless to say I think all these helper packages are way overboard. There is also the tendency under programmers to really “never repeat yourself”. Which ends up in a utility function that has to be introduced into every library or project and starts to grow because of different edge cases from all the different use-cases. Let’s add some flags and overloads. Not excepting the fact that it is sometimes cleaner to just have a copy with special adjustment of code then desperately trying to generalize it.
zahlman•4mo ago
Two common ideas I see ITT are that the problem is caused by JS not having either a) static typing or b) a large standard library.

I don't understand this. Python lacks static typing, but it also doesn't have a need for the kind of type-checking that is-number and is-array perform — it's how Python's dynamic typing already works. What JavaScript is missing is strong typing: i.e. it's "fault tolerant" by performing implicit conversions everywhere and minimizing the chance of control flow ever being disrupted by an exception, presumably intended to maximize the chance that the user sees some kind of DOM update rather than the page ceasing to function. Python sidesteps these issues by just raising `TypeError` instead, and by making proper consideration of exception handling an expected part of the programmer's job. The boilerplate in TFA's opening example is essentially what the Python runtime already does.

Similarly, Python has a large standard library, but it's irrelevant to the problems described. The Python standard library doesn't solve the problem of determining whether something is "a number" or "an array"; trying to use it that way does. The Python standard library doesn't solve the problem of determining whether something is "a regex"; if anything, JS' functionality here is more native because you don't need the standard library to create a regex.

As for the "pascalcase" example, the standard library isn't really helping Python there, either. There are multiple third-party libraries for string-casing operations in Python; there are some things you can do first-party but they're almost all methods of the built-in string type rather than part of the standard library. Aside from some useful constants like "a string with all the ASCII letters in it", the `string` standard library implements two failed attempts at string formatting routines that weren't popular even when they did solve real problems, and a "capwords" function with subtly different semantics from the "title" method of strings. Which has existed since at least 2.0.

(Granted, Python documentation has separate "library" and "language" categories, and classifies the built-in types and their methods as "library". But when people talk about the importance of a "large standard library" in a language, I generally understand that they're thinking of code that has to be explicitly pulled in.)

Havoc•4mo ago
The wild part is that a post about bloat concludes with here is more crap to install to visualize your JS stack.

Starting to think that JS based front end is a trashfire top to bottom. Not this framework vs that framework...the entire thing

meindnoch•4mo ago
clamp should actually accept anything comparable. E.g. clamp("foo", "bar", "baz") should return "baz".
Minor49er•4mo ago
Why wouldn't it return "foo" instead?
meindnoch•4mo ago
Because "bar" < "baz" < "foo". The function signature is: clamp(value, min, max)
gourlaysama•4mo ago
Putting API contracts aside, the problem is also that people use a package manager as if it was a code snippet manager.

As in, "how do I check if a string starts with a shebang" should result in some code being pasted in your editor, not in a new dependency. There is obviously a complexity threshold where the dependency becomes the better choice, but wherever it is it should be way higher than this.

hawk_•4mo ago
This is the gap that LLMs have been filling quite well now in my experience. There are these little isolated tasks that are described easily in natural language for which the idiomatic code snippet can be created and importantly modified by LLMs as needed. No need to pull in pesky dependencies. Though when to switch over to a full blown dependency is still a judgement call.
2muchcoffeeman•4mo ago
This is such a weird comment to me. These are trivial functions that could be faster to code than describe. I’d hope people aren’t relying on AI for functions as simple as clamp.

I mean, people have been writing these simple functions over and over for decades when they just needed one or two things that importing a library wasn’t needed. I wasn’t aware there was a gap to be filled.

hawk_•4mo ago
Not clamp per se, but there are enough tiny snippets which can have off by one or other issues not relevant to when you think about the problem description.
qazxcvbnmlp•4mo ago
I agree:

Senior engineer: "you shouldn't have to use llm to do simple things" Their boss: "hey I need you to write this thing in Go for performance reasons" Senior engineer: "you shouldn't make me learn new languages, js is plenty performant for this task just install a new server. why are they making us use all these new tools? everything is new again, blah blah excuses excuses"

vs.

Boss: "hey can you write this in go" Vibe Coder: "write this loop to ignore the first semicolon in this response", and then proceeds to move on with life.

It seems like when people evaluate the merits of what a code generation ai agent should and shouldn't do, they leave out a lot of their implicit assumptions about how project should work and their own theory of mind around coding.

2muchcoffeeman•4mo ago
Sure. Then all the common tools that you need should be bundled into a library. I should not be importing 1000 string manipulation libs with a single function each.
whatevaa•4mo ago
Like lodash?
wavemode•4mo ago
Clamp is a simple enough function that if you simply typed its name and arguments, copilot would autocomplete its body. No prompting needed, in that case.
lenkite•4mo ago
Man, people are prompting and copying LLM output for min/max functions. I saw my junior do this in a meeting - it was apparently too much work to lookup the standard library. When most people work with a LLM, the thinking part of their brain gets switched off for some reason.
vitonsky•4mo ago
Well, somebody must validate data on top level. So that's fine to have packages like `is-number`/`is-arrayish` etc.
croes•4mo ago
Those kind of function are like a second developer who doesn’t trust the developer of the program.

Have a little faith they validate the input and give the right min and max before they call your function or let them fail to learn.

kamma4434•4mo ago
I think the author has this wrong. Handling a ton of edge cases so I don’t have to do it myself is a good reason to use a library. Importing an endless tree of deps I have no control of - that is the issue.
hk__2•4mo ago
Interesting but the article never takes the time to really explain what exactly is the problem with using these libraries.
MagicMoonlight•4mo ago
Notice how this slop doesn't happen in real languages like C++ or Java. It's only the slop languages.

You don't need a "is it a number" package in java, because the function cannot be passed a string, because it actually has a type system. All this slop has come about because meme developers refuse to accept the truth - programming languages have proper syntax for a reason.

PaulHoule•4mo ago
Partially true.

A lot of times you have something in Java which could be

   ReturnValue someMethod(Parameter p) {
      …
      var x = anotherMethodThatMightThrowAnException(q);
      …
   }

and I’m going to argue that often that’s good enough but a lot of people think it isn’t so they might put 20 lines of code above that line to head off that exception or they might put it in a try-catch block [1] to respond to it or wrap it. Those unhappy paths bloat your code the same way as the guards in the blog post, make it unclear how the happy path works at a glance (place for bugs to hide in the happy path) and obviously create opportunities for bugs in the unhappy path.

I’ll argue that (1) error handling is a function of the application as a whole or of a “unit of work” (e.g. database transaction) so unless you’re at the top level method of a unit of work you should “let it throw”, (2) try-finally is preferable to try-catch when it comes to cleaning up and tearing down, and (3) it is fair to wrap and rethrow exceptions if you want the unit of work manager to have more context to know what to do, or provide a better error message.

In theory, you could spend time writing and maintaining error preempting and handling code and get better error messages. However that code isn’t always correct. One funny thing about AI agent programming for me is that if I ask Junie to code me some tests it works harder at testing unhappy paths than I would.

twosdai•4mo ago
I feel like the type guarding programming method present in some packages, is a hangover from node js development prior to typescripts rise in popularity.

So now some people just do it by habit.

Honestly the only place having code like that I see being useful is at the controller level inside an api or other untrusted input validation for an application.

dccoolgai•4mo ago
Always interesting to think about Postel's law in the context of these kinds of things. ("Be liberal in what you accept and conservative in what you send")
pizlonator•4mo ago
It’s interesting how multiple of the bad cases the post identifies exist because of how gross the dynamic types in JS are.

If JS’s dynamic types worked like Ruby’s or Python’s then you wouldn’t think to build a library that tells you if something is a regex.

rossant•4mo ago
> I don’t really agree with this and think downloading a package for #! 86 million times a week is a bit much.

Yeah. A bit.

minaguib•4mo ago
Entirely unrelated to the topic, but my mind was blown when I recently learnt that clamp() can be implemented via .median(val, low, high), and the param order doesn't matter!
darepublic•4mo ago
Fwiw in the example of guaranteeing number type a simple typeof number is sufficient.