DEV Community

Myroslav Martsin
Myroslav Martsin

Posted on

The CSV bug that compiles clean and breaks in production

Here is a bug I have shipped more than once.

You have an export that turns records into CSV. The column list is just strings:

import Papa from 'papaparse';

Papa.unparse(users, { columns: ['id', 'name', 'email'] }); // columns are plain strings
Enter fullscreen mode Exit fullscreen mode

Six months later someone renames name to fullName on the User type. The compiler is happy. The tests that do not touch this path are happy. The export keeps running. It just quietly emits an empty name column, and you find out when a customer asks why half of their download is blank.

The root cause: the column list and your data have no connection the compiler can see. 'name' is just a string, and renaming a field cannot break a string.

The fix is to tie the columns to the type. In csv-pipe the column list is keyof T, so it is checked against your records:

import { stringify } from 'csv-pipe';

type User = { id: number; fullName: string; email: string };

const users: User[] = [{ id: 1, fullName: 'Alex', email: 'alex@example.com' }];

stringify(users, { columns: ['id', 'fullName', 'email'] }); // ok
stringify(users, { columns: ['name'] }); // compile error: 'name' is not a key of User
Enter fullscreen mode Exit fullscreen mode

Now the rename cannot stay silent. The moment name becomes fullName on the type, every columns: ['name'] in your codebase turns red until you fix it. The column list moves with the data.

It is a small thing, but it is the difference between catching the bug in your editor and catching it in a support ticket.

csv-pipe is a typed, zero-dependency CSV library that does this for both encoding and parsing, on every runtime. Try it on your own data: https://martsinlabs.github.io/csv-pipe/playground

Top comments (1)

Collapse
 
nazar_boyko profile image
Nazar Boyko

There's a sibling bug hiding in the same "compiles clean, breaks in the wild" family worth guarding against here. If a CSV value starts with =, +, -, or @, Excel and Sheets will run it as a formula, so a field like "=HYPERLINK(...)" coming from user input does exactly what your post warns about, just one layer over. Prefixing those values with a quote or a tab shuts it down. The keyof T trick is a clean fix for the rename, this just rides alongside it on the data side.