Code Smell 253 - Silent Truncation

Maxi Contieri - May 24 - - Dev Community

You silently truncate your user data without warning

TL;DR: If you limit text lengths, enforce them everywhere!

Problems

  • The Fail Fast Principle Violation

  • Corrupted and Missing Data

  • Bijection Fault

  • UTF-8 Truncation

  • The Least Surprise principle violation

  • Separation of Concerns of the UI and the Model

Solutions

  1. Be consistent with length rules

  2. Enforce the rules in the domain objects

Context

Imagine the scenario where you need to persist your objects in a database restricting the size of your texts.

Most databases will silently truncate your data and you will not notice the problem until you retrieve them.

If you need to enforce an arbitrary limit, add these business rules in your objects following the bijection rule.

Adding this control only in the UI or external API is another code smell about misplaced responsibilities.

Sample Code

Wrong

const express = require('express');
const app = express();
const bodyParser = require('body-parser');

// The page architecture is over simplified
const db = require('./db');

app.use(bodyParser.urlencoded({ extended: true }));

app.post('/save-data', (req, res) => {
  const fullText = req.body.fullText;
  const truncatedText = fullText.slice(0, 255);
  // This truncation is not explicit sometimes  

  db.query('INSERT INTO table_name (truncated_text) VALUES (?)', 
           [truncatedText], (err, result) => {
    if (err) throw err;
    res.send('Data saved successfully');
  });
});

app.listen(3000, () => console.log('Server started on port 3000'));

<!DOCTYPE html>
<html>
<head>
  <title>Penrose Theory on Quantum Consciousness</title>
  <script>
    const form = document.getElementById('textForm');
    const textArea = document.getElementById('textArea');

    form.addEventListener('submit', (event) => {
      event.preventDefault();
      const fullText = textArea.value;

      if (fullText.length > 1000) {
        alert('Text cannot exceed 1000 characters');
        return;
      }

      fetch('/save-data', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded'
        },
        body: `fullText=${encodeURIComponent(fullText)}`
      })
        .then(response => response.text())
        .then(data => console.log(data))
        .catch(error => console.error('Error:', error));
    });
  </script>
</head>
<body>
  <h1>Penrose Theory on Quantum Consciousness</h1>
  <form id="textForm">
    <textarea id="textArea" 
      rows="10" 
      placeholder="Enter text about Penrose's theory (max 1000 characters)">
    </textarea>
    <button type="submit">Save</button>
  </form>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Right

const express = require('express');
const app = express();
const bodyParser = require('body-parser');

const db = require('./db');

// You should defined this constant in the backend
// Hopefully in a domain object
const MAX_CHARS = 255;

app.use(bodyParser.urlencoded({ extended: true }));

app.get('/max-chars', (req, res) => {
  res.json({ maxChars: MAX_CHARS });
});

app.post('/save-data', (req, res) => {
  const fullText = req.body.fullText;
  const truncatedText = fullText.slice(0, MAX_CHARS);

  db.query('INSERT INTO table_name (truncated_text) VALUES (?)',
           [truncatedText], (err, result) => {
    if (err) throw err;
    res.send('Data saved successfully');
  });
});

// Start the server
app.listen(3000, () => console.log('Server started on port 3000'));

<!DOCTYPE html>
<html>
<head>
  <title>Penrose Theory on Quantum Consciousness</title>
  <script>
    let maxChars;

    fetch('/max-chars')
      .then(response => response.json())
      .then(data => {
        maxChars = data.maxChars;
        const form = document.getElementById('textForm');
        const textArea = document.getElementById('textArea');

        form.addEventListener('submit', (event) => {
          event.preventDefault();
          const fullText = textArea.value;

          if (fullText.length > maxChars) {
            alert(`Text cannot exceed ${maxChars} characters`);
            return;
          }

          fetch('/save-data', {
            method: 'POST',
            headers: {
              'Content-Type': 'application/x-www-form-urlencoded'
            },
            body: `fullText=${encodeURIComponent(fullText)}`
          })
            .then(response => response.text())
            .then(data => console.log(data))
            .catch(error => console.error('Error:', error));
        });
      })
      .catch(error => console.error('Error:', error));
  </script>
</head>
<body>
  <h1>Penrose Theory on Quantum Consciousness</h1>
  <form id="textForm">
    <textarea id="textArea" 
      rows="10"
      placeholder="Enter text about Penrose's theory">
    </textarea>
    <button type="submit">Save</button>
  </form>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Detection

[X] Semi-Automatic

You can do boundary testing. For example, using Zombies methodology

Tags

  • Fail Fast

Level

[X] Intermediate

AI Generation

Ai generator usually duplicate these controls instead of placing in a single place

AI Detection

It was hard to tell AI to use this as a backend constant prompting with an accurate instruction

Conclusion

You need to handle a clear separation of concerns between the client-side (UI) and server-side (database operations).

The client-side handles user input validation and displaying data, while the server-side handles data storage and retrieval from the database.

You defined the maximum character limit in the backend and fetched by the client-side making it easier to update or change the limit across the application without modifying the client-side code and having ripple effect.

Relations

Disclaimer

Code Smells are my opinion.

Credits

Photo by Jametlene Reskp on Unsplash


In programming, if someone tells you “you’re overcomplicating it,” they’re either 10 steps behind you or 10 steps ahead of you.

Andrew Clark


This article is part of the CodeSmell Series.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .