Today I Learned - Rocky Kev

TIL Cleaner Functions

POSTED ON:

TAGS:

This is absolutely beautiful.

I was reading Writing Clean JavaScript and I usually get one or two take-aways.

This had so many take-aways, I had to break it into two posts.

Like any 'clean code', they're kinda rules of thumbs, and opinionated.

I like these opinions.

How to write Cleaner functions

Limit the number of arguments

// Don't ❌
function sendPushNotification(title, message, image, isSilent, delayMs) {
// ...
}

sendPushNotification("New Message", "...", "http://...", false, 1000);

// Do ✅
function sendPushNotification({ title, message, image, isSilent, delayMs }) {
// ...
}

const notificationConfig = {
title: "New Message",
message: "...",
image: "http://...",
isSilent: false,
delayMs: 1000,
};

sendPushNotification(notificationConfig);

Very opinionated. Why is this better?
Because after 3 arguments, who knows how much it'll grow? And worst, you'll have to remember the order of your arguments.

By having your function consume a object, it'll allow you to:

  1. Add as many arguments as you want inside your object
  2. The order doesn't matter

Avoid executing multiple actions in a function

// Don't ❌
function pingUsers(users) {
users.forEach((user) => {
const userRecord = database.lookup(user);
if (!userRecord.isActive()) {
ping(user);
}
});
}

// Do ✅
function pingInactiveUsers(users) {
users.filter(!isUserActive).forEach(ping);
}

function isUserActive(user) {
const userRecord = database.lookup(user);
return userRecord.isActive();
}

The original is tightly coupled. It's not wrong, and I have a lot of personal code just like.

The improved version makes the functions more 'atomic'. That allows us to start thinking in Higher-order functions, where we pass functions into function arguments to do some really magical things!

Avoid mutation

// Don't ❌
// This one mutates the data directly
function enrollStudentInCourse(course, student) {
course.push({ student, enrollmentDate: Date.now() });
}

// Do ✅
// This passes back an array, with the original course, and the new change.
function enrollStudentInCourse(course, student) {
return [...course, { student, enrollmentDate: Date.now() }];
}

Avoiding non-negatives

// Don't ❌
function isUserNotVerified(user) {
// ...
}

if (!isUserNotVerified(user)) {
// ...
}

// Do ✅
function isUserVerified(user) {
// ...
}

if (isUserVerified(user)) {
// ...
}

The ! reverses things.

By writing functions that return false, you end up with weird boolean like !false which means true, which gets really hard to read.

Return Early

// Don't ❌
function addUserService(db, user) {
if (!db) {
if (!db.isConnected()) {
if (!user) {
return db.insert("users", user);
} else {
throw new Error("No user");
}
} else {
throw new Error("No database connection");
}
} else {
throw new Error("No database");
}
}


// Do ✅
function addUserService(db, user) {
if (!db) throw new Error("No database");
if (!db.isConnected()) throw new Error("No database connection");
if (!user) throw new Error("No user");

return db.insert("users", user);
}

Related TILs

Tagged:

TIL not obsessively trying to be DRY

Quite often, DRY is applied to any duplication, rather than specifically to the duplication of business rules and knowledge. This is often made worse by automated code analysis tools, which will often highlight any duplication as a code smell.

TIL ways to organize eventListeners

Some code spaghetti I made recently was creating a lot of eventListeners for multiple buttons, and then forgot what they all connected to.

TIL Very Efficient Code

This is a great solution. Easy to understand. Executes really fast. No real-time string concatenation. No over-engineering. String appearance is easily customized.