HNHacker News
TopNewBestAskShowJobs

lewisl9029

3,792 karma · joined November 22, 2014

I'm Lewis Liu, former engineer at Brex, CircleCI, Metabase, Rangle.io. Currently consulting with Finch while building out Reflame: https://reflame.app/

Reflame came out of the frustration I continue to experience with the current state of frontend dev tooling and infrastructure. I've worked with some of the most amazingly talented frontend teams I could ever hope to work with, and we were always able to ship great software to customers at a breakneck pace when it truly mattered.

But looking back, this was in spite of the tooling and workflows that were available to us. At almost every step past local development, the tools and workflows we used ended up removing our ability to iterate with an tight and effective feedback loop, which I firmly believe is _the_ key to developer productivity.

The feedback loop is why many of us fell in love with local frontend dev tooling when we first started working with frontend, and why we keep pushing the envelope to tighten the feedback loop further and further, starting from manual reloading that took on the order of seconds, to live reloading that automatically reloaded as we saved, to hot reloading that preserved app state with sub-second latencies, and most recently to fast refresh that can achieve latencies in the double digit miliseconds.

Reflame is an reimagining of the end to end frontend development workflow that will empower us to _ship_ software with the instantaneous feedback loop that we love, not just develop with it locally.

If that sounds interesting to you, please head over to https://reflame.app/ and add yourself to the waiting list. Or feel free to shoot over an email to hi @ the same domain or a DM to https://twitter.com/lewisl9029 if you want to learn more, or just want to geek out about frontend dev. :)

submissionscomments
lewisl9029··on JavaScript hydration is a workaround, not a solution
In my experience, time-slicing and deferring renders for large lists (via APIs like useDeferredValue and/or requestIdleCallback, etc), combined with memoization can be a great alternative to virtualization.

For a lot of use cases where people jump straight to virtualization, it's not actually the number of elements that exist in the DOM at once that's the problem (React and browsers these days can handle a lot more than what's intuitive to most people). The problem is usually the cost of rendering all those elements at once in the initial mount, which often can cause visible frame drops and even noticeable freezes of the page.

Deferred rendering and time slicing can amortize this cost over a longer period of time while providing essentially the same UX (by eagerly rendering the first X # of items in a list), while memoization can keep transitions to new states fast by reusing the same large list of nodes when it hasn't changed. All of these techniques combined is still orders of magnitudes less complexity and requires fewer UX compromises (tearing, loss of browser search, etc) compared to virtualization, which should be reserved only for cases where there is no other workable solution IMO.

lewisl9029··on The future of Nginx: Getting back to our open source roots
I have but only briefly. I needed a pretty high degree of customization for my use case so njs for scripting was a huge value prop over having to do everything in VCL.
lewisl9029··on The future of Nginx: Getting back to our open source roots
This article came at an interesting timing for me, since I recently started to explore building my own CDN clusters on top of NGINX open source inspired by this article from Fly: https://fly.io/blog/the-5-hour-content-delivery-network/

I've worked with nginx in the past, and didn't have a great experience, so I was apprehensive diving in, but this time was very different. I think njs (their custom JS scripting environment) was a game changer. Support is built in to nginx core, and available by default in their docker containers, so it's much easier to get started with than Lua scripting. Their JS feature support has some quirks (no optional chaining, array destructuring, console.log's don't show up in logs, are some examples of things that threw me off) but overall nothing that blocked me from implementing the functionality I needed, and the integration points within the nginx config felt a lot more natural than I remember with Lua modules.

I did run into a number of things that were locked behind their commercial offering that made me a bit uncomfortable betting on it for the long term compared to purely open source alternatives. Off the top of my head:

- DNS discovery. There's a thread on the Fly example repo accompanying the blog post that describes the use case and proposes some workarounds: https://github.com/fly-apps/nginx-cluster/issues/2. Life would be a lot simpler if DNS discovery from the commercial offering was just available (i.e. we can outright delete a brittle bash script that makes DNS queries and reloads nginx on a 5 second interval). This was mentioned in the article as something they're planning to open source.

- Access to some kind of shared key-value store for custom caching logic in njs scripts. With Lua we could just connect to Redis, but njs can't seem to establish persistent network connections for now, so that's off the table. This wasn't mentioned in the article, but they did mention in this Github issue that they're planning on open sourcing their keyval module for this use case: https://github.com/nginx/njs/issues/437. I have some use cases where being able to connect to Redis would be ideal, since I'm already using Redis for caching across a bunch of other services, and syncing keyval across a cluster seems to be eventually consistent (https://docs.nginx.com/nginx/admin-guide/high-availability/z...), but for most of my caching use cases it should be sufficient.

So this article, along with their overall willingness to work with the community to identify and bring commercial features into open source (at least from what I've observed across their responses to Github issues) does a lot to alleviate those concerns.

Though at the end of the day, I don't necessarily need every nginx feature to be in open source. I have no problems with paying for great software like nginx to support its development. But as a small bootstrapped founder, their current pricing structure (from what I could gather on the internet is ~ 2k-5k per running instance) is completely prohibitive. It'd probably require a revamp to the way they sell the software (i.e. self-serve onboarding and automatic license provisioning for smaller customers instead of having customers of all sizes go through expensive sales people), but I'd love to see a more progressive pricing structure with a lower barrier to entry for their commercial product.

lewisl9029··on MacBook Air M1 screen crack for no apparent reason
A friend just had this happen to their M1 Air under warranty and Apple quoted him $400 for the repair claiming it was "accidental" damage, even though he didn't do anything to it, like the 50 pages of people who replied in the thread.

Wanted to make a quick PSA against purchasing one of these to save a few hundred bucks vs the M2 Air which hopefully won't have this problem, and possibly put some pressure on Apple to take care of their customers for what is clearly a widespread hardware defect.

lewisl9029··on Will Bun JavaScript Take Node's Crown
These days now that I'm fully in charge of building my own product, I often wonder if we place too much emphasis on centralized documentation sites that users have to deliberately seek out and visit vs in-context documentation snippets that show up as closely as possible to where users might actually need them, deeply integrated into the product and user journeys.

Users don't search for documentation for the sake of finding documentation. They search for documentation because they want to know how or if our product can solve a particular problem they have. My hypothesis is that the documentation discoverability problem is really just a symptom of the product discoverability problem, and that centralizing docs in 1 searchable website to make docs "discoverable" is only addressing the symptom, when that effort can be much better spent addressing the root cause by making the product itself more discoverable and deeply integrating useful documentation into it.

lewisl9029··on Finishing what you start makes teams more productive and predictable
I agree deadlines are a necessary evil to ensure we hold ourselves accountable to both customers and internal non-eng stakeholders who need to know when things will ship in order to make plans around them and collaborate with us effectively.

One approach to develop a healthy culture around deadlines I've been thinking about lately:

Plan the deadline around the minimal lovable product, while spec'ing out the minimal shippable product, and ensure there's enough of a delta between the two so we can have a large degree of freedom w.r.t. scope.

Without this freedom to vary scope, _when_ (not if) our estimates invariably fail, our only options would be to extend the deadline (defeating the purpose of setting deadlines in the first place if we resort to this often enough), or to burn ourselves out with overtime work in an attempt to meet those deadlines (obviously results in an unhealthy/unsustainable environment, and not even guaranteed to succeed).

lewisl9029··on Back from the Future: Global Tables in CockroachDB
Thanks, that's good to know! Happy to see more competition in this space.

Really love being able to reason about a globally replicated DB as if it was in a single location with strongly consistent reads. The mental model is so much simpler than a single-primary read replica setup.

The cost in write latency is worth it IMO since it's still usually fast enough for most use cases, and nudges me towards using alternative replication strategies for use cases that are sensitive to write latency (instead of deluding myself into believing my app is fast for everybody when it's only fast for me because I chose a primary that's physically close to where I live).

lewisl9029··on Back from the Future: Global Tables in CockroachDB
Does their serverless offering actually support Global Tables? Last time I checked I remember only being able to select a single region. Their dedicated offering supported multiple regions, but started at several hundred dollars per month, which is pretty prohibitive for someone just starting out.

I'm currently using Fauna which offers the same strongly consistent global reads with higher latency writes approach, and their pricing is much better for smaller-budget projects that can benefit from global replication.

lewisl9029··on Coinbase is reportedly selling geolocation data to ICE
This really is the eternal struggle of all decentralized technology:

They all eventually gravitate towards centralization as they gain mainstream appeal, as the very tangible competitive advantages from networks effects and economies of scale win over the mostly philosophical benefits of decentralization that have always failed to sway the masses.

We see this play out time and again, with email, git, and are already starting to see the same pattern emerge with cryptocurrencies.

Though that is not to say there is no benefit to having decentralized technologies. The fact remains that centralized platforms built on top of decentralized technology have a much harder time locking users in due to the low switching costs enabled by the underlying tech. This is why products like BitBucket and GitLab are still viable today despite the undeniable dominance of GitHub, and similarly alternative email providers like Fastmail and Outlook in the age of Gmail.

We rarely see this in markets built on top of centralized tech. In social networks, for instance, there really isn't any viable competitor to platforms like Facebook and Twitter because switching isn't possible without losing all of your investments in the product.

This low switching cost makes upstarts more viable alternatives compared to in markets built on top of centralized tech, and keeps incumbents on their toes and forces them to keep innovating in order to stay dominant instead of just simply riding high on the strength of their moats. As such I do still think the pursuit of decentralized technology is worthwhile, even if a large degree of centralization in itself might be all but inevitable in the long term.

lewisl9029··on Whatever happened to SHA-256 support in Git?
Also check out multihash from the IPFS folks: https://github.com/multiformats/multihash

It's a more robust, well-specified, interoperable version of this concept.

Though it's probably overkill if you control both the consumer and producer side (i.e. don't need the interoperability) and are just looking to make hash upgrades smoother, in that case a simple version prefix like Go's approach described above has lower overhead.

lewisl9029··on Whatever happened to SHA-256 support in Git?
Just the other day, I was actually forced to downgrade the file hash used in the product I'm working on to sha1 in order to interact with GitHub's APIs efficiently (to avoid having to download the entire file just to recompute a sha256 for matching).

Luckily I've versioned the internal hash so the upgrade path back to sha256 should be as smooth as the downgrade was. I'm still bitter about it though.

lewisl9029··on Why am I no longer qualified to be a Brex customer?
Haha this is awesome! Totally gonna steal this for the next time I want to be liked. :)
lewisl9029··on Fresh – Next-gen web framework
I explored using client-side service workers for build-less deployment workflows a while back, but the blocker was the initial visit when the service worker hasn't been installed yet. Ended up using es-module-shim's fetch hook (https://github.com/guybedford/es-module-shims#fetch-hook) instead, which worked quite well.

I kept the demo repo around here, in case it's helpful to anyone: https://github.com/lewisl9029/buildless-hot-reload-demo.

The repo itself is quite out of date at this point, but my current project, Reflame, is essentially the spiritual successor: https://reflame.app/

Reflame has the same ideals of achieving the developer experience I've always wanted for building client rendered React apps:

- instant production deployments (usually <200ms)

- instant preview environments that match production in pretty much every imaginable way (including the URL so we don't have to worry about special whitelisting for CORS and whatnot), that can also be flipped into development mode for fast-refresh (for the seamless feedback loop we're used to in local dev) and dev-mode dependencies (for better error messaging, etc)

- close-to-instant browser tests (1-3 seconds) that enable image snapshot comparisons that run with maximum parallelism, and only rerun when their dependency graphs change, and auto flake detection/recovery

lewisl9029··on New UUID Formats
> I think knowing when an ID was created is harmless - and if it isn't there is a design problem that is larger than ID choice.

I hope you're right, since I've been cautiously operating under that assumption so far. Cautiously, because of warnings, from people better versed in cryptography than I, in discussions like this one: https://news.ycombinator.com/item?id=29805433. I still haven't quite been able to internalize when this should vs shouldn't be something I should be concerned about, hence the comment.

lewisl9029··on New UUID Formats
I've been using ULID for a while now, which analogous to UUID v7 but with a different (better IMHO) string representation. They've been awesome for using as sort keys in dynamo for instance, since they're lexicographically sortable as strings.

But one thing I'm still wary about is exposing these IDs with millisecond-precision time components to end users, since I've seen multiple discussions here on HN about the potential for timing attacks.

How worried should I really be? Do people have useful heuristics on the kinds of data where it's safe/unsafe to expose timing information, or should I just only expose a separate UUID v4 externally across the board just to be safe?

lewisl9029··on Neon – Serverless Postgres
Seems like this might implement database branching in the way most people would assume: branching both the data and schema? I remember being a bit disappointed to learn that PlanetScale's database "branching" was only for the schema [1], which is still quite useful, but this would be so much cooler!

I couldn't find much info about the replication models available/planned however. I would consider this to be table stakes at this point for a serverless database with the recent trend of pushing compute to the edge. This is much more interesting to me than scaling to 0, which is only really useful during the prototyping phase.

PlanetScale is single primary with eventually consistent read replicas, Fauna has strongly consistent global writes (or regional if you choose, but no option for replication between regions if you do) with a write latency cost, Dynamo/Cosmos are active-active eventual consistently replicated with fast writes globally. All useful in different scenarios, but I'd love to have one DB tech that can operate in all of these modes for different use cases within the same app, using the same programming model to interact with data across the board.

I think the decoupled storage engine here would open up some really interesting strategies around replication. What are the team's plans here?

[1] https://docs.planetscale.com/concepts/branching

lewisl9029··on Fly Machines: An API for Fast-Booting VMs
For my use case, the main benefit of using manually pre-warmed Lambda instances over Fly Machines is the fact that "warm" Lambdas cost practically nothing (only have to pay for the warming events that are billed for milliseconds every few minutes). :)

This is why I want to see a similar mode of operation for Fly Machines, possibly through memory snapshots, so I can manually provide a "warm" suspend state for it to unsuspend into. In fact this would be even better than the lambda model since there would be _no_ cold starts.

lewisl9029··on Fly Machines: An API for Fast-Booting VMs
I do actually already use Fly for pretty much everything else.

For this use case though, I forgot to mention that it needs _much_ faster autoscaling than what Fly's regular VMs offer, with unbounded concurrency, and not ideal to run concurrently in a single VM due to each request being compute heavy and needing full isolation from each other since they run arbitrary customer code.

It's true that with Lambda, some amount of cold starts are probably inevitable with extreme spikes in traffic. But I'm hoping to mitigate most of that by sending artificial concurrent traffic on a schedule to keep a decent buffer of warmed up Lambdas above the current real traffic level. Still to be seen if that plan works out in practice.

lewisl9029··on PlanetScale Portals: Read-only regions
It could work in theory, since it's supposed to be compatible with Postgres, but last time I checked they don't test against it, and it's not anywhere close to 100% identical (https://www.cockroachlabs.com/docs/v22.1/postgresql-compatib...), so I'm personally not too comfortable relying on it for production workloads.

Things might have changed since though, I'd try asking folks in the forums to hear their experiences with it before diving in. Would be awesome if this setup worked well though!

I personally prefer Fauna from an operational perspective since it's a 0-maintenance SaaS that scales pricing with usage with a generous free tier, but it's probably unlikely to be supported anytime soon.

lewisl9029··on Fly Machines: An API for Fast-Booting VMs
Right, I was wondering about this too. There are mentions of instances vs machines, so I'm hoping it's possible to spin up multiple instances of the same machine to run tasks concurrently, but I haven't found this explicitly confirmed anywhere yet.
lewisl9029··on Fly Machines: An API for Fast-Booting VMs
I was really excited when reading this, but realized the lack of a faster "warm" start makes this less ideal for my highly latency-sensitive use case on Lambda. Lambdas start much faster than 300ms when warm IME, and I'm hoping with enough sustained traffic (be it real or artificial), most requests will be warm.

I'd love to be able to supply some kind of memory snapshot in addition to the docker image to cut down on cold starts. Probably blocked on snapshot support in Firecracker according to another thread? Eagerly awaiting this since it could make Fly Machine the best of both worlds!

Not a fan of how Lambda makes me scale memory and compute in tandem, when my use case benefits so much more from compute than memory. I basically have to pay for 2+ gigs I'm never going to use to get the compute performance I want. Makes 0 sense.

lewisl9029··on PlanetScale Portals: Read-only regions
Yeah... I wish Temporal had FaunaDB support. Seems like a great fit for globally distributed jobs.
lewisl9029··on Deno 1.22
At Brex we tried to speed up the typecheck speed in CI by saving and reusing the build output from the incremental cache.

It worked pretty well for the most part, and I would recommend looking into this for most teams since it's a pretty quick win. Most typecheck steps went from minutes to low-double-digit seconds (admittedly still slower than I would have hoped).

But occasionally there would be gigantic spikes back to minutes for seemingly small changes that seem like they should have been incrementally check-able. I suspect these might have something to do with circular dependency chains, but didn't have bandwidth to dig deeper.

I wish TypeScript's incremental checking mechanism and file formats was a bit better documented and less opaque, so people could better reason about and optimize typechecking performance, and possibly build support into other systems to perform & share some of the analysis they're already doing to avoid wasted work. That and to make it less of a giant blob for the entire codebase, and more of a set of files that are incrementally and individually update-able for better cache granularity and faster updates.

lewisl9029··on I finally got Twitter after years of not getting it
So I had a Twitter account since 2009 but never really used it till a few months ago. I have some mixed feelings about it.

On one hand, I really love the consumption side (following people and reading their tweets): being able to follow interesting people and populate my feed with all kinds of great posts about topics I'm interested in. I've honestly been learning a lot and getting exposed to a lot more perspectives I'd never have been exposed to from just passively scrolling over the past few months. This alone makes it more than worthwhile in my books, and makes me wish I started using it sooner.

On the other hand, the production side (actually posting my own tweets) almost always feels like shouting into a void given my rather tiny follower count. I've done it enough times with the same outcome to the point that I've mostly given up on actually posting my own tweets, and have toned down my engagement on the platform to just replying to other people's tweets and retweeting things I find worth sharing.

I'm not saying this needs to change, it's the system working as intended. I imagine people with high follower counts probably get enough engagement on their tweets to make it a completely different experience, and it's totally fair. They've spent years building up an audience, I haven't, and the difference in reach/engagement is a natural consequence of that.

Though this is why HN still feels so special. Everything I write here stands on its own merits. Good comments rise to the top and bad ones get ignored/downvoted into oblivion. The algorithm doesn't care about who's writing it. Sure, it's not a perfect meritocracy. There's a ton of luck involved, famous people in the community will still get their usernames recognized and noticed/upvoted more often as a result, and the downvote-for-disagreement culture breeds a lot of groupthink. But I still love this place despite all of its warts.

lewisl9029··on A dev's thoughts on developer productivity
Really enjoyed the article! Wholeheartedly agree with the use of iteration speed as the measure for productivity.

I think one key under-explored opportunity for improving productivity involves bringing the outer loop closer to the inner loop. The main reason we have a distinction between these loops is everything we need to do in the inner loop generally happens fast enough to keep us in flow state, while everything in the outer loop is usually slow enough to break flow state.

In the graphic illustrating the inner loop vs outer loop in the article, only the Plan, Review, Measure, React, steps have intrinsic latency floors based on product/market/human/organizational constraints. My hypothesis is that the other three, Author, Test, Deploy, are constrained in latency only by technology, and can be made fast enough keep us in flow state given the right technology, bringing them into the inner loop.

Disclaimer: My ulterior motive in writing about this is I've been building a product to do exactly what I described above, for client-side rendered React apps: https://reflame.app/. But I would honestly love to see more folks explore opportunities here, since I want to be able to build all kinds of software this way, not just the kinds I have the bandwidth/skills to work on.

lewisl9029··on Success in Canada means moving to America
Uh... I have a less rosy picture of the situation.

To brain drain Canadian talent, US companies used to need to convince people to physically move to a different country, a rather tall order, but even then people were leaving in droves.

Now, they can brain drain Canadian talent with practically 0 barriers through remote work.

Unless Canadian employers are willing and able to adapt quickly and start offering truly globally competitive salaries (and historically they have proven to be utterly incapable of this), I think this is game over.

lewisl9029··on Success in Canada means moving to America
Canadian tech employers have been completely myopic for decades, only willing to compete in salary against other local tech employers, while anyone who's actually any good can make 2x+ within a 3 hour drive south. This resulted in a ton of brain drain in the form of people physically moving out of Canada and into the US. (I was one of those people who were brain drained away.)

Now people can make 2x+ within the comfort of their own homes, making it even easier for US tech companies to brain drain talent away from Canadian companies. Will that finally force them to change their ways or are they going to keep scraping the bottom of the ever depleting barrel?

I honestly don't have a very positive outlook given the history here, but would love to be proven wrong.

lewisl9029··on Meta Is Transferring Jest to the OpenJS Foundation
Vitest is great for running unit tests, but for component testing I still think running in a real browser and using visual snapshots & diffs is preferable to dom snapshots & diffs in a fake browser env like node + jsdom.

It's not just the fact that jsdom doesn't fully support all browser APIs, but components are building blocks for a primarily visual medium, so being able to build and debug my component tests visually has been a complete game changer when it comes to productivity.

I think the very reasonable counterpoint to that is browser tests are slow to run, to the point that it's infeasible to run locally at scale. That's why for https://reflame.app, I've been building a testing strategy around using serverless compute to run browser component tests with maximum parallelization for a tight feedback loop that takes ~5s for a cold start and 1~2s thereafter, with dependency analysis to rerun only tests that could have changed to keep cost scaling under control.

lewisl9029··on Ask HN: Options for handling state at the edge?
One thing I forgot to mention: Reflame requires fast writes globally only for a small subset of use cases. For everything else, it only needs fast reads globally, and for those I've been really liking FaunaDB.

It's not SQL, but it offers strongly consistent global writes that allows me to reason about the data as if it lived in a regular strongly-consistent non-replicated DB. This has been incredibly powerful since I don't have to worry at all about reading stale data like I would with an eventually consistently read-replicated DB.

It comes at the cost of write latency of ~200ms, which is still perfectly serviceable for everything I'm using it for.

lewisl9029··on Ask HN: Options for handling state at the edge?
A lot of great info here already, but I just wanted to add my 2c as someone who's been chasing the fast writes everywhere dream for https://reflame.app.

Most of the approaches mentioned here will give you fast reads everywhere, but writes only fast if you're close to some arbitrarily chosen primary region.

A few technologies I've experimented with for doing fast, eventually consistently replicated writes: DynamoDB Global Tables, CosmosDB, Macrometa, KeyDB.

None of them are perfect, but in terms of write latency, active-active replicated KeyDB in my fly.io cluster has everything else beat. It's the only solution that offered _reliable_ sub-5ms latency writes (most are close to 1-2ms). Dynamo and Cosmos advertise sub-10ms, but in practice, while _most_ writes fall in that range, I've seen them fluctuate wildly to over 200ms (Cosmos was much worse than Dynamo IME), which is to be expected on the public internet with noisy neighbors.

Unfortunately, I got too wary of the operational complexity of running my own global persistent KeyDB cluster with potentially unbounded memory/storage requirements, and eventually migrated most app state over to use Dynamo as the source of truth, with the KeyDB cluster as a auto-replicating caching layer so I don't have to deal with perf/memory/storage scaling and backup. So far that has been working well, but I'm still pre-launch so it's not anywhere close to battle tested.

Would love to hear stories from other folks building systems with similar requirements/ambitions!

← PreviousPage 3 of 32Next →