The Data Layer Comes Before the Dashboard: One Source of Truth for Startup Finance

At one client, revenue existed as four different numbers — the billing dashboard, the ledger, the CRM and the investor deck — and all four were right by their own definition. The fix was not a reporting tool. Every in-house finance tool I have described on this blog reads from the same small data layer, and it is the first thing worth building: what it contains, what it costs, and what becomes cheap once it exists.

The board meeting where I first saw the problem clearly had four revenue figures in the room. The billing dashboard showed what had been collected that month, gross of tax and before refunds. The ledger showed what had been recognised, after deferring the annual contracts. The CRM showed closed-won contract value, which the sales lead called revenue because nobody had told him not to. And the deck showed a run-rate — the best recent month multiplied by twelve — chosen by the founder because it was the largest of the four. An investor asked which one was revenue. The answer took two weeks and involved a workbook with a tab for each definition, which is when the company decided it needed a dashboard.

It did not need a dashboard. It needed one definition of revenue, written down, owned by a named person, and computed in exactly one place from data that had been checked against the bank. A dashboard built on top of four definitions is a faster way to disagree. This is the first piece of work I do when a company wants to build its own finance tools, and it is the reason the tools in the forward-deployed finance post were cheap to build: every one of them reads from the same small layer, so each is a query and a template rather than a project.

You cannot automate a number nobody has defined. You can only automate the disagreement.

Why the dashboard is the wrong first build

When a founder decides to stop living in exports, the instinct is to buy or build the thing they can see: a dashboard, a self-generating board pack, a model that updates itself. Each of those has to get its numbers from somewhere, and the somewhere is usually five systems — ledger, bank, billing, payroll, CRM — each with its own idea of what a customer, a month and a dollar are. If every tool pulls from the sources directly and applies its own logic on the way, the four-numbers problem has been rebuilt in code, where it is harder to find and nobody will look for it until a board member does.

The version most companies actually run is the spreadsheet chain: monthly exports from each system, pasted into a workbook, joined by lookups, adjusted by hand, saved to someone's laptop as final_v3. It works until its author is on holiday in the week of the raise. Its real defect is not fragility but that the definitions live in cell formulas nobody can audit: the number changes when a formula changes, and nothing records that it did.

The data layer is the thing that sits between the sources and every tool. It is small, it is dull, and it is where the definitions live.

What the layer contains

I keep this deliberately minimal. A seed-stage company generates a few thousand transactions a month, a few hundred invoices and a few dozen contracts; the data volume that would justify a warehouse product does not exist and will not for years. What exists is a definitions problem with a small data problem attached. Five components cover it.

Pulls that copy and never edit

Once a night, a script pulls from each source using read-only credentials: the bank for every transaction and the closing balance, the billing or payment provider for invoices, charges and refunds, the ledger for journal lines — a scheduled export is fine where there is no API — payroll for the register, the CRM for contracts and stages, and the cap-table tool if the company has one. Each pull lands in a raw table that is never touched again. Raw tables are evidence. When a number is questioned six months later, the answer is what the source said on the day, not what someone later decided it should have said.

One store

Everything goes into a single database. A single-file engine such as DuckDB or SQLite is enough for years; a small managed Postgres is the upgrade when several people need to write at once. There is exactly one, it is backed up, and every tool reads from it and from nothing else. The moment a tool goes around it — a chart wired straight to the billing API because it was quicker — the company is back to competing numbers.

Five canonical tables

On top of the raw copies sit five tables in the company's own vocabulary. Transactions: every bank line and every ledger line, matched to each other where they match and flagged where they do not — this is the reconciliation, done once, in one place. Customers and contracts: who pays, from when, for what, on what terms and with what renewal date, joined from the CRM and billing so that the same customer has one identity in both. Invoices: issued, paid and outstanding, with the date the cash actually arrived, which is what a collections figure and a cash forecast both need. People: the headcount register with role, start date, end date and fully loaded cost — the same table the hiring plan is written against. Budget: the plan by month and by line, because an actual with no plan next to it is a fact rather than a finding.

The definitions file

This is the artefact that matters, and the one no vendor can supply. It is a short file, versioned alongside the code, in which every number the company reports is defined as a query: revenue, recognised on this basis, excluding these items; ARR, counted from contracts in these stages at this value; gross and net burn; cash, meaning these accounts and not those; headcount, meaning these employment types; runway, computed by this method. Each definition carries a comment saying why it was chosen and who approved it. When a definition changes — and it will, usually at the first institutional round — the change has a date and an author, and every historical figure can be restated on the new basis with one command rather than by re-opening a year of workbooks.

The definitions file is where the four revenue numbers went to be resolved. Three of them were retired. The fourth, the run-rate, survived, because it is a useful figure — but it is now labelled as run-rate, computed from the same table as recognised revenue, and it never appears on a slide without the recognised number beside it.

Tests that fail loudly

After every pull, a set of assertions runs. The closing balance in the store equals the closing balance at the bank. Cash received from customers agrees with invoices marked paid, within a stated timing tolerance. Everyone paid in payroll appears in the people table with an active contract, and nobody in the people table is missing from payroll. Ledger revenue reconciles to billing within a threshold somebody has signed. When an assertion fails, the pull is marked unfit and nothing downstream — no pack, no model, no chart — is produced from it until a person has looked. This is the same discipline as reading the exceptions on the bank reconciliation; it has simply been moved to where it can run every night without anyone having to remember.

The whole layer, on one page

A repository containing: a pull script per source, with read-only credentials; raw tables that are never edited; one database file; five canonical tables — transactions, customers and contracts, invoices, people, budget; a definitions file in which every reported number is a versioned, commented query with a named approver; and a test suite that runs after each pull and blocks downstream output on failure. If a new finance hire cannot read the entire repository in an afternoon, it has been over-built.

What it costs

A first version takes one person about a week, provided that person can write Python and SQL and also knows what deferred revenue is. The second condition is the scarce one. The code is ordinary; the judgement is in deciding which bank accounts count as cash, whether a contract at "verbal yes" is ARR, and what to do with the customer who exists twice in the CRM under two spellings. Maintenance is a day a month, most of it responding to a source that changed the shape of its export. Everything runs on infrastructure the company already pays for, plus a small server or a laptop that stays on overnight.

The comparison is not with doing nothing. It is with the business-intelligence subscription the company would otherwise buy, which produces handsome charts of whichever definition was connected first. The layer is cheap because it is narrow. The expensive part is the definitions, and those are meetings, not code: the founder, whoever runs sales and whoever keeps the books, in a room until revenue means one thing. That meeting has to happen regardless. The layer just makes its outcome permanent.

What becomes cheap once it exists

The monthly reporting pack becomes a template over the definitions file. The same five numbers, with the same fixed definitions, go to the board and to investors, and when a board member asks how a figure was calculated the answer is a link to the definition rather than a promise to check.

The runway model stops being a spreadsheet someone updates. Committed payroll comes from the people table, cash from transactions, the revenue trend from invoices, and the scenarios are parameters over live data rather than a snapshot.

The exceptions list is a query. The duplicate supplier payment, the subscription that doubled, the payee whose bank details changed since the last invoice — each is a short query against the transactions table, run nightly, with the results sent to a named person. Anomaly detection at this size starts as a dozen such queries and only later, if ever, earns a model.

The language-model layer becomes safe to build. I have argued that AI should read the ledger, not write it, and the practical precondition is a small, documented schema with named definitions for the model to read. Natural-language questions compile into queries against tables whose meaning is written down; the model never has to invent what revenue is, because the file already says. Pointed at five raw systems with five vocabularies, the same model produces fluent, confident, unreconciled answers.

Diligence gets shorter. The financials in the data room tie to the bank because they were produced from the table the bank feeds into, and the cap table reconciles because it was pulled from the source rather than retyped. An associate who asks for the definition of ARR receives a file, and the deal moves on.

Where it goes wrong

The first failure is building a warehouse. Someone — often an engineer who has worked somewhere large — proposes an orchestration tool, a transformation framework, a cloud warehouse, a semantic layer. At ten people this is a hobby with a monthly bill. The answer to "we will need this when we scale" is that the migration will be easy precisely because the definitions already exist.

The second is definitions by default. When nobody senior will sit down and decide what revenue means, the engineer decides, because the query has to return something. The number is then a technical accident that ends up in a board deck. Every definition needs a named approver, and that person is not the person who wrote the query.

The third is editing the store. The first time someone fixes a row in a raw table to make a test pass, the table stops being evidence. Corrections go into the source system, or into an adjustments table that records who, when and why. The raw copy stays as the source said it.

The fourth is the one I named in the forward-deployed post and will keep naming: no owner. A billing provider renames a field, a pull fails quietly, and the pack goes out with last month's invoices. The tests catch most of this, but only if someone reads the failures. One person owns the layer, by name, and it is written down next to the control matrix.

The fifth is timing. This is work for a calm month, not the six weeks before a raise, when every hour goes to the deck and the definitions get decided by whoever is loudest in the room.

Back to the four numbers

Six weeks after that board meeting the company had a definitions file, a nightly pull, and a test that compared the ledger to the bank every morning. At the next meeting the same investor asked the same question and got one number, with its definition on the slide. The dashboard came later, took an afternoon, and nobody remembers building it — which is the correct amount of attention for a dashboard.

The data layer is where a finance function's honesty lives when there is no finance team to embody it. It is small enough to build in a week and dull enough that nobody will want to. Build it first anyway. Every tool after it is cheap, and every tool before it is a faster way to disagree.