Just be aware you are now pinning ~20GB of VRAM permanently. Fine on a dedicated box, less fine if anyone else uses that GPU for anything, because their allocation will fail rather than queue.
Cass
@coworking_cass
Has tested the wifi in more coworking spaces than she can remember and reviews none of them for free stays.
71 credit Contributor
- From answers
- 0
- From questions
- 72
- Lifetime
- 72
Then the kernel OOM killer picks a victim by badness score, and it will frequently pick postgres, because postgres is the fattest process on the box. You'd be trading a clean four-second API restart for database downtime. Limits are how you choose who dies.
Because a long window is a capacity, not a promise about attention. Everything in there competes, and by 60k a large fraction of your context is stale: old file versions, superseded plans, tool output nobody needs any more. The model has no way to know that the version of parseInvoice from turn 6 is dead. Both versions are equally present and equally plausible.
What works:
- one task, one session. When the task changes, start over. This is the biggest single one
- keep the durable stuff in files, not in the conversation. A
PLAN.mdyou edit as you go survives a restart; a plan described in turn 4 doesn't - after a significant change, tell it to re-read the file instead of relying on what it saw earlier. Cheap, and it kills the stale-signature class of error outright
- when you feel the wobble, don't fight it. Ask for a handoff summary: what's done, what's left, which files matter - and paste that into a fresh session
Restarting feels like losing progress. It costs ninety seconds and it's usually the fastest thing available to you.
Bigger windows move the wall, they don't remove it. Same degradation shape, further out. And you will fill 1M with the same 60% garbage and hit it anyway.
Node doesn't know about your cgroup limit. V8 sizes its old-space heap from what it believes total system memory to be, on a 2 GB box that's roughly 1 GB, so it happily grows toward 1 GB while the cgroup kills you at 512 MB. It isn't failing to GC, it thinks it has plenty of room left.
Tell it the truth:
environment:
NODE_OPTIONS: --max-old-space-size=384
mem_limit: 512m
Leave headroom, because old space isn't the whole RSS - buffers, the code cache, native modules and stacks all live outside it. 384 under a 512 limit is about right; if you do a lot of Buffer work, leave more.
Then watch it. If it now GCs constantly and CPU spikes instead of dying, the limit really is too small and you raise it. Nine times out of ten this is the entire bug.
Everyone has. The tell is when a redeploy mysteriously breaks something that was working - that's your server-only edit getting overwritten.
It dissolves far better warm. I stir it into tea most mornings and there's nothing left in the bottom of the mug, which was never true with cold water.
I logged this because I got suspicious of my own answers. Forty three non-fiction books over three years, and by my own honest audit three of them left a behaviour I still do: one gave me the two minute rule for filing, one gave me a way to run a meeting that I have used weekly since, and one changed how I cook rather than how I work. That is a hit rate of about seven percent, which I think is normal and worth knowing before you buy the next stack.
Mild disagreement with the resale argument people will make. In our market, buyers looking at rural properties expected septic and did not pay a premium for sewer, the appraiser did not adjust for it either. If you are somewhere that people commute from and lots are being subdivided, sure. Where we are it moved nothing, so decide on running cost and risk, not on an imagined future buyer.
Constrain the blast radius mechanically instead of asking nicely.
What works for me, in order of effect:
- give it a failing test and say the only success condition is that test passing with no other test changing. Now there's a definition of done that isn't "improve the code"
- state the line budget out loud. "Fix this with the smallest possible diff. If the fix is more than 10 lines, stop and explain why before writing anything." The explain-first clause is the important half: it turns an unwanted refactor into a sentence you can say no to
- keep a rule in the repo config: no unrequested refactors, no renames, no new abstractions unless asked
The stop-and-explain pattern changed my day-to-day more than anything else. Most of the time it comes back with "it's three lines" and just does it.
Sour kimchi is not old kimchi, it is a different ingredient, and Korean home cooking treats it that way. Mine routinely goes a year in the fridge, and by then it is only for stew, fried rice and pancakes, where the softness stops mattering. What you have described, submerged, sour, fizzy, soft, no film - is exactly what a year-old jar looks like at month four. Keep it.
This got a friend of mine into a genuinely tedious dispute with a management company. Angle it so it covers your own door and as little of the shared hall as possible.
We hit the shm one last month and never traced it properly. That's the answer, thank you.
wal-g to R2 it is. I think we'd both quietly assumed PITR was a managed-only capability, which is just wrong.
The "eight usable days out of fourteen" framing killed the four city plan on the spot.
Neither uniformly. Route by blast radius, because that's the thing that actually varies.
What we landed on after about eight months:
- anything touching auth, payments, permissions, migrations or deletion: read every line, run it locally, second reviewer. No exceptions regardless of how small the diff looks. Maybe 15% of PRs
- anything where a wrong result is caught by a test that already exists: skim the diff shape, read the test changes carefully, merge. But if a test was modified rather than added, that's a stop sign: it's the most common way model-written code goes green
- everything else: read it properly, don't agonise
The highest-value rule in there is treating a modified assertion as a blocker. It catches the specific case where the code was wrong and the test moved to accommodate it.
And "rubber-stamping by Thursday" is the honest description of every uniform-review policy after a month. Design for it rather than pretending.
The floor-plus-percentage version is what I use and the percentage is the part that matters psychologically. A good month feels like a good month rather than money that vanishes into the account, which makes it much easier not to raid it later.
Mine refused. Servicer's policy was that they only recast loans they still owned rather than ones they serviced for someone else, and mine had been sold. Six weeks of phone calls to find that out. Ask early, ask for the policy in writing, and do not send the lump sum until you have it.
The plate-before-name thing is the one people skip. If a driver asks "are you Bri?" anyone standing there can say yes. You should be the one confirming their details.
Minimum viable is healthcheck plus a second replica plus letting caddy route around the unhealthy one.
api:
deploy:
replicas: 2
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3000/healthz"]
interval: 5s
timeout: 2s
retries: 3
start_period: 20s
Then docker compose up -d --wait, and caddy load-balancing over both with a health check on /healthz. It pulls an unhealthy backend out of rotation instead of 502ing at it.
That gets you most of the way, but compose will still cycle both replicas at once unless you push them one at a time. For true rolling on plain compose the honest answer is you script it: start new, wait for healthy, drain old, stop old. It's about 30 lines of bash and it works fine.
The other honest answer is that this is exactly what Kamal does, and Kamal is not kubernetes. Single VPS, compose-shaped config, does the health-check-and-swap for you.
Works right up until the deploy fails at 3am and it's six hours of downtime instead of 40 seconds. Deploy while you're awake and make the deploy boring.
Honest list of the trade-offs since you asked for pros and cons. No monthly water or sewer bill and often better tasting water; against that, you own every repair, a power cut means no water, and you are responsible for testing that a utility would otherwise do for you. Set aside a maintenance fund from day one and treat it as the bill you are not paying. Over ten years mine has still come out cheaper than the town supply my sister pays for.
The two travel together often enough that fixing one alone leaves you frustrated. Ask the workshop specifically whether they will look at shoulder slope and position, and if they say no, find another one.
Coverage is a fine metric read as a floor, 12% means you definitely have a problem. It's useless as a target, which is the thing that happened here. Different claim.
380 tests was the wrong ask. Coverage as a target produces exactly this, the cheapest way to execute a line is to mock everything around it, and you asked for lines executed.
Two changes fix it.
Ask for behaviour rather than coverage: "write tests for the pricing rules in this file, table-driven, real inputs and expected outputs, no mocks except the HTTP client." Naming what may be mocked is the important part; the default is to mock everything reachable.
Then verify with mutation testing rather than coverage. Stryker pointed at the pricing module will do automatically what you did by hand: flip a >=, run the suite, report that nothing failed. Start with one module, it's slow. A mutation score is the only coverage-adjacent number that can't be gamed by mocking.
Also: delete the 40 files. Seriously. They're 380 tests of assurance you do not have, and keeping them means every future failure is ambiguous.