126 entries

The dev log

Bugs I hit and patterns I kept, dated and searchable, straight from the notebook I keep while building.

How it failed

12 / 126 entries

Start here

security

A kiosk isn't a user

Authenticating physical hardware — a kiosk, a counter, a wall display — as its own identity class, because the threat model for unattended hardware is genuinely different.

backend

Healing stuck state on every boot

A process that dies mid-workflow leaves something half-finished, and nothing transitions it — because the thing that would have just stopped existing. Run a recovery pass on every startup.

devops

Nine Dockerfiles, and getting back to two

No single bad decision — just a dozen individually reasonable ones compounding. How the Docker setup sprawled, and the rule I took from consolidating it.

backend

Offline sync that's actually safe to replay

Queuing actions offline is the easy half. Replaying them without duplicating work or corrupting state is the half that needs two layers of idempotency.

backend

pgBouncer, and the search_path bug that corrupted data

Connection pooling becomes necessary at scale — and transaction pooling has one sharp edge that silently wrote to the wrong schema.

security

Revoking a token that's stateless by design

JWTs can't be taken back once issued. Here's the gap that caused, and the one-column fix I now build on day one.

security

Row-level multi-tenancy, and the one bug that matters

One schema, a tenant_id on every table, isolation enforced entirely in application logic. Simple to operate, and exactly as strong as the discipline of every single query.

backend

The transactional outbox

The dual-write problem, and the pattern I now reach for on every project so a side effect can't silently vanish.

backend

What a client missed while it was gone

Raw Socket.io tells you a client reconnected. It doesn't tell you whether it's caught up — and for an unattended screen, a silent gap is the whole problem.

devops

What a load test actually tells you

The measurement mistakes that quietly invalidate a result, and what 'the numbers got worse under more load' turned out to mean.

How they failed

Filed by failure mode rather than by which part of the stack broke, because that's the part that carried between projects. 15 of 73 land in one bucket: it could not have worked.

  • It could not have worked

    15

    A race, an ordering, one column holding two things. The shape ruled it out before the code ran.

  • It said it worked

    13

    Green, 200, saved, “already handled” — and wrong. No error anywhere to go on.

  • Two copies drifted

    13

    One idea kept by hand in two places — schema and migration, dev and prod, app and app.

  • The safety net had a hole

    13

    The test, the audit log, the scanner, the CI run — covering less than I assumed it did.

  • Only visible by using it

    11

    Invisible to a code read. Found by being the real user, on a real device, at a real width.

  • Built, and nothing used it

    8

    Shipped, typechecked, dependency-injected, unreachable. Nothing ever called it.

Where the bugs live

Two thirds of them sit outside the application logic — in infrastructure, data integrity, or the auth layer.

infrastructure
17
data integrity
15
auth & security
15
frontend
14
engineering
6
distributed & realtime
6

73 total

Notes by area

backend
13
devops
11
security
10
frontend
9
learning
6
networking
4

53 total

When they were found

18
Jul
20
Aug
4
Sep
5
Oct