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.
| Prefix | Owner |
|---|---|
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_casefor 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_, orcan_:is_active,has_attachments,can_edit. Avoid double negatives likeis_not_archived. - Timestamps end in
_at:created_at,updated_at,archived_at,last_seen_at. Stored astimestamptz, always UTC. - Counts end in
_count:unread_count,member_count. Cached aggregates use this; live aggregates do not. - JSON columns end in
_jsonor_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_idor 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 \doutput readable.
Code
File naming
| Type | Convention | Example |
|---|---|---|
| React components | PascalCase.tsx | ContactCard.tsx |
| Hooks | useCamelCase.ts | useContact.ts |
| Utilities / libs | kebab-case.ts | format-date.ts |
| Tests | <file>.test.ts or <file>.spec.ts | format-date.test.ts |
| Page routes (Next.js) | page.tsx, layout.tsx, route.ts | (Next conventions) |
Variable naming
camelCasefor variables, functions, props.PascalCasefor React components, types, classes.UPPER_SNAKE_CASEfor true constants with app-wide meaning (MAX_UPLOAD_SIZE,LICENSE_FORMAT_REGEX). Don't use it for local constants —const x = 5is fine forxthat 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.
- Stripe:
CSS
Custom properties
All theme tokens are CSS custom properties, prefixed with --color-,
--space-, --radius-, --shadow-, etc.
| Pattern | Example |
|---|---|
--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— success201— created (for resource-creating endpoints)400— invalid input401— not authenticated403— authenticated but not authorized404— not found409— conflict (e.g., duplicate email)429— rate limited500— 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:
- Cross-product navigability. Once you know the rules, every ARK product feels familiar.
- AI tooling reliability. Claude Code and similar tools work better when conventions are consistent — they pattern-match the existing code.
- 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.