Skip to content

Instantly share code, notes, and snippets.

@stevepeak
Last active February 13, 2025 21:09
Show Gist options
  • Save stevepeak/52a4c4fb434ce2a0ab6f86d79ec0bcda to your computer and use it in GitHub Desktop.
Save stevepeak/52a4c4fb434ce2a0ab6f86d79ec0bcda to your computer and use it in GitHub Desktop.
1. Code Quality
Linting and Formatting
Adherence to linting rules
Consistent formatting (e.g., Prettier)
TypeScript Practices
TypeScript compiler options
Type usage guidelines (e.g., avoiding any)
Testing
Test configurations
Frameworks like Jest or Vitest
Commenting and Documentation
Code commenting standards
Use of JSDoc or TSDoc
2. Architecture and Design
Project Structure
Directory organization
Project architecture guidelines
Coding Practices
Preference for functions vs. objects
Use of inline arguments vs. named arguments
Complexity Management
Desired code complexity levels
Guidelines on code depth and function nesting
Policy on duplicate code
Encouragement to search for existing functions before creating new ones
3. Security
Data Validation
Use of libraries like Zod for parsing untrusted data
Error Handling
Error handling protocols (e.g., logging, Sentry integration)
4. Framework and Library Configuration
JavaScript Frameworks
React configuration
Next.js configuration
Vite configuration
Build Tools
Webpack configuration
Babel configuration
Styling
CSS configuration
Tailwind CSS configuration
5. Deployment and Environment
Deployment Platforms
Vercel configuration
Netlify configuration
Environment Management
Environment configuration (e.g., location and management of .env files)
6. Package Management
Tools and Preferences
Preferred package managers (e.g., bun, pnpm, npm, yarn)
Policy on installing new packages
7. Versioning and TODOs
Version Control
Versioning guidelines for tools (e.g., bun or other utilities)
Task Management
Policy on leaving TODOs in code
Requirement for referencing task management tools (e.g., Linear)
Error in user YAML: (<unknown>): did not find expected alphabetic or numeric character while scanning an alias at line 2 column 8
---
description: This documentation is intended to provide comprehensive guidelines for contributing to the codebase.
files: */..
---

Coding Instructions

This documentation is intended to provide comprehensive guidelines for contributing to the codebase. It outlines the tools, environment, coding standards, and best practices that ensure our code remains clean, maintainable, and efficient.

General

  • Write elegant, concise, and readable code
  • Prefer const over let (never use var)
  • Use kebab-case for file and directory names
  • Use clear, descriptive names for variables, functions, and components

Modules

Imports & Exports

  • Always use ESM import and export (never use CJS require)
    • File imports should end with .js (NOT .ts or .tsx). Module or subpath imports don't need the extension.
    • GOOD examples:
      • import { Foo } from './foo.js';
      • import { type Route } from './+types/root.js';
      • import zod from 'zod';
      • import { logger } from '@dx/core-utils';
    • BAD examples:
      • import { Foo } from './foo'; (missing .js extension)
      • import { type Route } from './+types/root'; (missing .js extension)
      • import { Foo } from './foo.ts'; (should be .js not .ts)
      • import { AnInternalClass } from 'zod/dist/internals'; (shouldn't import internals)
      • import { logger } from '@dx/core-utils/src/logger.js'; (shouldn't import source files or use .js for packages)
  • Always prefer named exports over default exports
    • It's OK to use a default export in .tsx files (like Remix/React Router routes)
    • It's OK to use default exports in the CLI because it is required by oclif

Packages

All packages must follow these package.json rules:

  • type must be set to module
  • exports should be used instead of main or module
    • ALL source code should be exported from the root (.) path
    • Any test-specific code should be exported from a test path
    • Exports should use the .ts extension
    • Good example:
      • "exports": "./src/index.ts"
      • "exports": { ".": "./src/index.ts", "./test": "./src/test/index.ts" }
    • BAD example:
      • "main": "./src/index.ts" (don't use main or module)
      • "exports": "./src/index.js" (don't use .js for exports)
      • "exports": { ".": "./src/index.ts", "./test": "./src/test/index.ts", "./test/mocks": "./src/test/mocks.ts" } (don't export multiple paths: "./test" and "./test/mocks")
  • types should be used instead of typings
  • NEVER use TypeScript path resolution (paths in tsconfig.json)
    • Use ESM subpath imports instead if this is desired (eg: "imports": { "#/ui": "./ui/index.ts" })

Directory Structure

All packages must be placed in packages/<package-name>/. Each package should have the following:

  • A src/ directory containing the core source code.
  • A single entrypoint in src/index.ts that exports the package's public API.
  • Place test helpers, utilities, or mocks in src/test/ with a single entrypoint in src/test/index.ts.
  • Tests should be placed beside source code like src/my-file.ts and src/my-file.test.ts (NOT src/test/my-file.test.ts or test/my-file.test.ts).
  • A standard set of config files (like package.json and tsconfig.json) to ensure consistency.
  • The package.json must define an "exports" object with "." : "./src/index.ts" and optionally "./test": "./src/test/index.ts", ensuring clearly defined entrypoints for production and test.

Example:

packages/foo/package.json
packages/foo/tsconfig.json
packages/foo/src/index.ts
packages/foo/src/bar.ts
packages/foo/src/bar.test.ts
packages/foo/test/index.ts
packages/foo/test/setup.ts

TypeScript

- Eg: `export async function loader() {` and `export async function action() {` don't need return types
  • NEVER use any/unknown or cast values like (value as any) or value! in TypeScript outside of test files e.g. *.test.ts or test fixtures e.g. **/test-data.ts.
  • Don't rely on typeof, ReturnType<>, Awaited<>, etc for complex type inference (it's ok for simple types)
  • Use as const for better type inference
  • Use type guards to narrow types in conditional blocks
  • Create custom types for complex data structures used throughout the application
  • Utilize TypeScript's utility types (e.g., Partial, Pick, Omit) to manipulate existing types
  • Never use React.FC. Use a function declaration instead
  • Functions should accept an object parameter (like args or props) instead of multiple parameters
    • Good examples:
      function myFunction(args: { foo: boolean; bar: string }) {}
      function VideoPlayer(props: { sid: string }) {}
    • Bad examples:
      function myFunction(foo: boolean, bar: string, baz: number) {}
  • Arguments should be destructured in the function body, not the function definition. It's ok for React components to destructure props in the function definition.
    • Good example:
      function myFunction(args: { foo: boolean; bar: string }) {
        const { foo, bar } = args;
      }
    • Bad example:
      function myFunction({ foo, bar }: { foo: boolean; bar: string });
  • Zod should be used to parse untrusted data, but not for data that is trusted like function arguments
  • Zod unions should always be used instead of enums
    • For example, this union z.union([z.literal('youtube'), z.literal('spotify')]) is better than this enum z.enum(['youtube', 'spotify'])
  • Promises (and async functions which implicitly create Promises) must always be properly handled, either via:
    • Using await to wait for the Promise to resolve successfully
    • Using .then or .catch to handle Promise resolution
    • Returning a Promise to a calling function which itself has to handle the Promise. If you can't infer this from the available context, add a warning that the promise may not be handled properly.

Bun

Server-side code is written using Bun. Use native Bun (and the Node APIs it supports) when possible.

  • Use standard lib modules like Bun.file, $ shell commands, Glob, etc
  • Prefer standard lib modules over third-party alternatives
  • Utilize the node: protocol when importing Node.js modules (e.g., import fs from 'node:fs/promises')
  • Prefer the promise-based APIs over Node's legacy sync methods
  • Use Error objects for operational errors, and consider extending BaseError for specific error types
  • Use environment variables for configuration and secrets (avoid hardcoding sensitive information)

Web Standard APIs

Always prefer using standard web APIs like fetch, WebSocket, and ReadableStream when possible. Avoid Node.js-specific modules (like Buffer) or redundant libraries (like node-fetch).

  • Prefer the fetch API for making HTTP requests instead of Node.js modules like http or https
    • Use the native fetch API instead of node-fetch or polyfilled cross-fetch
    • Use the ky library for HTTP requests instead of axios or superagent
  • Use the WHATWG URL and URLSearchParams classes instead of the Node.js url module
  • Use Request and Response objects from the Fetch API instead of Node.js-specific request and response objects
  • Utilize Blob and File APIs for handling binary data when possible
  • Use TextEncoder and TextDecoder for encoding and decoding text

Error Handling

  • Prefer async/await over .then() and .catch()
  • Always handle errors correctly (eg: try/catch or .catch())
  • Implement React Error Boundaries to catch and handle errors in component trees
  • Use Remix Error Boundaries for handling errors in Remix routes
  • Avoid swallowing errors silently; always log or handle caught errors appropriately

Comments

Comments should be used to document and explain code. They should complement the use of descriptive variable and function names and type declarations.

  • Add comments to explain complex sections of code
  • Add comments that will improve the autocompletion preview in IDEs (eg: functions and types)
  • Don't add comments that just reword symbol names or repeat type declarations

Comment Format

  • Use JSDoc formatting for comments (not TSDoc or inline comments)
  • Common JSDoc tags to use:
    • @param: define a parameter on a function
    • @returns: define the return type of a function
    • @throws: define an exception that can be thrown by a function
    • @example: provide an example of how to use a function or class
    • @deprecated: mark a function or class as deprecated
    • @see: define an external reference related to the symbol
    • {@link}: create a link to the namepath or URL
    • @TODO: mark a task that needs to be completed
  • DO NOT use the following tags: @file, @async

Logging

  • Always use an instance of the pino logger from @dx/core-utils for logging.
  • NEVER use console or pino directly.

Testing

Unit Testing

  • All unit tests should Vitest
    • DO NOT attempt to install or use other testing libraries like Jest
  • Write unit tests for individual components and utility functions
  • Test files should be named [target].test.ts and placed in the same directory as the code they are testing (NOT a separate directory)
    • Good example: src/my-file.ts and src/my-file.test.ts
    • Bad example: src/my-file.ts and src/test/my-file.test.ts or test/my-file.test.ts or src/__tests__/my-file.test.ts
  • Tests should be run with bun run test (you can't do just bun test)
  • It's acceptable to use any/unknown in test files (such as *.test.ts) or test fixtures (like **/test-data.ts) to facilitate mocking or stubbing external modules or partial function arguments, referencing the usage guidelines in the TypeScript section. However, do not use any or unknown in production code.

Test Coverage

  • Test critical business logic and edge cases
  • Don't add tests for trivial code or just to increase test coverage
  • Don't make tests too brittle or flaky by relying on implementation details

Git & GitHub

  • Branch names should start with bot-123-the-issue-name where bot-123 is the Linear issue identifier associated with the branch and the-issue-name is the name of the issue (or a shortened version)
  • Commit messages should be short, concise, and descriptive
  • Pull requests should have a short descriptive title and a description that explains the changes made
  • PR titles and descriptions must accurately reflect the changes made and should be kept up to date if the PR changes significantly
  • Linear identifiers (eg: BOT-123) should not be included in PR titles (they should be in the branch name)
  • Proper capitalization and punctuation should always be used in commit messages and pull request titles and descriptions

Miscellaneous

  • When a new package (/packages/<package-name>) or app/service (/apps/<app-name>) is added to the repository, a corresponding entry should be added to the .github/CODEOWNERS and the readme.md file in the root directory.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment