The brief, and the timer.
Maktoob runs on its readers. There is no big advertiser behind it, no parent company underwriting the work. A part of every month already goes into asking the public to fund what they already read. The brief was to take that ask and put it inside a real product. A portal where a reader can subscribe, unlock the paywalled work, and have a place to talk to the desk and to each other. The timer on it said two weeks.
Two weeks is not enough time to build a membership platform in the abstract. It is exactly enough time to build the one this publisher actually needs and nothing more, if you are honest about which is which.
The publisher pays for journalism, not for our cleverness. That decided every trade-off.
What I refused to build.
Before any code, I wrote down what was not going to ship. Public user profiles. A search box across community posts. A recommendation engine for the paywalled work. A referral mechanic. A bespoke admin analytics suite. None of these were bad ideas. They were just not what mattered in week one.
What stayed was thinner. A way to subscribe. A way to sign in. A way to read what you paid for. A community wall where members talk to each other and to the desk. Comments and email flows stayed too, because neither is really a feature in a membership product. They are how a publication actually talks to its members.
One database, one trust boundary.
All of it ran on a single managed Postgres. Identity, the subscription state, the paywall rules, the wall. Four concerns that often get split across services in a slide deck, but at this size and this budget there was no benefit to pulling them apart. One database, one backup story, one place to look when something is wrong at midnight.
A small Redis cache sat in front for the reads members hit most. Session lookups, paywall checks, the few hot threads on the wall. The point was simply that Postgres should not have to answer the same question twice in a row when the answer had not changed.
The interesting work was not the schema. It was being strict about which tables a given route was allowed to touch, and writing those rules down before the code grew opinions of its own.
A community wall in five days.
The wall took five working days. Members can post a short note, reply to each other, and the desk can pin a thread when a conversation is worth surfacing. It is not a social network, and trying to make it one would have spoiled the thing about it that actually matters to a publication.
The parts that took thought were the escape hatches. A soft-moderation tool the desk could use without leaving the page. Rate limits so one enthusiastic voice could not drown out the rest. A report path that does not assume someone is on call at three in the morning. Most of that work is invisible on the wall, which is the point.
The wall is a publishing surface. Optimise it for the desk, not for engagement.
Cost as a feature.
Fundraising only works if the platform costs less to run than what it brings in. So the infrastructure was sized for a quiet Tuesday and not for an imagined launch spike, with a small automated check that pings me if any line on the bill starts climbing faster than the membership does.
Launch week was boring.
Readers migrated. Paid tiers opened. The desk got back to writing. There was no launch tweet, no bug bounty, no countdown. The product slid into the operation without anyone outside the office needing to take a position on it.
Two weeks before, the publisher had a Substack-shaped problem. By the time it shipped, they had something that looked and behaved like the publication, ran on infrastructure they owned, and would not surprise them on a Friday night. That is the kind of outcome Madian sets engagements up to produce.