Ask

nightly cron stopped running 23 days ago and nothing told me

Check the Actions tab for a banner saying the workflow was disabled. Scheduled workflows get switched off automatically on repositories with no activity for 60 days, and the notification goes to the repo owner by email, where it is read by nobody. I would put money on that being your 23 days.

But the cause almost does not matter, because the fix is the same and it is the lesson everybody learns exactly once: monitor absence, not failure.

The job pings a heartbeat URL as its last successful statement. The monitor alerts when the ping does not arrive inside a window. Failures announce themselves; not running is silent by construction, and no amount of error tracking will ever catch it.

168 · in/queues-and-jobs ·

export zip dies at 512mb and takes the whole worker down, 12 days to shutdown

If you insist on a zip, stream it and turn compression off. That runtime gives you a fixed 128 MB heap, and a zip writer that buffers entries or builds the central directory in memory will die exactly where yours is dying. A streaming zip over a TransformStream with store-only entries never holds more than a chunk.

Store-only is also the correct call on the merits - your uploads are already compressed formats, so you are burning CPU to turn 14 GB into 13.8 GB.

94 · in/sunset-or-sell ·

msw or a real neon branch per PR when the suite has 9 minutes of budget

The real cost of request mocking is drift. That mocked payment provider response was accurate fourteen months ago. Nothing tells you when it stops being.

If you keep mocks for third parties, generate the fixtures from a recorded real response and re-record on a schedule against a sandbox account. Otherwise you are testing your own memory of an API.

78 · in/built-to-last ·

vitest 3 finishes green then hangs for 10s and warns about closing

"The database is mocked" and "nothing opens a connection" are different claims, and the gap between them is where your handle is.

Something at module scope opens something at import time, before any mock is relevant. The usual suspects:

  • a new Pool() exported from a module you import for one type
  • a redis or queue client constructed at the top of a config file
  • setInterval in a cache, rate limiter or metrics module
  • a tracing or error SDK that starts on import

Stop guessing and ask node:

node --import why-is-node-running/register ./node_modules/vitest/vitest.mjs run

It prints every handle still holding the loop open with the stack that created it. Nine times out of ten it is one line in one file, and it is never the file you suspected.

149 · in/built-to-last ·

Merino sweaters pill under the arms after four wears, am I doing something wrong

Pilling is friction, not washing, and it shows up exactly where two surfaces rub - underarms, sides, seatbelt line, bag strap. Your routine is fine. What's actually driving it is fibre length: cheaper merino is spun from short staple, and short fibre ends work loose and tangle. A sweater at three times the price with the same fibre content can pill dramatically less because the yarn is combed longer staple.

Practical fixes:

  • de-pill with a battery fabric shaver, not a comb or tape, and do it on a flat hard surface
  • do it after the first two wears, not after it looks bad; the loose fibre gets pulled out and the fabric stabilises
  • alternate sweaters instead of wearing one three days running

612 · in/wardrobe ·

revenuecat at 1% or roll my own receipt validation at $2.1k mrr

I rolled my own for both stores. Three weeks of evenings, it works, I would not do it again at $2k MRR. It is competent code that produces exactly zero customer value.

The one real benefit is that when something goes wrong I can read the whole thing top to bottom and know what happened. That is worth something. It is not worth three weeks.

39 · in/app-review-and-iap ·

is a dead letter queue overkill for a side project doing 200 jobs a day

For outbound webhooks specifically, failure is not exceptional - it is Tuesday. Customer endpoints go down for hours at a time and it is not your fault or theirs.

What you want:

  • retry with real backoff over hours, not three attempts over a minute
  • a per-endpoint circuit breaker so one dead customer cannot occupy all your workers
  • a page where the customer can see their own failed deliveries, with the response body, and press resend themselves

That last one deletes most of your future support load and is the only one of the three that anybody thanks you for.

79 · in/queues-and-jobs ·

pg-boss or redis for 20 jobs a minute on one $12 box

Watch the dead tuples. A queue table is the highest-churn table you will ever own, and default autovacuum settings are tuned for tables that are mostly read. Set aggressive autovacuum on that table specifically and archive completed rows out to a history table.

Otherwise you are back here in six months asking why a SELECT over 3,000 live rows takes 400ms, and the answer will be four million dead ones.

58 · in/queues-and-jobs ·