HNHacker News
TopNewBestAskShowJobs

sa46

1,425 karma · joined December 2, 2015

Building AI for distributors, contractors, and manufacturers.

Paratrooper to programmer.

http://github.com/jschaf

hn@schafer.dev

submissionscomments
sa46··on When to use Bazel?
My experience with Bazel at a startup (also used Blaze at Google):

The good:

- Amazing for Go backends. I can push a reproducible Docker image to Kubernetes with a single Bazel command. I can run our entire backend with a single command that will work on any developer's box.

- Amazing for testing. All of our backends tests use a fully independent Postgres database managed by Bazel. It's really nice not having to worry about shared state in databases across tests.

- We can skip Docker on macOS for development which provides on the order of a 10x speedup for tests.

- BuildBuddy provides a really nice CI experience with remote execution. Bazel tests are structured so I can see exactly which tests failed without trawling through thousands of lines of log output. I've heard good things about EngFlow but BuildBuddy was free to start.

- Really nice for schema driven codegen like protobufs.

The bad:

- Bazel is much too hard for TypeScript and JavaScript. We don't use Bazel for our frontend. New bundlers like Vite are much faster and have a developer experience that's hard to replicate with Bazel. Aspect.dev is doing some work on this front. One large hurdle is there's not automatic BUILD file dependency updater like Gazelle for Go.

- Windows support is too hard largely because most third party dependencies don't work well with Windows.

- Third party dependencies are still painful. There's ongoing work with bzlmod but my impression is that it won't be usable for a couple of years.

- Getting started was incredibly painful. However, the ongoing maintenance burden is a few hours per month.

sa46··on Fuzzing Go APIs for SQL Injection
Prepared statements (in Postgres) don't work for:

- Dynamically generated queries, like a user specifying a query predicate via a UI.

- Dynamically selecting an identifier, like `SELECT * FROM $1` or `SELECT $1, count(*) FROM foo GROUP BY $1`.

You can use cleverness with types to parse user-provided data into a struct that emits sanitized SQL, but under the hood, there are only strings.

sa46··on Karmem: A fast binary serialization format faster than Google Flatbuffers
That is a neat tool but your comment gives off strong growth-hacking vibes.
sa46··on How I write React after 8 years
The author did not make his point well because he immediately contradicts himself.

> we don’t use libraries. The only libraries we use are where [...] I know the library will continue to be maintained.

I interpreted his point more charitably as "be very selective about introducing libraries". I've had the most success with framework-agnostic, foundational libraries (tables, date-time) and with best-in-class framework libraries (state management, calendars).

sa46··on I remember feeling derisive about marketing as a young techie
That DC Rainmaker blog sure looks like advertising. There's affiliate links, coupon codes, and even a custom ad platform [1]. I'm not suggesting it's a bad thing, but DC Rainmaker is most certainly "marketing".

[1]: https://www.dcrainmaker.com/advertise

sa46··on AWS open sourced the AWS console design system
Where’s the component preview code hosted? I’m interested in Storybook alternatives.
sa46··on Soft deletion probably isn't worth it
You lose traditional FK constraints with temporal tables since there's multiple copies of a row. One workaround is to partition current rows separately from historical rows and only enforce FK constraints on the current partition.
sa46··on How did REST come to mean the opposite of REST?
I'm on team RPC. I'll stay away from the term REST since my beef is with HTTP APIs that mix transport and application concerns.

- The Kingdom of Nouns comparison is forced. Yegge's complaint is that nouns own the verbs, meaning Java doesn't have first-class functions. The closest remote analog might be promise-pipelining which doesn't have much headway other than a single implementation for Cap-n-proto.

- RPC APIs are more consistent than an HTTP API. With HTTP, a unique method requires both a path and method, and if you're really unlucky, the method is polymorphic based on the contents of the request body.

- HTTP API requests can transport in different ways: request body, query parameters, HTTP path, and if you're really unlucky, headers.

Tremendous gains doesn't match my experience. The first step in using a HTTP API is to wrap it with an OpenAPI generator to build a consistent way to invoke the API, reinventing RPC client stubs in the process.

sa46··on Clipboard integration between tmux, nvim, zsh, x11, across SSH sessions
I went down this path to madness a few years ago [1]. HN discussion [2]. Nowadays, I use IntelliJ for everything.

[1]: https://blog.d46.us/zsh-tmux-emacs-copy-paste/

[2]: https://news.ycombinator.com/item?id=17002527

sa46··on Linus Torvalds: Rust for the Kernel Could Possibly Be Merged for Linux 5.20
One consequence is that you end with cute names instead of clear names. Cherry picking from the top 30 or so crates:

- parking_lot

- hashbrown

- ryu

- heck

The longer there's a shared namespace, the stranger the names get as the "good" names are already in use.

sa46··on 2022 Cloud Report
Other globally consistent NewSQL DBs: Spanner, TiDB, Yugabyte, SingleStore

Partitioning for existing DBs: Citus for Postgres and Vitess for MySQL.

Snowflake apparently just started to target transactional workloads with UniStore.

sa46··on Why I Don't Like Golang (2016)
The upside of structural typing, with some help from Go idioms, is that it encourages composition and narrow types. A type like bytes.Buffer implements Reader, Writer, ByteWriter, ByteReader and another 5 or so interfaces but the call site can declare which set of methods it needs.

The downside of nominal interfaces seems worse: you have to import the interface which can lead to awkward situations like cyclic dependencies and type-only packages (a la Haskell). In practice, the extra friction of nominal interfaces also seems to encourage wide interfaces in contrast to Go's narrow interfaces.

sa46··on Data Race Patterns in Go
A data race and garbage collection are unrelated. A data race occurs when:

> two or more threads in a single process access the same memory location concurrently, and at least one of the accesses is for writing.

Rust provides compile time protection against data races with the borrow checker. Go provides good but imperfect runtime detection of data races with the race detector. Like most things in engineering, either approach requires a trade off involving language complexity, safety, compile time speed, runtime speed, and tooling.

sa46··on Dhall: A Gateway Drug to Haskell
Dan Luu has a take on this: http://danluu.com/butler-lampson-1999/
sa46··on Connect: A Better gRPC
I don't think that's fair. It's telling that two companies have ditched the enormous complexity of gRPC for their own solution. There's a great comment from a gRPC engineer back when Twirp launched evaluating why Twirp has gained traction: https://news.ycombinator.com/item?id=18688942

I evaluated gRPC but the web story is a disaster; you need an envoy proxy, and TypeScript support was half-baked. I went with Twirp instead. If Connect had been around, I would have gone with it over Twirp because it's compatible with gRPC and Twirp makes a few weird choices like JSON for error messages.

sa46··on Paginating Requests in APIs (2020)
Expiring tokens has a section in that link:

> Many APIs store page tokens in a database internally. In this situation, APIs may expire page tokens a reasonable time after they have been sent, in order not to needlessly store large amounts of data that is unlikely to be used. It is not necessary to document this behavior.

sa46··on Paginating Requests in APIs (2020)
The API improvement proposals (AIP, yes it's a terrible acronym) are a rich source of API design wisdom. It assumes protobufs but the majority of proposals would work for any API.

The relevant one is https://google.aip.dev/158, which recommends cursor-based pagination and covers different pagination use-cases, including:

- support for different sort orders - mentioned in article as weakness of keyset pagination

- support for skipping results - mentioned as a weakness of cursor-based pagination

- why to use opaque page tokens

sa46··on AlloyDB for PostgreSQL under the hood: Columnar engine
They claim 4x faster against vanilla postgres for "commercial workloads" so it sounds really impressive.

> I wonder if there are performance tradeoffs sometimes leading to slower than vanilla postgres?

Speculation: alloydb probably has both a row store and a column store and some fancy logic to decide which one to use. That's usually what HTAP means.

In very broad strokes, columnar data stores struggle if you select many columns.

sa46··on AlloyDB for PostgreSQL under the hood: Columnar engine
It wasn't clear to me how it was related to Postgres so I spun up a cluster and alloydb is Postgres with a different storage engine (similar to the long awaited zedstore project). That means all Postgres features work.
sa46··on Stripe App Marketplace
Any interesting details on how it’s implemented? If I were guessing, register a stripe webhook. On each webhook, and fetch changed events and turn it into proper sql.
sa46··on Go nulls and SQL
That's the approach I've taken as well. We use coalesces to ensure queries to never return null and handle the zero value or a sentinel value specially. Since our API layer is protobufs which don't have nil, the zero value is correct the majority of the time.
sa46··on Quick fixes to your code review workflow
IntelliJ's integration with GitHub PRs is nice. The one thing I miss is you can't what changed if you switch from the PR diff view to the normal code view which you need for code navigation and refactoring.
sa46··on Speeding up sort performance in Postgres 15
Interesting, I hadn't thought of it that way. Looking through our codebase:

- We get a lot of mileage out of DISTINCT ON to get the most recent version of a row for some subset of columns. I think this a different use case than what you refer to.

- We typically use DISTINCT to get PKs for a table when starting from another table. Like, find all user IDs that commented on a given story by looking at the join table.

    SELECT DISTINCT user_id FROM story_comment WHERE story_id = 4
sa46··on Advanced techniques for reducing Emacs startup time
Author here. Things that have changed since I published this.

- Doom emacs does most (all) of these with a bunch of cleverer techniques than mine. I’d recommend doom over rolling your own emacs.

- I use IntelliJ just about exclusively.

- I now do the minimal amount of customization I can get away with.

sa46··on AlloyDB for PostgreSQL
I started a cluster and initial testing shows that alloydb is Postgres, so everything should just work. The blog post [1] has more details on the implementation [1].

[1]: https://cloud.google.com/blog/products/databases/alloydb-for...

sa46··on AlloyDB for PostgreSQL
What does 100% compatibility refer to? Does alloydb support triggers, partitions, and extensions?
sa46··on I'm all-in on server-side SQLite
> I want to check out the state of the world at a specific commit, write a change, a week later write another change, revert the first change 3 weeks later.

This is sort of like temporal tables but with the ability to branch from a previous point of history. I'm not sure it would play well with foreign keys.

You could branch the entire database with either point-in-time recovery or with the file-system using ZFS. Postgres.ai turned this into a product.

sa46··on Twelve-factor app anno 2022
I've seen credentials appear in logs from env vars. Logs tend to replicate to a few different systems and are usually less locked down than the app they came from. An attacker could get lucky with logs that live in a less-secure storage bucket or monitoring system.
sa46··on How I hunt down and fix errors in production
How would Lightrun have helped here? Skimming their docs, it looks like a fancy debugger that can log stuff on prod and display it in your IDE.

A debugger is most useful when you know where the problem is and you can either reproduce it or it occurs often enough to observe. Neither was true for OP. Additionally, the problem was in nginx and it's not clear if the error was visible from a part of the stack that Lightrun supports.

That said, I'm always game to hear about alternative debugging techniques.

sa46··on Four Eras of JavaScript Frameworks
GWT was replaced by the similar J2CL and is used for GMail, Inbox, Docs, Slides and Calendar.
← PreviousPage 7 of 15Next →