<?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: Labeeb Bappu</title>
    <description>The latest articles on DEV Community by Labeeb Bappu (@labeebbappu).</description>
    <link>https://dev.to/labeebbappu</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3325401%2Fa2e502a5-75aa-4a6e-a755-c37f030c955d.jpeg</url>
      <title>DEV Community: Labeeb Bappu</title>
      <link>https://dev.to/labeebbappu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/labeebbappu"/>
    <language>en</language>
    <item>
      <title>What Excel Doesn’t Tell You: Fixing Dirty Data at Scale</title>
      <dc:creator>Labeeb Bappu</dc:creator>
      <pubDate>Sat, 05 Jul 2025 10:15:38 +0000</pubDate>
      <link>https://dev.to/labeebbappu/from-spreadsheet-chaos-to-database-harmony-building-a-bulletproof-customer-import-tool-2bip</link>
      <guid>https://dev.to/labeebbappu/from-spreadsheet-chaos-to-database-harmony-building-a-bulletproof-customer-import-tool-2bip</guid>
      <description>&lt;p&gt;Today, we tackled a common challenge in web applications: safely and efficiently importing customer data from Excel spreadsheets. What started as a simple request evolved into a comprehensive tool designed for clarity, robustness, and user confidence.&lt;/p&gt;

&lt;p&gt;However, the path to a truly robust solution is rarely linear. We quickly discovered that initial assumptions about data cleanliness and user input were often challenged by real-world scenarios. From unexpected whitespace in headers and data cells, to missing mandatory columns, and even the subtle nuances of contact information, each "failed case" became a valuable lesson, driving us to refine and strengthen the tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. From Zero to Hero: Laying the Foundation
&lt;/h2&gt;

&lt;p&gt;Our journey began with scaffolding the basic UI for a new "Import Customers" tab. We implemented core file upload functionality, ensuring that users could easily select their Excel files. This initial step set the stage for all the powerful features that followed.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Taming Wild Data: The Art of Data Cleaning
&lt;/h2&gt;

&lt;p&gt;Real-world data is rarely pristine. (As any developer who's ever touched a spreadsheet knows, data often comes with its own unique personality, and sometimes, a few extra spaces.) A crucial part of our work involved making the tool resilient to common Excel quirks. We implemented intelligent data cleaning, automatically trimming whitespace from both column headers and individual data cells. Crucially, we also added logic to skip entirely empty rows, preventing unnecessary processing and potential errors down the line. This ensures that only meaningful data makes it into our system.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Decoding Contacts: Handling Complex Information
&lt;/h2&gt;

&lt;p&gt;One of the more interesting puzzles was extracting contact information. Instead of a single "contact" field, we had two distinct contact numbers: "AGREEMENT CONTACT" and "SITE CONTACT." We designed the system to correctly identify and extract these, mapping them to "Office" and "Site" contacts respectively. A small but significant detail was automatically removing spaces from these contact numbers, ensuring clean, usable data for communication.&lt;/p&gt;

&lt;p&gt;Here's a simplified look at how we processed these contacts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Inside the data processing logic&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;agreementContactNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AGREEMENT CONTACT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;agreementContactNumber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cleanedAgreementContactNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;agreementContactNumber&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Add "Office" contact if not already present&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;customerName&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;contacts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contact_name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Office&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;customerName&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;contacts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;contact_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Office&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;contact_number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cleanedAgreementContactNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;siteContactNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SITE CONTACT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;siteContactNumber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cleanedSiteContactNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;siteContactNumber&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Add "Site" contact if not already present&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;customerName&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;contacts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contact_name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Site&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;customerName&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;contacts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;contact_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Site&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;contact_number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cleanedSiteContactNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This snippet demonstrates how we ensure each contact type is uniquely added and its number is standardized.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The Power of Batches: Saving Data Safely and Efficiently
&lt;/h2&gt;

&lt;p&gt;Saving potentially thousands of records to a database requires careful planning. We devised a robust batching strategy, sending data in manageable chunks (e.g., 100 records at a time) to the API. This approach prevents overwhelming the server, mitigates the risk of hitting API rate limits, and ensures that even if an issue occurs, only a small portion of the data is affected, making recovery much simpler.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. User-First Design: Clarity Through Feedback
&lt;/h2&gt;

&lt;p&gt;Throughout the development, user experience was paramount. We integrated clear, immediate feedback mechanisms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Explicit Header Errors:&lt;/strong&gt; If a mandatory column was missing, the UI now clearly states which ones, guiding the user to fix their file.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Real-time Progress:&lt;/strong&gt; During the saving process, users see a live update of which batch is being processed (e.g., "Saving... (5/10)"), providing transparency and reducing anxiety.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Detailed Reports:&lt;/strong&gt; After the import, a downloadable Excel report provides a row-by-row status, indicating whether each customer was created, updated, or encountered an error, all while preserving the original column order for easy reconciliation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This project was a great example of how thoughtful design and incremental improvements can transform a complex task into a smooth, reliable, and user-friendly experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: Balancing User Experience with Infrastructure Health
&lt;/h2&gt;

&lt;p&gt;Our work on the customer import tool beautifully illustrates a core principle of software engineering: the delicate balance between providing an exceptional user experience and safeguarding the underlying infrastructure. By meticulously cleaning data, implementing smart batching, and offering transparent feedback, we not only empowered users with a powerful tool but also ensured the stability and performance of our systems. This commitment to both the user and the infrastructure is what truly defines a robust and sustainable solution.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
