Naming Conventions

Consistent naming is what keeps a multi-product platform legible. This page documents the conventions every ARK product follows so you can match them when you customize, and so you can navigate any ARK codebase the same way.

Database

Table prefixes

Every product owns a prefix; every product table starts with it. Shared platform tables have no prefix.

PrefixOwner
track_Track (CRM)
comms_Comms
pulse_Pulse (Standups)
ink_Ink (Documents)
recap_Recap (Meetings)
hive_Hive (Team Overview)
(none)Shared platform: profiles, roles, teams, team_members, audit_log

If you add your own tables, use a prefix that's clearly yours so you don't collide with future ARK products. We recommend your company initials (acme_invoices) or a project codename (atlas_ledger).

Column naming

  • snake_case for everything. Postgres convention.
  • Singular nouns for columns, plural for tables: track_contacts (table), name, email, owner_profile_id (columns).
  • Foreign keys end in _id: team_id, owner_profile_id, created_by_id. The referenced table is implied by the prefix before _id.
  • Boolean columns are positive and prefixed with is_, has_, or can_: is_active, has_attachments, can_edit. Avoid double negatives like is_not_archived.
  • Timestamps end in _at: created_at, updated_at, archived_at, last_seen_at. Stored as timestamptz, always UTC.
  • Counts end in _count: unread_count, member_count. Cached aggregates use this; live aggregates do not.
  • JSON columns end in _json or _data: metadata_json, payload_data. Document the shape in a comment on the column.

Required columns

Every product table includes:

  • id uuid primary key default gen_random_uuid()
  • created_at timestamptz not null default now()
  • updated_at timestamptz not null default now() (with a trigger to update on row change)
  • A team scope column (team_id or transitive via another FK) for RLS.

Indexes

Index names follow idx_<table>_<columns>:

idx_track_contacts_team_id
idx_comms_messages_channel_id_created_at

Unique indexes use uniq_<table>_<columns> instead of idx_.

Constraints

  • Foreign key constraints: <table>_<column>_fkey (Postgres default).
  • Check constraints: <table>_<column>_check (Postgres default).
  • Don't fight the defaults; they make psql \d output readable.

Code

File naming

TypeConventionExample
React componentsPascalCase.tsxContactCard.tsx
HooksuseCamelCase.tsuseContact.ts
Utilities / libskebab-case.tsformat-date.ts
Tests<file>.test.ts or <file>.spec.tsformat-date.test.ts
Page routes (Next.js)page.tsx, layout.tsx, route.ts(Next conventions)

Variable naming

  • camelCase for variables, functions, props.
  • PascalCase for React components, types, classes.
  • UPPER_SNAKE_CASE for true constants with app-wide meaning (MAX_UPLOAD_SIZE, LICENSE_FORMAT_REGEX). Don't use it for local constants — const x = 5 is fine for x that lives 4 lines.

IDs in code

  • Internal IDs: id, userId, boardId (camelCase).
  • External vendor IDs: keep the vendor's format.
    • Stripe: cs_test_..., pi_... — store and pass exactly as Stripe gives them.
    • GitHub: arkteams/track-a1b2c3 (owner/name).
    • Supabase user IDs: UUIDs, no prefix.

CSS

Custom properties

All theme tokens are CSS custom properties, prefixed with --color-, --space-, --radius-, --shadow-, etc.

PatternExample
--color-<name>--color-brand, --color-text-muted
--space-<size>--space-2, --space-4
--radius-<size>--radius-md, --radius-lg
--shadow-<name>--shadow-popover, --shadow-modal
--font-<family>--font-sans, --font-mono
--ark-<custom>Buyer-specific overrides

The --ark- prefix is reserved for ARK-internal values that buyers shouldn't override (some are part of the fingerprint system — see Security Model).

Utility classes

ARK products use Tailwind utility classes inline. We don't define custom utility classes — if you need a repeated style, extract a React component, not a CSS class.

API

Route paths

/api/<resource>/<action>
  • Resources are plural nouns: /api/contacts, /api/messages.
  • Actions are verbs only when they don't fit REST: /api/contacts/import.
  • Always lowercase, hyphenated for multi-word resources: /api/team-members, not /api/teamMembers.

Request/response shapes

JSON. Camel-case keys (matches JS convention; database snake_case gets translated at the boundary):

{
  "contactId": "abc-123",
  "fullName": "Jane Doe",
  "createdAt": "2026-04-15T12:00:00Z"
}

Error responses

Always JSON, always with an error field:

{ "error": "Contact not found" }

HTTP status codes:

  • 200 — success
  • 201 — created (for resource-creating endpoints)
  • 400 — invalid input
  • 401 — not authenticated
  • 403 — authenticated but not authorized
  • 404 — not found
  • 409 — conflict (e.g., duplicate email)
  • 429 — rate limited
  • 500 — server error

Branches and commits (in your fork)

If you customize your ARK repo, consider following the ARK convention so you can read your own history later:

  • Branches: feature/<short-name>, fix/<short-name>, chore/<short-name>.
  • Commit messages: imperative, short (under 72 characters), no trailing period. Reference an issue if you have one.

This matches what you'll see in the update PRs that arrive from us.

Why so opinionated

These conventions exist because:

  1. Cross-product navigability. Once you know the rules, every ARK product feels familiar.
  2. AI tooling reliability. Claude Code and similar tools work better when conventions are consistent — they pattern-match the existing code.
  3. Update PR diffs stay legible. When we ship updates as PRs, they slot into your codebase predictably.

You're free to deviate in your own customizations. Just be consistent within whatever you do.