Ask HN: What novel tools are you using to write web sites/apps?

345 points by yawn ↗ HN
I'd like to hear about different approaches others are using to write web sites or apps today. What languages, frameworks, or libraries are you using? If so, why and what do you like about it?

332 comments

[ 2.1 ms ] story [ 288 ms ] thread
Many things can be a text messaging service or an email digest if that helps you think outside of the box.
This is the next big thing. Companies like Attentive are way ahead of the game.
Semantic HTML, CSS, Vanilla JS, and JAMStack.

Semantic: embed data directly in HTML. If I have a list of wines with categories, each wine's "class" attribute has a class for each category.

HTML: can be generated by anything and consumed by anyone. Great SEO.

CSS: These websites were all made with the same HTML, only CSS changing. http://www.csszengarden.com/

Let's imagine we have a checkbox, that when checked, should dim every wine that is not a white wine. Next to it is a div of wines, with the "container" class. The div contains wine elements.

3 lines of CSS:

    input[type="checkbox"].whiteWine:checked + .container > :not(.whiteWine) {
        opacity: 0.5;
    }
You can replace the checkbox's appearance with an image, a video, just about anything– it doesn't have to look like a checkbox.

This would take many lines of JavaScript in any other framework.

Instead of attempting to warp HTML into a UI library, this uses HTML as a data format, and CSS as a way to present that data.

IS CSS really this well featured and expressive these days? I havent tinkered with it in years.
Yep, thank god for flexbox and grid layouts.
:checked and :not() have been dependable for quite a few years now. Firefox has had ’em since at least version 1 (2004), Safari got ’em in 3.1 and 3.2 (2008), and Internet Explorer was the last player to the table in Internet Explorer 9 (March 2011). The rest of the selector features (tag, attribute, class, adjacent sibling, child) are older still (IE7 supported it all).
That's totally awesome. Everyone always just jumped to JavaScript for that kind of thing with jQuery.
What’s old is new again <3

Great example, btw!

What happens when your wine types are dynamic? How do you typically bridge the gap?
I have been exploring offline-first approaches to building web apps.

In a nutshell, the UI interacts with local state, and a background service syncs local state with server-side state and vice-versa. This theoretically allows the app to remain fully functional even when offline, while maintaining the benefits of your stuff being accessible from all your devices. There's also UX gains as well - your UI interactions don't need to be blocked by network calls.

In practice, that means using something like IndexedDb to manage client-side state, and modelling data as CRDTs or similar in order to have eventual consistency with the server.

I am working on two alternative tools for building web apps:

https://htmx.org - uses HTML attributes and HTML-over-the-wire for AJAX/Web Socket/SSE interactions

https://hyperscript.org - an experimental front end programming language derived from HyperTalk that is event-oriented and that removes the distinction between synchronous and asynchronous code.

Both tools are HTML-oriented and are designed to be embedded directly in your HTML, rather than along side it. I am trying to popularize the term "Locality of Behaviour" for this idea:

https://htmx.org/essays/locality-of-behaviour/

There are a few tools that are moving people this direction these days, notably AlpineJS and Tailwinds.

on the same note, I'm really enjoying basecamp/hey's hotwire stuff together with go templates.

will be checking out htmx as well, thank you for the links.

"Locality of Behavior" is how everything started, when we used to embed PHP code into html. :)
yep

the idea wasn't wrong, but the level of expressivity wasn't there yet

trying to fix that

I've played with HTMX (and previously intercooler.js). Just wanted to say - everything you touch is awesome. Thanks for your efforts.

I also like Alpine.js and Stimulus.js projects. They're trying to simplify the insanity of front-end development.

Go + HTMX is super powers. It peels away the piles and piles of framework layers and still gets you 90% there. For the 10%, just use React/Angular/Vue.

HyperScript is so cool. I’m seriously going to look at this after I was stuck in a pit of addEventListeners yesterday.
:) it's pretty cool, but also pretty alpha

happy to help you get started if you want to jump on the discord

I'm not sure what came first, but it's unfortunate that hyperscript.org clashes with hyperscript, the pure JavaScript notation to describe HTML.
hyperscript (`h()`) had its first commit 9 years ago and the syntax is common in other rendering libraries (e.g. mithril, ivi).

_hyperscript (this one) seems to have released 0.0.1 only last year.

Definitely very confusing on the naming front. It is at least possible to disambiguate searches with the underscore.

I'm really looking forward to Hyperscript growing more capabilities. I love that it's very natural-language-oriented. I'm really interested in frameworks like this that could make HTML website creation more accessible for regular people. Watching a friend try to build a website with SquareSpace I was thinking... for the amount of effort she put into learning SS's tools and quirks, she could almost have just learned HTML.
yep, the tool chain startup costs in front end development are very high unfortunately
I honestly think for the sorts of websites you make in SquareSpace, they shouldn't be. There's no reason that to develop a static website (even with some dynamic elements like a shopping cart) that you should need e.g. a build process to target modern browsers (i.e. consumers in Western economies).

But this is probably me being idealistic. It's why I like things like hyperscript... it feels like we might be _just_ on the cusp of what I'm saying being true.

beautiful solutions, thank you for ur work

Is there similar solution for manipulating tables and json? I am not a front-end developer, so when i am preparing a poc, it always takes me so long to parse data in a table. Someone must have done the same thing as htmx for tables, or i hope ...

what kind of manipulations?

you can implement server side versions of most manipulations and then use htmx to trigger them and rerender the whole table, if that's acceptable from a UX standpoint

here is an example of active search that filters a table:

https://htmx.org/examples/active-search/

This is incredible. I’ve been looking for something like this for a long time. Love the site and documentation, please keep up the fantastic work. I am excited to play with this over the weekend and see what I can make!
Hugo running on AWS Amplify. A really great combo for static web sites.
Interesting. What made you go with AWS Amplify over netlify?
I already had an account at AWS. And I like paying for services that I use. Netlify has a free tier, whereas Amplify has a low and reasonable price from the start.
I built Nodewood: https://nodewood.com

I really enjoy NodeJS/Vue for web apps, but I got sick of having to connect together all the packages from scratch each time, set up ESLint, testing, database connections, write the user auth/management, all that common stuff. So instead, I put together a boilerplate with all my common practices and basic app features. I figure, it ultimately saves me time on all future web apps, and if other people want to build a similar style of app, spending a few hundred on a decent boilerplate is cheaper than hiring a freelancer to do it for it, or even doing it yourself, if you value your time at anything higher than minimum wage.

My ex-company was an web agency, we were spinning out new projects every few weeks. I developped a "project generator" (more dynamic than a boilerplate) that would create a full project with Django-React-ReactNative-Postgres, with the Terraform configuration to deploy to AWS. Apparently it was huge for sales, and got projects starting on the right foot. You can probably make that into a full product!
What kind of input did it take?
https://withkoji.com/community

Provides "core services" for web apps much like mobile OS's do for native apps. Also allows devs to fork existing published apps and built from a pre-existing codebase.

Fun to experiment with.

I'm currently working with the AWS CDK and the more I used it, the more I have the feeling tools like that could be the next big thing.

It's melting the walls between cloud infrastructure and application code and the fact that it does IaC with the same language I use to program web apps (JS/TS) plays an important role here.

I think, it's not 100% there yet, but it's on a good way.

I spent a lot of time with CDK last year using Typescript. Very impressed with it and I have zero regrets after moving over our app's infrastructure to being deployed with it after 9 months in production.

I felt like being forced to use typed IaC helped me understand AWS API's and services themselves better.

While I'm not a big fan of some decisions the team made when building the CDK (the whole class/context stuff feels very un-javascript-y), it was the first project where I appreciated TypeScript over JavaScript.
I'm really enjoying working with MDX (JSX & markdown hybrid). https://mdxjs.com/

I was able to quickly put together a blog with some pretty nice features (e.g. margin notes): https://pratik.is/writing/essays/media-as-food

Writing content in markdown while adding interactivity with JSX is pretty nifty.

If you click "Raw" here you can see how the blog post is just markdown with some custom JSX elements littered in, e.g. <WrappedSidenote />: https://github.com/pringshia/pratik.is/blob/master/pages/wri...

SvelteKit, Serverless, Backendless.com

SvelteKit[1] is a framework for SvelteJS (like Next.js is a framework for ReactJS). I've tried both Svelte and React. Svelte seems to be more elegant and lets me implement my ideas faster with less code. Svelte is very flexible; SvelteKit adds some opinions on how to do things like routing.

SvelteKit also embraces the serverless paradigm[2] (AKA JAMstack[3]). Although a node.js server is still an option, you can also have pages rendered in serverless functions or pre-rendered at build time. Even static pages can be "hydrated" on the client so they are not totally static. So this results in fine-grained SSR (server-side rendering) at the page level. The two main reasons for SSR are performance (especially on mobile devices) and SEO.

Backendless[4] is a VADP/MBaaS. This platform offers a lot of services, but the main one I'm looking at is authentication/identity. I was looking for an authentication service that supports anonymous guest login, social login, as well as traditional email/password login. The other contenders were Google Firebase (slow, and confusing sign in/sign up flow[5]) and AWS Cognito (too complicated/difficult to use). Auth0 was a contender, but they don't support guest logins.

[1]: https://kit.svelte.dev

[2]: https://www.serverless.com

[3]: https://jamstack.org

[4]: https://backendless.com

[5]: https://github.com/firebase/firebaseui-web/issues/665

This whole thing feels like layers upon layers of complexity. Not to mention, the brittle aspects of any javascript project - immature, lack of robustness and dependability. Your stuff is working on Node v12? Good luck with Node v14 - both LTS versions. It is a shitshow. Let's keep Javascript inside the browser IMO.

SveltKit is literally 2 months old.

SvelteKit as a project is literally 4 months old (since announced), but Sapper (the predecessor) have been around for longer, and SvelteKit has been used on NYT's county-level COVID tracker. There will be some teething problems but it's definitely not a totally greenfield project.
Well, Svelte itself is a bit older than that. And of all the modern Javascript frameworks, Svelte has been, for me, the most joyful to work with.

However, to your comment, I agree - there's unnecessary amount of complexity in the stack above. For me, it's usually Svelte in front, with any server side application.

"What novel tools are you using to write web sites/apps?" ??? is that not the prompt
https://rescript-lang.org/

Based on ocaml’s type system which is a delight to use.

I’ve been working on this codebase for over a year and I’ve yet to see a runtime error. Maybe there was one minor one, not sure. It’s remarkable.

For quick personal things I have a massive hack over webpack that makes anything "<filename>.static.<suffix>" into an output. This means there are no parallel directories for assets and my code can be a million times more modular.

Its a hack. There are edge cases. It was a proof of concept that has carried me over the line. I even have extra plugins for server side render and compile time render.

I keep meaning to write it up and try and port the tests to weboack itself (so I can argue for missing features), but it does what I need, I'm not a front end dev, im busy elsewhere (in life and work).

This partially might be more of a what's old is new again, but here's what I use:

- 100% server side rendered

- Progressively enhanced (fully works without JS, but having JS enabled makes it nicer)

- In select places where I need SPA-like snappiness I use a ~100 line long DOM diffing algorithm (I request full HTML through AJAX and diff it into the current page; if the user has no JS enabled then the page just refreshes normally)

- Running on a single, cheap VPS; no Docker, no Kubernetes, no serverless

- No SQL database; I just keep everything in memory in native data structures and flush to disk periodically; I have a big swap file set up so that I can keep a working set bigger than the RAM

- Written in Rust, so it's fast; I can support tens of thousands of connections per second and thousands concurrent users on my cheap VPS

What are “native data structures”?
Not OP, but we had a similar project long back. All data is in maps and structs. We didn't had free text search, all filters had a corresponding Map of key to []ids. A primary map from id to object. Needs lots of RAM, but that is the cheapest thing we can add
Whatever data types are available in the programming language of choice here; Rust.
Normal structs, `Vec`s, `HashMap`s, `HashSet`s, etc. Basically whatever is natively available in Rust.
(comment deleted)
(comment deleted)
I do this a lot these days with prototypes and poc’s but usually with Go since I’m more familiar with it. Sure it only works on one machine, but it’s usually so fast scaling means it was a success and now you have a war chest.

I’d add adding s3 to backup things though.

No SQL database is the one thing I disagree with. What if you want to write a SQL query/report? Do you have transactions? As the app gets bigger you might want that.
Adding in a service like a SQL database because "you might want that" in the future is not a good idea. Apps should do what they need to do and no more. "Future proofing" is almost always a waste of time - even if you definitely want the feature in the future it's usually better to add it in when it's needed than adding it early and have to support something that adds no value yet.

As for reports and transactions specifically, noSQL databases support both.

Unless, of course, switching to SQL turns out to be a major headache because it requires major rewrites, organizational changes, etc.
OP isn't talking about noSQL databases, but no database at all (if I understand correctly - OP says '- No SQL database; I just keep everything in memory in native data structures'.

In this case, I don't think OP is even thinking about noSQL databases.

Correct. Sorry, I should have worded that better; unfortunately I cannot edit the post anymore.
> "Future proofing" is almost always a waste of time

Not really, unless you lack the experience to know what to future proof and what not to future proof.

I've saved days/weeks of development by future proofing certain things, I've also saved days/weeks of development by taking pragmatic YAGNI in the right places.

Obviously this will only surface on projects that last longer than a couple months ;)

> No SQL database is the one thing I disagree with.

I'm not saying this approach is a good idea in every case. There are many situations where I would use a proper database. It really depends on what kind of app you're building, what kind of queries you want to run, how big is your dataset, your failover and availability requirements, your programming language, etc.

In my case it was a great decision. The code is simple, and the data is convenient to access since it is already packed in native data structures. And it's really, really fast since all of the "queries" are basically LLVM-optimized machine code.

> What if you want to write a SQL query/report?

I just write a few lines of normal Rust code to do that. Sure, it's not as convenient. But I don't need to do that as often, so I don't care.

> Do you have transactions?

The concept of 'transaction' doesn't really make sense in this approach, since I'm directly modifying the data in memory, every change I make is atomic and can't fail, and I flush the data to the disk atomically as well.

>The concept of 'transaction' doesn't really make sense in this approach, since I'm directly modifying the data in memory, every change I make is atomic and can't fail, and I flush the data to the disk atomically as well.

So you don't have mutable data shared between threads then?

The basic stuff Rust will solve for you, but it is easy to forget logical dependencies and get an inconsistent state anyways... Just the ordinary close a session, send error message if not alive has bitten us in a 20 person project.
> So you don't have mutable data shared between threads then?

I might have worded that one sentence a little better, sorry.

I do technically need to share data between threads, since my event loop runs on multiple threads, so if I want to mutably modify something I do need to wrap it either in a `Mutex` or an `RwLock` and lock it.

What I meant was that it is not the same as a "transaction" in the SQL sense. It can't fail, there is no rollback, I decide on my own what level of granularity I need, and I bake the necessary atomicity into the data structures itself. So there are no SQL-style transactions anywhere - I just use a lock where it's appropriate to directly modify the memory, just as you'd do in any other situation. (Either by wrapping the whole structure in a lock, or wrapping only a single field, or by simply making the field atomic by itself.)

So it's a simple mmap database like early versions of MongoDB? I like using atomic commands, but I imagine it would become tedious fast to do the extra work of doing the accounting of the transaction manually every time you want to do a non atomic transaction.
> So it's a simple mmap database like early versions of MongoDB?

Even simpler. I use mmap only for things that I build offline and that never change during the runtime. Everything else is just kept as your garden variety objects in memory.

(Well, although I guess you could say it is an mmap database, it's just that the mmaped file is the swap, and the OS manages it for me.)

> I like using atomic commands, but I imagine it would become tedious fast to do the extra work of doing the accounting of the transaction manually every time you want to do a non atomic transaction.

In my case it's not really tedious at all. Usually when I want to mutate something it looks something like this:

    let user_ref = state.require_login()?;
    let user = user_ref.write();
    user.set_field(value);
    drop(user);
Of course this depends on your requirements. If you'd have 20 different objects of 10 different types that you'd have to lock separately and you'd want to update all of them atomically it might become hairy (SQL would definitely be better in such a case), but I don't have such cases.
I do similar stuff to what you do, writing my backend code in Rust and not using a database but opting instead to keep things in memory and flushing to disk on my own. And running servers that I manage with SSH.

I would love to talk with you and exchange knowledge. Your HN profile has no publicly visible contact info though.

Do you have an email address I can reach you on? Can you put it in your HN profile description?

Sure. You can email me at j@exia.io.

(Just please note that if I apparently don't reply and if you're using gmail then my reply probably went to spam or gmail's /dev/null, so email me again; lately gmail doesn't like emails that I send and either puts them into spam or just sends them to /dev/null, even when I'm replying to someone that emailed me first! So much for Google having no monopoly, sigh...)

I run my own mail server so there’s also a chance that I end up in your spam folder instead when I send you a mail, if you do spam filtering :p
Isn't this the problem? "What if I need to do something I don't need to do right now? I know I best add it in just incase!"

You have now recreated Drupal.

Yeah, this seems problematic. What if you hard power off your server, how do you know your data didn't get corrupted? As someone who has written databases, and knows how difficult this is to get right, it makes my skin crawl.

Backups and restore will also have to be DIY. Point in time restore probably won't be possible - and that's a life saver when you need it.

> What if you hard power off your server, how do you know your data didn't get corrupted?

Atomically replacing the whole file instead of updating the data in-place, and using a checksumming filesystem like ZFS should give you such guarantee, no?

> Backups and restore will also have to be DIY.

Well, yeah. A daily cron job that `scp`s the data to an offsite location. Personally I don't really see much problem with that? It's easy to backup, and easy to restore.

> Atomically replacing the whole file instead of updating the data in-place, and using a checksumming filesystem like ZFS should give you such guarantee, no?

No. You need to use fsync to ensure the file is flushed to disk. Also careful not to rename the file across mount points (/tmp is often a different mount and the most common location for people trying this.)

> Well, yeah. A daily cron job that `scp`s the data to an offsite location. Personally I don't really see much problem with that? It's easy to backup, and easy to restore.

That's fine, but your backup and restore granularity is daily. If you introduce a bug that writes the wrong / partial data, you can't recover anything that isn't in the previous day's backup.

With a typical database you have a transaction log and you could restore back to any point during the day.

This has been necessary about a dozen times during my career (once every two years or so on average.)

> No. You need to use fsync to ensure the file is flushed to disk.

Are you sure? At least in case of ZFS AFAIK the file wouldn't get corrupted, since it uses copy-on-write internally. Sure, you might get the old data back, but you wouldn't get corruption. And it automatically checksums your files anyway so it will detect any data that did get corrupted, and if you're using RAID-1 (as you should for any important data) it will automatically repair it.

> Also careful not to rename the file across mount points (/tmp is often a different mount and the most common location for people trying this.)

You can't rename across mount points. (Linux's `rename` syscall and the corresponding C API will return an EXDEV error if you try.)

> If you introduce a bug that writes the wrong / partial data, you can't recover anything that isn't in the previous day's backup.

Sure. If I was working in a bank not being able to do that would be totally unacceptable. But in my case that's the tradeoff I'm willing to live with.

What kinds of things have you made with your setup?
> I just keep everything in memory in native data structures and flush to disk periodically;

Can you expand on what you mean by this? I am interpreting that to mean that you are actually writing to a file and reading from a file rather than using SQL?

They're storing their data in native Rust objects and read or write them in server application RAM. In case data becomes larger than available RAM, they have set up a large swap, which is a file or a disk partition that the OS uses when no more real RAM is available, while acting like nothing is happening (and everything gets 1000x slower). They periodically serialize these in-memory objects to a file on disk, and presumably (they didn't specify, but I feel it's implied) they're reading this file and deserialize it during initialization/startup phase.
Correct. Although I don't read everything on startup. Some of the data (e.g. per user data) is only loaded once it's needed.

Also, the swap doesn't make everything slower as long as my active working set is smaller than the available RAM. That is, I have more data loaded into "memory" than I have RAM, but I'm not using all of it all the time, so things are still fast. It's basically an OS managed database. (If I were using a normal SQL database I'd get a similar behavior where actively used data would sit in RAM and less used data would be read from the disk.)

The obvious difference is that swap files are not as optimized for disk io. There is a serious performance issue if you ever have to hit disk often. It all depends on how you structure your in memory/on disk data. But it might never be an issue, have scaled to tenish million user in memory there is so much else that can go wrong first.
> I use a ~100 line long DOM diffing algorithm

Since this is not vDOM diffing (which most JS frameworks nowadays provide) can you help share the algorithm if its already out in the open / non-proprietary?

It's really nothing special. I wrote the whole thing in, like, a single afternoon. If you're interested, here it is:

https://pastebin.com/V7UiWj1e

It's not really a general purpose diffing algorithm. It probably doesn't handle every corner case as it only does what I need it to do in the few places I actually need it.

Would you even need diffing if, for example, the relevant nodes had an ID functionally derived from their contents?
Sorry, I'm not sure I understand your question?

Basically, I use diffing because it's simple. I don't want to mess around with manually updating whatever content I want to update, and I don't want to be forced to generate a specially crafted piece of HTML so that it's updatable. This way I only have to have only one function that updates the current page in-place by diffing, and I can use it everywhere I need to.

I meant that the old tree and the new tree both originate from the same source (i.e., your code), and if both were annotated with some kind of hashes of their contents, you wouldn't need any diffing to find the changes. You could simply compare the annotations on corresponding pairs of nodes and if they're the same, you don't need to descend any further and compare their contents - you already know than anything below that node is going to be identical.

Also, have you compared the performance of whatever you're doing with a simple PJAX-like approach? Considering that browsers are very good at parsing HTML, I wonder how fast that would be compared to doing quite a lot of DOM calls, which you seem to be doing (?). I'd definitely be interested in that comparison.

> if both were annotated with some kind of hashes of their contents

That's exactly what I don't want to do, since it's extra complexity and extra work on my part. (:

> Also, have you compared the performance of whatever you're doing with a simple PJAX-like approach? Considering that browsers are very good at parsing HTML, I wonder how fast that would be compared to doing quite a lot of DOM calls, which you seem to be doing (?).

Nope, sorry, I haven't done any benchmarks. From what I've tested the performance is great. If the pages where I use it were heavier then maybe I'd have performance problems, but so far I don't.

This is really neat. What's the site? I'd love to see it. And could you tell us more about the DOM diffing algorithm?
> What's the site? I'd love to see it.

One of my projects where I'm using this approach is this one: https://jpdb.io

> And could you tell us more about the DOM diffing algorithm?

I'll paste what I wrote in another reply to a post asking the same question:

It's really nothing special. I wrote the whole thing in, like, a single afternoon. If you're interested, here it is:

https://pastebin.com/V7UiWj1e

It's not really a general purpose diffing algorithm. It probably doesn't handle every corner case as it only does what I need it to do in the few places I actually need it.

To be expected from your previous comments, but your website is snappy. Navigating it feels great.
The search works great. Having furigana next to kanji is very useful. Fantastic work!

While playing around, I got the play button to work only once and then got this error:

  Uncaught TypeError: this.previousElementSibling.play is not a function
    onclick https://jpdb.io/search?q=ごめんあさい#a:1
That's interesting! It works for me, both on Firefox and Chromium.

May I ask what web browser and OS are you using?

Firefox 87.0 (64-bit) on Win10. It is reproducible. Disabling uBlock Origin didn't make a difference. Let me know if I can help debug!
Okay, so this is indeed very interesting. I just launched the same version of Firefox in a Win10 VM, and it works there too. So it might be one of your extensions, maybe? (I use uBlock Origin too, so that's not it.)

Could you try these things out please?

1. Try disabling all of your extensions and try again. (IIRC by default private mode disables all of the extensions, so that should be an easy way to do it.)

2. Can you open the dev console and try running something like this in it?

    document.querySelector("audio").play()
Based on your original error either the `previousElementSibling` is not an `<audio>` tag, or for some reason audio tags don't have a play method, so it'd be good to know which one it is.
I'm doing the same old skool thing (first 4 points) for a large app, except the last 2 points are:

- using MySQL and Redis

- using Perl and the awesome CGI::FormBuilder module

how do you control for swapping to much?
I don't.

Linux's memory management subsystem is really good. It's almost magic. And modern SSDs are really fast. So whatever swapping is going on in the background I really don't feel it.

It's basically an OS managed database. Instead of the SQL server doing the swapping to-and-from the disk, Linux does it for me.

This speaks to my soul! How cheap of a VPS?
Currently 4.29 EUR/month VPS on Hetzner. (Although I have some fun features planned and might need some more processing power so I might upgrade in the future.)

They have great AMD-based VMs, 2x perf/$ over Intel. (I've benchmarked.)

what benchmarks do you use? I'm curious how my VPSes compare.
It was quite some time ago so I don't remember exactly, but I've basically used one of the usual benchmarks that everyone uses to measure the number of requests per second that a server can sustain.

(I've rented another VPS in the same data center and ran the benchmark from there.)

How much traffic per month does it cover in this amount?

I want to launch some toy projects and confuses whether go for AWS or vps.

I am currently using a bunch of AWS technologies, e.g. AWS EBS, RDS etc. I am also thinking to consolidate all projects to a single AWS LightSail, which offers pretty cheap instances, e.g. 512 Mb, 1vCPU, 20Gb SSD for 3.5$/month.
20 TB.

I would definitely recommend going with a VPS over AWS in your case, unless you want to play with AWS-specific tech to build up your resume.

are you also installing the DB within the VPS?

I would be needing a web app connecting with MySQL and a bunch of Crons.

Also, could I host multiple domains?

I don't use a DB. But sure, you could run both your app and the DB on a single VPS.

Yes, you can host multiple domains. You can either point multiple domains to a single IP of your VPS, and configure your web server to use name-based virtual hosting, or you could buy an extra floating IP, assign it to your VM, and use IP-based virtual hosting.

Is it done by hosting guys or I have to do on my own? Also what about backup? I am not quite good at devops/admin stuff hence asking
You have to do it yourself. But it's not that hard. The easiest way of hosting multiple sites is probably to just assign multiple IPs to a single VM and simply run multiple servers simultaneously, each listening only on a single IP.

As far as the backup is concerned, Hetzner has the option of handling it for you for 20% extra money.

In my opinion this question seems like a good reason to get your feet wet just a bit. Just gaining some understanding of how things work under the hood is worth it even if you end up never using it professionally.

The basics of operating a (virtual) server to run your apps on could be seen as similar to learning the basics of cooking food. You can eat fairly well for the rest of your life without knowing a thing about it but if you just spend a single hour every week preparing your own food, you will become more familiar with the food itself. You would larn what individual flavors you prefer and where they come from, and you'll even be able to survive on less money if you would need to, or even survive more easily in a potential crisis where home delivery isn't an option.

If you spend an hour every week to learn some system administration (what people call "ops" nowadays) you'll be able to realize when you're being overcharged for simple services, feel the power of building a larger part of your stack yourself, play with new things, etc. Running multiple apps on a single host with proper backups is a trivial problem, especially if you know how to code, and in my opinion these things shouldn't be "left to the devops team" or seen as "IT stuff".

Not that long ago most developers had to know what environment their software ran in as part of their job, and despite many attempts to move away from it, this is still mostly true even if we have switched from the Linux CLI to cloud dashboards, CI/CD pipelines and Terraform scripts...

20 TB of outgoing traffic is included with a Hetzner Cloud VPS, any additional TB costs 1,19€. Incoming traffic is free.
> Progressively enhanced (fully works without JS, but having JS enabled makes it nicer)

This is something I've struggled to get developers to understand forever. It doesn't help that no browser really allows users to switch off js. The number of developers who think doing client-side validation on forms is enough is far too high!

Regardless of JS vs no JS, only validating forms on the client side is just an awful practice begging for trouble.
The post was about 'novel' tools. I really dont see what is novel about your approach
Let me quote the OP:

> I'd like to hear about different approaches others are using to write web sites or apps today. What languages, frameworks, or libraries are you using?

So the question as I understood it was not "what truly novel and unique approach you're using that no one else has before?" but more of a "what non-mainstream approach you're using?". And what I'm doing is definitely non-mainstream.

Actually, the title of the post specifically says "novel tools". That apparently didn't make it into the body of the post, but almost everyone is primed by the title to expect novel solutions in the answers.

That said, I don't think there's any harm in reminding people that there are plenty of non-novel solutions already that they might not have fully considered.

He is using a novel templating library, novel HTTP server, novel JS diffing algorithm ... and this approach has generated more questions than any other on this thread so clearly it is unfamiliar to much of the audience.
How do you deploy changes? git hooks or something like ftp?
Basically it's more-or-less this:

1. Compile the binary.

2. SCP it to the server.

3. Restart the running process.

Brilliant, I feel that would simplify much of my life
Yes! I am a big fan of "keep data hot" approaches to things. I first came across this approach with the Prevayler library years ago and I love it.

People who haven't tried it will be excited to discover how much faster it is. The only trick is to store your data in a format where you can write the changes easily. E.g., logging changes and then playing back the log on startup.

What Rust framework/crate are you using for webserver?
My own. (:

In general I was a little frustrated with the state of Rust's web server ecosystem since it has, what I personally call, the NPM syndrome. Pulling a single crate will often pull along with it hundreds of dependencies, and murder your compile times. I know that those dependencies are often necessary, but during development I do not need all of that.

So I made my own.

But of course it's generally a bad idea to run your own HTTP stack in production. So my framework works like this - it can be compiled either in development mode, or in production mode.

In development mode it uses my own crappy HTTP stack and my own crappy async executor and has *zero* dependencies, and compiles super fast. In production mode it uses `hyper` and `tokio`. In both cases the API is exactly the same. So I can have my cake and eat it too.

Any chance any of this can be made open source? Sounds like a killer feature
Possibly. I will make it open source eventually. It's just that's extra work that I don't really have much time for.
Fascinating. Would you like a full-time Rust job? :)
...are you offering one? (: If so I would definitely be interested.
email me, my email in my profile.
There is no email in your profile. You've probably added an email to your HN account, not to your profile.

To make it appear in your profile, add it to the "about:" section in your account settings (the page you go to when you click your own username)

We're also looking for Rust developers. E-Mail is in my profile. Happy to talk :)
> flush to disk periodically

How do you protect yourself against incomplete writes? What about when there's a critical update that you don't want lost?

I've done similar stuff in the past, but I handled things slightly differently. For tiny data structures I just spat out a short XML file. For larger data structures I synchronized all changes with a SQLite database.

> How do you protect yourself against incomplete writes?

I don't update files on disk in-place. I write to a new file, and once it's done I do an atomic move and replace the old file with the new one.

> What about when there's a critical update that you don't want lost?

I immediately write it to the disk? (:

I don't think it's possible to completely avoid this problem. In a traditional database if you suddenly lose power you'll also lose that critical update, if it hasn't been yet committed to the disk.

I like the approach of tailoring the app to the use case, so I'm not arguing you should do something differently.

The database has commit for exactly this reason. If you lose power, you will not acknowledge the commit to the client, so the client may retry. For example, the end user may get an error page. This is different from a case when the app would show modified values, but the next day, the modifications would be gone.

With the database, you still have the problem, that after a user sees an error, the transaction might have suceeded. Eg if connection drops at the moment when the DB receives the commit command.

How big are the files?

The old version of the application that was written before my version always wrote the entire file like you do. But when the file got very large it was too slow, which was why we used SQLite in newer versions.

My "old is new" setup is similar in spirit but quite different in effect.

I use rails, postgres, and docker. The only weird thing in the stack is that I use a pretty big base image - the ubuntu base image.

Where I need SPA-like performance, I use react-rails to just do that little bit in React.

Rails has so many batteries included, I just can't imagine being as productive in anything less mature.

Which Rust templating tools are you using for server side HTML rendering?
I've had a good time with Tera, but I haven't used it extensively.
I have my own crate for template rendering. I tried various existing ones but I ended up not liking any of them for one reason or another, so I've made my own.

Some of the features are:

* Fully static. It's compiled into Rust code instead of being dynamic. (I wanted to be able to use normal Rust code in my templates instead of a special template-only language, and I wanted the extra speed.)

* Built-in automatic HTML whitespace minification at compile time.

* Built-in XSS-safe string interpolation. You have to explicitly use the unsafe raw interpolation.

* It can interpolate any Rust value which implements the `Display` trait.

* Doesn't use Jinja's syntax.

* Doesn't have any extra unnecessary dependencies.

* Doesn't use inheritance for template composition, instead it uses simple textual inclusion plus late binding to achieve composition. Since it's hard to explain let me give a simple example. Let's assume I have a skeleton.html which looks like this:

    <html>
    <head>
        <title><~ @paste title ~></title>
    </head>
    <body>
        ~~ @paste body
    </body>
    </html>
And now I have index.html which is the actual template I render, and it looks like this:

    ~~ @include "skeleton.html"
    ~~ @snippet title
        Index
    ~~ @end
    ~~ @snippet body
        <h1>Hello world!</h1>
    ~~ @end
As you can see I first include the skeleton.html, and then I define the "title" and the "body" snippets after the skeleton has already pasted them. Normally this wouldn't work, but since the `@paste` directive is lazy it only gets resolved once the whole template is processed.

This makes it really simple to customize the templates, since you can just add `@paste`s wherever you'd like to have a customization point, and you don't have to think about doing things in the right order since the templating engine will take care of it for you.

(If you forget to define a snippet that was `@paste`d you'll get a compile time error; I also have other directives that allow you to set a default if there was no snippet defined.)

I love the overall simplicity of your approach. Any chance of sharing your template rendering crate with us?
That looks a lot like ructe [0], a templating language that I really love. It compiles templates to functions that you can call from Rust using a build script. The functions parameters are accessible from your templates. You can even import and run arbitrary rust code from your app:

    @use any::rust::Type;
    @use super::statics::style_css;
    
    @(name: &str, items: &[Type])
    
    <html>
       <head>
         <title>@name</title>
         <link rel="stylesheet" href="/static/@style_css.name" type="text/css"/>
       </head>
       <body>
         <h1>@name</h1>
         <dl>
         @for item in items {
           <dt>@item.title()</dt>
           <dd>@item.description()</dd>
         }
         </dl>
       <body>
    </html>

[0]: https://github.com/kaj/ructe
> 100% server side rendered

> Progressively enhanced

> request full HTML through AJAX and diff it into the current page

These first three points are actually starting to be "in" again. They're called HTML-over-the-wire[1, 2]. And I am currently looking into implementing them for my site (which uses ASP.NET Core Blazor), because I think this approach is awesome.

[1] https://hotwire.dev/

[2] https://alistapart.com/article/the-future-of-web-software-is...

Why not use something like lmdb instead of writing raw files? It's a performant k/v store and memory mapped. It beats file access syscalls most of the time.
I'm well aware of lmdb and I did consider using it.

It is a reasonable option, yes. It's just mostly unnecessary in my particular case with my architecture, so I went for the simpler option.

What are the requirements that need it to have no database?
It's the other way around. I have no requirements that need it to have a normal database.

The biggest two advantages of not having a database are simplicity (I don't have to query the database to access the data, as it's already in memory) and speed (my average response times are well under 1ms, and that is with compressing every reply, having more data in memory than I have RAM, running on a cheap dual core VPS, and being flooded with requests from linking to my site on HN).

How does progressively enhanced work without Javascript please?
I'm really impressed at how many people have come back with critiques or suggestions about your architecture without actually even knowing what your app does!

So, what does it do?

Well, to me it's more of a "no one I know develops like this; tell me more" kind of a thing rather than a normal critique of my architecture. (:

I've posted this link in another comment already, but this is one of my projects where I'm using this approach: https://jpdb.io

I do the same thing with "no SQL db". The only thing I do differently is not make snapshot of the state but rather replay events to achieve the same state as before (or different t if we migrate to different logic). Also all the changes that are possible are typed events. It wont be a problem to save the state like you do but right now our application changes so often that we have to make sure we can replicate the state even if the logic changes.

Would love to talk to you about this.

I am creating content websites only using Notion nowadays. It is pretty efficient and quick to launch.
PostgREST + React.
PostgREST is excellent. I’m using it with Elm on the front end.
Ever try PostGraphile? I'm evaluating the two.
I use janet-lang

The stack lately has been

- osprey (sinatra like framework)

- tailwindcss

- sqlite

- alpine.js

- turbo (from hotwire)

Memory usage is really low, so I can have quite a few websites on a single VPS

Cannot recommend gqless highly enough for making graphql actually fun to use and closer to something like meteor/firebase syntax[0].

And I’ll self-promote, I’ve been working on what I consider to be a “next generation” style system for React that solves my biggest issue with it currently: being able to performantly write styles in a nice syntax that optimize for both web and native. Called SnackUI, still in beta[1]. It has an optimizing compiler that really speeds up React apps a ton, but still lets them run fully on native, even with themes and responsive queries.

I’ve been hacking on a cool project for the last year using the two of them and really believe in them both.

[0] https://gqless.com

[1] https://github.com/snackui/snackui

A while ago I created a simple website using an XML file as a storage technology and XSLT to render it to HTML. The data stored would be updated infrequently and suited a consistent format, making XML suitable for the job. Originally the site index file pointed to the XML file and the XSLT was rendered by the browser. This worked perfectly for a long time. Even though there was some technology to learn, it was a zero dependency, browser-native solution.

After a few years I decided to make everything completely static to facilitate some changes. It now uses lxml in a Python script to perform the XML transformation. This is run by a makefile and produces static HTML, which is then uploaded.

My blog (https://ajxs.me) is rendered using a static-site-generator I built myself in Python. Using Python template strings stored in HTML files to create a simple templating engine. The blog entries are written in HTML format and stored in an SQLite database loaded by the build script. The final output is entirely static HTML, not even any Javascript.

I'm experimenting with different formats for writing the entries themselves in, however I haven't found anything yet that gives me more simplicity and control than HTML. The LaTeX to HTML conversions I've tried were way too bloated and messy, and markdown doesn't seem to support the level of control I want. With HTML I have very granular control over the final output in the browser and can create whatever arbitrary content I need to.

Fav thread ever!

A good 5+ years ago I built HasGluten [1] with react backed by a google spreadsheet, hosted on github pages. Proven technology, it's still working.

More recently I hacked together MultiPreview [2] with react + API served by firebase functions. I'm also temp using firebase hosting as I have little traffic, but plan to replace it with edge computing / CDN caching.

MultiPreview is backed by Saasform [3]. This is more like a classic node/express (nestjs) application, but I really like how we're separating concerns: the SaaS = MultiPreview handles the biz logic / Saasform handles landing page + auth + payments.

While building the new landing page I played with and liked a lot Bulma [4], I used but didn't like AlpineJS [5] and I'm thinking to move to Hotwire [6].

My personal experience is that performance only matters when you're big enough, so in the near future I want to experiment more on usability / nocode side. And I'd love to see more open source alternatives to the big ones.

[1] https://hasgluten.com

[2] https://multipreview.com

[3] https://github.com/saasform/saasform

[4] https://bulma.io

[5] https://github.com/alpinejs/alpine

[6] https://hotwire.dev

You might want to check hasgluten.com's cert:

>> Firefox does not trust this site because it uses a certificate that is not valid for hasgluten.com. The certificate is only valid for the following names: www.github.com, .github.com, github.com, .github.io, github.io, *.githubusercontent.com, githubusercontent.com