HNHacker News
TopNewBestAskShowJobs

spion

4,134 karma · joined July 31, 2012

https://blog.spion.dev/
submissionscomments
spion··on A Node, TypeScript, TS-Node and ESM experience that works
How about in Docker on ARM Mac?
spion··on You don't need a CRDT to build a collaborative experience
For offline first apps, or for applications where very high degree of control for the content is needed (e.g. legal docs) and realtime collaboration isn't that valuable, there is also the option to use 3-way merge instead.

The benefit is that you can even allow the user to resolve conflicts in a satisfactory way.

Another benefit is that the document doesn't even have to be derived from the original, it could go through exports and re-imports and it will still be possible to run a 3-way merge as long as a common base version is declared. This can be especially covnenient for systems that involve e.g. MS Word.

spion··on Switching to Elixir
This particular example with the string types was Haskell, but Rust often makes you think about performance decisions at all times as well. I'm fully aware that that's the point of Rust, however (and yes there are ways to stop worrying and just `clone`)
spion··on Switching to Elixir
The example is, interestingly possible in TypeScript, at a very cheap cost

  function modifiesValField(struct: {val: number}) {
    struct.val = 0;
  }
Structural types and inline interfaces (or as I like to call them inyourfaces) are pretty cool.

But I do get your point - although type systems have gotten much less annoying, its not like they're perfect.

spion··on Switching to Elixir
I've been on both camps, so I'm hoping I can give some insight.

Lots of statically typed languages are very strict about their types and have too many of them. You have to admit, when you're trying to build something difficult and focus on getting the business logic part of your program right, the last thing you want to be thinking about is e.g. whether you need a String, ByteString, LazyByteString or any of the other types of strings, and which library decided to accept which one. At some level its definitely useful to distinguish between those, and I'm sure a lot of libraries make sensible choices. But initially in the development of the program its just unnecessary busywork that distracts you from the important bits.

In the past, typed languages also made it a bit harder than necessary to create new types, especially in situations where you have serialization and deserialization. And finally, we had to do all this work, and for what? To be unable to prevent the most basic of errors i.e. NullPointerException? You have to admit, it was a hard sell.

A lot of things have changed, however. TypeScript makes it really easy to define types, anywhere - inline on the function in the argument, or right above it. You can pretend the type is correct at deserialization time, or you can use libraries like `zod` to both define a validator and a type in the same syntax - its up to you. Rust similarly has powerful and easy to use tools like serde - structs and enums are easy to define, and easy to attach metadata to them quickly to (de)serialize them accurately. Huge differences from the old world of getters and setters and NPEs.

When using dynamic languages, there are techniques to make things easier. There is the often mentioned testing, which does help verify a large subset of code paths. Lesser known and more subtle technique is coming up with and following a strict naming convention for properties, for example, and to keep things as consistent as you can in general. This is why you often see so much focus in code reviews about conventions, linting and so on.

Nowadays I guess it mostly depends on how good your dynamic language techniques (at the team level) are, as well as what your treshold for type system annoyances is. There are still some annoyances even in the best type systems, but its gotten way, way better.

spion··on Write PHP Code Within Next.js Components
Is this documented anywhere?
spion··on Write PHP Code Within Next.js Components
How do you version the functions? How does the server determine which function to call? Does it use the name of the function, or its source code file, or something else? If I rename the old version and call it `actionOld` while the new version is `action` will it know how to call the old one, or do I need to do the opposite (call it `actionNew`)? What if I move the old one to a different file?
spion··on Write PHP Code Within Next.js Components
> You just define a function and call it, very nice.

What do you do when you change the action API (arguments / return type) and deploy? What happens with browsers still having the old code that assumes the old contract?

spion··on PostgresJs: PostgreSQL client for Node.js and Deno
You don't need to use drizzle migrations. There are much better migration tools.
spion··on C Is Not a Low-level Language (2018)
Can you elaborate?
spion··on PostgresJs: PostgreSQL client for Node.js and Deno
Have you tried drizzle? https://orm.drizzle.team/docs/select#aggregations

(note: it produces type-safe results too)

spion··on Scrollbars are becoming a problem
I have the opposite problem - I don't want to see scrollbars. I have the touchpad and scrollwheel for that. I don't need anything taking away space from actual content, especially in narrow sidebar menus. A semi-transparent strip visible only while scrolling sounds perfectly fine.

Unfortunately, as the author notes, its not always easy to get this customized.

spion··on We migrated to SQL. Our biggest learning? Don't use Prisma
The drizzle example I linked to is a realistic example into what would actually be involved in using JSON aggregation in the datbase in PG. Its painful to write manually.

I wish postgres had FOR JSON (https://learn.microsoft.com/en-us/sql/relational-databases/j...)

spion··on We migrated to SQL. Our biggest learning? Don't use Prisma
MS should've dropped the .NET branding once it became cross-platform.
spion··on We migrated to SQL. Our biggest learning? Don't use Prisma
If this is not done carefully, it can also be slow in some situations, even with the right indeces in place. Lets say we have the following slightly contrieved case:

organizaiton(id, name, description) -> users(id, name) -> posts(id, content) -> comments(id, userId, content)

We want to get one organization with all its users. Lets imagine our org has 100 users, each of which has 100 posts on average, each of which has 100 comments on average. In a naive, flat join, we'll be repeating the organization description column 1 million times, and each post's content about 100 times on average.

This can still be problematic without joins, as sending 1 million comment IDs in a query to the DB isn't going to work, so even the multiple queries version will need to do better than that

spion··on We migrated to SQL. Our biggest learning? Don't use Prisma
I would modify this to "Don't use Prisma if you're using serverless"

With actual servers and prisma running close to the underlying database, it should be much better.

Regarding using joins, that's not necessarily the best choice either. When using simple flat joins naively, you get a lot of repetition for the more toplevel nodes of the join tree, which eventually adds up to a lot of network traffic and allocation. (TypeORM tends to have this issue, for example)

What you really want are joins that build the JSON on the server. In MSSQL this can be as simple as `FOR JSON`. In Postgres it can get... a bit more involved - Drizzle can do it: https://github.com/drizzle-team/drizzle-orm/releases#:~:text...

This is probably okay, although I do wonder what happens after you hit the compute limits of your database server (i.e. scenarios when you don't use things like planetscale). In those cases (again, non-serverless), Prisma's choice might work better as long as it runs close to the DB. It will still likely be slower than the fancy JSON join, but probably need fewer database resources (depending on how its done).

spion··on Strong static typing, a hill I'm willing to die on
I often rename a field in a typed language, just as a way to ask the compiler which parts of the code are affected by or use that field then I continue my reading there.

Its like a grep, except it can, for example, differentiate between "name" (of user) and "name" (of project)

spion··on Strong static typing, a hill I'm willing to die on
https://www.bmj.com/content/363/bmj.k5094
spion··on How to Pick a Programming Language (2002)
For TypeScript (browser, chrome), it should be in the devtools under the Performance tab, (backend) is node --prof and --prof-process
spion··on FBI warns “phantom” hacker scams are emptying financial accounts
You use OpenID which sorta does all of this for you.
spion··on Deno Queues
Yeah it did. My bad writing "at most". But the problem is with "at least once" (email sending cannot be made idempotent AFAIK)
spion··on That's a Lot of YAML
The more I look at it, the more I think that Deno is actually a perfect fit for infrastructure as code.

The first reaction I get is usually "What, JavaScript!?". Here is why its better than it looks like.

Deno easily runs TypeScript without a compile step. TypeScript is a very mature, developer friendly language that was designed to model the super complicated types often seen in JS. This includes unions and intersections which help you model complex rules between optional properties, as well as template literal types which can help you restrict string constants. As it happens, those same types of objects are present in our configurations.

TypeScript is also flexible about how much you'd like to model. Do you want only properties with really nice autocomplete with docs from the language server? Sure we can do just that. Do you want to only allow strings that look like a time duration for that property? Can be done too with template literal types. You choose where to invest your energy.

Building abstractions on top of IaC is seen as painful and obscure. TypeScript has tools to help you avoid that, such as api-extractor: https://api-extractor.com/ - it can enforce that all your function abstractions are documented and it can generate that documentation for you.

The larger ecosystem also comes with a lot of ready-made codegen tools that can help, from small modules such as json-schema-to-typescript (https://www.npmjs.com/package/json-schema-to-typescript) to larger mature projects such as jsii and https://github.com/cdklabs/json2jsii . They can be used to build the tooling to import things like CRDs and other external schemas.

What about running arbitrary code, launching rockets, turing completness? This is where Deno comes in, with its permissions model (https://docs.deno.com/runtime/manual/basics/permissions). You can allow only a subset of commands (lets say, `helm` and `kustomize`, while you're migrating away from them), a subset of (writable) directories, accessible network hosts - or none at all.

What about the pains of managing JS modules? Deno lets you import files directly from URLs. It also allows you to set private tokens in env vars to ensure the imports succeed: https://docs.deno.com/runtime/manual/basics/modules/private - as a result, you can manage your IaC libraries the way that makes sense to you, without dealing with the unwieldy package managers of the node ecosystem.

spion··on Deno Queues
I like how updating the data and enqueueing a message can be part of a single transaction. That's indeed pretty powerful.

A bit of an aside, but not sure we want at-most-once delivery for email.

spion··on CloudNativePG – Postgres Operator for Kubernetes
That's because they're not really that hard. Compilers are essentially pure functions, encryption is as well. State is another beast entirely.

If I had to put on my innovator cap and do a relatively weakly informed guess, I'd say its because querying capabilities and reliable storage are still too conflated. If we focused on reliable storage that only has great replication support to other querying systems, the problem might get easier.

spion··on CloudNativePG – Postgres Operator for Kubernetes
The problem (very reliable storage of large amounts of data) is really hard too.
spion··on CloudNativePG – Postgres Operator for Kubernetes
More like six days lately.
spion··on Relational is more than SQL
How is the language server support?
spion··on You Don't Need UUID
I would use it anyway. This article misses some subtleties that make uuids really great, as well as some real points against uuids (impact of v4 uuids on index access and uuid v7 as a solution to that)
spion··on jq 1.7
Or maybe jq does have some design flaws.
spion··on jq 1.7
The main problem is treating one-thing and many-things the same way. Its not a great PL design choice (and its why we can't have slurp as a filter). If streams (not arrays) were also first-class, we would easily have `smap`, `sselect` etc and the code would look like a functional programming language where | is the pipeline operator.

Otherwise, its fine if you try to keep the thought "everything is a 'filter' or a composition of filters, and a 'filter' is a function that either maps, flatMaps or filters things" in your mind at all times

← PreviousPage 4 of 34Next →