Carbonteq
Best Practices

JavaScript/TypeScript

Variable Declarations

Don't

Use var for variable declarations. It allows redeclaration and has function scope, leading to:

  • Unexpected variable hoisting
  • Accidental redeclarations
  • Hard-to-debug scope issues
DoClick to view

Use const for variable declarations when possible. It provides:

  • Block scope instead of function scope
  • No redeclaration allowed
  • Clear intent about variable immutability
var userName = "john";
var userName = "jane"; // Redeclaration allowed!
if (true) {
var userName = "bob"; // Same variable!
}
console.log(userName); // "bob" - unexpected!

Function Definitions

Don't

Use verbose function declarations for simple operations. This creates unnecessary complexity:

  • More verbose syntax
  • Potential this binding issues
  • Less readable in callbacks
DoClick to view

Use arrow functions for concise operations. They provide:

  • Cleaner syntax for simple functions
  • Predictable behavior without this binding issues
  • Better readability in functional programming
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(function(num) {
return num * 2;
});
function processUser(user) {
return user.name.toUpperCase();
}

Asynchronous Programming

Don't

Use nested callbacks for async operations. This creates:

  • Deeply nested code (callback hell)
  • Poor error handling
  • Difficult debugging and maintenance
DoClick to view

Use async/await for cleaner async code. It provides:

  • Linear code flow that's easy to read
  • Better error handling with try/catch
  • Simplified debugging with proper stack traces
const getUserData = (userId, callback) => {
getUserFromDb(userId, (user) => {
fetchUserProfiles(user, (profiles) => {
processProfiles(profiles, (result) => {
callback(result);
});
});
});
};

String Handling

Don't

Use string concatenation for formatting. This leads to:

  • Poor readability with complex strings
  • Error-prone quote escaping
  • Performance overhead
DoClick to view

Use template literals for string formatting. They provide:

  • Clean syntax with embedded expressions
  • Multi-line support without escape characters
  • Better performance and readability
const dbUser = "admin";
const dbPass = "secret123";
const dbHost = "localhost";
const dbPort = 5432;
const dbURI = "postgresql://" + dbUser + ":" +
dbPass + "@" + dbHost + ":" + dbPort + "/mydb";
const message = "Hello " + user.name + "!\n" +
"You have " + user.notifications + " new messages.";

Default Values

Don't

Use manual fallback logic for default values. This approach:

  • Obscures function intent
  • Fails with falsy values like 0 or ""
  • Adds unnecessary complexity
DoClick to view

Use default parameters in function signature. They provide:

  • Clear intent about default behavior
  • Proper handling of undefined values
  • Cleaner function body without fallback logic
const createUser = (name, role, isActive) => {
const userName = name || "Anonymous";
const userRole = role || "user";
const active = isActive !== undefined ? isActive : true;
return { userName, userRole, active };
};
// Problems with falsy values
createUser("", "admin", false); // Name becomes "Anonymous"!

Null Handling

Don't

Use logical OR (||) for null checks. This incorrectly treats falsy values:

  • 0, "", false are treated as null
  • Unexpected behavior with valid falsy data
  • Imprecise null handling
DoClick to view

Use nullish coalescing operator (??). It provides:

  • Precise null/undefined checking only
  • Preserves falsy values like 0, "", false
  • Clear intent about null handling
const displayCount = (count) => {
return count || "No items";
};
displayCount(0); // "No items" - Wrong!
displayCount(""); // "No items" - Wrong!
displayCount(false); // "No items" - Wrong!
displayCount(null); // "No items" - Correct

Object Access

Don't

Use manual null checks for nested properties. This creates:

  • Verbose and repetitive code
  • Error-prone validation chains
  • Poor readability
DoClick to view

Use optional chaining (?.) for safe property access. It provides:

  • Concise syntax for nested property access
  • Automatic null/undefined handling
  • Better readability and maintainability
const getProfileEmail = (user) => {
if (user && user.profile && user.profile.contact && user.profile.contact.email) {
return user.profile.contact.email;
}
return null;
};
// Multiple variables needed for complex access
const street = user && user.address && user.address.street;
const zipCode = user && user.address && user.address.zipCode;

Array Iteration

Don't

Use traditional for loops for simple iteration. This approach:

  • Requires manual index management
  • More verbose for simple operations
  • Higher chance of off-by-one errors
DoClick to view

Use for...of loops and array methods. They provide:

  • Cleaner syntax without index management
  • Direct access to values
  • Functional programming patterns
const numbers = [1, 2, 3, 4, 5];
const names = ["Alice", "Bob", "Charlie"];
// Traditional for loop
for (let i = 0; i < numbers.length; i++) {
console.log(numbers[i]);
}
// Manual forEach implementation
for (let i = 0; i < names.length; i++) {
const name = names[i];
console.log(`Hello, ${name}!`);
}

Object Manipulation

Don't

Use repetitive property access. This leads to:

  • Verbose variable assignments
  • Repeated object references
  • Less readable function parameters
DoClick to view

Use destructuring assignment. It provides:

  • Clean variable extraction from objects
  • Flexible function parameters with objects
  • Nested destructuring for complex data
const processUser = (userObj) => {
const id = userObj.id;
const name = userObj.name;
const email = userObj.profile.email;
const phone = userObj.profile.phone;
return `${name} (${id}): ${email}, ${phone}`;
};
// Function with many parameters
const createAccount = (email, username, firstName, lastName, age) => {
// Must remember parameter order
return { email, username, firstName, lastName, age };
};

Concurrent Operations

Don't

Await promises sequentially when they're independent. This causes:

  • Unnecessary waiting time
  • Poor performance with multiple API calls
  • Blocking execution
DoClick to view

Use Promise.all() for concurrent operations. It provides:

  • Parallel execution of independent operations
  • Significant performance improvement
  • Clean syntax with destructuring
const fetchUserData = async (userId) => {
const profile = await fetchProfile(userId); // 2s
const preferences = await fetchPreferences(userId); // 1.5s
const notifications = await fetchNotifications(userId); // 3s
return { profile, preferences, notifications };
// Total time: 6.5 seconds
};

Data Structures

Don't

Use arrays for membership testing and lookups. This results in:

  • O(n) time complexity for searches
  • Performance issues with large datasets
  • Inefficient duplicate checking
DoClick to view

Use Set and Map for fast lookups. They provide:

  • O(1) average time complexity for lookups
  • Built-in uniqueness with Set
  • Better performance with large datasets
const allowedMethods = ["GET", "POST", "PUT", "DELETE"];
const processedIds = [];
const isAllowedMethod = (method) => {
return allowedMethods.includes(method); // O(n) search
};
const isProcessed = (id) => {
return processedIds.includes(id); // O(n) search
};
const markAsProcessed = (id) => {
if (!processedIds.includes(id)) {
processedIds.push(id);
}
};