I've also felt that composition is unnecessarily hard in web & data infrastructure. I do game development as a hobby, and web + data technology is in the stone age compared to game engines when it comes to composability.
For example, say I develop some object (scene) in Godot Engine. It interacts with the environment using physics simulation, renders 3D graphics to the screen with some shaders and textures, and plays back audio from its 3D location.
I can send this scene to some other user of Godot, and it will naturally just work in their project, including colliding with their objects, rendering in their viewport (including lighting and effects), and the player will hear the location of the object spatially.
Of course there is much more you can do in Godot, too: network requests, event-driven updates, localization, cross-platform inputs, the list goes on. And all of these compose and scale in a manageable way as projects grow.
Plus the engine provides a common data and behavior backbone that makes it possible for a single project to have code in C++, C#, GDScript, and even other languages simultaneously (all of these languages talk with the engine, and the engine exposes state and behaviors to each language's APIs).
In fact, I've been thinking about making a Godot-inspired (or perhaps even powered) business application framework because it's just such a productive way of building complex logic and behavior in a way that is still easy to change and maintain.
So I imagine if Cambra can bring a similar level of composability for web & data software, it could dramatically improve the development speed and quality of complex applications.
Back in the days we had this in programming as well with palettes of drag & drop components.
It is kind of broken now, much thanks to using web applications (and applications that are basically just wrappers for web applications), but I don't know I if want to go back.
On one side it was much easier when I could hack together a program that was good enough (since everything was the same bland grey).
On the other hand some programs certainly looks nicer today.
And it has become easier to compose logic with solutions like Maven, Nuget and the various frontend package managers.
But yes, we lost drag and drop UI development, we lost consistency and we lost a lot of UX (at least temporarily).
I think there's a lot of potential for some more old-school UI in business software. People using points of sale, CRM/ERP/PLM/etc. systems, intranet portals, and so on don't really care about how nice it looks. Efficiency is more important.
Especially if it can be easy for non-technical people to build efficient UIs and databases (so they don't have to resort to spreadsheet contraptions), I think there's an opportunity here...
I've heard of people using Godot for non-game applications and I'm tempted to try something similar in the (near?) future because supposedly Low Processor Usage Mode makes it kinda feasible/sensible if you develop with that in mind from the start.
And while web pages can masquerade as desktop and mobile apps why wouldn't games be allowed to do the same? Godot for example can do desktop multi-window while something like Flutter (which is amazing in its own right) can't do.
But yeah, someone needs to spend time and build out UI toolkits for Godot and sadly that's not really a long weekend undertaking.
Still! It's nice to dream from time to time and imagine a reality where we can either do some generic cookie cutter UI because it's meant to get things done without much ceremony or we can pull out all the stops and plop a 3D scene to walk around the file system and shot files to delete them. And yeah, I'm aware someone did a thing like that in VS Code with Three.js (I think?)[0] and for Flutter you can do something similar in a webview inside the app proper.
Yet somehow I would rather do those things inside Godot for reasons unknown to me.
Your Godot scene doesn't work in Unreal Engine. I always felt that composition is unnecessarily hard in game engines infrastructure. I do web development as a hobby, and any .js file targeting the DOM works on any browser. I can't even run GDScript in Unity!
You're just comparing the wrong things. Yes, when you're locked into one environment, everything works together well. The moment you interact with outside systems, all hell breaks loose. If anything, what you're saying is just that platforms should have a much larger stdlib, or abstract platform differences properly (hint: this is only doable if you're a game engine and can afford to absolutely ignore _everything_ the OS does and just concern yourself with reinventing every wheel).
Not to say there's nothing good in the games side of things: a bunch of software could benefit from accepting that some systems like a big fat central message bus and singletons can be good when handled well.
You can just git submodule in the dependencies. Super easy. Also makes it straightforward to develop patches to send upstream from within your project. Or to replace a dependency with a private fork.
In my experience, this works great for libraries internal to an organization (UI components, custom file formats, API type definitions, etc.). I don't see why it wouldn't also work for managing public dependencies.
Plus it's ecosystem-agnostic. Git submodules work just as well for JS as they do for Go, sample data/binary assets, or whatever other dependencies you need to manage.
How do I decline it?? I keep clicking no, hide, not interested, cancel, etc. but it keeps showing up and activating...if I had a nickel for every time I clicked it on accident in Azure because a layout shift moved it under my mouse when trying to press a button I would have a lot of nickels. It even showed up as an app on my phone because I guess the Office 365 entry got hijacked...
I really think writing dependency-free JavaScript is the way to go nowadays. The standard library in JS/CSS is great. So are static analysis (TypeScript can check JSDoc), imports (ES modules), UI (web components), etc.
People keep telling me the approach I am taking won't scale or will be hard to maintain, yet my experience has been that things stay simple and easy to change in a way I haven't experienced in dependency-heavy projects.
I’ve been exploring this for years, even made a tutorial website about building sites and apps without dependencies (plainvanillaweb.com). What I’ve learned is that many of the things the frameworks, libraries and build tools do can be replaced by browser built-ins and vanilla patterns, but also that making things that way is at present an obscure domain of knowledge.
I think this is because the whole web dev knowledge ecosystem of youtubers and tutorial platforms is oriented around big frameworks and big tooling. People think it is much harder than it actually is to build without frameworks or build tools, or that the resulting web app will perform much worse than it actually will. A typical react codebase ported to a fully vanilla codebase ends up just as modular and around 1.5x the number of lines of code, and is tiny in total footprint due to the lack of dependencies so typically performs well.
To be clear though: I’m not arguing the dependencies are bad or don’t have any benefits at all or that vanilla coding is a superior way. Coding this way takes longer and the resulting codebase has more lines of code, and web components are “uglier” than framework components. What I’m saying is that most web developers are trapped in a mindset that these dependencies must be used when in reality they are optional and not always the best choice.
Thanks for creating and sharing that resource! I'm reading through it now, and it looks fantastic. I'll share it the next time someone asks where to get started with web dev.
Come to think of it, I should write up the techniques I use, too...e.g. I have simple wrappers around querySelector() and createElement() with a bit of TypeScript gymnastics in a JSDoc annotation to add intellisense + type checking for custom elements.
Would you be open to a pull request with a page on static analysis/type checking for vanilla JS? (intro to JSDoc, useful patterns for custom elements, etc.) If not, that's totally OK, but I figure it could be interesting to readers of the site.
And agreed on vanilla/dependency-free not being a silver bullet. There aren't really one-size-fits-all solutions in software, but I've found a vanilla approach (and then adding dependencies only if/when necessary) tends to help the software evolve in a natural way and stay simple where possible.
I think a blog post on jsdoc would be a better fit, with a link out from one of the main tutorial pages. Reach out to me over mail and we can work something out.
> To be clear though: I’m not arguing the dependencies are bad or don’t have any benefits at all or that vanilla coding is a superior way. Coding this way takes longer and the resulting codebase has more lines of code, and web components are “uglier” than framework components.
If you do it long enough, presumably you start to develop your own miniature "framework" (most likely really just some libraries, i.e. not inverting control and handing it over to your previous work). After all, it's still programming; JS isn't exceptional even if it has lots of quirks.
Anyway, love the website concept, just a quick petition: would it be possible to apply some progressive enhancement/graceful degradation love to <x-code-viewer> such that there's at least the basic preformatted text of the code displayed without JS?
Did this for a project in 2022. Haven't had any drama related to CVEs, hadn't had any issues related to migration from some version of something to another.
The client has not had to pay a cent for any sort of migration work.
There are certainly security benefits to keeping things in-house. Less exposure to supply-chain attacks (e.g. shai-hulud malware) and widespread security bugs (e.g. react server components server-side RCE). Plus it's much easier to do a complete audit and threat model of the application when you built and understand everything soup-to-nuts.
Of course, it also means you have to be cautious about problems that dependencies promise to solve (e.g. XSS), but at the same time, bringing in a bunch of third-party code isn't a substitute for fully understanding your own system.
Is the lack of CVE because the implementations you wrote are better written and safer than those in the standard libraries or because no one has checked?
Presumably the latter. However, mindlessly bumping package versions to fix bullshit security vulnerabilities is now industry standard practice. Once your client/company reaches a certain size, you will pretty much have to do it to satisfy the demands of some sort of security/compliance jarl.
Very laudable, though this is probably also part of the issue: If the client doesn't need any migration work, the dev doesn't get more money, which in turn might be phrased: "It is difficult to get a man to understand something, when his salary depends upon his not understanding it!" -- by someone other than me.
I have worked at employer, where one could have done the frontend easily in a traditional server side templating language since most of the pages where static information anyway and very little interactive. But instead of doing that and have 1 person do that, making an easily accessible and standard-conforming frontend, they decided to go with nextjs and required 3 people fulltime to maintain this, including all the usual churn and burn of updating dependencies and changing the "router" and stuff. Porting a menu from one instance of the frontend to another frontend took 3 weeks. Fixing a menu display bug after I reported it took 2 or 3 months.
web components are reactive. They have a render pattern similar to React's render function. Granted, web-components are much more wonky than React, but the functionality is there.
It seems best practice to use the component's attributes directly. So the component is subscribed to its attributes change lifecycle and renders updates.
been doing something similar. the projects ive been building recently use as few dependencies as possible and honestly the maintenance burden dropped significantly. when something breaks you actually know where to look instead of digging through 15 layers of node_modules. people said the same thing to me about it not scaling but the opposite turned out to be true.
yeah, plus stack traces, debuggers, and profiling tools are easier to use when all of the non-essential complexity is stripped out. which in turn means it's possible to work productively on software that solves more complex problems.
that's in contrast with the sort of stuff that invariably shows up when something falls over somewhere in a dependency:
cannot access property "apply" of null
at forEach()
at setTimeout()
at digest()
at callback()
at then()
...
it's not fun to step through or profile that sort of code either...
I've been doing JS for nearly a couple decades now (both front and back) and I landed on the same approach a few years ago. Pick your absolutely minimal set of dependencies, and then just make what you need for everything else. Maybe counter-intuitive to some, I feel like I'm more comfortable maintaining a larger codebase with less people.
What's more, given the tools we have today, it fits really well with agentic engineering. It's even easier to create and understand a homegrown version of a dependency you may have used before.
It depends a lot what kind of thing you're building. If it's a simple marketing website, sure. But any application that does stuff is likely using dependencies that provide a lot of functionality.
I make a lot of sites with maps. There's no real alternative to mapbox/maplibre/openlayers.
That should be self-evident and obvious, but apparently it still needs to be pointed out.
On one extreme, you have the current world where there are millions of 1-line or 1-function packages like leftPad and isArray.
On the other extreme, there are no packages and everyone builds everything from scratch, every single time.
It should not be controversial or groundbreaking to suggest that perhaps there is a reasonable middle ground in between! Someone builds a substantial piece of functionality, and it can be reused.
Yeah I've been doing this more and more. The friction of keeping dependencies updated has gotten worse than the friction of just maintaining the (often times) 20 or so lines you need to write yourself. Not to mention the pain you'll eventually find yourself when a bug isn't patched fast enough, or when you need to make a small change, or when a transitive dependency you've never heard of gets compromised...
Sure it's not officially called the "standard library," more precisely it would be "the parts of the ECMAScript and CSS standards implemented by all popular evergreen browsers," but "standard library" expresses this in the way people usually talk about programming languages.
This is one of the best parts of agentic development. I almost never had to reach for any npm package besides the foundational staples like the tailwinds and react queries. My pacakge.json dependency array vs project size ratio is just incredible nowadays
This is the way. Even more so now that LLMs can reliably write simple utilities, the kind of things a dependency would previously frag in hundreds of other utilities (that go unused) all while depending on another dozen dependencies
Depends on what cryptography you're talking about, the Web Crypto API exists for quite some time, so I'd say that fits in (usually) with "The standard library in JS/CSS is great".
Take async for example. You have to choose some third-party async runtime which may or may not work with other runtimes, libraries, platforms, etc.
With Go, async code written in Go 1.0 compiles and runs the same in Go 1.26, and there is no fragmentation or necessity to reach for third party components.
The places where it poses challenges in my experience are high quality typesetting/rich text, responsive UI that requires a wide range of presentations (i.e. different layout structures on mobile vs desktop), and granular control over rendering details.
But for functionality-oriented UI, it's hard to beat its simplicity and scalability, particularly when you want to develop on one platform (e.g. computer) and have everything "just work" identically on other form factors (web/tablet/etc.).
For example, Godot's editor is bootstrapped/built in Godot, and runs natively on Web, Android, and Quest XR among other platforms.
Godot is pretty lightweight (especially considering how powerful it is), generally about a second or less. But maybe they are looking for a fast enough startup time that the engine can be started when showing something onscreen and torn down when not visible. In which case, I can see the startup time being an issue.
Much of the problem stems from inconsistent application of icons. This would have gone better if Apple established (and followed!) clear guidelines for exactly which icon to use for which standard action (e.g. search).
That the icons exist is not necessarily a problem, since they can help teach users which buttons in the UI do which actions. (menu bar for discovery, app UI for less mouse travel + contextual options). But that requires consistency, which the current implementation lacks...
Accordion behavior is discussed in the article in the "Accordions / Expanding Content Panels" section:
> Use the same name attribute on all related details (like radio buttons) to restrict only one open panel at a time
And tabs can be a <details>-based accordion with some creative CSS to adjust the layout (left as an exercise for the reader, but I could write up an example if that would be helpful!)
Yes, the tabs in a tabs pattern should be keyboard navigated using arrow keys (ironically not the Tab key).
Also, the summary for the currently open details element will have the wrong state, 'expanded' instead of 'selected'. And while a set of details can now have a maximum of one open at a time, you can't ensure exactly one is always open (without JavaScript) as the tabs pattern requires.
For example, say I develop some object (scene) in Godot Engine. It interacts with the environment using physics simulation, renders 3D graphics to the screen with some shaders and textures, and plays back audio from its 3D location.
I can send this scene to some other user of Godot, and it will naturally just work in their project, including colliding with their objects, rendering in their viewport (including lighting and effects), and the player will hear the location of the object spatially.
Of course there is much more you can do in Godot, too: network requests, event-driven updates, localization, cross-platform inputs, the list goes on. And all of these compose and scale in a manageable way as projects grow.
Plus the engine provides a common data and behavior backbone that makes it possible for a single project to have code in C++, C#, GDScript, and even other languages simultaneously (all of these languages talk with the engine, and the engine exposes state and behaviors to each language's APIs).
In fact, I've been thinking about making a Godot-inspired (or perhaps even powered) business application framework because it's just such a productive way of building complex logic and behavior in a way that is still easy to change and maintain.
So I imagine if Cambra can bring a similar level of composability for web & data software, it could dramatically improve the development speed and quality of complex applications.
reply