<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: elecson</title>
    <description>The latest articles on DEV Community by elecson (@square12_82a85fc8609fdd1f).</description>
    <link>https://dev.to/square12_82a85fc8609fdd1f</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4036571%2F3c9d6984-4680-4c27-a9a4-f6219da8b2a5.png</url>
      <title>DEV Community: elecson</title>
      <link>https://dev.to/square12_82a85fc8609fdd1f</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/square12_82a85fc8609fdd1f"/>
    <language>en</language>
    <item>
      <title>A CSV quality report should not echo the data it rejects</title>
      <dc:creator>elecson</dc:creator>
      <pubDate>Sun, 19 Jul 2026 13:19:01 +0000</pubDate>
      <link>https://dev.to/square12_82a85fc8609fdd1f/a-csv-quality-report-should-not-echo-the-data-it-rejects-4h96</link>
      <guid>https://dev.to/square12_82a85fc8609fdd1f/a-csv-quality-report-should-not-echo-the-data-it-rejects-4h96</guid>
      <description>&lt;p&gt;CSV validation usually starts with rules: required fields, types, ranges, allowed&lt;br&gt;
values, and uniqueness.&lt;/p&gt;

&lt;p&gt;But if the validation output will be attached to an issue or sent to a teammate,&lt;br&gt;
there is an earlier design question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should the report repeat the value that failed?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For customer exports, the answer should usually be no. Email addresses,&lt;br&gt;
transaction identifiers, employee data, and internal account states can leak&lt;br&gt;
through a report even when the original CSV stays private.&lt;/p&gt;

&lt;p&gt;I used three separate fields for each validation failure:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Location&lt;/strong&gt; — physical CSV row and named column.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reason&lt;/strong&gt; — a stable code such as &lt;code&gt;type_mismatch&lt;/code&gt; or &lt;code&gt;duplicate_value&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Value&lt;/strong&gt; — omitted by default and included only through an explicit option.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first two fields are normally enough to repair an export:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;row 3, signup_date: expected %Y-%m-%d
row 8, customer_id: duplicate_value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neither message needs to copy the rejected date or customer identifier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Redaction does not have to break reproducibility
&lt;/h3&gt;

&lt;p&gt;A report can omit raw values while still recording:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the SHA-256 hash of the input;&lt;/li&gt;
&lt;li&gt;the SHA-256 hash of the rule file;&lt;/li&gt;
&lt;li&gt;stable error codes;&lt;/li&gt;
&lt;li&gt;row and column locations;&lt;/li&gt;
&lt;li&gt;a generated timestamp.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two authorized people can then confirm that they validated identical input bytes.&lt;br&gt;
A hash is a build fingerprint, not anonymization, so small or predictable secrets&lt;br&gt;
still need care.&lt;/p&gt;
&lt;h3&gt;
  
  
  Offline should mean offline
&lt;/h3&gt;

&lt;p&gt;For the small CLI I built around this idea, the generated HTML uses embedded CSS&lt;br&gt;
and no JavaScript, remote fonts, analytics, or CDN assets. The validator uses only&lt;br&gt;
Python's standard library and does not upload the CSV.&lt;/p&gt;

&lt;p&gt;That narrow scope has tradeoffs. It expects Python 3.10+, UTF-8 CSV input, and&lt;br&gt;
files that fit in memory. It does not guess encodings, repair data, or open Excel&lt;br&gt;
files.&lt;/p&gt;
&lt;h3&gt;
  
  
  Exit codes make the report operational
&lt;/h3&gt;

&lt;p&gt;The command distinguishes three outcomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0  checks completed and passed
1  checks completed and found violations
2  the check could not run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lets the same run create a human-readable HTML report and machine-readable&lt;br&gt;
JSON while still blocking a local script or CI job.&lt;/p&gt;

&lt;p&gt;The broader lesson is simple: &lt;strong&gt;when a report may travel farther than its input,&lt;br&gt;
make data minimization the default rather than an afterthought.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Disclosure: I built &lt;strong&gt;DataProof CSV&lt;/strong&gt;, the small offline source toolkit described&lt;br&gt;
in this post. It includes failing and passing synthetic fixtures, redacted&lt;br&gt;
HTML/JSON reports, tests, and editable Python source:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://elecson.gumroad.com/l/dataproof-csv?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=dataproof_csv_launch" rel="noopener noreferrer"&gt;View DataProof CSV on Gumroad&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AI assistance disclosure: This article was prepared with AI assistance and&lt;br&gt;
reviewed by the product creator before publication.&lt;/p&gt;

</description>
      <category>python</category>
      <category>dataengineering</category>
      <category>privacy</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
