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>: summaryTypes: fix, feat, docs, chore, refactor, test, perf.
Examples:
fix: correct token holder pagination cursorfeat: add method filter to address transactions endpointdocs: update API rate limit valueschore: bump dependencies- Imperative mood, lowercase summary, no trailing period.
- Squash fixup commits before merge.
Elixir (backend & indexer)
- Run
mix formatbefore committing; the CI check ismix format --check-formatted. - Run
mix credo --strictand address findings — don’t silence them without a code comment explaining why. - Follow the existing
Explorer/Indexerapp boundaries: chain data structures inExplorer.Chain, fetch logic inIndexer.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
anywithout 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
fetchcalls 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
h1via 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 . --writebefore 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
.mdxfiles, never leave a broken import: if you remove a component’s usages, remove its import too.
Testing expectations
| Change type | Expected verification |
|---|---|
| Backend logic | mix test covering the new path |
| Fetcher/indexer change | Happy path + one failure/reorg test |
| Frontend UI | Unit test for logic; screenshots in the PR |
| Docs | npm 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.