Code Conventions & Standards

Write consistent, maintainable code that follows OpenScan.AI standards.

This guide covers coding conventions for the backend/indexer (Elixir), frontend (TypeScript/React), and documentation (Astro/MDX) projects.


General principles

  • Match the surrounding code. Consistency within a file beats personal preference.
  • Minimal diffs. A bug fix fixes the bug; drive-by refactors go in their own PR.
  • No speculative abstractions. Three similar lines are better than a premature helper.
  • Comments explain why, not what. If the code needs a “what” comment, rename things until it doesn’t.

Commit messages

Conventional format, as seen across the repositories:

<type>: summary

Types: fix, feat, docs, chore, refactor, test, perf.

Examples:

fix: correct token holder pagination cursor
feat: add method filter to address transactions endpoint
docs: update API rate limit values
chore: bump dependencies
  • Imperative mood, lowercase summary, no trailing period.
  • Squash fixup commits before merge.

Elixir (backend & indexer)

  • Run mix format before committing; the CI check is mix format --check-formatted.
  • Run mix credo --strict and address findings — don’t silence them without a code comment explaining why.
  • Follow the existing Explorer / Indexer app boundaries: chain data structures in Explorer.Chain, fetch logic in Indexer.Fetcher.*.
  • Fetchers must be idempotent and tolerate reorgs; never assume a block, once seen, is final.
  • Add typespecs to public module functions.
  • Tests: ExUnit, with factories where the repo provides them. Cover failure and retry paths for anything that talks to JSON-RPC.

TypeScript / React (frontend)

  • TypeScript strict mode is on; no any without justification.
  • Format with Prettier and lint with ESLint — both run in CI.
  • Components: PascalCase files, functional components, hooks for state.
  • Styling: use the design system’s tokens and existing components; avoid one-off colors and spacing values.
  • Data fetching: go through the API client layer rather than scattering fetch calls through components.
  • API response types are generated/derived from the Blockscout-compatible schemas — don’t hand-edit generated files.

Documentation (Astro/MDX)

  • One topic per page; keep the heading hierarchy sane (single h1 via frontmatter title, ## sections below).
  • Every page keeps its frontmatter keys (title, description, weight, and any extras).
  • Code examples must be copy-paste runnable — verify them against xdcscan.io before committing.
  • Use npx prettier . --write before opening a PR; Prettier is configured with the Astro and Tailwind plugins.
  • Internal links use absolute docs paths (/docs/developers/...) that match real files in the tree.
  • In .mdx files, never leave a broken import: if you remove a component’s usages, remove its import too.

Testing expectations

Change typeExpected verification
Backend logicmix test covering the new path
Fetcher/indexer changeHappy path + one failure/reorg test
Frontend UIUnit test for logic; screenshots in the PR
Docsnpm run astro -- check + verified examples

Naming

  • Variables/functions: camelCase (TS), snake_case (Elixir).
  • Types/components: PascalCase.
  • Files: match the repo you’re in — kebab-case for docs pages, PascalCase for React components.
  • API fields: snake_case, consistent with the Blockscout v2 schema.

When in doubt

Look at three recent merged PRs in the repository you’re touching and match them. If a convention question is genuinely ambiguous, ask in the PR discussion rather than guessing.