📐 Every significant project at Pursuit adheres to its unique style guide: a set of conventions that aim to create a consistent coding framework. When navigating a substantial codebase, consistency is key for comprehension and collaboration amongst students.
The term "style" encompasses aspects like "adopt camelCase for variable names" and principles like "modularize code" or "handle errors properly."
This document (Pursuit styleguide) introduces you to the stylistic standards we advocate for at Pursuit. As you code with Pursuit, you'll be referred back to this guide to ensure your code aligns with our best practices. Following these standards will help you develop good coding habits.
Here is an example from Google: Google JavaScript Style Guide
Here is another from Airbnb: Airbnb JavaScript Style Guide
- Variables: Use
camelCase
and descriptive names. E.g.,userAge
rather thanua
. - Functions: Names should describe what they do. E.g.,
calculateTotalPrice()
instead ofctp()
. - Constants: Use
UPPERCASE_WITH_UNDERSCORES
. E.g.,MAX_LIMIT
.
- Function Comments: Before every function, write a brief comment about what it does, its parameters, and its return value.
/** * Calculates the total price including tax. * @param {number} price - The base price of the item. * @param {number} taxRate - The tax rate as a decimal (e.g., 0.07 for 7%). * @returns {number} Total price including tax. */ function calculateTotalPrice(price, taxRate) { /* ... */ }
- Major Blocks: Comment on major sections of code, especially if they serve a specific purpose within a function.
function processData(input) {
// --- Input validation block ---
if (typeof input !== 'string') {
throw new Error('Input must be a string');
}
// --- Data processing block ---
const processed = input.trim().toLowerCase();
// --- Output formatting block ---
const output = `Data: ${processed}`;
return output;
}
- Tricky Code: If there's a piece of code that's not straightforward, briefly explain why it's written that way.
function getMagicNumber() {
// Using bitwise XOR for performance reasons over traditional arithmetic
return (5 ^ 3) + (8 ^ 2);
}
- Indentation: Use spaces (typically 2 or 4) for indentation rather than tabs to ensure consistent viewing in various editors.
- Braces: Use 1TBS variant for braces (K&R style).
if (condition) { // code -> two tabs } else { // code -> two tabs }
- Prefer
const
for variables that won't be reassigned. - Use
let
for variables that will be reassigned. - Avoid using
var
as it's function-scoped and can lead to unexpected behaviors.
- Use
try-catch
blocks for areas of code where you expect potential failures (e.g., parsing JSON, API calls).
Example 1: Parsing JSON:
const jsonString = '{ "name": "John", "age": 30 }'; // this is a valid JSON string
try {
const jsonObject = JSON.parse(jsonString);
console.log(jsonObject.name); // Outputs: John
} catch (error) {
console.error("Error parsing JSON:", error.message);
}
Example 2: API Calls:
async function fetchData(url) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error("Failed to fetch data:", error.message);
}
}
// Usage
fetchData('https://api.example.com/data');
- Break large functions into smaller, more manageable sub-functions.
Without Breaking Down the Functions:
function generateMonthlyReport(data) {
// Calculate total sales
let totalSales = 0;
for (const item of data) {
totalSales += item.price * item.sold;
}
// Calculate best selling item
let bestSelling = data[0];
for (const item of data) {
if (item.sold > bestSelling.sold) {
bestSelling = item;
}
}
// Calculate average sale price
let totalItemsSold = 0;
for (const item of data) {
totalItemsSold += item.sold;
}
const averageSalePrice = totalSales / totalItemsSold;
return {
totalSales: totalSales,
bestSellingItem: bestSelling,
averageSalePrice: averageSalePrice
};
}
With Breaking Down the Functions:
function calculateTotalSales(data) {
return data.reduce((acc, item) => acc + (item.price * item.sold), 0);
}
function findBestSellingItem(data) {
return data.reduce((best, item) => (item.sold > best.sold ? item : best));
}
function calculateAverageSalePrice(totalSales, data) {
const totalItemsSold = data.reduce((acc, item) => acc + item.sold, 0);
return totalSales / totalItemsSold;
}
function generateMonthlyReport(data) {
const totalSales = calculateTotalSales(data);
const bestSellingItem = findBestSellingItem(data);
const averageSalePrice = calculateAverageSalePrice(totalSales, data);
return {
totalSales,
bestSellingItem,
averageSalePrice
};
}
- Group related functions and variables together, possibly in classes or modules.
Without Grouping (Scattered Functions and Variables):
const users = [];
function addUser(name) {
users.push({ name, id: Date.now() });
}
function removeUser(id) {
const index = users.findIndex(user => user.id === id);
if (index !== -1) {
users.splice(index, 1);
}
}
function getUser(id) {
return users.find(user => user.id === id);
}
With Grouping (Using a Class):
class UserStore {
constructor() {
this.users = [];
}
addUser(name) {
this.users.push({ name, id: Date.now() });
}
removeUser(id) {
const index = this.users.findIndex(user => user.id === id);
if (index !== -1) {
this.users.splice(index, 1);
}
}
getUser(id) {
return this.users.find(user => user.id === id);
}
}
const userStore = new UserStore();
- Minimize the use of global variables. Instead, use function parameters, return values, and local variables.
async function displayUser(userId) {
try {
const user = await fetchUserData(userId);
console.log(user);
} catch (error) {
console.error(error.message);
}
}
displayUser(1); // Outputs: { id: 1, name: 'John' }
displayUser(3); // Outputs: User not found
- While JavaScript can often run fine without semicolons due to Automatic Semicolon Insertion (ASI), it's safer and more consistent to always use them.
- Consider using tools like ESLint or Prettier to ensure a consistent code style and catch potential issues.
- Implement unit tests for critical parts of the application using frameworks like Jest.
- Encourage test-driven development (TDD) where tests are written before the actual code.
- Instead of using arbitrary numbers in your code, define them as named constants so their purpose is clear.
Without Named Constants (using "magic numbers"):
function calculateTotalPrice(price) {
return price + price * 0.07; // 0.07 is a tax rate, but it's unclear in this context.
}
With Named Constants:
const TAX_RATE = 0.07; // Now it's clear that this is a tax rate.
function calculateTotalPrice(price) {
return price + price * TAX_RATE;
}