Hacker News new | ask | show | jobs
by thex10 1153 days ago
Not enough discussion here of the parts under "Our Optimization Strategies", which was the most interesting to me. Assorted reactions:

> We found that react-icons had an issue that lead to everything being imported. This meant that we were including every single react-icon in our package whether we need it or not.

Kudos to the Linen team for proactively finding this - I have a feeling tons of projects blindly trust that tree-shaking their dependencies will "just work" even though for many libraries it won't!

> We also noticed that we were only using AWS client for s3 upload on the client side and it was taking up significantly more bundle size we need so we replaced the entire client side package with a 2 api calls to the AWS api.

For such a minimal use case, this feels like a logical choice even if it's slightly more work to implement.

> We ended up moving the code highlight code to a backend api that would cache the results.

Love seeing websites make smart choices about which work to handle in the server versus the client.

8 comments

With icons, I've stopped using icon libraries a while back and now import just the SVG code that I need. I'm a big fan of Hero Icons[1] and they offer a way to quickly copy JSX or SVG code to the clipboard to faciliate this workflow.

[1]: https://heroicons.com/

Phosphor icons are also great: https://phosphoricons.com
If we're listing icons, I use these ones: https://ionic.io/ionicons/

They provide a full framework but you can also download SVGs.

Icones is my go to https://icones.js.org/
Another one: Material Design Icons, https://pictogrammers.com/library/mdi/
Remix Icons are also solid: https://remixicon.com/
Used https://lineicons.com the last time around.
Radix icon also good: https://icons.radix-ui.com/
FYI there is a middle ground with the AWS SDK. If you don’t need the entire thing (you certainly do not), you can import product specific SDKs as standalone. Still quite a bit fatter than building your own HTTP client, but it’s an easy win if you already use the SDK.

ex, an s3 only Java sdk:

https://stackoverflow.com/questions/35591248/aws-sdk-for-s3-...

But also, if you're just making two API calls (the use case for Linen) which are using JSON, you can get both request making and json parsing pretty much out-of-the-box without doing anything, web browsers JS API ships with this by default, window.fetch and response.json(), no "building your own HTTP client" required :)

You need to have two calls at least, generate a signed URL and then actually uploading it. How many lines on the client would you guess that requires? My guess is less than 30, and you cover 100% of your use case without putting in any 3rd party code what so ever. Sounds like a solid win with few drawbacks.

That's a good trick for JSON-based AWS APIs, but the S3 API is all XML AFAIK. I had to fix some signing bugs in the unofficial Haskell AWS SDK, and found it rather fiddly and a fair bit more than 30 LoC. Compared to standard AWS SigV4, S3 is also a special case in a few ways (request chunking, support for presigned PUTs with unsigned payloads, etc).
Ah, good point, but of course the browser also ships with APIs for dealing with XML, not doing so would be weird as HTML and the XML structure a pretty fundamental part of the web. See XMLHttpRequest, XMLSerializer and DOMParser.
If you are using pre-signed urls, or public objects, then there is really no reason you need to use an s3 library, any old http client will work fine. But if you need to authenticate the request yourself, then you have to deal with the somewhat complicated process of signing the url, headers and body, if applicable. It isn't terrible, but it is probably more than you want to do if you have a library available.
What about retries, status code handling, timeouts, and backoff? (It's not a lot of code to add that stuff in but I still call it a "client".)
AWS doesn’t do that for you either.
yes it does, to some extent e.g., retries https://boto3.amazonaws.com/v1/documentation/api/latest/guid...
You linked the Python SDK, just FYI. Here’s the JavaScript version: https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clien...
The same thing happened in a past job for me — we discovered we were included every single icon in FontAwesome. I wrote a codemod script to refactor how we were importing icons so it was treeshakeable. Cut load times from 10s to 2s — was glorious.
Oh, we're using FontAwesome here.

Any possibility of you sharing your "codemod script" so others can potentially implement it too? :)

> Kudos to the Linen team for proactively finding this - I have a feeling tons of projects blindly trust that tree-shaking their dependencies will "just work" even though for many libraries it won't!

For one particular microsoft product, this is brought to attention by documentation: https://learn.microsoft.com/en-us/power-apps/developer/compo...

> I have a feeling tons of projects blindly trust that tree-shaking their dependencies will "just work" even though for many libraries it won't!

Is there an easier way to verify this than, say, observing the final bundle size and looking inside the JS bundle? I'm not a frontend developer so this is outside my usual area of knowledge.

I use source-map-explorer to see what's inside. Lighthouse Treemap provides a similar view.

[source-map-explorer]: https://github.com/danvk/source-map-explorer

[Lighthouse Treemap]: https://umaar.com/dev-tips/270-devtools-lighthouse-treemap/

i just write everything from scratch every time. (so don't take my advice)

I notice that if I lower the bar and allow half baked hackish solutions to slip in, beyond some point, upkeep consumes infinite time. There seems no way back from that besides starting from scratch.

There is no good reason for front end things to break. I can only think of document.writeln() but who in his right mind...

> Love seeing websites make smart choices about which work to handle in the server versus the client.

The intro community discussion page at https://www.linen.dev/s/linen does need to load 20+ javascript files for some reason to show the latest messages.

Whereas messages still show up even if javascript is entirely blocked, just starting from the beginning in 10/2022 (?).

So I'm not quite sure if it's as optimized as it can be.

> tree-shaking their dependencies

What tool did they use: Browserify, Webpack, Gulp, Rollup, Babel, Parcel, ESBuild, etc.

It appears that they used Webpack, which I found interesting given other alternatives.
The UI package appears to use Rollup.

And this looks like the PR for the icons update: https://github.com/Linen-dev/linen.dev/pull/1001

they had to change every import for an icon in 60 files. Wouldn’t it have been better if a PR was done against Rollup to fix this “bug”? probably much more difficult/not possible?
It may not be a "bug" in rollup.

Unfortunately, the current state of ESM in Node is such a mess that you can't assume most packages are tree-shakeable. There is package.json metadata to do that.

It's more likely to be a "simple" bug (with far reaching consequences) in react-icons' package.json and/or build process.

Taking a quick glance at https://github.com/react-icons/react-icons/blob/master/packa...

Yeah, it's using the old non-standardized "module" field as opposed to the modern and mostly standard (now at least) "exports" field [1] or "type" field.

"exports" would be a quick fix of the existing package.json file with no other changes to the build process, but given this is a UI package intended primarily for browser usage I'm having a hard time understanding why it bothers to include CommonJS at all and isn't just `"type": "module"` and remove CommonJS from the build entirely.

That probably points to why this hasn't been done yet: it gets into a bikeshed argument and a lot of potential discussion on big changes to a presumably "not broke" build system.

[1] https://nodejs.org/api/packages.html#packages_exports

(ETA: Existing Issue on this subject in that repo: https://github.com/react-icons/react-icons/issues/717)

Many javascript programmers including library authors don't understand the tree shaking mechanisms available to them. Setting the "sideEffects" property in package.json can be very effective in detailing whether to include/exclude an entire module or certain source files:

https://webpack.js.org/guides/tree-shaking/#mark-the-file-as...

All popular bundlers (webpack, rollup, esbuild) respect that field.

A commuter typically takes a free public bus ride to work. The bus broke down this morning. The commuter had to go out of their way to take a different bus to get to work. Wouldn’t it have been better if they stayed and fixed the first bus?
I get the proverb but you have to admit that it's kind of a big deal that a tree-shaker bundler optimizer has a pretty big glaring known broken issue with it where it doesn't bundle.

The bus gets fixed eventually. This doesn't (unless somebody fixes it).

> tree-shaking

Always fascinated when I hear that JS term for what's essentially dead code elimination.

As far as I understand it, that's because it's different. Dead code elimination analyses the code and removes paths that can't be executed.

Tree shaking only looks at the imports and removes code that isn't imported from the bundle. If code is imported but not executed, then tree shaking won't remove this code, even though it is dead code.

Using a different term is reasonable to avoid confusion, in this case.

Ah, wasn't aware of this, thanks for clarifying.
I also think terming something in JS culture is much more welcoming than the one that thinks dead code elimination is the way it should be.