· Niklas Nilsson, CEO

What the parts nobody enjoys building actually cost: 17% of the code, 69% of the days

Every platform needs the same parts: who may log in, who may see what, who pays, what happens to a person's data when they leave. Nobody enjoys building them twice and everybody underestimates them the first time. We have four months of measured history on exactly this, so here is what they cost — in code and in days, not in money.

The parts came to 17% of all changed lines and 13% of all code commits. They were touched on 68 of the 98 days with a commit, 69%, and were the majority of the day's work on only 13. That last pair of numbers is the finding. The plumbing was never a phase that started and finished. It ran alongside everything else, nearly every day, at a fraction of the day.

What we measured

One codebase of our own: a subscription product with a mobile app, a web admin and a Postgres database, which over the period turned from a single-brand app into a platform where independent coaches run their own practice on it. From the first commit on 5 May to 10 September: 1,797 commits, 141 database tables, 533 migrations, 154 database test files with 424 tests, 109 design documents, three people.

"The parts" are five areas, defined by file path and by migration and test name before we counted anything: authentication; access, roles and tenancy; billing and entitlements; consent, legal pages and account deletion; and the audit log. Everything else — workouts, programs, recipes, calendars, logging, search, the mobile UI, the AI features — is the product. Documentation, translation files, the lockfile and native build directories are excluded from both sides.

A commit counts as a plumbing commit when at least half of its changed lines are in those paths. A day counts as touching plumbing if any commit that day did.

The five bills

Access, roles and tenancy: the biggest, and rebuilt once

36,149 changed lines, touched on 50 of the 98 days. Row-level security was there from the first day: five of the first fifteen migrations were the security policies, the entitlement helpers and the trigger that creates a user. By the end, row-level security was enabled on nearly every one of the 141 tables, and the migrations had written 631 policies and dropped 257 of them along the way. Twenty-five migrations across all four months have "rls", "gate" or "access" in their name. The gates were not written once. They were reworked roughly every other week.

Then, 109 days after the first commit, the model was replaced. A single role column on the user held one value, and two of the three coaches on the platform were also administrators, so the column forced a choice between running the platform and running a practice. On 22 August that became a role-grants table with history — who, which role, when, granted by whom, revoked when — and the coach became a business of its own, with members, instead of a person with a flag. 6,191 lines of migration and 3,248 lines of tests, over two days.

This is the multi-tenant moment, and it is worth naming what triggered it: not scale, not a customer, but the third coach. A permission model that is right for the first tenant type is wrong the day a second kind of relationship exists, and the rebuild cost more than the original.

Billing and entitlements: designed in a day, still open four months later

10,288 lines over 26 days. The three design documents that define paying — the access gate, store subscriptions through the app stores, and coupons — were all written on the same day, 11 June, five weeks in. Manual grants for the admin came the same day. Wellness-allowance payments, a Swedish employer benefit that arrives by email and has to be matched to an account, took another design in July.

What is not built is the money between the platform and its tenants. The design for that, dated 25 August, opens by saying the business model is explicitly not set and locks no numbers. Receipts and the settlement ledger were built two days later so the model can be expressed when it exists. Four months in, billing is the one part whose most expensive question is still a question — and that is the normal state, not a failure.

Consent, legal and deletion: the September bill

36,154 lines, touched on only 16 days, 26,469 of those lines in September. This is the part that arrives on somebody else's schedule. Versioned legal pages and recorded consent landed at the end of August. Then two external rules landed at once: an app store guideline that requires a user to be able to delete their account from inside the app, and a decision to admit users under 13 with a parent's consent.

Account deletion in a database with 87 foreign keys pointing at the users table — measured on the day the design was written — is not a delete statement. It is a decision per table about what is the person's and what is somebody else's evidence: a coupon redemption stays, anonymised; a receipt stays, because bookkeeping law says so; a progress photo goes. Eight migrations in four days in September did that, and the consent work before it cost about as much again.

None of this was in anybody's estimate in May. It could not have been: the guideline and the age decision did not exist for this product yet.

Authentication: small, and finished early

5,068 lines over 30 days, most of it in the first month. One-time codes by email, a session, a trigger on sign-up. It is the part everyone budgets for and it is the cheapest of the five.

Audit log: cheapest of all, and the one people skip

1,048 lines. One design document in July, a handful of migrations, a trigger that records who changed what on an allowlist of tables, an admin page to read it. It cost less than the coupon system, and it is the part every other part leans on when something goes wrong.

The shape of it, by month

Plumbing as a share of all changed lines:

  • May — 8%. The foundations, then product.
  • June — 23%. Billing designed and built.
  • July — 8%. Product.
  • August — 14% of a very large month. The roles rebuild.
  • September — 42%. Consent and deletion.

Two quiet months, three loud ones, and the loud ones were not planned as loud. June was the only one anybody scheduled.

What to do with the numbers

  • Budget it as a share of every week, not as a line item. Seventeen percent of the code, seven days in ten. An estimate that has "auth and permissions: 2 weeks" as a row has already got the shape wrong.
  • Expect to rebuild the permission model once, at the moment a second kind of tenant relationship appears. Ours came 109 days in and cost more than the original. It is cheaper to know that than to be surprised by it.
  • The billing model between you and your tenants will be the last thing decided. Build the ledger so it can express any answer, and do not let the open question stop the rest.
  • The compliance bill arrives on an external clock. A store rule, an age limit, a data-protection request. Ours was 42% of September. It will not be in the May estimate, and the honest estimate says so.
  • Build the audit log first, not last. It is the cheapest part and the only one that helps you debug the other four.

The parts nobody enjoys building twice are also the parts nobody finishes once. That is the cost: not a number of weeks, but a fraction of all of them.