DEV Community

56kode
56kode

Posted on

1

One Level of Abstraction: The Key to Clean Functions

Ever looked at a function and felt lost in its complexity? Let's explore a fundamental principle of clean code: functions should maintain only one level of abstraction.

Here's a real-world example of user creation in a web application:

// ❌ A function doing too many things at different abstraction levels
function createUser(userData) {
  // Validate data
  if (!userData.email || !userData.email.includes('@')) {
    return 'Invalid email';
  }
  if (userData.password.length < 8) {
    return 'Password too short';
  }

  // Hash password
  const salt = generateSalt();
  const hashedPassword = hashWithSalt(userData.password, salt);

  // Format user data
  const user = {
    email: userData.email.toLowerCase(),
    password: hashedPassword,
    salt: salt,
    createdAt: new Date()
  };

  // Save to DB
  saveUserToDatabase(user);
}
Enter fullscreen mode Exit fullscreen mode

This function mixes different levels of abstraction:

  • High-level business logic (user creation flow)
  • Mid-level operations (data validation, formatting)
  • Low-level details (password hashing)

Let's refactor it following the single level of abstraction principle:

// ✅ Clean version with one level of abstraction
function createUser(userData) {
  const validationError = validateUserData(userData);
  if (validationError) return validationError;

  const securePassword = hashPassword(userData.password);
  const formattedUser = formatUserData(userData.email, securePassword);

  return saveUserToDatabase(formattedUser);
}

function validateUserData({ email, password }) {
  if (!email || !email.includes('@')) return 'Invalid email';
  if (password.length < 8) return 'Password too short';
  return null;
}

function hashPassword(password) {
  const salt = generateSalt();
  return {
    hash: hashWithSalt(password, salt),
    salt
  };
}

function formatUserData(email, securePassword) {
  return {
    email: email.toLowerCase(),
    password: securePassword.hash,
    salt: securePassword.salt,
    createdAt: new Date()
  };
}
Enter fullscreen mode Exit fullscreen mode

Benefits of This Approach

  1. Readability: The main function reads like a story, describing what happens at a high level
  2. Maintainability: Each function has a single responsibility, making changes safer
  3. Testability: You can test each piece of logic independently
  4. Reusability: These focused functions can be reused in other contexts

Key Takeaways

When writing functions:

  • Keep them focused on one level of abstraction
  • Extract complex operations into well-named functions
  • Make the main function tell a story
  • Think of each function as a single step in your process

Remember: If you're mixing "how" and "what" in the same function, you're probably dealing with multiple levels of abstraction. Split them up!

SurveyJS custom survey software

Simplify data collection in your JS app with a fully integrated form management platform. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more. Integrates with any backend system, giving you full control over your data and no user limits.

Learn more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay