Build1 distinct publisher3 min readUpdated
A cleaning exercise on 290 bus bookings silently dropped five completed trips and KES 3,840 of revenue, because the month-first heuristic that saved most rows cannot fire on the rest.
The Engineer · Build desk
Compiled by The EngineerSomething wrong?How this is made
A week spent cleaning 290 booking records from Safari Connect, a Nairobi bus platform, with 21 columns and 23 catalogued data problems, produced five SQL bugs that ran to completion and returned plausible output [1][2]. One of them removed five completed bookings and KES 3,840 of revenue from every downstream total without raising an error or a warning [10].
The mechanism deserves attention because the code is the code most people would write. The departure_date column held three formats: 2024-09-15, 15/09/2024, and 09-25-2024 [4]. Two are ambiguous. As the author notes, 01-18-2024 can only be MM-DD-YYYY because there is no month 18, while 04-10-2024 could be read either way [5]. The supplied guide resolved this by inspecting the second component and treating the string as month-first when that value exceeded 12 [6].
That test can only be true when the day is 13 or higher [7]. Five rows had days between 1 and 12, so the UPDATE never touched them [8]. The next step inserted from staging into the target table with a WHERE clause matching the ISO pattern, and those five rows failed the match and were dropped [9]. That is 1.7 percent of the file [2], at an average of KES 768 per lost booking [1].
The tolerance check was the second failure. The guide's expected row count was written as "~280+" [11], which leaves ten rows of slack against a 290-row input, twice the number actually lost [3]. A spot check does not catch this either, because the broken subset is defined by a value in the data rather than by position in the file [7][8].
The recommended fix is to classify on shape rather than infer from values, using an anchored pattern such as '^\d{2}-\d{2}-\d{4}$' [12]. Because anchored patterns are mutually exclusive, every row can be bucketed and counted with a CASE expression before anything is modified [13]. The important bucket is UNRECOGNISED: if it is non-zero, stop [14]. The general form of the lesson, in the author's words, is that WHERE clauses inside an INSERT ... SELECT are the quietest place data disappears, so count what you are about to lose before you lose it [15].
The other four bugs share the shape. A month-over-month growth query with a 100.0 multiplier and a NULLIF guard against divide-by-zero returned a column of zeros [16]; multiplication and division have equal precedence and evaluate left to right, so the integer division truncated to zero before the decimal ever entered the expression [17]. Moving the constant ahead of the divisor, as (a - b) * 100.0 / c, fixes it [18]. A column of round zeros with no error is the signature [19].
The reporting layer had a view filtered to booking_status = 'Completed' [20]. One of the six business questions asked for the cancellation rate and the cost of cancellations, and against that view the answer is 0 percent and KES 0 [21]. The fix was to expose all bookings and split the money into realised and lost columns with CASE expressions [22]. A view encodes an assumption about which rows matter, and that assumption is invisible to whoever queries it later [23]. The ranking query showed the same class of problem from the other direction: eight drivers, nine rows, from a CTE grouped by driver_name and vehicle_type [24].
Worth watching in your own pipelines: whether any staging-to-production insert filters rows without logging the delta, and whether your row-count assertions are written as ranges loose enough to absorb a real loss.
Follow any of these and your For You feed starts watching them — no settings page required.
Ranked by verification strength, evidence, and original report placement.
The exercise involved cleaning a deliberately messy dataset of 290 booking records from Safari Connect, a Nairobi bus platform, with 21 columns and 23 catalogued data problems; it was a class exercise built from real failure modes.
The problems the author had been warned about took an afternoon; the five that cost him ran perfectly, returned plausible output, and were wrong.
Every one of the five bugs produced a result and none produced an error.
The dataset had three date formats in one column: 2024-09-15, 15/09/2024, and 09-25-2024.
01-18-2024 is unmistakably MM-DD-YYYY because there is no month 18, but 04-10-2024 could be either format.
The supplied guide normalised dates with an UPDATE that set departure_date = TO_DATE(departure_date,'MM-DD-YYYY')::TEXT where the string contained a hyphen, had length 10, and SPLIT_PART(departure_date,'-',2)::INTEGER > 12.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Self-verifying SQL mechanics, single unaudited account
Each defect is presented as quoted, runnable SQL with the observed output and the mechanism, and the core mechanics (equal precedence of * and /, integer truncation, mutual exclusivity of anchored regexes, grain expansion from an extra GROUP BY key) are checkable by any reader without trusting the author. That lifts evidence above bare anecdote. It is capped by the fact that there is exactly one source, the dataset is a teaching artefact with no published link or repository, no engine or version is named, and the specific counts and money figures (five rows, KES 3,840) rest solely on the author's report.
No adoption signal in cluster
The cluster contains no release, deployment, benchmark, pricing, licensing, security or usage-disclosure evidence. It is a pedagogical write-up of a class exercise, so there is nothing to measure and no adoption observations were recorded; inferring uptake of the described practices would be speculation.
Slightly understated relative to what is shown
The post's claims stay inside what its own SQL demonstrates and are explicitly scoped to a class exercise; the author even declines to present the r = −0.507 correlation as a finding on significance grounds and labels the loose '~280+' check rather than dramatising it. If anything the framing undersells generality, since the demonstrated mechanisms (value-inference normalisation feeding a shape-based filter, integer truncation, view-encoded row assumptions, GROUP BY grain drift) apply well beyond 290 bus bookings. The negative value is small because the cluster offers no external corroboration that would justify a stronger understatement reading.
Personal credibility stake, no commercial interest
The only actor is an individual practitioner publishing a learning retrospective on a developer blogging platform. No product, vendor, sponsor, employer, funding round or tool is promoted anywhere in the source, so commercial distortion pressure is low; the residual incentive is reputational — a self-critical 'bugs I made' narrative builds portfolio credibility, which favours tidy, quotable framings such as the exact five-bug count and the memorable KES figures.
Moderate: mechanisms solid, sourcing thin, adoption absent
Confidence is held mid-range. The technical claims are internally consistent and independently checkable, and the derived arithmetic (KES 768 average, roughly 1.7 percent of rows, a ten-row tolerance versus five rows lost) follows directly from the source's own numbers. Against that: one publisher, no corroboration, no accessible dataset or code, an unnamed SQL dialect, and no adoption dimension at all.
build
Cost per customer is an allocation policy, not a warehouse project1 distinct publisher
build
Scheduling a prompt is not monitoring: keep the LLM upstream of the cron1 distinct publisher
build
Force the tool call, then hand Lightsail a long-lived key1 distinct publisher
build
AI-written code fails the same four ways, and every gate you own reports green1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 17, 2026