HN user

justsomeuser

396 karma
Posts4
Comments250
View on HN

I don’t think it is that serious of an issue:

- The API is likely to be simple (JSON in and out, or something similar). Those devs already know they domain well.

- The Rust compiler enforces only valid programs, so you won’t get runtime errors for things you would in dynamic languages.

- This leaves just getting the logic right, which the developers know how to do in general, and Rust is a c-like language so it’s familiar.

What does this prove about SQL being inferior to other languages

My point is that I think it is inferior for general application/business logic programs. For queries that filter large datasets to small result sets, SQL is probably better as it has the built in indexes and query planner (plus ACID).

I am pointing out that the current environment (better text editors and package managers) favours general languages, so they are a better current choice (to use in combination with SQL) over just writing everything, including application logic, in SQL.

Type systems

Sure SQL has types, but they are checked at runtime, not compile time. Also you cannot define function input and return arguments with types that are checked before you run the program.

compilers

If you want efficient and/or portable code. They will check your code for type errors before you run them. They give you coding assistance in your editor.

debuggers

Being able to break a program and see its state and function stack is useful. The quality of the tools for real languages are much better than SQL.

I agree that databases do concurrency better than most languages with their transactions (I mentioned I would use the db for ACID).

text editors, package managers.

Editor support of real languages is much better than SQL.

Package managers enable code re-use.

C FFI

Take for example Python. A huge amount of the value comes from re-using C libraries that are wrapped with a nice Python API.

You might be able to do this in SQL, but you'll have to wrap the C library yourself as there is no package manager, and no one else is doing the same thing.

I think general programming languages are better for general programs than SQL.

Specifically they have: Type systems, compilers, debuggers, text editors, package managers, C FFI etc.

But I agree that having the data and the program in the same process has benefits.

Writing programs in SQL is one way.

Another way is to move your data to your general program with SQLite.

I like using SQL for ACID, and queries as a first filter to get the data into my program where I may do further processing with the general language.

Of course. This is just a simplification that I find useful.

Software is less like traditional engineering as there are no "right" ways to do things.

I sometimes simplify social interactions by dividing them into two sets: friendly-social and engineering.

In friendly-social environments, it is all about avoiding overly negative topics. It is not lying, but just keeping the conversation track on things that are positive and interesting.

In engineering environments, logic is king. You need to be able to tell an engineer that their rocket designs will not fly and put ego's aside.

I think it is more to do with the so called "Object–relational impedance mismatch", which I guess is related to schema migrations.

NoSQL would still have migration issues as the types change over time, much like the table schemas. You would need to be able to read/write older objects stored, or transform them all to the newer object schema in one go.

I remember when Mongo DB first came out, it seemed the most liked feature was that you could just JSON.stringify and JSON.parse things into and out of the db.

I personally prefer SQL tables, as the act of designing the tables and their relations seems to half way solve a problem, and removes the issue of "which tree structure should this value live in, how do I copy it to both of these types?".

Under "Everything is an Expression":

        items = (() => {
          const results = [];
          for (const item of items) {
            if (item.length) {
              results.push(item.toUpperCase());
            } else {
              results.push("<empty>");
            }
          }
          return results;
        })();

Seems like they are purposely making the JS version extra long. It could be:
        items = items.map(x => x.length > 0 ? x.toUpperCase() : `<empty>`)

Edit: Just realised the code on the right is the compiled code, not the "equivalent hand written JS".

I think CSS and the domain of laying out things in 2d (and responding to state changes) with code is actually inherently complex, so which ever layout system you use is going to have a steep learning curve.

Many of the newer CSS features prevent you using JavaScript to achieve the same thing, which is better as it results in better performance and easier to understand code.

I would rather have newer standardised selectors than see the same pattern re-implemented with JS differently in every codebase.

I like the way you use a combination of <textarea> for editing and <pre> for DOM elements.

I actually have come to the same solution for a transcription web app.

I have found that <textarea> manipulation from JS is not really possible without `Document.execCommand` which is deprecated. Some edits will clear the undo history.

Do you have any notes you can share?

My notes on this:

- <dialog>.showModal() is an indirect API to `top-layer`.

- `top-layer` is kind of like a sibling to the root <html>, elements can be placed into the top-layer from any position in the existing DOM (it is like they have two positions). This allows co-locating your <dialog> next to the relevant elements in the tree.

- There is only one `top-layer` but it can have many children. Last opened === current element on top.

- Z-index has no effect in the top-layer. No need to compete for a higher z-index.

- ::backdrop is a pseudo element that you can style behind the <dialog>. It is always below the last <dialog> opened.

- Not supported in Safari <= 15.3

Sorry I do not, although I have been meaning to publish the "skeleton" template repo's I have locally.

There are two template repos I use: `web-ui` and `server`

The both have a `sh` dir with common commands in .sh files (watch tailwind, watch esbuild, browsersync to serve and live reload during dev).

You config Google Container Linux with a `cloud-config.yaml`, which can take a bit of time to tailor at first, but after that every project uses the same config with small changes. I use Caddy to terminate HTTPS (it will auto generate and renew certs).

If you have a contact I can message you when I put them on Github.

- Server: Node.js + SQLite

I know JS very well, so writing HTTP handlers is quite fast.

Node runs on V8, which is probably the fastest runtime for dynamic code.

SQLite makes development easier as it’s just a file, gives you ACID.

- Frontend: React/Mobx/Tailwind SPA hosted on firebase hosting.

I think the concept of JSX (write your HTML with JS) is good as it gives you a real language instead of a restricted templating DSL.

Tailwind for the fast iteration speed.

Firebase hosting for the simple CLI and fast CDN.

- OS and hosting: Docker running on Google Container Linux

Docker so that the OS level dependencies of my server are defined somewhere.

Container Linux as it auto updates and has all the GCP logging and monitoring built in.

GCP for the incremental disk snapshots for simple backup of the SQLite state.

If I had to scale the service I would add more CPU cores and faster disk. I would also move the parts that need scaling to Go/Rust, and design the code to make use of the cores.

A few principles I use when choosing tech:

- Avoid distributed state (network boundaries) when possible (SQLite instead of SQL server, function calls instead of micro services).

- Use tools for their primary purpose. No shoehorning. Issues arise when you try to use something for what it was not designed exactly for. If you have >3 tools in your stack that you are shoehorning, things are more likely to break in the future.

- Things should still work in 10 years with minimal updates. Lindy effect. Bet on older tools if possible as they are more likely to be around and maintained.

- Good enough vs optimal: stop trying to find the perfect tech. Web tech is sometimes messy and imperfect. Opinion over what is right changes.

I do not think "JSON does not support incremental parsing, HTTP does" is the reason why JSON-RPC is less popular.

Most typed languages give you two options for parsing JSON:

- 1. General JSON values (hash maps of JSON scalar values).

- 2. Strongly typed - convert JSON string to a specific type that you defined in your application code.

You could use 1 to then use in a switch statement to branch to 2.

You may have to parse the JSON twice in some languages to do this, but JSON is typically small and fast to parse.

Why Not Mars 4 years ago

I think Elon is betting on AGI, and is merely bootstrapping the initial delivery systems.

SQLite 2022 Recap 4 years ago

This does seem strange, as you can read and write whole files to/from the user's file system once they give your app permission.

Adding those API's to read/write parts of the file is the next logical step. But it looks like it is limited to the isolated OPFS only.

SQLite 2022 Recap 4 years ago

It would be nice if OPFS (Origin Private File System) allowed reading/writing to an actual SQLite file on the users disk.

At the moment, as I understand it, the OPFS virtual disk is completely isolated from the users disk.

This means you cannot just lightly query a 1GB file without first copying the 1GB from the users filesystem to the OPFS.

Any writes mean you must then copy the 1GB SQLite db file from OPFS to the users local filesystem too.

Is this correct?

I agree. For public interfaces/types or global variables descriptive names are important.

But for temp vars in short functions or closures, I also use single letter vars because I want to see what is being done to the variables rather than the description. If a description is needed I'll use a comment.

Another pattern I find myself using is trying not to use `else`, instead using a function return (either returning early with an if, or a default return at the bottom of the function).

I like type systems as you can design with types and check things tie together before even running the code. This gives you very fast iteration cycles compared to CMD-R refreshing the browser/code.

Some issues with TS are:

- There is no hard guarantee at runtime as everything is partially/optionally typed (including your deps).

- The types are not used to produce faster code at runtime (like in a compiled language).

It is almost like TS leaves half of the benefits of type checking on the table, obviously by design by being a JS superset.

This is true. To be honest I was only considering the benefits to the client - if there are 10m client's polling this could mean a lot of server resources for Stripe.

But an indexed last_update_ts could take less than 1ms to query, HTTP connections are kept alive, meaning the server handling the poll is still not using a huge amount of resources.

It is biased as it is arguing in favour of events over webhooks (and may be missing some of the benefits of webhooks). But I feel it is quite accurate.

Which points do you disagree with?