• Note

    Concurrency in transactions

    There’s a temptation to make independent database operations concurrent:

    await db.transaction(async (tx) => {
      await Promise.all([
        tx
          .update(accounts)
          .set({ balance: sql`${accounts.balance} - 100.00` })
          .where(eq(accounts.name, 'Dan')),
        tx
          .update(accounts)
          .set({ balance: sql`${accounts.balance} + 100.00` })
          .where(eq(accounts.name, 'Andrew')),
      ]);
    });TypeScript

    Inside a transaction, though, there usually isn’t any point. Even if both promises fire at once, the driver still has to send the statements down a single connection.

    Anything not bound to the database connection can still run concurrently:

    await db.transaction(async (tx) => {
      await Promise.all([
        otherAsyncWork(),
        tx
          .update(accounts)
          .set({ balance: sql`${accounts.balance} + 100.00` })
          .where(eq(accounts.name, 'Andrew')),
      ]);
    });TypeScript

    But you probably shouldn’t do other I/O in the middle of a transaction anyway if you can avoid it (especially writes). It keeps the transaction open for longer, and any external side effects won’t be rolled back.

  • Note

    Avoiding prettified logs

    It’s common to pretty-print logs to stdout during development:

    [10:42:18] INFO  Request started
      method: GET
      path: /api/posts
      requestId: req_123

    Then switch to structured logs in production:

    {"time":"2026-05-24T10:42:18.120Z","level":"info","message":"Request started","method":"GET","path":"/api/posts","requestId":"req_123"}JSON

    Pretty logs are nice when an app is small, but they become painful once things scale. Thousands of lines fly past, terminal search only gets you so far (your scrollback needs to be set absurdly high), and when the terminal closes, the logs are gone.

    I’ve started to prefer writing structured logs to disk during development. Send them over to something like Logdy (if you want something lightweight), or VictoriaLogs (if you want something a bit more capable).

    Then you get the same filtering, grouping, and JSON-attribute goodness you rely on in production. It’s also very useful to be able to point your agent at the logs of an already running process.

  • Note

    Distributed Zod discriminated unions

    Here’s a trick that seems obvious in retrospect. If you want to represent a type like this in Zod:

    type Order = { name: string } & (
      | { orderType: 'DELIVERY'; details: DeliveryOrderDetails }
      | { orderType: 'PICKUP'; details: PickupOrderDetails }
    );TypeScript

    You can just create the union type separately and then intersect it:

    const OrderDetails = z.discriminatedUnion('orderType', [
      z.object({
        orderType: z.literal('DELIVERY'),
        details: DeliveryDetailsSchema,
      }),
      z.object({
        orderType: z.literal('PICKUP'),
        details: PickupDetailsSchema,
      }),
    ]);
    
    const OrderSchema = z.object({ name: z.string() }).and(OrderDetails);
    
    // Or this:
    // const OrderSchema = z.intersection(z.object({ name: z.string() }), OrderDetails);TypeScript

    Neat if you’re forced to keep the type discriminator top-level.

  • Note

    Vitest async fixture destructuring

    Vitest’s custom fixture support is great. Originally inspired by Playwright’s fixture setup, it’s now evolved into an auto-typed flexible builder:

    const test = baseTest
      .extend('client', () =>  new UserClient())
      .extend('user', async ({ client }) => client.getUser())
    
    test('Does the thing', async ({ user }) => {
      // No need to await user, it's already resolved.
    })TypeScript

    I’ve long wondered how they manage to lazily initialise fixtures based on object destructuring — even with a Proxy or similar it’s not possible to hide the async part. Today I finally took a look, and it’s actually pretty cursed:

    // https://github.com/vitest-dev/vitest/blob/v4.1/packages/runner/src/fixture.ts#L646
    let fnString = filterOutComments(implementation.toString())TypeScript

    The trick is to stringify the function and extract with regex the names of the fixtures requested. Vitest can then build up the fixture dependency tree and resolve these before passing them to your test fixture.

    It’s also why this doesn’t work:

    test('Does the thing', async (ctx) => {
      // No proxy magic or anything wild, so this won't work -
      // only the function arguments are parsed.
      const { user } = ctx
    })TypeScript

    Turns out Playwright does the extact same thing.