HNHacker News
TopNewBestAskShowJobs

ryanrasti

103 karma · joined July 8, 2025

Former Google tech lead. Interested in systems, languages, databases.

Building ExoAgent: exoagent.io

Contact: ryanrasti@protonmail.com

submissionscomments
ryanrasti··on Why I Chose Elixir Phoenix over Rails, Laravel, and Next.js
I share the OP's enthusiasm for Elixir, but as the CTO of a startup that ran it for three years in production, our experience was a mixed bag as the codebase grew.

The core promises of the BEAM (concurrency, fault tolerance) absolutely held up. Libraries like Ecto and Oban are world-class, remote `iex` is a lifesaver in prod, and the talent pool is exceptional.

However, developer experience (DX) was our biggest bottleneck. At our scale of 300k lines of code, the pain points were sharp:

* Compile times: A one-line change could easily take >10 seconds to compile in dev, constantly shattering flow.

* Tooling: ElixirLS was a coin flip. Unreliable autocomplete in a large codebase meant constantly grepping for function names and schema fields.

* LiveView: It wasn't a fit for our complex UI, which required a lot of client-side interactivity, forcing us to build a React frontend. This introduced the exact split-stack complexity (GraphQL overhead, context switching) LiveView promises to fix

I wrote a full retrospective for anyone considering the stack for a long-term project: https://ryanrasti.com/blog/elixir-three-years-production/

ryanrasti··on Why Nix Will Win (and What's Stopping It)
Author here. Thanks to `kqr` for sharing this!

I wrote this post to distill the tough lessons from using Nix in production for three years with a small team building a full-stack Elixir/React app. My core take is that Nix has already solved the impossible (reproducibility); what remains is solving the approachable (adoption).

Happy to answer any questions about our production experience or the proposals in the article.

ryanrasti··on Ask HN: What are you working on? (September 2025)
Thanks, glad you think so!
ryanrasti··on Ask HN: What are you working on? (September 2025)
Thanks! That's a great question.

First off, I'm a huge fan of Kysely and it's a massive source of inspiration for Typegres.

You've nailed the two big differences:

* Architected for Business Logic: The primary innovation is the class-based model. This is all about co-locating your business logic (like calculated fields and relations) directly with your data model. The cool part is that these methods aren't just for SELECT; they're composable SQL expressions you can use anywhere: in a WHERE, an ORDER BY, etc. The goal is to create a single, type-safe source of truth for your logic that compiles directly to SQL.

* PostgreSQL-Native: The other fundamental difference is the focus on going deep on a single database rather than being database-agnostic. That massive list of functions you saw is a core feature, designed to provide exhaustive, type-safe, and autocomplete-friendly coverage for the entire PostgreSQL feature set. The philosophy is to stop forcing developers to reinvent database logic in their application code.

Philosophically, it's a shift from composing type-safe SQL strings (like Kysely, which is brilliant for its WYSIWYG approach) to composing SQL expressions as if they were first-class TypeScript objects.

ryanrasti··on Ask HN: What are you working on? (September 2025)
Thanks for the great points and link to SafeQL! I'm a big fan of its approach to bringing type safety to raw SQL strings. For static queries, it's a fantastic solution.

My take is that while "Just use SQL" is healthy pushback against heavy ORMs, a good query builder solves two fundamental problems that raw SQL can't in the application context:

1. Dynamic composition: A query builder is the macro system that SQL is missing. The moment you need to build a query programatically (e.g., conditional filters or joins) you're left with messy/unsafe string concatenation

2. Handling Relations (and other common patterns): Using raw SQL, a complex query with JOINs returns a flat list of rows that now becomes the application's job to properly denormalize. It greatly reduces cognitive load to think in terms of relations, not just join conditions.

Again, showing is stronger than telling. To illustrate, I'd urge you to go through the first couple of examples in the playground and think about how you'd express them (e.g., the composability of the "example1" query) in something like SafeQL: https://typegres.com/play/

ryanrasti··on Ask HN: What are you working on? (September 2025)
I'm working on Typegres, a new data layer for the modern stack (TypeScript + PostgreSQL).

My take is that for years, ORMs have hidden the power of PostgreSQL behind generic, database-agnostic abstractions. This made sense in 2010, but now it's a bottleneck.

Typegres rejects this. It's a "translator, not an abstraction," designed to express the full power of PostgreSQL (all statements, built-in functions, etc.) in a type-safe TypeScript API.

The latest killer feature my take of "object-relational mapping done right": class-based models with methods that are actually composable SQL expressions. This lets you extend your tables with expressive logic and fully-composable relations.

It's easier to show than tell. Take a look: https://typegres.com/play

ryanrasti··on Cap'n Web: a new RPC system for browsers and web servers
> but I don't we've solved the map problem.

Agreed! If we use `map` directly, Cap'n Web is still constrained by the ORM.

The solution would be what you're getting at -- something that directly composes the query builder primitives. In Typegres, that would look like this:

``` let friendsWithPhotos = friendsPromise.select((f) => ({...f, photo: f.photo()}) // `photo()` is a scalar subquery -- it could also be a join ```

i.e., use promise pipelining to build up the query on the server.

The idea is that Cap'n Web would allow you to pipeline the Typegres query builder operations. Note this should be possible in other fluent-based query builders (e.g., Kysely/Drizzle). But where Typegres really synergizes with Cap'n Web is that everything is already expressed as methods on classes, so the architecture is capability-ready.

P.S. Thanks for your generous offer to help! My contact info is in my HN profile. Would love to connect.

ryanrasti··on Cap'n Web: a new RPC system for browsers and web servers
Really nice summary of the core challenges with this DSL/code-as-data pattern.

I've spent a lot of time thinking about this in the database context:

> No printf debugging

Yeah, spot on. The solutions here would be something like a `toSQL` that let's you inspect the compiled output at any step in the AST construction.

Also, if the backend supports it, you could compile a `printf` function all the way to the backend (this isn't supported in SQL though)

> No "natural" control flow in the DSL/tracing context

Agreed -- that can be a source of confusion and subtle bugs.

You could have a build rule that actually compile `if`/`while`/`for` into your AST (instead of evaluate them in the frontend DSL). Or you could have custom lint rules to forbid them in the DSL.

At the same time -- part of what makes query builders so powerful is the ability to dynamically construct queries. Runtime conditionals is what makes that possible.

> No side effects in the DSL/tracing context because that's not a real "running" context

Agreed -- similar to the above: this is something that needs to be forbidden (e.g., by a lint rule) or clearly understood before using it.

> Is this all just necessary complexity? Or is it because we're missing something, not quite seeing it right?

My take is that, at least in the SQL case: 100% the complexity is justified.

Big reasons why: 1. A *huge* impediment to productive engineering is context switching. A DSL in the same language as your app (i.e., an ORM) makes the bridge to your application code also seamless. (This is similar to the argument of having your entire stack be a single language) 2. The additional layer of indirection (building an AST) allows you to dynamically construct expressions in a way that isn't possible in SQL. This is effectively adding a (very useful) macro system on top of SQL. 3. In the case of Typescript, because its type-system is so flexible, you can have stronger typing on your DSL than the backend target.

tl;dr is these DSLs can enable better ergonomics in practice and the indirection can unlock powerful new primitives

ryanrasti··on Cap'n Web: a new RPC system for browsers and web servers
Thanks, Kenton! Really encouraging to hear you find the idea interesting.

Right now I'm focused on Postgres (biggest market-share for full-stack apps). A sqlite version is definitely possible conceptually.

You're right about the bigger picture, though: Cap'n Web + Typegres (or a "Typesqlite" :) could enable the dream dev stack: a SQL layer in the client that is both sandboxed (via capabilities) and fully-featured (via SQL composability).

ryanrasti··on Cap'n Web: a new RPC system for browsers and web servers
Agree -- I think that's a powerful generalization you're making.

> We're often nowadays working in dynamic languages, so they become essentially the frontend to new DSLs, and instead of defining new syntax, we embed the AST construction into the scripting language.

And I'd say that TypeScript is the real game-changer here. You get the flexibility of the JavaScript runtime (e.g., how Cap'n Web cleverly uses `Proxy`s) while still being able to provide static types for the embedded DSL you're creating. It’s the best of both worlds.

I've been spending all of my time in the ORM-analog here. Most ORMs are severely lacking on composability because they're fundamentally imperative and eager. A call like `db.orders.findAll()` executes immediately and you're stuck without a way to add operations before it hits the database.

A truly composable ORM should act like the compilers you mentioned: use TypeScript to define a fully typed DSL over the entirety of SQL, build an AST from the query, and then only at the end compile the graph into the final SQL query. That's the core idea I'm working on with my project, Typegres.

If you find the pattern interesting: https://typegres.com/play/

ryanrasti··on Cap'n Web: a new RPC system for browsers and web servers
Agree, and to add, from what I see, the main issue is that server-side data frameworks (e.g., ORMs) aren't generally built for the combination of security & composability that make them naturally synergize with Cap'n Web. Another way to put it: promise pipelining is a killer feature but if your ORM doesn't support pipelining, then you have to build a complex bridge to support them both.

I've been working on this issue from the other side. Specifically, a TS ORM that has the level of composability to make promise pipelining a killer feature out of the box. And analagous to Cap'n Web's use of classes, it even models tables as classes with methods that return composable SQL expressions.

If curious: https://typegres.com/play/

ryanrasti··on Cap'n Web: a new RPC system for browsers and web servers
> I find the choice of TypeScript to be disappointing.

Genuinely curious, is the disappointment because it's limited to the JS/TS ecosystem?

My take is that by going all-in on TypeScript, they get a huge advantage: they can skip a separate schema language and use pure TS interfaces as the source of truth for the API.

The moment they need to support multiple languages, they need to introduce a new complex layer (like Protobuf), which forces design into a "lowest common denominator" and loses the advanced TypeScript features that make the approach so powerful in the first place.

ryanrasti··on Cap'n Web: a new RPC system for browsers and web servers
> Not sure why this doesn't seem to be a common practice TBH, might be missing something.

Yeah... I've been deep in this problem space myself. The two big points of friction are: 1. Requiring a build-step to generate runtime code from the TS types 2. TS doesn't officially support compiler transforms that do it

That said, the two most promising approaches I've found so far: 1. https://github.com/GoogleFeud/ts-runtime-checks -- it does exactly what you describe 2. https://arktype.io/ -- a very interesting take on the Zod model, but feels like writing native Typescript

Congrats on the launch, really exciting to see a way to get capabilities into the JS ecosystem!

ryanrasti··on Show HN: Typegres – I made every Postgres function type-safe in TypeScript
Hi HN, I'm Ryan, the creator of Typegres.

For years, I've felt stuck between two unsatisfying options for talking to my database: use a traditional ORM that forces you to learn its quirks instead of Postgres itself, or write raw SQL strings and sacrifice type safety and composability.

ORMs discourage you from using the database's powerful native features, forcing you to pull data into the backend to transform it. Raw SQL is powerful but error-prone, and dynamic queries quickly become a tangled mess of string concatenation.

That frustration led me to build Typegres, my take on a new kind of query builder with a simple philosophy: minimal abstraction, maximum surface area.

Instead of hiding SQL, Typegres embraces it. It code-generates a type-safe TypeScript method for every one of Postgres's 3000+ built-in functions and operators. This approach has a huge benefit for iterating faster: since everything is a method, you get full discoverability via autocomplete. The ultimate vision is that Typegres not only helps you write safe queries, but actually helps you learn Postgres as you type.

Some of the things I'm most excited about:

* Full(er) Postgres Coverage: If you can do it in Postgres, you can do it in Typegres, with type-safety. This includes first-class support for advanced features like set-returning functions (e.g., `jsonb_each`).

* Composable & Type-Safe: Build complex queries from smaller, reusable parts—including subqueries—while maintaining complete type safety from start to finish.

* Ergonomic Operator Syntax: SQL operators like `+`, `=`, or `>` are just methods, allowing for natural chaining with auto-complete: (e.g., `.where(u => u.age['>='](21))`)

* Compile-Time Safety: The type system understands SQL rules. For example, it will show a TypeScript error if you try to select a non-aggregated column in a `groupBy` clause.

The coolest part is the in-browser playground, which is powered by Sam Willis's incredible PGlite project. You can try out the full library with zero setup.

A quick note on its status: this is an early developer preview. My main goal today is to see if this philosophy resonates with other developers. The core type-safety is robust, but there's many edge cases to still cover and it's not yet ready for production use. Following the project on GitHub is the best way to stay updated on the journey to 1.0.

Starting with this level of fidelity to Postgres opens the door to some pretty exciting future features— the tl;dr is that I'm optimistic this route can make significant headway into tackling the object-relational impedance mismatch (see the github README for more). Because Typegres understands the database so deeply, it can create healthier, more powerful abstractions.

I'd love to hear all your feedback in the comments. Thank you!

Playground: https://typegres.com/play

GitHub: https://github.com/ryanrasti/typegres

ryanrasti··on Taking over 60k spyware user accounts with SQL injection
Haha! That gave me a good laugh.
ryanrasti··on Google can now read your WhatsApp messages
Agreed the privacy that keeping AI "in a corner" appeals to me too.

The fundamental catch here is that 80%+ of the future benefit will likely come from the very thing that erodes privacy: deep integration and context. Imagine if a Gemini had your entire life in its context (haha scary I know!), prompting would be so much more powerful.

That's the core, uncomfortable trade-off we're all facing now.

ryanrasti··on Google can now read your WhatsApp messages
> With Gemini Apps Activity turned off, their Gemini chats are not being reviewed or used to improve our AI models.

Indeed bizarre as the statement doesn't say much about data collection or retention.

More generally, I'm conflicted here -- I'm big on personal privacy but the power & convenience that AI will bring will probably be too great to overcome. I'm hoping that powerful, locally-run AI models will become a mainstream alternative.

ryanrasti··on Why Elixir? A Rebuttal to Common Misconceptions
Yeah good point re: functional programming and reasoning.

When there's no global mutable state you only need generally need a smaller context to figure out what's going on. I imagine it's the same for LLMs.

ryanrasti··on Show HN: Sumble – knowledge graph for GTM data – query tech stack, key projects
Awesome, go crush it!
ryanrasti··on Taking over 60k spyware user accounts with SQL injection
> Q: Can I monitor a phone without them knowing?

> A: Yes, you can monitor a phone without them knowing with mobile phone monitoring software. The app is invisible and undetectable on the phone. It works in a hidden and stealth mode.

How is that even possible on a modern Android? I'd think one of the explicit goals of the security model would be to prevent this.

ryanrasti··on Show HN: Sumble – knowledge graph for GTM data – query tech stack, key projects
Wow -- tried it out and looks quite impressive. The granularity of data for these companies is amazing!

My last startup was selling to SMBs. It looks like Sumble is most likely targeted at mid-market and enterprise companies. Any plans to expand coverage into the long tail of smaller companies?

ryanrasti··on Blind to Disruption – The CEOs Who Missed the Future
> With the carriage / car situation, individual transportation is their core business, and most companies are not in the field of Artificial Intelligence.

Agreed. The analogy breaks down because the car disrupted a single vertical but AI is a horizontal, general-purpose technology.

I think this also explains why we're seeing "forced" adoption everywhere (e.g., the ubiquitous chatbot) -- as a result of:

1. Massive dose of FOMO from leadership terrified of falling behind

2. A fundamental lack of core competency. Many of these companies companies (I'm talking more than just tech) can't quickly and meaningfully integrate AI, so they just bolt on a product

ryanrasti··on Show HN: OffChess – Offline chess puzzles app
Congrats on the launch!

I was wondering -- how does the point-based rating system work?

ryanrasti··on Why Elixir? A Rebuttal to Common Misconceptions
> I'm curious about the areas you've struggled with, particularly number 3.

At the beginning things were very quick like your experiences. It's when we built up a larger code-base (we're currently at ~100K Elixir LoC -- excluding tests) over the last three years, that making a change in a file triggered hundreds of other files to recompile and that cycle took ~10s on modern machines.

> I abhor JS as a language (I know, I know, I'm fully aware of my biases!),

Yeah I hear you. What changed my mind was actually TypeScript -- it's the most advanced (and approachable) type system I've worked with. Take a look at the GIF here: https://kysely.dev/ -- all of the auto-completion is powered by TypeScript.

> However, with Elixir/Phoenix/LiveView, I have created a more-than-credible web front end that would have otherwise taken a React-based team weeks, if not months, more time and complexity.

It's hard to gauge. My assumption is that the big gains would come from having a single language framework (e.g., Phoenix/LiveView vs. Node/React).

> I do take your point on Fly.io, tho. I am currently operating at a very small scale, and I have been experiencing regular uptime issues. I'm in the process of working out what to do about that as I scale.

FWIW we haven't had any issues after migrating to GCP -- even though the setup is more hands-on (VMs). I'm not sure there are other turnkey robust solutions if you want clustering.

I think the last big unknown will be the entire AI aspect of things. I see competing possibilities: 1. AI enables faster development for Elixir & ecosystem -- allowing them to e.g., address issues faster and shine more clearly 2. AI accelerates consolidation and before you know it every new project is fullstack NextJS and old projects are re-written to migrate onto it

Perhaps the most important will be feedback to the LLM (e.g., in the form of static analysis and running tests) -- TypeScript has a huge advantage here IMO. We got to a place where we had basically no runtime type issues (e.g., NPE, exceptions/crashing of FE) in our startup with TS on the FE. Have only heard of similar stories in cases in more niche FP languages like Elm

ryanrasti··on Why Elixir? A Rebuttal to Common Misconceptions
Thanks for compiling this. As a CTO running Elixir for 3 years (1 year of MVP deployed in production), my take is that while the BEAM's fundamentals are incredible, the ecosystem and DX have major trade-offs depending on the application.

The Good Parts:

1. The BEAM's fault tolerance is real and it "just works."

2. The talent pool is a huge win—the engineers we've hired have been outstanding.

3. Tools like Oban and remote iex were absolute lifesavers for us.

Where We Struggled:

1. Ecosystem: Depth over Breadth. Phoenix, Ecto, and Oban are fantastic, but we often hit walls on things that would be trivial in other ecosystems, like a good Shopify library, forcing us to build our own.

2. The Full-Stack Problem: LiveView wasn't enough for our complex UI. So we had to adopt React anyway, which put us in a polyglot stack where, all things equal, a full-stack JS framework would have been simpler.

3. Developer Experience. This might be the biggest friction point. Compared to the instant feedback of Vite/TypeScript, Elixir felt slow. Autocomplete was inconsistent, and a 15-second recompile/test cycle on our large codebase killed momentum.

4. Deployment & DevOps: Elixir's clustering forced us into a more complex deployment (managing VMs with NixOS) because we couldn't use simpler container platforms like Cloud Run (we had to migrate off of fly.io due to its managed Postgres immaturity and observed general instability).

(Also one quick correction: hot code upgrades aren't practically supported by modern Elixir tooling, so it’s tough to count as a benefit.)

I happened on this post that nails many of the areas for improvement that have also been painful for us: https://boredhacking.com/areas-of-improvement-for-elixir/

Ultimately, my take is that Elixir is a killer tool for specific problems, but for our full-stack app, it's not nearly as one-sided. I'm curious to hear if other teams have run into the same trade-offs: do you see them as fundamental issues or just a matter of ecosystem maturity? And if the latter, how are these improvements getting prioritized?

ryanrasti··on I am uninstalling AI coding assistants from my personal computer
Yeah that captures what I've been feeling: our work is changing from being craftsmen to managers.

Engineering used to be my go-to to enter flow state. Now, I spend a few minutes thinking about what I want and then a lot of time babysitting Claude code -- similar to experiences here.

Has anyone found a way to make the "manager" part feel as engaging and creative as the "craftsman" part used to?

← PreviousPage 2 of 2