[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)