# Week of 24 August 2026: the schema before the product

> Eleven commits in one day, and none of them a feature a customer could see. Why the first thing built was a database that refuses to show one workspace another's rows, and a test that proves it.

By Adarsh Mamgain, 31 October 2026. From the Hellofew blog at https://hellofew.com/blog.

Part 1 of Building Hellofew, week by week. Every part: [Week of 24 August 2026: the schema before the product](https://hellofew.com/blog/building-week-1-the-schema-before-the-product.md), [Week of 31 August 2026: everything that touches money or the account, first](https://hellofew.com/blog/building-week-2-money-and-the-account-first.md), [Week of 7 September 2026: the loop closes, on a fake LinkedIn first](https://hellofew.com/blog/building-week-3-the-loop-closes.md), [Week of 14 September 2026: read the person before writing to them](https://hellofew.com/blog/building-week-4-read-before-you-write.md), [Weeks of 21 September 2026 to 5 October 2026: the quiet weeks, and the first two replies](https://hellofew.com/blog/building-week-5-the-quiet-weeks.md), [Week of 12 October 2026: the launch week, a name, and 94 commits](https://hellofew.com/blog/building-week-6-the-launch-week.md), [Week of 19 October 2026: writing that reads like a person, and the first fourteen replies](https://hellofew.com/blog/building-week-7-writing-that-reads-like-a-person.md), [Week of 26 October 2026: the week the replies came, and what broke on the way](https://hellofew.com/blog/building-week-8-the-week-the-replies-came.md).

On 28 August 2026 the repository had one commit, an architecture document, and by the end of the day it had eleven. Not one of them did anything a customer would notice. There was no product page, no LinkedIn, no message. There was a Postgres schema, a login, a worker that said it was alive, and a Dockerfile. I want to start the series there because it is the least interesting week and the one I would do the same way again.

The product I was setting out to build runs other people's LinkedIn accounts. That sentence should scare anyone writing the first line of code for it. A bug in a to-do app loses a to-do. A bug in this loses somebody the account they spent ten years building, or shows one customer another customer's replies. So the first day was spent on the two things that make those two failures hard rather than easy, and Hellofew did not exist as a name for another six weeks.

**Commits a week, from the first one to the day this was counted**

Counted from the git log on 2026-10-29. The two empty weeks are real: nothing was pushed.

| Week of | Commits |
| --- | --- |
| 24 Aug | 11 |
| 31 Aug | 8 |
| 7 Sept | 51 |
| 14 Sept | 23 |
| 21 Sept | 0 |
| 28 Sept | 6 |
| 5 Oct | 0 |
| 12 Oct | 94 |
| 19 Oct | 26 |
| 26 Oct | 85 |

Check it: [the repository's own log, which these posts are written from](https://hellofew.com/blog#building-hellofew)

## Every table knows which workspace it belongs to, and Postgres enforces it

The third commit of the day is a tenant schema with row-level security switched on. Every table that holds a customer's data carries a workspace id, and Postgres has a policy on each one that says a query may only see rows whose workspace matches the one the connection has declared. The application sets that declaration at the start of every transaction. A query that forgets to set it does not see everybody's rows; it sees nobody's.

The fourth commit is a test that opens two workspaces, writes a row into each, and proves the first cannot read the second's. That test is still in the suite. In September it caught two cross-tenant reads that were returning zero rows and saying nothing about it, which is exactly the failure the policy is designed to produce instead of a leak: a page quietly empty rather than a page quietly full of the wrong person's data. Later still, a second test asks Postgres which tables carry a workspace id and refuses to pass if any of them has no policy, because a list of tables to check is only ever as good as the last time somebody remembered to add to it.

## A login with no password, and a worker that reports in

The login is a magic link: an email, a signed token, a session. No password table to protect, and nothing a customer could reuse from another site. Later that became a proper auth library with Google sign-in beside it, but the shape held: this product holds as little of a person's secrets as it can, which matters more than usual when the next thing it will ask for is access to their LinkedIn.

The worker is the half of the product that does the work when nobody is looking: it will find people, write to them, send, poll for replies. On day one it did none of that. It started, wrote a heartbeat into Redis every few seconds, and stopped. That heartbeat is the oldest alarm in the system. Two months later the admin report would flag a worker that had been silent for ten hours, and it did so because of a commit made before there was anything for the worker to do.

*Figure: The app and the worker share one Postgres database; the worker talks to the LinkedIn session provider and to the model.*

*What the day's eleven commits laid out, roughly as it still is. The arrows are the only paths data takes; a customer never talks to the session provider or the model directly.*

## What I would say to someone starting the same thing

- Write the row-level policy before the first feature, and write the test that proves it the same day. Adding it later means auditing every query you have written since, and you will miss one.
- Make the background worker report that it is alive before it does anything else. The silence of a dead worker is the most expensive silence this kind of product has.
- Put the deploy in on day one. The Dockerfile from this week is still the one in use, and the first real deploy, two weeks later, was still a mess. It would have been a worse mess without it.

The limits this product runs under were not written this week; they were written as constants in the second week and have not moved since. They are on [the limits page](/limits). The point of this post is that the product's two biggest promises, your data stays yours and your account is handled carefully, were being built before the product had a single feature, because they are not features.
