One year as a Data Analytics Engineer at Cowrywise.
About two months in, I wrote a query I was proud of. It ran clean, it returned rows, and it was wrong.
The mistake was a status code. An integer, the kind every system has, and I read it the way that same integer had meant everywhere else I had worked: pending. Here it meant the opposite. The transaction had gone through. The definition existed. Someone had written it down, and the people who worked with that table every day knew it cold. I was two months in and did not yet know to go looking for it, and the schema itself was never going to tell me.
That is the whole lesson of my first year, and it took me most of the year to be able to say it this plainly. Knowing SQL gets you in the door. It does not get you very far past it. The dangerous query is not the ugly one. It is the elegant one, built on the wrong definition.
The engine room
Hundreds of databases. Thousands of tables. Billions of records. More than ten products serving millions of customers, kept upright by a team small enough that everyone still knows everyone’s name.
Behind the “Total Balance” a customer glances at for two seconds sits the product database that recorded the movement, the ingestion that lands it in the warehouse, the transformations that turn it into something you can actually ask questions of, and a reporting layer where it finally becomes a number on a screen. And at every one of those steps, someone whose job is to make sure it is still the truth.
What still surprises me a year in is how rarely anything catches fire. That is not luck. Data flows the way it should because the pipelines are watched, the team talks to each other constantly, and the standard for a number is set high from the top. Our CEO reads the numbers closely enough to notice when two of them disagree, sometimes before anyone has raised a flag. The first time he asked about a discrepancy in the data channel, on a Friday evening, I was terrified. A year later, what stays with me is not that he caught it. It is what it says about the expectation here: a number should survive scrutiny from anyone who depends on it.
What the schema cannot tell you
Off the top of your head: what is an active customer?
If your answer is “someone who logged in,” you would be wrong here, and wrong in a way that produces a clean, confident, fully executed report. What counts as an active customer at Cowrywise is deliberate, internally agreed, and documented. The same is true of what counts as a conversion, and of what distinguishes one product’s customer from another’s.
Much of the data we work with originates in systems designed by backend engineers to run the product, not to answer analytical questions. So the schema tells you what the system does. It does not tell you what the business has decided it means. Between those two things is a translation layer, and somebody has to own it.
So I built for the gap
Somewhere in the middle of the year I realised that the most useful thing I could build was not another dashboard or another pipeline. It was shared meaning: one agreed answer to “what does this field actually represent,” available to anyone who needs it before they write the query rather than after the report is wrong.
That is what our internal data dictionary is for. It is not documentation in the housekeeping sense. It is infrastructure, the layer that lets several teams read the same field and reach the same conclusion. Backend moves fast, fields change, product context shifts, and the map goes stale quietly while everyone assumes it is current. Keeping it current has been one of the more consequential things I did this year.
When I joined, our most-used tables were documented in the sense that somebody had written down the column names. What a value actually represents, which product it belongs to, what it should never be used for, that lived in the heads of the people who had built them, and it left the building whenever they did. Over the years, we have documented just over 120 tables across our savings, investment, and transaction domains, starting with the ones that show up most often in reports rather than the ones that were easiest to write up.
One field taught me why this matters more than it sounds. Three separate reports were using the same date column as the moment a customer’s money moved. It was not that. It recorded when the record was written, which for most transaction types falls on the same day and for a few does not, enough to move figures across a month boundary, which is exactly when people are looking hardest. I sat with Product and Backend until we agreed which timestamp actually represents settlement, documented both fields and the difference between them, rewrote the two wrong reports, and retired a third calculation that nobody could find an owner for.
The week it stopped being theory
Charles, our team manager, has drilled one line into me: our currency is data quality. Not speed. Not cleverness. Correctness.
Growth, Product, Finance, CX, Portfolio- every team here needs data to move to make real decisions. So when a sync fails quietly, or a scheduled run writes rows twice, it does not stay a data-team problem for long. It can become a CX agent trying to explain a balance that no longer matches what the customer sees. Then it is a customer’s problem. That chain is much shorter than it looks from inside a query editor.
In June, Charles went on leave, and I held the fort. For that week, the daily checks were mine.
On a Tuesday morning, one of our syncs had finished. That was the problem. It had finished, not failed. It reported success, having pulled roughly two-thirds of the previous day’s transactions before quietly stopping. Nothing alerted, because as far as the pipeline was concerned, nothing had gone wrong.
I found it the boring way. I had picked up the habit of eyeballing daily transaction counts against the same day the week before, mostly because Charles did it and I was copying him without fully knowing why. Tuesday’s number was low. Not absurdly low. Low enough to pass for a quiet weekday, which is exactly what made it dangerous.
It took me most of the morning. I traced it back through the transformation layer to the ingestion step, found the sync’s watermark sitting where it had no business sitting, reran the affected job, and then validated the counts against the source tables, period by period, until they matched. Then I went and found everything that had been built on top of the incomplete data overnight and rebuilt it too.
What I remember is not the fix. It is what was sitting downstream. Those figures feed the reports the business reads at the start of the week. If I had looked at that low Tuesday number and accepted it as a slow day, nothing further down the line would have caught it. It would have travelled into a weekly summary, then into a conversation about whether something had slowed down, then into a decision made on a number that was two-thirds of the truth.
The pipeline was green. The job had run. The only thing standing between a wrong number and a room full of people making decisions was that somebody had bothered to look at it and think, that seems low.
Until that week, “our currency is data quality” was a line Charles said often enough that I could repeat it from memory. By the time he came back, I understood why he keeps saying it.
Context is distributed, and collecting it is the technical work
Nobody hands you business meaning in one place. It sits in different heads in different teams, and going to get it is not a soft skill bolted onto the job. It is the job.
Backend can tell me how the system records an event. Product can tell me what the experience was meant to do in the first place: Yami has walked me through more table fields than I can count, patiently explaining what a field represents in the product rather than what it is called in the schema. Chimdi walked me through our behaviour-driven customer tiering model, and understanding how it reflects the way customers actually use our products changed how I think about segmentation entirely. And CX will tell you where a wrong number stops being abstract: someone on a call, with a real customer, trying to explain a figure that does not match what that customer is looking at.
Which is the unglamorous truth about this work: you are only as good as the context you are willing to go and collect. The best queries here are not written by whoever knows the most SQL. They are written by whoever asks the most questions.
One thing I would tell the version of me who joined last year: when someone explains what a number means, write it down. Context that lives only in one person’s head is technical debt with a very good disguise. A year ago, I would have nodded politely at that advice and gone back to writing joins.
Year two, then. I still write SQL every day. I just ask a lot more questions before I open the editor.


