Comparing htmx (modifiers in html, lot of abstraction) vs remix (front/backend in same file) and then the readability of rust (lot of chaining/calls and "decorators" eg. tokio). It just works but if you get used to that syntax/rely on abstraction. I'll learn what's hot to be employed but yeah. I've been happy with just ReactJS/Express.
Right and computers wouldn't advance more/be capable of cool things like VR.
Seems like one day you'll just say "import website" and it's done which for me isn't fun but does get the job done. Keep up with the change or get left behind.
At my job they outsourced the html/css writing so we just glue stuff together, it' s depressing.
If your company outsources the HTML/CSS, then presumably the people who are working at the company are working on the backend services and none of the frontend at all?
Haha ownership is trying to replace people with AI. Gave a presentation on chatGPT... content writer/SEO people sweating. It's just funny/ironic the infighting in my viewpoint, they don't trust us to make high quality front end code so they pay another company to do it and then we have to templatize/glue it back together. Oh well... sucks to suck move to another company.
edit: response to below, the html/css stuff they did outsource to other humans, I'm saying it's a joke because that's our job as frontend devs
Sure, but is it really progress to swap our tooling around every couple of years? Do you know what users think about the progression of spaghetti js, to jquery, to backbone, to angular, to ember, to react, to svelte, to htmx? They don't think about it. It literally does not matter to them except that your site may have better (or worse) performance and fewer (or more) bugs.
<button hx-post="/clicked" hx-swap="outerHTML">
Click Me
</button>
But that is rarely what I want to do when a button is clicked. My typical example would be I have list of - say - cars, and each car provides a button to delete it:
<button onclick="carList.deleteItem(this)">
Delete this car
</button>
And then in deleteItem(), I typically:
let item=getItem(e);
if (!confirm(`Really delete ${item['name']}?`)) return;
let item_id=getItemId(e);
items.splice(item_id,1);
document.getElementById('items').innerHTML = template(items);
I think where htmx would differ in your example is that it would not maintain the state of the list of cars in javascript, but would instead keep that state in the html, and the server.
Also if you didn't want to do a hx-confirm, you could pop up a separate modal on button press. And then when the delete is pressed in the modal use a hx-target to update the specific row.
Hmm.. I don't think you can simplify their example to just that one line. For example, where is the url which is supposed to be requested to delete the item on the server.
Beyond these very limited examples I just cannot see any appeal of HTMX... The real world large web apps have a ton of state that needs to be updated, UI elements that need to be swapped, multiple remote calls that need to be made on these actions. Why would you push all of that to the BE?
A simple click is almost never just "delete a row": you need to update the row count in the UI, validate whether it's the last row and therefore can't be deleted, send a log message that pulls data from user state (are you going to construct this state in the API handler every time a row is deleted?), update a heavy graph that shows a visualization of the data in rows, etc...
Maybe some very basic websites can survive with HTMX but that's incredibly not future-proof.
in htmx you would do all that through a single action and target the area necessary to update
if you had something like a count that was outside that area you could use an event, an out of band swap or just expand the target. we also have morph-based swaps that make it possible to retain more UI state while expanding the target area, if you'd like
if you have some sort of visualization, you'd recompute it server side and stream it back, or use events to trigger an update
adam james has done some cool stuff w/clojure and htmx visualizations:
you are correct that htmx isn't for every application, but it can likely pull off a lot more than you think, and at much lower complexity levels than reactive options, which has been borne out by real world experience:
as far as htmx being future-proof, I would point to it's predecessor, intercooler.js, which was released in 2013 and is still actively supported, simply because the code isn't hard to deal with. you could probably figure out and maintain htmx.js in a day or two, as a good JavaScript developer, something that can't be said for most other javascript frameworks.
I started working in the early 2010s doing stuff on the backend was pretty normal with asp.net razor pages. Going from razor pages + jquery to AngularJs was one hell of transition.
I think i can like HTMX because its backend agnostic just template it out and return the html. Given 99% of the angular work i do is "get the json, render the returned json to html in angular". I think HTMX can pretty much replace angular with those sort of tasks. That makes being full stack dev quiet easy only need to know my backend stuff, html(x), css(tailwind).
Maybe you need some lightweight frontend state framework to do a bit more in the frontend.
But its a shitload less to learn than angular, node, typescript, build tools etc etc.
I would argue that HTMLX is far less backend agnostic than something like React. In most cases, a React SPA just consumes an API. You could switch to an entirely different backend with no impact on the users. How easy would it be to migrate all your HTML templates to a different backend?
Well from experience backend tech stacks are way less mutable.
But let assume you did want to change backend you will take into account your HTML templates and find the right template engine where you might just copy past your templates folder and do some light configuration work.. If that is not possible refactoring the templates parts shouldn't such a big issue.
I see HTMX more as a way to keep far and far away from frontend frameworks, I can focus on being productive with Html, CSS and HTMX and call it a day. With react you need to learn HTML, CSS, moderate amount of javascript/typescript, state management, build tools etc etc.
But you make a good point about React SPA just consuming json or whatever other non html data format.
You'd use the hx-delete attribute and have a server side handler that responded to that by deleting the item & returning a new list of items. You can see an example of that approach (with Rust + Actix Web) here that I'm currently working on (the partial to list todo items is in templates, and you can follow the delete link to the relevant handler in routes/todo/delete.rs):
Normally I attach the ID to any newly created item via a templating engine so that when you use `hx-delete` to send a DELETE request, you'll be able to easily refer to what item you want to delete.
This is approaching the code size of the vanilla js version. And it does not have the confirmation modal.
If there would be additional computational logic involved (say calculating how many rides this car has done this year so far to say "Really delete this car? It delivered 124 rides this year.") - would you also cram all of that into the button element?
What I am trying to say: I don't see which real world use cases are tackled by htmx. I usually don't have a use case as simple as "When element X is clicked, load url Y and replace element Z with it".
From an htmx perspective, you would probably calculate things like that on the server. So the row delete button might have an hx-get="<delete-confirm-url>" that returns an HTML snippet with your modal content ("Really delete this car?") and then the button in that modal would have hx-delete="<delete-url>".
Of course you can still add vanilla JS with htmx, and/or send the car.rides_delivered value from your model as part of the initial page load, etc. You don't have to do everything with the hx- attributes.
In my opinion htmx is excellent when you have a state that can be easily managed on the server, because it makes it simple to get an interactive SPA feel while still keeping state on the server. I think a lot of the htmx fan base would argue that most web apps don't need to manage their state client-side and therefore don't need all the overhead of React, etc. But of course htmx isn't a cure-all.
You can check out the writing on htmx.org if you're curious to hear more of the author's perspective and goals.
I would say, yes, in this case if you count your html for the button + the ajax request you would need to do to notify the server HTMX is shorter.
But, let's talk about the confirm. For a simple one, you would use hx-confirm and be done.
However, for a more complex confirm, you would make a delete request that would answer HTML.
The HTML would be showing the confirmation with the data calculated from the server (124 rides this year).
The response doesn't have to be a button, the server can answer a modal or a new subpage, which would contain a hx-delete with a "userconfirm=1" in qs and a cancel button to go back.
You don't have to limit yourself to replace the current element with the response, and the response is not limited to basic workflow.
Also HTMX is mostly about using hypermedia to communicate and favor the server to manage the state, but there is nothing saying you can't use JS.
You can even be naughty and have new HTML responses inject new JS in the page.
> This is approaching the code size of the vanilla js version.
Code size isn't everything. The htmx version is more restricted, consistent and declarative. Assuming you get familiar with htmx, if you come back to both code snippets in 6 months, which one will you able to figure out faster?
The HTMX-oriented response would be, if you anticipate needing to know how many rides a car has completed, why not calculate that on the server or in the database, as opposed to in real-time on the client (in which case, presumably, you need to deliver a LOT more data to the client, such as a full list of all rides so you can calculate those which this one car was involved in).
> I don't see which real world use cases are tackled by htmx
Because you are prioritizing state on the client. Free yourself of that, and the use cases for HTMX start expanding enormously.
Sure, but you've gotten rid of a shit-ton of complexity from your stack. No REST+JSON api/graphql. No frontend SPA. The templating system you use can call your db layer directly (or go through an intermediary layer if that's your preference). You solve the "single language" problem, with the backend being favored (rather than the "use npm on the backend because the browser speaks js/ts" approach of the last 10 years). Performance is kept reasonable because you're swapping out elements mainly rather than rerendering the whole page (i.e. it's not a return to servlets/cgi).
Humanized time means, now, a second ago, 3 hours ago etc.
When you create let's say a post and it's returned the rendered humanized time, every post you create will return "now" as its time.
And next how to you keep track of the humanized time client side.
Why does this need doing? Pages aren't kept open for that long, especially not when using HTMX and page loads happen more often. Saying "now" for the post you've just submitted isn't that useful, but neither is saying "1 minute ago" when you leave the page open for 1 minute. Humanisation of date times is easy to do server side, so the next time you render the page it will have newly correct timestamps. The vagueness of humanised times also reduces the need to update as they can be quite out of date and still look up to date.
It depends, but typically you wouldn't keep a page open long enough to be able to do this.
If you were, you could probably use an SSE stream that maps to humanised time and then send an event every second. However, I'm not sure this is 100% optimal as it requires an update every second which is probably more overhead than you want.
Came to upvote this. I think that github component in particular worked really well for me on a recent experiment. Server-rendered HTML can use custom components in our fancy world today! It's a feel-good place to be compared to just a decade ago imo.
Not really an htmx user or supporter but as an outside observer I think this can be accomplished but it might not be what you want -- which is okay.
So, if I understand correctly, I think what you're saying is you "create a post" and the timestamp might have "now" listed as a humanized representation of it being just posted. When it reaches 1 minute, what you're saying is you'd like it to say "1 minute ago" etc. I think this is generally against the goals of htmx and the way you'd accomplish this is one of two ways:
* Find a humanized representation that does not need constant updating
* Use ordinary clientside javascript alongside htmx rendered data attributes to update the output.
I don't think htmx aims to do loads of rerender cycle updates like this. I don't think that means its not a good library, just not a good fit for sites that need this. There are plenty of tools like django templates, rails templates, hugo sites etc that all act like this and require some hand-authored clientside js to handle updates. I think the goal of htmx is to require *as few as possible* without overtly adding js to clientside runtimes.
The point of htmx is to introduce more flexible tools for interacting with the server through html. Instead of being limited to forms which redirect you to a new page when you click submit, you can send requests and dynamically replace parts of the page on arbitrary events.
You can use htmx to regularly send the server the current time and have it respond with an updated humanized timestamp, but that's not really what you want. You're looking for some client-side display sugar, which is not the type of problem htmx was designed to solve. You can just use a client-side javascript snippet. htmx complements js; it doesn't replace it.
Don't know why this would be a problem with htmx in particular; I'd expect this to work like with every other site, by using moment.js and a JS timer event changing date text into a relative date/time. Not a good practice to send "now" or other indeterministic data as string in a server-rendered page anyway, and I wouldn't expect anyone to do it like that (well, except for fingerprinting or sth).
Though I don't even know if it's possible to tunnel your own JS through hmtx pages after all tbh. If not, that would be a problem/a no go from my PoV as I see absolutely no reason to add (or subtract, for that matter) syntax constructs to/from HTML when SGML has plenty, and in fact can already describe adding or replacing fragments at a purely syntactical level without mixing in opinionated "semantics".
Unsure why these kind of questions always pop up around HTMX. I do not think HTMX sets out to solve state problems. HTMX to me is an easy way for me to render HTML partials. If I needed to do something like update state such as your example, I would write javascript function inside of that html partial to update the time based on the posts timestamp...yes I am sure there is some complexity or issue around the clients, time and the posts timestamp I am handwaving that issue for the sake of the example.
HTMX is great for rapid prototyping, but Rust has never struck me as suitable for rapid prototyping. There are some nice abstractions here than reduce the amount of code for sure, but that's only one aspect of rapid prototyping. Rust still has a high learning curve, compiles relatively slowly, and still takes a lot more code to achieve things than the equivalent Python or even JavaScript/TypeScript.
Shuttle looks pretty cool as a platform, but it seems that it has decided that because Rust is a good language for a platform (makes sense!) that it must be good for usage on the platform. I really don't think I agree with this.
In my experience, Django + Heroku (or the Heroku competitor du jour) is a convincing rapid prototyping setup for larger sites. And things like RunKit or Glitch are a convincing setup for smaller builds. I'm not sure whether Shuttle adds anything over these options unless the requirement is to use Rust.
> HTMX is great for rapid prototyping, but Rust has never struck me as suitable for rapid prototyping. There are some nice abstractions here than reduce the amount of code for sure, but that's only one aspect of rapid prototyping. Rust still has a high learning curve, compiles relatively slowly, and still takes a lot more code to achieve things than the equivalent Python or even JavaScript/TypeScript.
In order to support the borrow checker, Rust has a richer type system that allows programmers to encode far more of the business domain than most other languages. Short of logic bugs, the vast majority of my Rust code is in the "if it compiles, it works" bucket which makes it great for rapid prototyping despite the slow compile times. I have to spend a lot less time and mental bandwidth on testing and defensive coding and Rust's version of the public/private distinction o makes it great for wrapping the data model behind types that can only be used correctly - like making sure a user id can only come from a User model type instantiated by the database code and not an arbitrary integer.
My favorite part though is that web frameworks like Rocket make it really easy to manage all of the magic going on behind the scenes using trait definitions, in contrast to mega-frameworks like Django. (Rocket was created by the Flask author iirc)
Rust's richer type system provides relatively little value over other contemporary type systems like those of Python, TypeScript, and C#, while not providing anything remotely resembling an "interactive" development experience (no REPL, incredibly slow compilation, not hotpatching), and imposing the extremely burdensome and completely unnecessary burden of manual lifetime management.
Until these flaws are fixed, Rust is utterly unsuitable for rapid prototyping.
> it seems that it has decided that because Rust is a good language for a platform (makes sense!) that it must be good for usage on the platform.
This isn't true - or at least it wasn't our thought process when starting to build Shuttle. Yes Rust made a ton of sense as the language with which to build the platform, but the point of Shuttle was always about providing a great developer experience to the end user. _That's_ why we chose Rust for the 'front end' of the platform.
Our position was that the amazing type system combined with generics, metaprogramming (macros) and excellent compiler errors would enable us to build something truly special when it comes to how devs build on Shuttle.
That's fair about it not being your thought process, but do you think your familiarity with Rust may have affected how you assessed the suitability?
Your view on Rust matches my views on what makes Rust a great language, but I don't feel like they make Rust a fast to develop language.
- Type systems are great, but pay off more as a codebase grows. Rapid prototyping skews towards small codebases. Type systems do have a non-trivial cost, and that has outsized impact on smaller codebases.
- Generics are nice, but small codebases have less need for them.
- Metaprogramming and macros are nice and allow building new abstractions, but smaller codebases need fewer abstractions. They also add to the learning curve, as they come with their own learning process.
- Compiler error messages are indeed helpful in moving faster, but while Python's error messages aren't great, I hit fewer of them because the language is more flexible, more things "just work" and fewer things are errors. Same for JS, etc.
Correctness and safety are great, and I think Rust gets these much faster than other languages, but memory safety is a big benefit of Rust that is entirely unnecessary for almost all rapid prototyping. Speed is also a benefit, but is again rarely needed for prototyping and early stage projects.
I'd love to understand more why you feel that Rust is faster, and not just for Rust developers.
I also have done rapid prototyping in Python and Rust, and have started only using Rust for such projects.
- Tooling and packaging are just better. I find myself fighting Python sometimes when setting up testing with pytest, or packaging with poetry/pdm. This overhead is eliminated with Cargo.
- I like to write small PoCs for complicated data models, to show how they interact before integrating the concepts into a larger system. I use to do this in Python, but switched to Rust. Python's poor typehinting makes it very hard to know when the concept I'm implementing actually doesn't work. Instead I have to write tons of unit tests to validate it, which ends up taking much longer than using the typesystem as a form of unit testing, and then writing a few logic validating tests.
- Large refactorings are much easier in Rust. This is absolutely critical for rapid prototyping, because it's common to rip out large systems. The typesystem tends to point me to every area of the codebase I need to worry about. I've started considering it a code smell if related functionality isn't evident through the type system.
- At least in my experience, it's easier to organically grow a codebase. I feel comfortable using a ton of `unwrap`s and `todo!`s in my codebase, because it's easy to grep for them and retrofit proper error handling. I can change a function's type to include a `Result` and the typesystem will direct me to every location I need to update to properly implement error handling. Most languages I work with I feel I must do everything roughly correctly from the beginning. Rust makes me comfortable hacking stuff together, because there's a clean way to slowly remove the tech debt.
Hopefully that gives you some idea why Rust can actually be an amazing prototyping language. It can be hard to see when you're still learning the language, but once you have, you rarely run into weird lifetime compiler errors. It really is a language that teaches the programmer.
> Tooling and packaging are just better. I find myself fighting Python sometimes when setting up testing with pytest, or packaging with poetry/pdm. This overhead is eliminated with Cargo.
Python's package / project management story is seriously abysmal compared to its competitors. It's crazy that for a language with as much adoption the best we have is poetry. Adding and isolating project dependencies should not be a difficult thing to do, it should be first class in any serious language like it is in node, ruby, elixir, rust, etc etc etc.
I think they are making steps with Pipfiles, but yeah it's definitely not great. I had to use Python for a hackathon a while ago and it was probably one of the worst experiences I had trying to get set up with my group.
Thanks for the input, and I completely agree in the value of all of these aspects, just not so much for prototyping.
Tooling and packaging disappear with a sufficiently good prototyping environment (and Cargo doesn't completely eliminate this in my experience anyway), Rust's type system means also working with the borrow checker which is not useful for data modelling (outside of safety concerns), large refactorings aren't necessary in small prototypes (even as things change).
You're right that it's easier to grow a codebase organically from a good starting point, and Rust will offer that, but I'm not sure that's the right focus for rapid prototyping. The aim is not about setting solid foundations, it's about testing ideas.
Rust is nice, don't get me wrong. I don't disagree with any of these points. I just think that they're all in the mode of improving "traditional" backend development, not in the mode of making rapid prototyping as good as it can be.
Re data modeling: If you’re using copilot or something similar I’ve found it really fast to drop in a comment with a sample of the data and then quickly crank out the appropriate types. It doesn’t get them all perfect in one shot usually but it reduces the overhead enough to make it effectively free when considering the safety and refactoring benefits.
Also rust offers you out of the box tests, so you can write tests to prove your code does what you'd expect, before finish the whole prototype, it helps a lot versus most languages, most of the time you can just run `cargo watch -q -c -x test` and start hacking.
Many other languages have similar easy ways of running tests on file changes.
For example, in (Node)JS, you can just do `npx jest -o` if you are using Jest testing library, and with Scala you would usually use `~ test` in sbt shell.
This is likely just a me problem, but I always found Jest extremely annoying to set up with React and TypeScript - particularly with attempting to follow the documentation for jest and then failing spectacularly (in spite of using React Testing Library, jsdom + jest).
Cypress on the other hand I found to be really, really good
Cypress and Jest aren't really operating at the same level though. Cypress competes more with Playwright (functional, E2E tests), and Jest competes more with Vitest, Chai, Mocha, Jasmine for unit tests (though it can also do much more).
Personally I prefer Vitest to Jest... if it will work for the codebase I'm in.
That's not the same thing, I think he's referring to the fact that in Rust you can keep unit tests and code in the same file, and strip them away at build time. That's not a thing in the js world (and most other languages), where tests must be in a different file and you are forced to export functions to test them, even if you wanted to keep them private in that file.
To be honest you might be able to use just the built in node test runner (deno and bun also have their own test runners), but IMHO are not as ergonomic as Rust, in Rust you can test private functions in the same file, and that let's you prototype faster, you write a simple function and a test (even when the prototype is not fully functional).
In JS land you could do the same I think, but you would need to export all methods that you want to test and they are in other files, and to make things worse you need to setup Typescript to make your IDE more useful and also write less tests, I would argue that in that case vite/vitest will be more helpful because the tests runs with the same config as the bundler, but still, it does not work out of the box as Rust, there might be other languages that have also good ergonomics as Rust in that regard, but none of the languages that I use besides Rust do that.
You cut down a chunk of tests by not needing to figure out what type something is. You can also use assert! and assert_eq! in your main code without even using tests which also cuts down on tests, where applicable.
You can also write unit tests in the same files as your main code. Eventually you'll want some kind of test suite when your codebase gets big enough, but it works well enough that you don't need to if you don't want to (for a small codebase).
Frameworks like Axum also of course support testing - for instance this crate lets you quickly mock up your server without having to do much: https://docs.rs/axum-test/latest/axum_test/
When you are prototyping you change code here and there, so a type system helps in that regard and I consider type system as a kind of test. So by having that built in, you need to write less tests, but AFAIK python has an optional type system, not sure how that works, but I suppose it would help in that regard.
I guess if you're argument is for the watcher...great but I use `entr` https://github.com/eradman/entr when I want that feature and it just works across languages but I've not used continuous testing for ages.
I never considered Django (or anything python related, eg. FastAPI) to be something good for prototyping because the package manager is a nightmare, deployment is clunky, I often hit pain points or limits in Django.
Node.js before the whole require/import mess was neat and simple. All the libraries were working nicely, no problems with npm. Nowadays it's a hit or miss experience.
I'm using bun these days to prototype and if you restrict yourself to bun stuff you're good and fast and zero hassle.
Rust, contrarily to JS (or TS, the land of any) or Python, has serious safety belts.
This means for prototyping it won't be as fast, but you'll sleep well at night.
In my experience, if you're not a beginner you'll be able to be fast and iterate quickly with Rust as well, albeit it's not the same. On the other side, once you're done with your prototype I feel way more confident in my Rust prototypes, compared to my JS ones.
So, does it really slow you down?
The Shuttle boilerplate is nice, but it doesn't really add that much, the equivalent setup in Axum is not that much of a bother (I tend to use a axum + sqlx template I built for new projects).
Shuttle the platform is nice, albeit I remember being annoyed by the limits of the free plan (I know, I know, I am cheapskate but I don't want all that nice marketing money go to waste), I generally deploy on fly.io or some more cost effective VPS.
> but Rust has never struck me as suitable for rapid prototyping
Having Rust a lot, and coming from JavaScript, I'd say it depend at what stage of prototyping you want to stop:
If you want to make a one-shot implementation of a quick demo without ever iterating on, then, yes, Rust is going to slow you down.
If your prototyping effort involves lots of iterations because you're learning in the process (learning about the use-case, the market and so on), then Rust quickly pays of compared to JavaScript or say Python because refactoring is straightforward and you're almost never going to break things in the process. Most of the benefit comes from static typing alone, but Rust type system allows you to express things in a more robust way than C# or Java for instance, leading to an even more streamlined iteration cycle (but it's not a game changer either).
But the place where I've found Rust to shine, with no match whatsoever, is going from the stable prototype to the production ready stuff: in addition to the ability to get the best achievable performance on your hardware, I find the error handling to be the killer feature in that scenario: when prototyping, you use unwrap everywhere without paying attention (and it basically behaves as in a language exceptions where you don't catch them) but then you just ctrl-F `unwrap` and you can deal exhaustively with all possible error scenario with very limited effort. Ensuring after the fact that every possible exception gets handled properly is a much harder task.
I can spin up a generic Axum backend service within probably about half an hour to an hour that contains most of the things I need (auth, CRUD, similar things) once I know what I need, configuration and business logic depending. I think the key phrasing is primarily "when you're familiar with Rust" - if you're not familiar, then it will definitely be a slog.
Certain things that would require you to manually implement a complex trait (for example, Send + Sync + 'static plus whatever else) would probably slow you down though, but presumably by that time you probably already know what you're doing.
Do you have a goto library for auth in Axum? Maybe I just don’t implement it often enough but auth in Axum usually takes me an hour at least even if I’ve implemented it somewhat recently.
>> The place where I've found Rust to shine, with no match whatsoever, is going from the stable prototype to the production ready stuff
Love it or loathe it, spring boot is going to win that race every time.
A kind of sweet thing about modern spring is it can be pretty easy to rip it out when you've arrived at your feature complete state. So if you wanted to, you could get rid of reflection without losing JIT for example (i think Graal is getting JIT eventually, which would be the other way to force yourself rid of reflection today, so maybe this becomes moot in future).
But either way, if you approach your spring boot development with this exit strategy in mind, you can reap the rewards of removing a big project dependency to own over time leading to cheaper TCO long term once the project has reached feature complete state. It'll even likely be slightly faster if you're ripping out all those classes from the classloader, removing all that reflection and proxies etc. - but you can do all this while scoring the benefits of rapid iteration & extensive code reuse / building on the shoulders of giants, at the start of the project by exploiting spring and especially by exploiting boot.
Could you do something like that with say Axum? Maybe. Probably not.
I mean that's quite a niche idea spurred on by your next comment i've quoted below but even if you're not interested in eeking out the last say in performance and cloud memory footprint costs then spring boot will still more than likely be absolutely fine without doing any of this. And you'll be done sooner too of course.
As for production, does that mean stuff like robust and safe, automated scrubbing measures for handling developer access to sensitive prod memory dumps for diagnostics? Or maybe just mature observability tools - it's like bringing the "tokio console" knife to a "JMC & JFR" gun fight.
I guess my TL;DR here is just: Java. But it could equally be C# or maybe even something else. It's definitely not "with no match whatsoever".
>> in addition to the ability to get the best achievable performance on your hardware
I've ended up spewing so much above i don't feel like going much further and it was actually this part that made me reply.
What you're saying seems unlikely - you're claiming not just fast but the best possible performance on given hardware. So we're talking really annoying things to own like repurposing #[repr(C)] not for FFI but to allow explicit ordering of struct fields to maximise memory throughput. We can do that kind of stuff in Java too (heck most languages allow something like this), java has some unsafe off-heap facilities which can be (ab)used for extreme cases - although there's potentially much nicer and safer APIs in the latest Java which i haven't looked at yet - https://openjdk.org/jeps/442
Best achievable performance usually means for one part of your process at the cost of another code path - when you get down to the most extreme levels, it becomes trade offs. If you want to go that far in java, you can. It's not like you're getting extreme performance for nothing by going another way. E.g. rustc won't magically recognise every time i could use vector instructions, you have to write your code careful of branching and dependencies, be friendly to the compiler for it to do its magic. But of course, we have vectorisation (and even some auto-vectorisation) in java too.
Not every project has to be suitable for every team of developers. Rust has a good number of people that both like it and are also deeply familiar with it, so it makes sense someone would build something that caters to them. Rust can be great for rapid prototyping when everyone on the project is already a Rust expert.
- serde; this beats both cpp and Python in terms of not spending a second of dev time on implementing serialisation deserialisation unless you're doing something funky. I would argue that this is one of the cases where it's much harder to handle it in Python than in rust
- other proc-macros allowing you to quickly map your structs to sql or whatever else
- lsp that works with zero configuration and beats many languages (again, cpp and Python included) because of rust's strictness, and rust-analyser is an absolute pleasure to work with
- cargo tooling and zero-pain dependency management; high quality of packages in general
Once you get past the initial learning curve and get comfortable with the language, these factors (and quite a few more not directly related to the language itself) eventually become more and more important for prototyping. (all subjective of course)
It's not "they", it's whoever wants to implement serde support for their own thing, can do it (so, many 3rd party crates have a "serde" feature that can be toggled on/off).
> Which format is zero penalty
This one is tough to answer unless you provide details re: what's your in-process data format, what's your target serialization format and whether it's binary or not, what's the objective function (serialization speed, deserialization speed, storage efficiency), and what exactly you mean by 'zero penalty' (which penalty?).
// note that there's also alternative frameworks worth mentioning like rkyv and borsh-rs, each optimizing their own objective function
When I wrote C++ full time I frequently prototyped algorithms in Rust.
Prototyping in C++ is awful: it's so easy to accidentally trigger UB and then you're wasting time debugging some memory corruption instead of exploring your problem.
Rust is close enough to C++ that you can get a good idea of equivalent C++ performance, and it isn't too hard to translate the prototype when you're finished. An exception being porting Serde (de)serialization over.
> The runtime will then do static code analysis to figure out what needs provisioning and will then spin up the relevant infrastructure required - for example, if you need a Postgres instance, you can just declare it in your fn main arguments, use cargo shuttle run to run locally and then it'll spin up a container for you using Docker without any further input on your part!
I've never heard of Shuttle before, that is wild. Maybe I'll play with it soon
If you don't want state management at the UI layer, totally understandable, move it to your server. You don't need a new framework with some whacky untyped DSL to do it. The swings in the front-end community move from one extreme to another so violently.
> You literally do though, if you want to keep user experience on par.
On par with what? Use any of your existing UI frameworks and instead of keeping state client side, let the server tell you. Instead of doing things like eagerly updating client side state (deleting an item -> removing it from your in-memory array), let the server tell you after it's processed and updated its state. Dumbly reflect what the server returns.
I understand this isn't what the tutorials for React, for example, show and breaks away from all the auxiliary things they've been using with it for state management, but there's nothing stopping you from doing this.
You can do all of these things today. You don't need to adopt this. You don't need to move to server side templating and downloading chunks of HTML to inject into elements.
>Instead of doing things like eagerly updating client side state (deleting an item -> removing it from your in-memory array), let the server tell you after it's processed and updated its state.
So this literally what htmx does, no?
Except You'd be operating on framework-sourced components rather then built in html structures.
And the swapping itself would be performed by frameworks code rather then htmx library.
Is that what you mean?
If so then sure, I get that it would be cleaner then with htmx, but logic is the same and htmx simply let's you expand this logic for core html elements, rather then having to remake all of them into frameworks components that can handle the partial updating.
htmx isn't supposed to replace templating engines, it's supposed to provide the "last mile" to make your SSR pages (usually rendered with some template engine) more dynamic.
I think it's a good idea. I have been writing vanilla JS for SSR'd pages for a while and inevitably reimplement some common patterns over and over (such as "load another page, select part of it, replace part of current page"), of which htmx looks to do a good job of capturing.
> it's supposed to provide the "last mile" to make your SSR pages (usually rendered with some template engine) more dynamic.
I completely agree. It's basically breathed new life into my job's antiquated .Net SSR [Razor] pages.
There is nothing HTMX is doing that I wasn't already able to accomplish with vanilla JS or Jquery, but holy shit has it reduced my need for either (which has also saved me time too).
That gives me a button which, when clicked, POSTs target_stewards=1 to /shifts/33/edit/ and replaces the nearest div class="shift-mini" with the HTML returned by that endpoint.
The fuck is this? Is this a string wrapping JSON in an HTML tag? In 2023?! And is the second one some weird DSL that targets an HTML element by CSS class?
Why would I learn this. What does it look like to use in a large code base? I think the main difference between me and the folks who love this is I don't hate Javascript enough yet to explore this life.
> It's a DSL in HTML attributes which covers almost all of what I need to build a dynamic web application.
Not quite though right? There's a very tightly coupled (and fairly useless for anything else) sibling endpoint for each of those calls, which exists on a backend somewhere to return the html that you need to progress.
The alternative exists. You can spin up a rest API around a database schema in all of 5 minutes (eg. Pocketbase). Put alpinejs on the front end and you have everything htmx can do with no need for the server to get involved in whether or not a modal is open or closed on some client somewhere.
Unfortunately, the more of this I read the more I'm convinced. Especially in the context of rapid prototyping but also in larger projects.
I love serverside templating. I use go templates, asp net razorpages and old laravel blade across various projects. It's hard for me to see the enthusiasm for htmx as anything but an overcorrection from react, and the project itself feels like it chooses ideology over function (nothing wrong with that, just not great for getting things done).
In a year I do think we'll see the typical stream of rewrites.
> I use go templates, asp net razorpages and old laravel blade across various projects.
HTMX + Razor Pages/Views is so freaking nice. It's seriously increased reusability so much for me. HTMX has also greatly reduced the amount of JS I have to write for the same functionality I was getting with Vanilla JS.
Depending on what I need, I find the JS API that HTMX has to be very helpful, albeit I wish a few things were a tad better/more clear in the docs for that area (or I was just too much of an idiot to figure out). None the less, I have no intentions of jumping ship from HTMX anytime soon.
> Put alpinejs on the front end and you have everything htmx can do with no need for the server to get involved in whether or not a modal is open or closed on some client somewhere.
Modals are driven by database state, so you can't really get rid of that.
What's weird is it's an untyped string embedded in an HTML template that does things. The API signature is string in magic out. How do I unit test this? How do we know when someone fat fingered something?
GP probably means business logic, not setting background color on a div. Coupling HTML with business logic inline reminds me of the good old DHTML. I am really glad the industry moved on from that mess.
Business logic must always be executed server-side anyway, so regardless, the client is asking the server "show me the results of this applying business logic".
You can do this by having the business logic return a data structure that the client then renders into UI itself, but now you have a state synchronization problem, ie. you have to keep client data structures in sync with server data structures.
Alternately, you can use the classic REST pattern: hypermedia as the engine of application state (HATEOAS), and let the server literally tell you what to render, so client state trivially matches server state. This is the htmx way.
Do you have similar objections about untyped strings when setting the "class" attribute? The class attribute also "does things", like change layout, text styling and even behaviour in CSS animations.
How do you unit test any dynamic behaviour on HTML pages? There are multiple ways depending on what you want to check.
HTMX/Rust seem like great choices for making good architectural decisions, having long-term scalability, and having great performance, but certainly not for rapid prototyping.
If I'm validating a business idea / building a proof of concept, I'm using something like SvelteKit or even NextJS. Going the middle-of-the-road seems to me like either a waste of resources or a waste of time depending on your goals.
Having worked on a home-tooled stack similar to this with Rust and HTMX, I think HTMX suffers from the same issue as Tailwind where it's a huge step in the right direction, but just adding your functionality as strings in HTML loses you all the hard-fought type safety of the clientside-js-heavy solutions they are trying to replace. Locality of behavior is a great trend but it's going through tooling growing pains. I think in a few years we'll get versions of these tools that aren't just strings inside HTML props.
Rust can be pretty rapid if you unwrap() and clone() everything. You are building a prototype after all. Adding error handling, traits and modules can be done later if the project has merit.
But I don't recommend Rust for this kind of stack - only because Rust's ecosystem is not that stable. I swear rust has some of the fastest iteration, with sometimes really complicated setups out of any mainstream language. There's always some nightly build, some feature flag, some cargo setup that often gets in the way. Just consider that Axum here is only two years old. Built on top of and heavily tied with tokio, which is really popular, but no guarantees as the defacto solution moving forward.
I'd say anything else ts, python, sigh even golang would be better alternatives.
Here's to hoping Rust's ecosystem becomes more stable in the future!
I'm very much looking forward to things like generators/coroutines (or whatever they're called now) once they're made stable as well as async traits and whatever makes the Pavex framework able to be used on stable Rust.
Full-time rust developer here. We have never experienced instability of any form outside of what instructions the WASM target supports; statements like this tend to conflate 'what's shiny and new' with 'what works'. Code written yesterday will work tomorrow, and that's all that matters; pick the most popular thing by download count and stick with it and you will have zero problems. Nightly unstable features are best worked around by not using them, and everything else related to 'cargo setups' are caused by not reading the instructions carefully enough.
Funny that you then compare to typescript i.e. npm, which is infamous for actually having this problem.
Definitely! I remember my first thoughts when moving from Next.js 13 to using a HTML templating engine were "wow, this is much simpler" and "should've done this sooner". It's saved me quite a lot of time in terms of not having to think about props/SSR
Why would Rust need a Dependency Injection framework? It's functional, no? I really hope drifting OO practitioners don't wreck the language putting a bunch of OO stuff that is better done in functional programming.
It's not automatically generated though, it's just specified somewhere else. This is what makes OO projects so hard to read from the outside: you first need to figure out what thing is having an effect on what. In a proper functional style I can go to main and follow everything from there.
As someone else said, if you have some kind of interface to "mock" you can just make this a parameter to a function and pass in the "mock" versions for tests and the regular one for the real program (or select from a set of them based on some parameter or something). A framework would just mean I can't just read from main, I'll have to figure out which DI framework is being used, where that is configured and what things effect its decision making. I see why we ended up doing this in OO but I really don't see the benefit anymore in functional programming with the more direct (and no less flexible) approach.
Serious question: what is a dependency injection framework? If I need to pass in a mock/fake for testing, then I define an interface for that dependency and pass it in as a parameter. Takes a couple minutes. Why does this need a framework?
Of all the great things rust is top notch at .....rapid prototyping just isn't one of them. In fact just about any other stack is better at that. Elixir, Ruby, JS, Python all blow it away for rapid prototyping. Just my experience.
142 comments
[ 2.6 ms ] story [ 182 ms ] threadComparing htmx (modifiers in html, lot of abstraction) vs remix (front/backend in same file) and then the readability of rust (lot of chaining/calls and "decorators" eg. tokio). It just works but if you get used to that syntax/rely on abstraction. I'll learn what's hot to be employed but yeah. I've been happy with just ReactJS/Express.
Its never enough, progress continually marches forward. If we accepted no progress we'd still be running fortran/cobol and other old tooling.
Seems like one day you'll just say "import website" and it's done which for me isn't fun but does get the job done. Keep up with the change or get left behind.
At my job they outsourced the html/css writing so we just glue stuff together, it' s depressing.
edit: response to below, the html/css stuff they did outsource to other humans, I'm saying it's a joke because that's our job as frontend devs
Their primary example is this:
But that is rarely what I want to do when a button is clicked. My typical example would be I have list of - say - cars, and each car provides a button to delete it: And then in deleteItem(), I typically: Where "template" is a handlebars template.Would htmx have anything to say about this?
https://htmx.org/examples/delete-row/
The Examples page in general shows most typical use cases.
I think the button might be something like this, I'm borrowing from their [delete row example](https://htmx.org/examples/delete-row/).
In terms of your update example, yeah I think that's what htmx would do, just on the server, and returning rendered html.but also, I'm not trying to provide a complete solution, just a direction to look in.
A simple click is almost never just "delete a row": you need to update the row count in the UI, validate whether it's the last row and therefore can't be deleted, send a log message that pulls data from user state (are you going to construct this state in the API handler every time a row is deleted?), update a heavy graph that shows a visualization of the data in rows, etc...
Maybe some very basic websites can survive with HTMX but that's incredibly not future-proof.
if you had something like a count that was outside that area you could use an event, an out of band swap or just expand the target. we also have morph-based swaps that make it possible to retain more UI state while expanding the target area, if you'd like
if you have some sort of visualization, you'd recompute it server side and stream it back, or use events to trigger an update
adam james has done some cool stuff w/clojure and htmx visualizations:
https://twitter.com/RustyVermeer/status/1701048955105391044
a little bit of hypermedia-friendly scripting can go a long way:
https://htmx.org/essays/hypermedia-friendly-scripting/
you are correct that htmx isn't for every application, but it can likely pull off a lot more than you think, and at much lower complexity levels than reactive options, which has been borne out by real world experience:
https://htmx.org/essays/a-real-world-react-to-htmx-port/
as far as htmx being future-proof, I would point to it's predecessor, intercooler.js, which was released in 2013 and is still actively supported, simply because the code isn't hard to deal with. you could probably figure out and maintain htmx.js in a day or two, as a good JavaScript developer, something that can't be said for most other javascript frameworks.
I think i can like HTMX because its backend agnostic just template it out and return the html. Given 99% of the angular work i do is "get the json, render the returned json to html in angular". I think HTMX can pretty much replace angular with those sort of tasks. That makes being full stack dev quiet easy only need to know my backend stuff, html(x), css(tailwind). Maybe you need some lightweight frontend state framework to do a bit more in the frontend. But its a shitload less to learn than angular, node, typescript, build tools etc etc.
But let assume you did want to change backend you will take into account your HTML templates and find the right template engine where you might just copy past your templates folder and do some light configuration work.. If that is not possible refactoring the templates parts shouldn't such a big issue.
I see HTMX more as a way to keep far and far away from frontend frameworks, I can focus on being productive with Html, CSS and HTMX and call it a day. With react you need to learn HTML, CSS, moderate amount of javascript/typescript, state management, build tools etc etc.
But you make a good point about React SPA just consuming json or whatever other non html data format.
https://htmx.org/essays/splitting-your-apis/
https://htmx.org/essays/two-approaches-to-decoupling/
https://github.com/welshdave/actix-htmx/tree/main/examples/t...
If this is your case, I'm writing a series of 5 tutorials on HTMX with this in mind:
https://www.bitecode.dev/p/a-little-taste-of-htmx-part-1
If there would be additional computational logic involved (say calculating how many rides this car has done this year so far to say "Really delete this car? It delivered 124 rides this year.") - would you also cram all of that into the button element?
What I am trying to say: I don't see which real world use cases are tackled by htmx. I usually don't have a use case as simple as "When element X is clicked, load url Y and replace element Z with it".
Of course you can still add vanilla JS with htmx, and/or send the car.rides_delivered value from your model as part of the initial page load, etc. You don't have to do everything with the hx- attributes.
In my opinion htmx is excellent when you have a state that can be easily managed on the server, because it makes it simple to get an interactive SPA feel while still keeping state on the server. I think a lot of the htmx fan base would argue that most web apps don't need to manage their state client-side and therefore don't need all the overhead of React, etc. But of course htmx isn't a cure-all.
You can check out the writing on htmx.org if you're curious to hear more of the author's perspective and goals.
But, let's talk about the confirm. For a simple one, you would use hx-confirm and be done.
However, for a more complex confirm, you would make a delete request that would answer HTML.
The HTML would be showing the confirmation with the data calculated from the server (124 rides this year).
The response doesn't have to be a button, the server can answer a modal or a new subpage, which would contain a hx-delete with a "userconfirm=1" in qs and a cancel button to go back.
You don't have to limit yourself to replace the current element with the response, and the response is not limited to basic workflow.
Also HTMX is mostly about using hypermedia to communicate and favor the server to manage the state, but there is nothing saying you can't use JS.
You can even be naughty and have new HTML responses inject new JS in the page.
Code size isn't everything. The htmx version is more restricted, consistent and declarative. Assuming you get familiar with htmx, if you come back to both code snippets in 6 months, which one will you able to figure out faster?
> I don't see which real world use cases are tackled by htmx
Because you are prioritizing state on the client. Free yourself of that, and the use cases for HTMX start expanding enormously.
Humanized time means, now, a second ago, 3 hours ago etc.
When you create let's say a post and it's returned the rendered humanized time, every post you create will return "now" as its time. And next how to you keep track of the humanized time client side.
Riddle me this and I'll start believing in htmx
If you were, you could probably use an SSE stream that maps to humanised time and then send an event every second. However, I'm not sure this is 100% optimal as it requires an update every second which is probably more overhead than you want.
https://css-tricks.com/styling-a-web-component/
So, if I understand correctly, I think what you're saying is you "create a post" and the timestamp might have "now" listed as a humanized representation of it being just posted. When it reaches 1 minute, what you're saying is you'd like it to say "1 minute ago" etc. I think this is generally against the goals of htmx and the way you'd accomplish this is one of two ways:
* Find a humanized representation that does not need constant updating
* Use ordinary clientside javascript alongside htmx rendered data attributes to update the output.
I don't think htmx aims to do loads of rerender cycle updates like this. I don't think that means its not a good library, just not a good fit for sites that need this. There are plenty of tools like django templates, rails templates, hugo sites etc that all act like this and require some hand-authored clientside js to handle updates. I think the goal of htmx is to require *as few as possible* without overtly adding js to clientside runtimes.
You can use htmx to regularly send the server the current time and have it respond with an updated humanized timestamp, but that's not really what you want. You're looking for some client-side display sugar, which is not the type of problem htmx was designed to solve. You can just use a client-side javascript snippet. htmx complements js; it doesn't replace it.
Though I don't even know if it's possible to tunnel your own JS through hmtx pages after all tbh. If not, that would be a problem/a no go from my PoV as I see absolutely no reason to add (or subtract, for that matter) syntax constructs to/from HTML when SGML has plenty, and in fact can already describe adding or replacing fragments at a purely syntactical level without mixing in opinionated "semantics".
htmx does one thing: it generalizes hypermedia controls
that's it
Shuttle looks pretty cool as a platform, but it seems that it has decided that because Rust is a good language for a platform (makes sense!) that it must be good for usage on the platform. I really don't think I agree with this.
In my experience, Django + Heroku (or the Heroku competitor du jour) is a convincing rapid prototyping setup for larger sites. And things like RunKit or Glitch are a convincing setup for smaller builds. I'm not sure whether Shuttle adds anything over these options unless the requirement is to use Rust.
In order to support the borrow checker, Rust has a richer type system that allows programmers to encode far more of the business domain than most other languages. Short of logic bugs, the vast majority of my Rust code is in the "if it compiles, it works" bucket which makes it great for rapid prototyping despite the slow compile times. I have to spend a lot less time and mental bandwidth on testing and defensive coding and Rust's version of the public/private distinction o makes it great for wrapping the data model behind types that can only be used correctly - like making sure a user id can only come from a User model type instantiated by the database code and not an arbitrary integer.
My favorite part though is that web frameworks like Rocket make it really easy to manage all of the magic going on behind the scenes using trait definitions, in contrast to mega-frameworks like Django. (Rocket was created by the Flask author iirc)
I'm sad that the development pace for it has slowed down extremely significantly though.
unrelated individuals (Flask, Armin Ronacher; Rocket, Sergio Benitez)
you're right though that Armin is writing Rust these days though!
Until these flaws are fixed, Rust is utterly unsuitable for rapid prototyping.
This isn't true - or at least it wasn't our thought process when starting to build Shuttle. Yes Rust made a ton of sense as the language with which to build the platform, but the point of Shuttle was always about providing a great developer experience to the end user. _That's_ why we chose Rust for the 'front end' of the platform.
Our position was that the amazing type system combined with generics, metaprogramming (macros) and excellent compiler errors would enable us to build something truly special when it comes to how devs build on Shuttle.
Your view on Rust matches my views on what makes Rust a great language, but I don't feel like they make Rust a fast to develop language.
- Type systems are great, but pay off more as a codebase grows. Rapid prototyping skews towards small codebases. Type systems do have a non-trivial cost, and that has outsized impact on smaller codebases.
- Generics are nice, but small codebases have less need for them.
- Metaprogramming and macros are nice and allow building new abstractions, but smaller codebases need fewer abstractions. They also add to the learning curve, as they come with their own learning process.
- Compiler error messages are indeed helpful in moving faster, but while Python's error messages aren't great, I hit fewer of them because the language is more flexible, more things "just work" and fewer things are errors. Same for JS, etc.
Correctness and safety are great, and I think Rust gets these much faster than other languages, but memory safety is a big benefit of Rust that is entirely unnecessary for almost all rapid prototyping. Speed is also a benefit, but is again rarely needed for prototyping and early stage projects.
I'd love to understand more why you feel that Rust is faster, and not just for Rust developers.
- Tooling and packaging are just better. I find myself fighting Python sometimes when setting up testing with pytest, or packaging with poetry/pdm. This overhead is eliminated with Cargo.
- I like to write small PoCs for complicated data models, to show how they interact before integrating the concepts into a larger system. I use to do this in Python, but switched to Rust. Python's poor typehinting makes it very hard to know when the concept I'm implementing actually doesn't work. Instead I have to write tons of unit tests to validate it, which ends up taking much longer than using the typesystem as a form of unit testing, and then writing a few logic validating tests.
- Large refactorings are much easier in Rust. This is absolutely critical for rapid prototyping, because it's common to rip out large systems. The typesystem tends to point me to every area of the codebase I need to worry about. I've started considering it a code smell if related functionality isn't evident through the type system.
- At least in my experience, it's easier to organically grow a codebase. I feel comfortable using a ton of `unwrap`s and `todo!`s in my codebase, because it's easy to grep for them and retrofit proper error handling. I can change a function's type to include a `Result` and the typesystem will direct me to every location I need to update to properly implement error handling. Most languages I work with I feel I must do everything roughly correctly from the beginning. Rust makes me comfortable hacking stuff together, because there's a clean way to slowly remove the tech debt.
Hopefully that gives you some idea why Rust can actually be an amazing prototyping language. It can be hard to see when you're still learning the language, but once you have, you rarely run into weird lifetime compiler errors. It really is a language that teaches the programmer.
Python's package / project management story is seriously abysmal compared to its competitors. It's crazy that for a language with as much adoption the best we have is poetry. Adding and isolating project dependencies should not be a difficult thing to do, it should be first class in any serious language like it is in node, ruby, elixir, rust, etc etc etc.
Tooling and packaging disappear with a sufficiently good prototyping environment (and Cargo doesn't completely eliminate this in my experience anyway), Rust's type system means also working with the borrow checker which is not useful for data modelling (outside of safety concerns), large refactorings aren't necessary in small prototypes (even as things change).
You're right that it's easier to grow a codebase organically from a good starting point, and Rust will offer that, but I'm not sure that's the right focus for rapid prototyping. The aim is not about setting solid foundations, it's about testing ideas.
Rust is nice, don't get me wrong. I don't disagree with any of these points. I just think that they're all in the mode of improving "traditional" backend development, not in the mode of making rapid prototyping as good as it can be.
For example, in (Node)JS, you can just do `npx jest -o` if you are using Jest testing library, and with Scala you would usually use `~ test` in sbt shell.
Cypress on the other hand I found to be really, really good
Personally I prefer Vitest to Jest... if it will work for the codebase I'm in.
In JS land you could do the same I think, but you would need to export all methods that you want to test and they are in other files, and to make things worse you need to setup Typescript to make your IDE more useful and also write less tests, I would argue that in that case vite/vitest will be more helpful because the tests runs with the same config as the bundler, but still, it does not work out of the box as Rust, there might be other languages that have also good ergonomics as Rust in that regard, but none of the languages that I use besides Rust do that.
You can also write unit tests in the same files as your main code. Eventually you'll want some kind of test suite when your codebase gets big enough, but it works well enough that you don't need to if you don't want to (for a small codebase).
Frameworks like Axum also of course support testing - for instance this crate lets you quickly mock up your server without having to do much: https://docs.rs/axum-test/latest/axum_test/
I have literally never written such a test in a rapid prototyping context.
>>Test Discovery¶ >> >>New in version 3.2.
It's invoked by:
>>python -m unittest
Edit:
I guess if you're argument is for the watcher...great but I use `entr` https://github.com/eradman/entr when I want that feature and it just works across languages but I've not used continuous testing for ages.
I never considered Django (or anything python related, eg. FastAPI) to be something good for prototyping because the package manager is a nightmare, deployment is clunky, I often hit pain points or limits in Django.
Node.js before the whole require/import mess was neat and simple. All the libraries were working nicely, no problems with npm. Nowadays it's a hit or miss experience. I'm using bun these days to prototype and if you restrict yourself to bun stuff you're good and fast and zero hassle.
Rust, contrarily to JS (or TS, the land of any) or Python, has serious safety belts. This means for prototyping it won't be as fast, but you'll sleep well at night.
In my experience, if you're not a beginner you'll be able to be fast and iterate quickly with Rust as well, albeit it's not the same. On the other side, once you're done with your prototype I feel way more confident in my Rust prototypes, compared to my JS ones.
So, does it really slow you down?
The Shuttle boilerplate is nice, but it doesn't really add that much, the equivalent setup in Axum is not that much of a bother (I tend to use a axum + sqlx template I built for new projects).
Shuttle the platform is nice, albeit I remember being annoyed by the limits of the free plan (I know, I know, I am cheapskate but I don't want all that nice marketing money go to waste), I generally deploy on fly.io or some more cost effective VPS.
Having Rust a lot, and coming from JavaScript, I'd say it depend at what stage of prototyping you want to stop:
If you want to make a one-shot implementation of a quick demo without ever iterating on, then, yes, Rust is going to slow you down.
If your prototyping effort involves lots of iterations because you're learning in the process (learning about the use-case, the market and so on), then Rust quickly pays of compared to JavaScript or say Python because refactoring is straightforward and you're almost never going to break things in the process. Most of the benefit comes from static typing alone, but Rust type system allows you to express things in a more robust way than C# or Java for instance, leading to an even more streamlined iteration cycle (but it's not a game changer either).
But the place where I've found Rust to shine, with no match whatsoever, is going from the stable prototype to the production ready stuff: in addition to the ability to get the best achievable performance on your hardware, I find the error handling to be the killer feature in that scenario: when prototyping, you use unwrap everywhere without paying attention (and it basically behaves as in a language exceptions where you don't catch them) but then you just ctrl-F `unwrap` and you can deal exhaustively with all possible error scenario with very limited effort. Ensuring after the fact that every possible exception gets handled properly is a much harder task.
I can spin up a generic Axum backend service within probably about half an hour to an hour that contains most of the things I need (auth, CRUD, similar things) once I know what I need, configuration and business logic depending. I think the key phrasing is primarily "when you're familiar with Rust" - if you're not familiar, then it will definitely be a slog.
Certain things that would require you to manually implement a complex trait (for example, Send + Sync + 'static plus whatever else) would probably slow you down though, but presumably by that time you probably already know what you're doing.
Love it or loathe it, spring boot is going to win that race every time.
A kind of sweet thing about modern spring is it can be pretty easy to rip it out when you've arrived at your feature complete state. So if you wanted to, you could get rid of reflection without losing JIT for example (i think Graal is getting JIT eventually, which would be the other way to force yourself rid of reflection today, so maybe this becomes moot in future).
But either way, if you approach your spring boot development with this exit strategy in mind, you can reap the rewards of removing a big project dependency to own over time leading to cheaper TCO long term once the project has reached feature complete state. It'll even likely be slightly faster if you're ripping out all those classes from the classloader, removing all that reflection and proxies etc. - but you can do all this while scoring the benefits of rapid iteration & extensive code reuse / building on the shoulders of giants, at the start of the project by exploiting spring and especially by exploiting boot.
Could you do something like that with say Axum? Maybe. Probably not.
I mean that's quite a niche idea spurred on by your next comment i've quoted below but even if you're not interested in eeking out the last say in performance and cloud memory footprint costs then spring boot will still more than likely be absolutely fine without doing any of this. And you'll be done sooner too of course.
As for production, does that mean stuff like robust and safe, automated scrubbing measures for handling developer access to sensitive prod memory dumps for diagnostics? Or maybe just mature observability tools - it's like bringing the "tokio console" knife to a "JMC & JFR" gun fight.
I guess my TL;DR here is just: Java. But it could equally be C# or maybe even something else. It's definitely not "with no match whatsoever".
>> in addition to the ability to get the best achievable performance on your hardware
I've ended up spewing so much above i don't feel like going much further and it was actually this part that made me reply.
What you're saying seems unlikely - you're claiming not just fast but the best possible performance on given hardware. So we're talking really annoying things to own like repurposing #[repr(C)] not for FFI but to allow explicit ordering of struct fields to maximise memory throughput. We can do that kind of stuff in Java too (heck most languages allow something like this), java has some unsafe off-heap facilities which can be (ab)used for extreme cases - although there's potentially much nicer and safer APIs in the latest Java which i haven't looked at yet - https://openjdk.org/jeps/442
Best achievable performance usually means for one part of your process at the cost of another code path - when you get down to the most extreme levels, it becomes trade offs. If you want to go that far in java, you can. It's not like you're getting extreme performance for nothing by going another way. E.g. rustc won't magically recognise every time i could use vector instructions, you have to write your code careful of branching and dependencies, be friendly to the compiler for it to do its magic. But of course, we have vectorisation (and even some auto-vectorisation) in java too.
- serde; this beats both cpp and Python in terms of not spending a second of dev time on implementing serialisation deserialisation unless you're doing something funky. I would argue that this is one of the cases where it's much harder to handle it in Python than in rust
- other proc-macros allowing you to quickly map your structs to sql or whatever else
- lsp that works with zero configuration and beats many languages (again, cpp and Python included) because of rust's strictness, and rust-analyser is an absolute pleasure to work with
- cargo tooling and zero-pain dependency management; high quality of packages in general
Once you get past the initial learning curve and get comfortable with the language, these factors (and quite a few more not directly related to the language itself) eventually become more and more important for prototyping. (all subjective of course)
they have very many formats implemented. Which format is zero penalty, so I can use it and not write my own logic?
It's not "they", it's whoever wants to implement serde support for their own thing, can do it (so, many 3rd party crates have a "serde" feature that can be toggled on/off).
> Which format is zero penalty
This one is tough to answer unless you provide details re: what's your in-process data format, what's your target serialization format and whether it's binary or not, what's the objective function (serialization speed, deserialization speed, storage efficiency), and what exactly you mean by 'zero penalty' (which penalty?).
// note that there's also alternative frameworks worth mentioning like rkyv and borsh-rs, each optimizing their own objective function
[1] https://docs.rs/sqlx/latest/sqlx/
Prototyping in C++ is awful: it's so easy to accidentally trigger UB and then you're wasting time debugging some memory corruption instead of exploring your problem.
Rust is close enough to C++ that you can get a good idea of equivalent C++ performance, and it isn't too hard to translate the prototype when you're finished. An exception being porting Serde (de)serialization over.
I've never heard of Shuttle before, that is wild. Maybe I'll play with it soon
Infra-as-code part is half the job (and requiring separate domain expertise) for lots of apps so it's no small thing.
I guess the catch is that you have to use their cloud platform.
What alternative are you implying?
On par with what? Use any of your existing UI frameworks and instead of keeping state client side, let the server tell you. Instead of doing things like eagerly updating client side state (deleting an item -> removing it from your in-memory array), let the server tell you after it's processed and updated its state. Dumbly reflect what the server returns.
I understand this isn't what the tutorials for React, for example, show and breaks away from all the auxiliary things they've been using with it for state management, but there's nothing stopping you from doing this.
You can do all of these things today. You don't need to adopt this. You don't need to move to server side templating and downloading chunks of HTML to inject into elements.
So this literally what htmx does, no?
Except You'd be operating on framework-sourced components rather then built in html structures.
And the swapping itself would be performed by frameworks code rather then htmx library.
Is that what you mean? If so then sure, I get that it would be cleaner then with htmx, but logic is the same and htmx simply let's you expand this logic for core html elements, rather then having to remake all of them into frameworks components that can handle the partial updating.
I think it's a good idea. I have been writing vanilla JS for SSR'd pages for a while and inevitably reimplement some common patterns over and over (such as "load another page, select part of it, replace part of current page"), of which htmx looks to do a good job of capturing.
I completely agree. It's basically breathed new life into my job's antiquated .Net SSR [Razor] pages.
There is nothing HTMX is doing that I wasn't already able to accomplish with vanilla JS or Jquery, but holy shit has it reduced my need for either (which has also saved me time too).
I've been dipping more into HTMX recently and finding it to be a really productive way of building things.
(Your onclick example there is a bit weird, I've not needed to use handlers like that at all.)
Here's some recent HTMX I wrote:
That gives me a button which, when clicked, POSTs target_stewards=1 to /shifts/33/edit/ and replaces the nearest div class="shift-mini" with the HTML returned by that endpoint.Why would I learn this. What does it look like to use in a large code base? I think the main difference between me and the folks who love this is I don't hate Javascript enough yet to explore this life.
https://htmx.org/reference/#attributes
When this is clicked, submit this form to this place, then replace this thing with the returned HTML.
It's such a breath of fresh air compared to the massive warren of SPA JavaScript I've had to fight with in the past.
Not quite though right? There's a very tightly coupled (and fairly useless for anything else) sibling endpoint for each of those calls, which exists on a backend somewhere to return the html that you need to progress.
The alternative exists. You can spin up a rest API around a database schema in all of 5 minutes (eg. Pocketbase). Put alpinejs on the front end and you have everything htmx can do with no need for the server to get involved in whether or not a modal is open or closed on some client somewhere.
https://htmx.org/essays/splitting-your-apis/
I love serverside templating. I use go templates, asp net razorpages and old laravel blade across various projects. It's hard for me to see the enthusiasm for htmx as anything but an overcorrection from react, and the project itself feels like it chooses ideology over function (nothing wrong with that, just not great for getting things done).
In a year I do think we'll see the typical stream of rewrites.
https://htmx.org/essays/a-real-world-react-to-htmx-port/
HTMX + Razor Pages/Views is so freaking nice. It's seriously increased reusability so much for me. HTMX has also greatly reduced the amount of JS I have to write for the same functionality I was getting with Vanilla JS.
Depending on what I need, I find the JS API that HTMX has to be very helpful, albeit I wish a few things were a tad better/more clear in the docs for that area (or I was just too much of an idiot to figure out). None the less, I have no intentions of jumping ship from HTMX anytime soon.
Modals are driven by database state, so you can't really get rid of that.
What's weird about standard CSS selectors? Maybe you should actually see some testimonials before judging, like: https://www.youtube.com/watch?v=3GObi93tjZI
Playwright should work great for that.
How is that any different?
You can do this by having the business logic return a data structure that the client then renders into UI itself, but now you have a state synchronization problem, ie. you have to keep client data structures in sync with server data structures.
Alternately, you can use the classic REST pattern: hypermedia as the engine of application state (HATEOAS), and let the server literally tell you what to render, so client state trivially matches server state. This is the htmx way.
How do you unit test any dynamic behaviour on HTML pages? There are multiple ways depending on what you want to check.
The majority of the front-end community is still programming React applications in their day job, like they have been for years, as far as I know.
If I'm validating a business idea / building a proof of concept, I'm using something like SvelteKit or even NextJS. Going the middle-of-the-road seems to me like either a waste of resources or a waste of time depending on your goals.
But I don't recommend Rust for this kind of stack - only because Rust's ecosystem is not that stable. I swear rust has some of the fastest iteration, with sometimes really complicated setups out of any mainstream language. There's always some nightly build, some feature flag, some cargo setup that often gets in the way. Just consider that Axum here is only two years old. Built on top of and heavily tied with tokio, which is really popular, but no guarantees as the defacto solution moving forward.
I'd say anything else ts, python, sigh even golang would be better alternatives.
I'm very much looking forward to things like generators/coroutines (or whatever they're called now) once they're made stable as well as async traits and whatever makes the Pavex framework able to be used on stable Rust.
Funny that you then compare to typescript i.e. npm, which is infamous for actually having this problem.
If Rust gets a Dependency Injection framework (like CDI) and a Mocking framework (like Mockito), I'm 100% in.
As someone else said, if you have some kind of interface to "mock" you can just make this a parameter to a function and pass in the "mock" versions for tests and the regular one for the real program (or select from a set of them based on some parameter or something). A framework would just mean I can't just read from main, I'll have to figure out which DI framework is being used, where that is configured and what things effect its decision making. I see why we ended up doing this in OO but I really don't see the benefit anymore in functional programming with the more direct (and no less flexible) approach.