Firas Mohammed
← All work
Data engineering · ETL

Seventeen years of archives, seven formats, one queryable corpus

Every platform will export your data. None of them will give it to you in a shape you can actually use, and two of them will corrupt it on the way out without telling you.

Personal project · Python, SQLite · 2026

Context

Data-portability rules mean you can request an export from almost any platform. What arrives is seven different answers to the same question: a folder of plain-text files, several flavours of JSON with incompatible schemas, and a mail archive in a format designed in the 1980s. Each has its own idea of what a timestamp is, what a conversation is, and who a participant is.

I wanted my own archive queryable as one thing rather than seven. The data is entirely my own, it never leaves the machine, and the outputs stay private — so the interesting part of this project, and the only part described here, is the pipeline.

What I built

~570,000 messages · 7 sources · 2009 to 2026

Normalised into a single queryable corpus, alongside 3,500+ mail attachments indexed and searchable.

The bug worth writing down

One platform serves UTF-8 content labelled as Latin-1. Parse the export the way it describes itself and every Latin character survives — so the import looks successful, the message counts are right, and nothing errors. Every Arabic character is silently replaced with mojibake.

Roughly half my archive is Arabic. Had that gone unnoticed, the corpus would have been quietly half-destroyed while reporting a clean ingest, and every downstream analysis would have been computed on the surviving half without ever saying so. The fix is three lines. Finding it required not trusting a successful-looking run — the same failure mode I now build gates for by default.

Auditing my own conclusions

Once the corpus existed, it was easy to produce confident-sounding findings from it. So I ran a deliberate second pass whose only job was to attack the first: challenge the method, name what the data could not support, and separate what was measured from what was inferred. It forced the retraction of a conclusion I had been fairly attached to.

A dataset large enough to be interesting is large enough to confirm almost anything you go in believing. Building the adversarial pass as a deliverable, rather than relying on being rigorous in the moment, is the only version of this I trust.

Where this transfers

Consolidating messy multi-source data into one usable store is a substantial share of real operations and analytics work: systems that disagree on identifiers, exports with undocumented quirks, and the standing risk of a migration that appears to succeed while dropping a category of record nobody happened to check. I have now done that end to end, including the part where the encoding was wrong and nothing complained.