Most active commenters
  • user34283(4)
  • zelphirkalt(3)
  • dmix(3)
  • Ajedi32(3)

←back to thread

1208 points jamesberthoty | 34 comments | | HN request time: 0.624s | source | bottom
1. homebrewer ◴[] No.45261199[source]
When the left-pad debacle happened, one commenter here said of a well known npm maintainer something to the effect of that he's an "author of 600 npm packages, and 1200 lines of JavaScript".

Not much has changed since then. The best counter-example I know is esbuild, which is a fully featured bundler/minifier/etc that has zero external dependencies except for the Go stdlib + one package maintained by the Go project itself:

https://www.npmjs.com/package/esbuild?activeTab=dependencies

https://github.com/evanw/esbuild/blob/755da31752d759f1ea70b8...

Other "next generation" projects are trading one problematic ecosystem for another. When you study dependency chains of e.g. biomejs and swc, it looks pretty good:

https://www.npmjs.com/package/@biomejs/biome/v/latest?active...

https://www.npmjs.com/package/@swc/types?activeTab=dependenc...

Replacing the tire fire of eslint (and its hundreds to low thousands of dependencies) with zero of them! Very encouraging, until you find the Rust source:

https://github.com/biomejs/biome/blob/a0039fd5457d0df18242fe...

https://github.com/swc-project/swc/blob/6c54969d69551f516032...

I think as these projects gain more momentum, we will see similar things cropping up in the cargo ecosystem.

Does anyone know of other major projects written in as strict a style as esbuild?

replies(6): >>45261429 #>>45261662 #>>45261809 #>>45264078 #>>45267771 #>>45267783 #
2. cookiengineer ◴[] No.45261429[source]
Part of the reason of my switch to using Go as my primary language is that there's this trend of purego implementations which usually aim towards zero dependencies besides the stdlib and golang.org/x.

These kind of projects usually are pretty great because they aim to work with CGO_ENABLED=0 so the libs are very portable and work with different syscall backends.

Additionally I really like to go mod vendor my snapshot of dependencies which is great for short term fixes, but it won't fix the cause in the long run.

However, the go ecosystem is just as vulnerable here because of lack of signing off package updates. As long as there's no verification possible end-to-end when it comes to "who signed this package" then there's no way this will get better.

Additionally most supply chaib attacks focussed on the CI/CD infrastructure in the past, because they are just as broken with just as many problems. There needs to be a better CI/CD workflow where signing keys don't have to be available on the runners themselves, otherwise this will just shift the attack surface to a different location.

In my opinion the package managers are somewhat to blame here, too. They should encourage and mandate gpg signatures, and especially in git commits when they rely on git tags for distribution.

replies(1): >>45262299 #
3. zelphirkalt ◴[] No.45261662[source]
The answer is to not draw in dependencies for things you are easily able to write yourself. That would probably reduce dependencies by 2/3 or so in many projects. Especially, left-pad things. If you write properly self contained small parts and a few tests, you probably don't have to touch them much, and the maintenance burden is not that high. Compare that with having to check every little dependency like left pad and all its code and its dependencies. If a dependency is not strictly necessary, then don't do it.
replies(1): >>45274228 #
4. benmccann ◴[] No.45261809[source]
Yes, eslint is particularly frustrating: https://npmgraph.js.org/?q=eslint

There are plenty of people in the community who would help reduce the number of dependencies, but it really requires the maintainers to make it a priority. Otherwise the only way to address it is to switch to another solution like oxlint.

replies(2): >>45262741 #>>45274220 #
5. juliend2 ◴[] No.45262299[source]
> there's this trend of purego implementations which usually aim towards zero dependencies besides the stdlib and golang.org/x.

I'm interested in knowing whether there's something intrinsic to Go that encourages such a culture.

IMO, it might be due to the fact that Go mod came rather late in the game, while NPM was introduced near the beginning of NodeJS. But it might be more related to Go's target audience being more low-level, where such tools are less ubiquitous?

replies(5): >>45262496 #>>45262505 #>>45262822 #>>45263409 #>>45263724 #
6. christophilus ◴[] No.45262496{3}[source]
"A little duplication is better than a little dependency," -- Rob Pike

I think the culture was set from the top. Also, the fairly comprehensive standard library helps a lot. C# was in a similar boat back when I used it.

7. johnisgood ◴[] No.45262505{3}[source]
C encourages such culture, too, FWIW.
replies(1): >>45268641 #
8. dmix ◴[] No.45262741[source]
I tried upgrading ESLint recently and it took me forever to fix all the dependency issues. I wish I never used ESLint prettier as now my codebase styling is locked into an ESLint config :/
replies(2): >>45263269 #>>45264063 #
9. Ajedi32 ◴[] No.45262822{3}[source]
> I'm interested in knowing whether there's something intrinsic to Go that encourages such a culture.

I think it's because the final deliverable of Go projects is usually a single self-contained binary executable with no dependencies, whereas with Node the final deliverable is usually an NPM package which pulls its dependencies automatically.

replies(2): >>45263431 #>>45263465 #
10. azemetre ◴[] No.45263269{3}[source]
Have you looked into biome? We recently switched at work. It’s fine and fast. If you overly rely on 3rd party plugins it might be hard but it covered our use case fine for a network based react app.

Way less dependencies too.

replies(1): >>45264459 #
11. cesarb ◴[] No.45263409{3}[source]
> I'm interested in knowing whether there's something intrinsic to Go that encourages such a culture.

I've also seen something similar with Java, with its culture of "pure Java" code which reimplements everything in Java instead of calling into preexisting native libraries. What's common between Java and Go is that they don't play well with native code; they really want to have full control of the process, which is made harder by code running outside their runtime environment.

replies(1): >>45267570 #
12. yunohn ◴[] No.45263431{4}[source]
> usually an NPM package which pulls its dependencies automatically

Built applications do not pull dependencies at runtime, just like with golang. If you want to use a library/source, you pull in all the deps, again just like golang.

replies(1): >>45263537 #
13. int_19h ◴[] No.45263465{4}[source]
With Node the final deliverable is an app that comes packaged with all its dependencies, and often bundled into a single .js file, which is conceptually the same as a single binary produced by Go.
replies(1): >>45263585 #
14. Ajedi32 ◴[] No.45263537{5}[source]
Not at runtime no, but at install time yes. In contrast, with Go programs I often see "install time" being just `curl $url > /usr/local/bin/my_application` which is basically never the case with Node (for obvious reasons).
15. Ajedi32 ◴[] No.45263585{5}[source]
Can you give an example? While theoretically possible I almost never see that in Node projects. It's not even very practical because even if you do cram everything into a single .js file you still need an external dependency on the Node runtime.
16. Icathian ◴[] No.45263724{3}[source]
Go sits at about the same level of abstraction as Python or Java, just with less OO baked in. I'm not sure where go's reputation as "low-level" comes from. I'd be curious to hear why that's the category you think of it in?
replies(1): >>45268715 #
17. WorldMaker ◴[] No.45264063{3}[source]
Deno has a similar formatter to prettier and similar linter to eslint (with Typescript plugins) out-of-the-box. (Some parts of those written in Rust.) I have been finding myself moving to Deno more and more. I also haven't noticed too many reformatting problems with migrating from prettier to Deno. (If there are major changes, you can also add the commit to a .git-ignore-revisions file.)
18. a99c43f2d565504 ◴[] No.45264078[source]
> Does anyone know of other major projects written in as strict a style as esbuild?

As in any random major project with focus on not having dependencies? SQLite comes to mind.

19. dmix ◴[] No.45264459{4}[source]
Even minor styling rule changes would result in a huge PR across our frontend so I tend to avoid any change in tooling. But using old tools is not the end of the world. I only upgrade ESLint because I had to upgrade something else.
replies(1): >>45264987 #
20. adhamsalama ◴[] No.45264987{5}[source]
Would omitting this commit from git blame solve the issue?
replies(1): >>45265933 #
21. dmix ◴[] No.45265933{6}[source]
Oh that's a great idea. I forgot about git --ignore-revs
22. kelnos ◴[] No.45267570{4}[source]
I think it's important for managed/safe languages to have their own implementations of things, and avoid dropping down into C/C++ code unless absolutely necessary.

~13 years ago I needed to do DTLS (TLS-over-UDP) from a Java backend, something that would be exposed to the public internet. There were exactly zero Java DTLS implementations at the time, so I chose to write JNI bindings to OpenSSL. I was very unhappy with this: my choices were to 1) accept that my service could now segfault -- possibly in an exploitable way -- if there was a bug in my bindings or in OpenSSL's (not super well tested) DTLS code, or 2) write my own DTLS implementation in Java, and virtually guarantee I'd get something wrong and break it cryptographically.

These were not great choices, and I wished I had a Java DTLS implementation to use.

This is why in my Rust projects, I generally prefer to tell my dependencies to use rustls over native (usually OpenSSL) TLS when there's an option between the two. All the safety guarantees of my chosen language just disappear whenever I have to call out to a C library. Sure, now I have to worry about rustls having bugs (as a much less mature implementation), but at least in this case there are people working on it who actually know things about cryptography and security that I don't, and they've had third-party audits that give me more confidence.

replies(2): >>45268863 #>>45271672 #
23. philipwhiuk ◴[] No.45267771[source]
The downside is now I need to know Golang to audit my JavaScript project.

And it runs a post-install: node install.js

So I do really have to trust it or read all the code.

24. duped ◴[] No.45267783[source]
> Very encouraging, until you find the Rust source

Those are the workspace dependencies, not the dependencies of the specific crates you may use within the project. You have to actually look closer to find that out, most of `swc-` crates have shallow dependency trees.

25. ◴[] No.45268641{4}[source]
26. cookiengineer ◴[] No.45268715{4}[source]
I'd argue that Go is somewhere in between static C and memory safe VM languages, because the compiler always tries to "monomorphize" everything as much as possible.

Generic methods are somewhat an antipattern to how the language was designed from the start. That is kind of the reason they're not there yet, because Go maintainers don't want boxing in their runtime, and also don't want compile time expansions (or JIT compilation for that matter).

So I'd argue that this way of handling compilation is more low level than other VM based languages where almost everything is JITed now.

27. cesarb ◴[] No.45268863{5}[source]
> or 2) write my own DTLS implementation in Java, and virtually guarantee I'd get something wrong and break it cryptographically.

Java doesn't have constant time guarantees, so for at least the cryptographic part you have to call to a non-Java library, ideally one which implements the cryptographic primitives in assembly (unfortunately, even C doesn't have constant time guarantees, though you can get close by using vector intrinsics).

28. ◴[] No.45271672{5}[source]
29. user34283 ◴[] No.45274220[source]
That's like 85 dependencies, not hundreds or even thousands.

Jest pulls in 300 by the way.

30. user34283 ◴[] No.45274228[source]
That's not an answer at all. Jest alone adds 300 packages.

Why don't you share with us what your project does and how many packages are present?

replies(1): >>45274378 #
31. zelphirkalt ◴[] No.45274378{3}[source]
My current project? Not sure what that has to do with the discussion, but my current project uses only a tiny bit of JS and has a fallback for users who don't run JS. It is a few pages taking a file to upload and the all the actual sauce is in the backend, and it is rendering templates.

So I simply avoid the whole problem altogether in my current project. But aside from the JS stuff, the backend is in Python and I avoid adding dependencies from PyPI wherever possible. For example I had the choice of going with Pydantic and dataclasses and whatnot, but I resisted that, and came up with a quite minimalistic way to type check JSON documents, that is contained in one short module and easily extensible. Does it go to the same length as pydantic? No, it doesn't. If it did, I would be a genious. But it is quite sufficient for type safety in my project.

Keeping things simple is possible, if we set our minds to it. Sometimes one cannot avoid a big dependency, sure, but in many cases we actually can! We just need to beat that beast of habit of quickly adding a familiar dependency without thinking about the cost.

replies(1): >>45274537 #
32. user34283 ◴[] No.45274537{4}[source]
So you're not using the npm ecosystem at all.

Surely you see how that might be relevant to the discussion where you appeared to give advice on how to solve the npm dependency graph problem.

That you're not using npm or other node package managers at all is the key information here. Not that it's invalid, but it's a very different setup.

replies(1): >>45275650 #
33. zelphirkalt ◴[] No.45275650{5}[source]
It's not like I haven't worked on projects using NPM before ... Where I avoided adding dependencies willy-nilly. So trying to pin me on my current one project, disregarding any previous experience is quite a faulty argumentation, that doesn't invalidate anything I wrote.
replies(1): >>45275860 #
34. user34283 ◴[] No.45275860{6}[source]
Adding a single dependency on one popular package - such as Jest - can add 300 packages.

I am not convinced you ever checked how many packages were actually present in your projects, as you shared no specifics.

I assume you just did not check, and may have had hundreds of packages installed despite avoiding adding dependencies willy-nilly. This invalidates your suggestion.