Use Bun runtime and package manager:
bun install
bunx tsc
- Do NOT use section header comments (like
// ---) - Do not write obvious comments that restate what the code is doing without adding meaningful context.
- Inline logic that is 1-2 lines instead of defining a function that is only referenced once.
- Avoid const deconstruction.
- Module designed to be used should have no side-effects.
- Use named exports over default export.
- Private, or internal, modules live in an
internal/subdirectory next to the public modules that use them, likesrc/internal/ContentNegotiation.tsorsrc/studio/internal/Pretty.ts.
// when importing Node modules name them like such:
import * as NUrl from "node:url`
// always import test module as namespace:
import * as test from "bun:test"
// import Effect sub-modules directly
import * as Schema from "effect/Schema"
// Always use extension in local module imports:
import type * as Files from "./Files.ts"
// When import module is lowercase, prefer named imports:
import { server } from "./server.ts"- Export smallest possible surface to make the module useful.
- Do NOT export types, functions, or symbols that are not used outside of the module.
The Effect source code is in ./vendor/effect/src for reference.
Use this to explore APIs, find usage examples, and understand implementation
details when the documentation isn't enough.
// Always use tryPromise() and map to TaggedError
Effect.tryPromise({
try: () => fetch(`https://example.com`),
// remap the error
catch: (cause) => new FetchError({ reason: "Network", cause }),
})
class FetchError extends Data.TaggedError("FetchError")<{
reason: "Network" | "Status"
cause?: unknown
message: string
}> {}Run test when making final changes:
# run all tests
bun test
# run specific test
bun test routing.test.ts
# type check
tsgoimport * as test from "bun:test"
// use test.expect when testing runtime
test
.expect(routes)
.toEqual([
{
type: "Literal",
},
])
// use test.expectTypeOf when testing types
test
.expectTypeOf(context)
.toMatchObjectType<{
method: "GET"
}>()// when test runs effect, wrap the entire body in Effect.gen/pipe
test.it("does something", () =>
Effect.gen(function* () {
const parsed = yield* Commander.parse(cmd, args)
test
.expect(parsed.args)
.toBe([])
}).pipe(Effect.scoped, Effect.runPromise),
)
// bad
test.it("does something", async () => {
await Effect.runPromise(Commander.parse(cmd, args))
test
.expect(executed)
.toBe(false)
})