Skip to main content

General Principles

No diffs unless an actual change is made. Code changes need to be as minimal as possible. Avoid making unnecessary whitespace diffs.
Ensure you check your code changes before committing and raising a PR. While ESLint handles most formatting, always review your diffs.

Style Rules

Indentation and Spacing

function calculateTotal(items) {
  return items.reduce((sum, item) => {
    return sum + item.price;
  }, 0);
}
Use 2 spaces for indentation. No tabs, just spaces – keeps everything neat and uniform.

Quotes and Semicolons

const message = 'Hello, world!';
const name = 'Bruno';
const url = 'https://usebruno.com';
Rules:
  • Stick to single quotes for strings in JavaScript
  • Use double quotes for JSX/TSX attributes (React convention)
  • Always add semicolons at the end of statements

Arrow Functions

const double = (x) => x * 2;
const sum = (a, b) => a + b;
const greet = (name) => `Hello, ${name}!`;
Always use parentheses around parameters in arrow functions, even for single params. Consistency is key.

Braces and Line Breaks

if (isValid) {
  processRequest();
}

const config = {
  url: 'https://api.example.com',
  method: 'GET'
};
Rules:
  • Put opening braces on the same line
  • Minimum 2 elements for multiline constructs
  • No newlines inside function parentheses
  • Space before and after the arrow: () => {}
  • No space between function name and parentheses: func() not func ()

Trailing Commas

const user = {
  name: 'John',
  email: '[email protected]'
};

const colors = [
  'red',
  'blue',
  'green'
];
No trailing commas. Keep it clean, no extra commas hanging around.

React Guidelines

Hook Imports

import { useState, useEffect, useCallback, useMemo } from 'react';

function MyComponent() {
  const [count, setCount] = useState(0);
  const increment = useCallback(() => setCount((c) => c + 1), []);
  
  return <button onClick={increment}>{count}</button>;
}
MUST: Do not use namespace access for hooks in app code (e.g., React.useCallback, React.useState). Import hooks directly.

Custom Hooks for Logic

// hooks/useApiRequest.js
import { useState, useCallback } from 'react';

export function useApiRequest() {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  
  const execute = useCallback(async (url) => {
    setLoading(true);
    try {
      const response = await fetch(url);
      const data = await response.json();
      return data;
    } catch (err) {
      setError(err);
    } finally {
      setLoading(false);
    }
  }, []);
  
  return { loading, error, execute };
}

// Component.js
function RequestButton() {
  const { loading, error, execute } = useApiRequest();
  
  return (
    <button onClick={() => execute('/api/data')}>
      {loading ? 'Loading...' : 'Fetch Data'}
    </button>
  );
}
Best Practices:
  • MUST: Prefer custom hooks for business logic, data fetching, and side-effects
  • MUST: Avoid useEffect unless absolutely needed. Prefer derived state and event handlers
  • SHOULD: Memoize only when necessary (useMemo/useCallback), prefer moving logic into hooks first

Styled Components vs Tailwind

import styled from 'styled-components';

const Button = styled.button`
  background-color: ${(props) => props.theme.colors.primary};
  color: ${(props) => props.theme.colors.text};
  border: 1px solid ${(props) => props.theme.colors.border};
  
  &:hover {
    background-color: ${(props) => props.theme.colors.primaryHover};
  }
`;

// Use Tailwind for layout
<Button className="px-4 py-2 rounded-md flex items-center gap-2">
  Submit
</Button>
Rules:
  • Use styled component’s theme prop for CSS colors, not CSS variables
  • Styled Components define both self and children component styles
  • Tailwind classes are used specifically for layout-based styles
  • Styled Component CSS might also change layout, but Tailwind classes shouldn’t define colors

Testing Attributes

<button data-testid="submit-button" onClick={handleSubmit}>
  Submit Request
</button>

<input data-testid="url-input" value={url} onChange={handleChange} />
Add data-testid to testable elements for Playwright E2E tests.

Component Organization

  • Co-locate utilities that are truly component-specific next to the component
  • Place shared items under a common folder
  • Keep components focused and single-purpose

Testing Standards

Test Philosophy

Behavior-Driven

Test real expected output and observable behavior, not internal implementation

High-Value Focus

Prioritize critical, complex, or likely-to-break behavior over coverage numbers

Minimize Mocking

Use real flows where practical; mock only external services or non-deterministic behavior

Readable Tests

Optimize for clarity over cleverness with descriptive names and minimal setup

Test Requirements

1

Add Tests for Changes

Add tests for any new functionality or meaningful changes. If code is added, removed, or significantly modified, corresponding tests should be updated or created.
2

Cover Key Paths

Cover both the “happy path” and realistically problematic paths. Validate expected success behavior and error handling.
3

Ensure Determinism

Ensure tests are deterministic and reproducible. No randomness, timing dependencies, or environment-specific assumptions without explicit control.
4

Make Failures Useful

Aim for tests that fail usefully. When a test fails, it should clearly indicate what behavior broke and why.

Test Examples

test('should parse valid Bru request format', () => {
  const bruContent = `
    get {
      url: https://api.example.com/users
    }
  `;
  
  const result = parseBru(bruContent);
  
  expect(result.method).toBe('GET');
  expect(result.url).toBe('https://api.example.com/users');
});

test('should handle invalid URL gracefully', () => {
  const bruContent = `
    get {
      url: not-a-valid-url
    }
  `;
  
  expect(() => parseBru(bruContent)).toThrow('Invalid URL');
});
Key Points:
  • Write behavior-driven tests, not implementation-driven ones
  • Minimize mocking unless it meaningfully increases clarity
  • Keep tests readable and maintainable
  • Avoid overfitting tests to current behavior
  • Use consistent patterns and helper utilities
  • Tests should be fast enough to run continuously

Code Quality

Readability and Abstractions

Avoid abstractions unless the exact same code is being used in more than 3 places.
/**
 * Converts a Bru collection to Postman format
 * @param {Object} bruCollection - The Bruno collection object
 * @returns {Object} Postman collection format
 */
function convertBruToPostman(bruCollection) {
  // Implementation
}
Guidelines:
  • Function names need to be concise and descriptive
  • Add JSDoc comments to add more details to abstractions if needed
  • Follow functional programming but just enough to be readable
  • Avoid single line abstractions where all that’s being done is increasing the call stack
  • Add meaningful comments instead of obvious ones where complex code flow needs explanation

Comments

// OAuth2 spec requires state parameter for CSRF protection
// We generate a random state and store it to validate the callback
const state = generateRandomState();
storeOAuthState(state);

// Parse the URL using a lenient parser because some APIs
// return malformed Location headers that the strict parser rejects
const redirectUrl = parseLenientUrl(response.headers.location);
Add meaningful comments that explain why, not what. The code already shows what it does.

Linting

Bruno uses ESLint with custom configuration:
# Check for issues
npm run lint

# Auto-fix issues
npm run lint:fix

Pre-commit Hooks

The project uses Husky and nano-staged to run linting on staged files:
"nano-staged": {
  "*.{js,ts,jsx}": [
    "npm run lint:fix"
  ]
}
This automatically formats and lints your code before commits.

Code Review Checklist

Before submitting a PR, ensure:
  • Code follows style guidelines (2 spaces, single quotes, semicolons)
  • React hooks are imported directly, not via namespace
  • Custom hooks used for business logic and side effects
  • Styled components for colors, Tailwind for layout
  • Tests added for new functionality
  • Tests are behavior-driven, not implementation-driven
  • No unnecessary abstractions or single-line wrappers
  • Function names are clear and descriptive
  • Comments explain “why”, not “what”
  • data-testid added to testable UI elements
  • No trailing commas
  • No unnecessary whitespace changes
  • ESLint passes without errors

Enforcement

These standards are enforced through:
  1. ESLint: Automated linting catches most issues
  2. Pre-commit hooks: Husky runs lint:fix before commits
  3. CI/CD: GitHub Actions runs tests and linting
  4. Code review: Maintainers review for adherence
Run npm run lint:fix before committing to automatically fix most style issues.

Questions?

If something doesn’t fit perfectly or you’re unsure about a guideline:
  • Ask in the PR comments
  • Open a discussion on GitHub
  • Join our Discord community
Remember: These rules are here to make our codebase harmonious. Let’s chat if you have questions!

Build docs developers (and LLMs) love