Skip to main content
Version: Next

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this directory. It also contains our style guide, which can be consumed by engineers.

Style Guide

File Structure

  • What should our top-level folders be?
    • app: components that implement the entire application
      • Based on nav page
      • Each new high-level page gets a new top-level folder
      • Nested pages are within the same top-level folder
    • graphql: graphql files and generated types
    • images: all custom images
    • conf / fonts / providers / utils: do we need these?
    • When do we create a new one?
  • What to put in index.ts(x)?
    • Do not create these files
  • Where to put tests?
    • In __tests__/ in the same directory as the file
    • Named OriginalFile.test.ts(x)
  • Where to put utils?
    • For utils used in a single file: SourceFile.utils.ts
    • For utils used in a folder: folderName/folderName.utils.ts
  • Where to put hooks?
    • For helper hooks used in a single file: SourceFile.hooks.ts(x)
    • For standalone, reusable hooks: hookName.ts(x)
    • Try not to write hooks that generate JSX, but sometimes it may make sense
  • Where to put types?
    • Types only used once should be in the same file where they’re used
    • Shared types go in folderName/folderName.types.ts
  • Where to put helper components?
    • If the component is only used once and is small: parent/SourceFile.components.tsx
      • e.g. search/SearchPage.components.tsx
    • If a component is shared / large and doesn’t have its own children: parent/NewComponent.tsx
      • e.g. search/SearchPageHeader.tsx
    • If the component has its own child components, put it in a new folder:parent/NewComponent/NewComponent.tsx
      • e.g. search/SearchCard/SearchCard.tsx

Component Library

  • What is an DataHub component? (previously: alchemy component)
    • Can be reused anywhere in the app
    • Generalized
      • Doesn’t depend on our aspect / data structure
      • Doesn’t depend on graphql types
  • Where do reusable non-DataHub components go?
    • e.g. EntityHealth icon, HoverEntityTooltip
      • Can be reused anywhere within the web app
      • Not general: can take in very DataHub/gms specific inputs, e.g. graphql types
    • shared/
      • components/
        • entity/
          • entity.types.ts
          • health/
            • EntityHealth
          • tooltip
            • HoverEntityTooltip
        • button/
          • DownloadSearchResultsButton
      • hooks/
      • utils/

Code Structure

  • When to break into a new component?
    • We should err on the side of more and smaller components (files)
    • When JSX is getting too large or complex
    • Any logical or reusable chunk
  • When to break into a hook?
    • When a component’s logic is getting too large or complex
    • Any logical or reusable chunk of logic
  • When to use a React context vs passing props?
    • Err on the side of not creating contexts — only add when necessary
    • Globally: use a context when we want values available globally, e.g. AppContext or UserContext
    • Low-level, small-scope: use a tightly-defined context for a small, self-contained set of files
      • folder/<Folder>Context.ts
        • Contains type, default value, helper hooks
      • folder/<Folder>ContextProvider.tsx
        • Contains just the provider
    • TBD: How to handle contexts for entity components?
      • shared/entity/EntityContext.tsx?
  • How to pass props?
    • For small amount of props, can use individual values
    • When props gets larger, try to break into logical pieces
      • e.g. entityData rather than individual props for each aspect of the entity
    • For components reused between OSS and SaaS:
      • SaaS-only props should go in a single field: acrylProps: { ... }
  • Component file sections:
    • Imports
    • Constants (in SNAKE_CASE)
    • Styled components
    • Props
    • Main component

Code Conventions

  • How to pass styles to a component?
    • Use styled components for any simple components
    • For custom components (e.g. our DataHub components), take className in as a prop and pass it to the top-level element — whatever it makes sense for custom styles to be applied to
      • This allows us to pass styles by styled component inheritance
        • e.g. if there’s a custom component Button, this allows us to define const CustomButton = styled(Button)
    • If there are multiple elements for which custom styles can be applied, non-top-level ones should be passed as a CSSProperties prop, with a specific name, e.g. textStyle or buttonStyle
  • How to handle images and icons
    • For component library icons, use <Icon> DataHub component and specify color and size via props
      • If size is not known, use size="inherit" and set font-size in parent div
      • Always use phosphor icons — ant and material ui icons are deprecated
    • For custom images, use <Image> (once it’s built) and specify color and size via props
      • For svgs, in svg definition, set fill="currentColor" and then … TODO
      • Alt text should be a required field
  • How to do theming?
    • Use styled-components theming, with stringed css rather than object css
// YES
styled.div`
border-radius: 2px;
`;

// NO
styled.div({ borderRadius: '2px' });

Code Style

Mostly handled by linter and formatter (prettier). Mostly unimportant changes that we should be consistent on to improve readability.

  • Functions vs lambdas
    • Top-level, prefer named functions (export default function f() { })
    • Nested, use lambdas (const onClick = () => { })
  • Prefer type over interface (except when using classes)
  • When to omit types
    • For variables, do not need to specify
    • For function signatures, try to specify, unless it’s too difficult. In that case, prefer inferred typing over any
    • Prefer a mapper function over unsafe type casting
      • If you must cast types, do so as early (high) as possible
  • Prefer destructuring over dot notation, but use your own intuition
    • When accessing arrays, always option chain (i.e. x?.[0])
  • When to optional chain
    • If it type checks, it should be fine, except for the array access case above
  • Prefer direct imports, e.g. import React, { useState } from 'react' over React.useState

Development Commands

Setup and Dependencies

# Install dependencies and set up dev environment
# Update dependencies after changes to package.json
../gradlew yarnInstall

Running the Service

# Local development on localhost:3000 (with hot reload)
../gradlew yarnServe

# Development with remote GMS (e.g., dev01)
../gradlew yarnPreview -Pproxy="<remote-instance-url>"
# e.g. ../gradlew yarnPreview -Pproxy="https://dev01.acryl.io/"

Testing and Code Quality

Run formatting / linting / type checking / relevant tests after all changes

# Generate ts files based on graphql
yarn generate

# Run formatting
yarn format

# Run linting
yarn lint

# Run type checking
yarn type-check

# Run tests
yarn test