Skip to content

Instantly share code, notes, and snippets.

@CastonPursuit
Last active August 14, 2023 15:38
Show Gist options
  • Save CastonPursuit/64301bc7e8a15174f748a008b1e1d569 to your computer and use it in GitHub Desktop.
Save CastonPursuit/64301bc7e8a15174f748a008b1e1d569 to your computer and use it in GitHub Desktop.

Pursuit Styling Guide


📐 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


1. Descriptive Naming:

  • Variables: Use camelCase and descriptive names. E.g., userAge rather than ua.
  • Functions: Names should describe what they do. E.g., calculateTotalPrice() instead of ctp().
  • Constants: Use UPPERCASE_WITH_UNDERSCORES. E.g., MAX_LIMIT.

2. Comments:

  • 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);
}

3. Code Formatting:

  • 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 
    }

4. Use let, const, and var Properly:

  • 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.

5. Error Handling:

  • 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');

6. Code Modularity:

  • 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();

7. Avoid Global Variables:

  • Minimize the use of global variables. Instead, use function parameters, return values, and local variables.

8. Use Promises or async/await for Asynchronous Operations:

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

9. Consistent Use of Semicolons:

  • While JavaScript can often run fine without semicolons due to Automatic Semicolon Insertion (ASI), it's safer and more consistent to always use them.

10. Linting and Formatting Tools:

  • Consider using tools like ESLint or Prettier to ensure a consistent code style and catch potential issues.

11. Testing:

  • 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.

12. Avoid Magic Numbers:

  • 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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment