[HN Gopher] A masochist's guide to web development
___________________________________________________________________
A masochist's guide to web development
Author : sebtron
Score : 263 points
Date : 2025-06-06 13:48 UTC (1 days ago)
(HTM) web link (sebastiano.tronto.net)
(TXT) w3m dump (sebastiano.tronto.net)
| udev4096 wrote:
| What's with the use of port 48 for SSL? Any particular reason?
| sebtron wrote:
| Ah, that's a good question. It's kinda random (except that the
| name of the solver is "H48"). Setting up that web app required
| some extra HTTP headers (I explain that in the post), and the
| easiest way I found to do that without messing up the rest of
| my website was using a different port. https://h48.tronto.net
| redirects there too.
|
| Later I looked into a better way to do this, but I could not
| fully work it out. I use OpenBSD's httpd, which does not
| support setting extra headers, and relayd. At some point I'll
| take a look at this again, or I'll move the tool to another
| domain.
| wmichelin wrote:
| `var myLibraryInstance = away MyLibrary();`
| lerax wrote:
| Masochist? that's much more sane than the js clusterfuck
| ecosystem
| rapind wrote:
| "Clusterfuck" is implicit and may be omitted.
| broken_broken_ wrote:
| Good article, I also have a C program (a compiler) that I would
| like to compile to webassembly to offer a playground web page, so
| that is good information. Thank you!
|
| About the file system stuff: modern browsers ship with SQLite
| which is available to JavaScript (is it available to webassembly?
| No idea) so I would probably use that instead. Ideally you could
| use the sqlite API directly in C and emscripten would bridge the
| calls to the browser SQLite db. Something to investigate.
| lscharen wrote:
| > Notice I have changed the extension from .js to .mjs. Don't
| worry, either extension can be used. _And you are going to run
| into issues with either choice_
|
| As someone that has used module systems from dojo to CommonsJS to
| AMD to ESM with webpack and esbuild and rollup and a few others
| thrown in ... this statement hits hard.
| bubblyworld wrote:
| Yeah, modules in jsland are just trauma... now we have import
| maps in the browser too. Let's see what kinds of fun we can
| have with those.
| ajayvk wrote:
| A recent improvement in import map support in the browser
| https://shopify.engineering/resilient-import-maps
| hdjrudni wrote:
| The interesting bit is this:
| https://philipwalton.com/articles/cascading-cache-
| invalidati... Without that one sentence, the entire article
| is moot because I thought bundlers solved the problem.
|
| I haven't thought about that in years. I didn't realize it
| had been solved.
|
| Browser support looks pretty good.
|
| I guess now I have to figure out how to get this to play
| nice with Vite and TypeScript module resolution.... and now
| it's starting to hurt my brain again, great.
| SCLeo wrote:
| Yeah, the commonjs to esm transition has been the python 2 to
| python 3 transition of JavaScript, except the benefits are
| limited (at least compared to the hassle created).
|
| There are many libraries that have switched to esm only
| (meaning they don't support commonjs), but even today, the best
| way to find the last commonjs version of those libraries is to
| go to the "versions" tab on npm, and find the most downloaded
| version in the last month, and chances are, that will be the
| last commonjs version.
|
| Yes, in a vacuum, esm is objectively a better than commonjs,
| but how tc39 almost intentionally made it incompatible with
| commonjs (via top-level awaits) is just bizarre to me.
| eyelidlessness wrote:
| It had to be incompatible with CommonJS regardless of top
| level await. There is no imaginable scenario where browsers
| would ship a module system with synchronous request and
| resolution semantics. A module graph can be arbitrarily deep,
| meaning that synchronous modules would block page load for
| arbitrarily deep network waterfalls. That's a complete non-
| starter.
|
| Given that, top-level await is a sensible affordance, which
| you'd have to go out of your way to block because async
| modules already have the same semantics.
|
| Recently, Node has compromised by allowing ESM to be loaded
| synchronously absent TLA, but that's only feasible because
| Node is loading those models from the file system, rather
| than any network-accessible location (and because it already
| has _those_ semantics for CJS). That compromise makes sense
| locally, too. But it still doesn't make sense in a browser.
| apatheticonion wrote:
| Bundler engineer here. ESM is great when it comes to build-
| time optimisations. When a bundler runs into CJS code it
| literally deopts the output to accommodate - so from that
| side it's amazing.
|
| But also, there's a special place in hell for the people that
| decided to add default exports, "export * from" and top level
| await.
|
| Commonjs is also very weird as a "module" instance can be
| reassigned to a reference of another module
|
| module.exports = require('./foo')
|
| and there's no way to do this in ESM (for good reason, but
| also no one warned us). In fact major projects like React use
| CJS exports and the entire project cannot be optimized by
| bundlers. So, rather than porting to ESM, they created a
| compiler LOL
| pwdisswordfishz wrote:
| > But also, there's a special place in hell for the people
| that decided to add [...] top level await.
|
| There is also a special place in extra-hell for those who
| export a function named 'then'.
| sirsuki wrote:
| Wait what? What would that module even be for?!
| sirsuki wrote:
| If I may, the evil is not in the top level await but in the
| use of a top level await in a module that exports. That is
| evil. But a top level await in a programs main script seems
| ok to me.
| zackify wrote:
| That's why everyone should be using bun.sh
| VWWHFSfQ wrote:
| Web dev is full of "you should just use _some other tool that
| I like_ "
|
| I'm convinced that 90% of the JavaScript ecosystem only
| exists to build tools for itself. It's tools all the way down
| wild_egg wrote:
| For those of us forced to be in the JS ecosystem, finally
| having a runtime that Just Works has been great.
|
| Bun has replaced a massive number of tools and dependencies
| from our stack and really counteracted the tooling
| explosion that we were forced into with node.
| zackify wrote:
| Imagine complaining that a language has so many users and
| projects so it sucks.
|
| Exactly, bun is killer. The test runner is extremely
| fast.
|
| I can build apps with 1-5 total dependencies and
| everything just works, and works incredibly fast.
| diggan wrote:
| > really counteracted the tooling explosion that we were
| forced into with node
|
| Isn't this more-or-less a self-inflected wound? Who
| forced you into working with node?
| edoceo wrote:
| $JOB
| throw-the-towel wrote:
| Could you please elaborate on this? What tools besides
| Node itself did you replace with Bun?
| BostonFern wrote:
| Won't .esm.js work?
| goodthink wrote:
| I recently found out that the Function object will compile any
| javascript you care to feed to it. At runtime! new
| Function('class MyClass { ...}; return MyClass') My system does
| not allow for "imports". No npm etc. So this is a bit of a
| lifesaver for me. I realize in js land it may not be as useful
| but it is pretty handy.
| divbzero wrote:
| I always assumed that compiling code to run in the browser would
| be slow, but OP points out that this is not the case. As the
| Emscripten project describes:
|
| > _Thanks to the combination of LLVM, Emscripten, Binaryen, and
| WebAssembly, the output is compact and runs at near-native
| speed._
|
| https://emscripten.org/
| RobRivera wrote:
| Yellow bus syndrom in action for me today.
|
| Last week I never heard of Emscripten.
|
| Integrating SDL for a project, there were CMake callouta for
| APPLE, MSVC, and EMSCRIPTEN.
|
| And here we are seeing it again on hn in a few days.
|
| I should put an afternoon aside for some deep diving on it for
| context.
| scubbo wrote:
| > Yellow bus syndrome in action for me today
|
| There's a certain irony to being able to introduce you to the
| term "Baader-Meinhof Phenomenon" (which is the more-common
| name for what I assume you're referring to, as Google
| searches for "Yellow Bus Syndrome" didn't bring anything up
| for me). Now you know the name, you'll see it everywhere!
| 57473m3n7Fur7h3 wrote:
| The colloquial term they were misremembering is "yellow
| car" effect.
| phatskat wrote:
| Funny, I always called it "the GTA effect" as in either
| Grand Theft Auto 1 or 2, one of the top-down ones, once
| you got a particular kind of car you would see more of
| that same car on the road. I don't know if it was an
| optimization strategy or just me falling victim to the
| effect I ascribed to the game.
| 57473m3n7Fur7h3 wrote:
| In GTA III for example, which I have played a lot, it is
| definitely the case that it spawns a lot more of the
| players car, whatever model of car the player happens to
| be in.
|
| Various sources online say that it's because only a
| certain number of cars fit in memory at the same time so
| they use the car of the player along with some others. It
| makes sense, but it would be cool to get that confirmed
| from someone who actually worked on the GTA games/engine.
| detaro wrote:
| Later GTAs do this too, and at least for San Andreas I'm
| fairly sure its been confirmed by reverse engineering
| that that's how the engine works. Speedrunners use tricks
| to manipulate that cache so they have a better chance of
| getting something good.
| scubbo wrote:
| Thank you, TI(2)L!
| burningChrome wrote:
| >> the output is compact and runs at near-native speed.
|
| This kind of subjective, no? I wonder what they consider "near
| native speed"? I couldn't find any real numbers in their
| documentation.
| gspencley wrote:
| Not only is it subjective but V8 does so much to optimize
| JavaScript code that I wouldn't be surprised if the benefits
| for most applications were negligible anyway.
|
| Although JavaScript is still an interpreted language, it
| basically gets "compiled" when the browser parses the bundle.
| On the surface, the only thing WebAssembly automatically gets
| you is you get to skip the runtime compilation phase.
|
| I might be talking out of my ass, so take this with a grain
| of salt, but I wouldn't be surprised if once we start
| collecting real data on this stuff, SOME WebAssembly code
| could actually run slower than just using JS code. My
| hypothesis is that if you're starting with non-JavaScript
| code, you might be doing things in that language that would
| be slower to do the same way in JavaScript. I'm thinking of
| things like Array.map(), .filter() etc. ... which are hyper-
| optimized in V8. If you're taking an algorithm from C code or
| something which then gets compiled to WebAssembly, it's not
| an automatic given that it's going to compile to WebAssembly
| that is just as optimized as what V8 would do when it comes
| across those API calls. Again, this is just a hypothesis and
| I could be way off base.
|
| In any case, what we need is real world data. I have no doubt
| that for certain applications you can probably avoid land
| mines by hiring devs who are experienced building certain
| performance-critical things at a lower-level than your
| average JS dev... and their experience in those languages may
| transfer very well to the browser. In this scenario, you're
| not getting huge perf wins from using WebAssembly per-se...
| you're getting huge perf wins for not doing typical stupid,
| lazy, ignorant things that most average JS devs do ... like
| cloning large objects using the spread operator and then
| doing that over and over and over again "because
| immutability."
| aDyslecticCrow wrote:
| WebAssembly is still a flavour of assembly. It's only
| nearly native performance to the real code because the
| interface to JavaScript has overhead. Every action in
| JavaScript incurs overhead due to dynamic types and
| objects, as well as dynamic memory allocation and garbage
| collection. Wasm can theoretically ignore it all and run as
| if it were compiled for the host system, except when it
| needs to interact with the JavaScript environment.
|
| It's astonishing how fast JavaScript has become. But even
| if it were fully compiled, it would still be a language
| with higher overhead.
|
| You can still write bad code, or compile a language with
| high overhead into WASM. This remains valuable for porting
| existing libraries into the browser and reducing bandwidth
| usage. But properly done with a fast compiled language like
| c or rust.... wasm can unlock some magical things into the
| web ecosystem.
| wavemode wrote:
| > It's only nearly native performance to the real code
| because the interface to JavaScript has overhead.
|
| That's not at all the only reason WASM is slower than
| native. WASM is bytecode. It still has to be JIT
| compiled, just like JavaScript. And WASM to begin with
| does not have a very complex instruction set, so the code
| generated by your language's LANG-to-WASM backend can't
| be optimized as heavily as its native backend.
|
| As a rule of thumb (from my experience), you're almost
| never going to achieve significantly better performance
| in WASM than the equivalent algorithm written in
| optimized JS.
| aDyslecticCrow wrote:
| > It still has to be JIT compiled
|
| Eeh. Comparing a garbage collected jit language to
| bytecode jit parsing is... quite possibly the most insane
| argument you could make.
|
| And what does instruction count have to do with
| optimization? Most languages optimize in architecture
| invariant representations before creating the bytecode.
| So the wasm binary is already optimized.
|
| From searching the web to make sure; the language barrier
| between wasm and js is the highest performance
| bottleneck. So its generally recommended to not bother
| for simple algorithms until it gets better.
| wavemode wrote:
| > Eeh. Comparing a garbage collected jit language to
| bytecode jit parsing is... quite possibly the most insane
| argument you could make.
|
| Not understanding that WASM still has to be optimized and
| compiled to machine code, and then calling me insane over
| it, is certainly an approach to discourse
|
| > And what does instruction count have to do with
| optimization?
|
| Not going to bother with this one. Do some research into
| how compilers work, maybe.
|
| > From searching the web to make sure; the language
| barrier between wasm and js is the highest performance
| bottleneck.
|
| It certainly is. Not sure where I claimed it wasn't. What
| I'm saying is that there are also other reasons a program
| will run slower when compiled to WASM compared to when
| compiled to native.
| knallfrosch wrote:
| Nice writeup! You definitely chose a pretty hard way, but the
| project setup is always the most complex part. Bonus points for
| immediately running into security/header issues, but my bet would
| have been CORS.
|
| At $WORK, we're also building with emscripten/C++. We'll add
| WebGPU/shaders and WebAudio for bonus pain.
| socalgal2 wrote:
| There is more here that is likely to cause problems in the
| future. One is the author's use of var instead of let or const.
| var continues to work but most JS devs have linters that ban the
| use of var. The issue is, var has function scope, not brace
| scope. Most non-JS devs coming from other languages will
| eventually run into this issue.
|
| Another issue porting native apps is, native apps are compiled
| for a specific platform and hardcoded to that platform's
| conventions. A good example of this is hardcoding Ctrl-C (copy),
| Ctrl-V (paste) at compile time, which maybe works on Linux and
| Windows but doesn't work on Mac.
|
| IIRC the way you're supposed to handle this on the web is listen
| for copy and paste events. AFAIK Unity has this issue. They hard
| coded Ctrl-C, Ctrl-P and so copy and paste don't work on Mac.
| Most games don't need copy and paste but once in a while someone
| does something that does need it, then exports to the web and
| runs into this issue.
| maximevince wrote:
| Thanks for this wonderful writeup!
| apatheticonion wrote:
| God I hate multi-threading on the web/nodejs. Rather than
| implementing syncronization primatives like mutexes or rwlocks
| that capture the contained values and make them "transferrable"
| between JavaScript contexts (v8 isolates) - they introduced
| SharedArrayBuffer that is almost entirely unusable for anything
| meaningful.
|
| Syncronizing between threads involves thunking and copying data
| through RPC layers.
|
| Sucks for me because our production app has grown faster than we
| are able to rewrite it and uses 70-100gb of ram (written before
| my time). To try to get around this, we are investigating exotic
| solutions like using native code to manually manage pages of
| shared memory that contains custom data structures and as little
| serialization/deserializing logic as possible - but of course v8
| uses utf16 for string encoding which means working with
| JavaScript values in the native layer is expensive.
| knallfrosch wrote:
| 100GB of RAM -- I'm curious, why is this a web app? Sounds like
| an internal tool that could have been written in, for example,
| C#.
___________________________________________________________________
(page generated 2025-06-07 23:01 UTC)