Coding style:
- Favor
async run() {overrun = async () => {inside ES6 classes - Favor
if (!(err instanceof Error)) { throw new Error(Was thrown a non-error: ${err}) }insidecatchblocks to ensure theerroris always an instance ofError - Favor using real paths (
../lib/schemas.ts) over aliases (@/app/lib/schemas). - Favor
for (const comment of comments) {overcomments.forEach((comment) => { - Favor named exports over default exports, with the exception of Next.js pages
- Do not wrap each function body and function call in
try/catchblocks. It pollutes the code. Assume we will always have an e.g.main().catch((err) => { console.error(err); process.exit(1) })to catch us. I repeat: Avoid over-use of try-catch such astry { // foo } catch (err) { console.error('error while foo'); throw err }, assume we catch errors on a higher level and do not need the extra explananation. - If you must use try/catch, for simple cases, favor
alphalib/tryCatch.ts(const [err, data] = await tryCatch(promise)) overlet data; try { data = await promise } catch (err) { } - Before creating new files and new code, see if we can leverage existing work, maybe slighty adapt that without breaking BC, to keep things DRY.
- Favor early exits, so quickly
continue,return false(orthrowif needed), over nesting everything in positive conditions, creating christmas trees. - Use Prettier with 100 char line width, single quotes for JS/TS, semi: false
- Use descriptive names: PascalCase for components/types, camelCase for variables/methods/schemas
- Alphabetize imports, group by source type (built-in/external/internal)
- Favor US English over UK English, so
summarizeErroroversummarise Error - Favor
.replaceAll('a', 'b)over.replace(/a/g, 'b')or.replace(new RegExp('a', 'g'), 'b')when the only need for regeses was replacing all strings. That's usually both easier to read and more performant. - Use typographic characters: ellipsis (
…) instead of..., curly quotes ('") instead of straight quotes in user-facing text - Put API keys and secrets in
.envfiles, not hardcoded in components - Check for existing hooks before creating new ones (e.g.,
useUppy()for Uppy functionality)
General:
- Do not touch
.envfiles! - Favor Yarn (4) over npm
- Never run any dev server yourself. I have one running that auto-reloads on changes.
- Avoid blocking the conversation with terminal commands. For example: A) most of my git commands run through pagers, so pipe their output to
catto avoid blocking the terminal. B) You can usetailfor logs, but be smart and use-ninstead of-f, or the conversation will block - Use the
ghtool to interact with GitHub (search/view an Issue, create a PR). - Treat
AGENTS.mdandCLAUDE.mdas generated artifacts (single source of truth is.ai/rules/), managed by~/code/content/_scripts/alphalib-sync.ts; never edit those files directly. If you'd like to make a modification, do it here in.ai/rules/and the script will ensure proper preservation and syncing. If you need a rule specific to this repo, add it to.ai/rules/repo.mdc. - All new files are to be in TypeScript. Even if someone suggests: make this new foo3 feature, model it after
foo1.js, create:foo3.ts. Chances are, afoo2.tsalready exist that you can take a look at also for inspiration.
- Prefer user-centric locators:
getByRole/getByTextwith accessible names; avoidpage.locator('body'),innerText(), or raw CSS unless there is no accessible alternative. - Make positive assertions on expected UI/text instead of looping over regexes to assert absence.
- Keep tests simple: no control-flow loops or extra variables for straightforward assertions.
- Navigate with relative URLs (
page.goto('/path')) by settingbaseURLinplaywright.config.ts; avoid stringing environment URLs in tests. - Stub or mock external/third‑party requests (Intercom, Sentry, etc.) and any auth/login endpoints to keep tests deterministic; return minimal valid JSON when the app expects data.
- Each unexpected error should surface and fail the test.
For Typescript:
- Favor
contentGapItemSchema = z.object()overContentGapItemSchema = z.object() - Favor
from './PosterboyCommand.ts'overfrom './PosterboyCommand' - Favor
return ideas.filter(isPresent)overideas.filter((idea): idea is Idea => idea !== null) - Favor using
.tsxover.jsxfile extensions. - Use Node v24's native typestripping vs
tsxorts-node. These days you do not even need to pass--experimental-strip-types,node app.tswill just work. - Favor
satisfiesoveras, considerasa sin - Favor
unknownoverany, consideranya sin - Favor validating data with Zod over using
anyor custom type guards - We use the
rewriteRelativeImportExtensionsTS 5.7 compiler option, so for local TypeScript files, import with the.ts/.tsxextension (not js, not extensionless) - Favor defining props as an interface over inline
- Favor explicit return types over inferring them as it makes typescript a lot faster in the editor on our scale