DIACHRON.md explains the framework to newcomers joining a diachron-based project. diachron/AGENTS.md helps AI coding agents work with the framework conventions and commands. backend/types.ts and backend/services.ts are sample starting points for application-specific types and services. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
169 lines
6.1 KiB
Markdown
169 lines
6.1 KiB
Markdown
# What is diachron?
|
|
|
|
diachron is a web framework for TypeScript and Node.js. It uses a Go-based
|
|
master process that handles file watching, building, process management, and
|
|
request proxying. The application code is TypeScript running on Express.js.
|
|
|
|
If you're joining a project that uses diachron, this document will orient you.
|
|
|
|
## Why diachron exists
|
|
|
|
diachron was built around a few frustrations with mainstream web frameworks:
|
|
|
|
- **No dev/prod split.** Most frameworks behave differently in development and
|
|
production. diachron doesn't. The master process watches files, rebuilds,
|
|
and manages workers the same way everywhere. There is no `NODE_ENV`.
|
|
|
|
- **Managed tooling.** Node.js, pnpm, and other tools are downloaded and
|
|
pinned to exact versions inside the project. You don't install them
|
|
system-wide. Everyone on the team runs the same binaries.
|
|
|
|
- **PostgreSQL, directly.** No ORM, no database abstraction layer. You write
|
|
SQL (via Kysely for type safety) and talk to PostgreSQL. If you need
|
|
MySQL or SQLite support, this is not the framework for you.
|
|
|
|
- **Debuggability over magic.** Everything is explicit and inspectable.
|
|
Logging and observability are first-class concerns, not afterthoughts.
|
|
|
|
diachron is inspired by the
|
|
[Taking PHP Seriously](https://slack.engineering/taking-php-seriously/) essay
|
|
from Slack Engineering. It's designed for small to medium systems (what we
|
|
call "Ring 0 and Ring 1") -- not heavy-compliance or banking-scale
|
|
applications.
|
|
|
|
## How it works
|
|
|
|
When you run `./master`, the following happens:
|
|
|
|
1. The Go master process starts and watches your TypeScript source files.
|
|
2. It builds the backend using `@vercel/ncc`, producing a single bundled JS
|
|
file.
|
|
3. It starts one or more Node.js worker processes running your Express app.
|
|
4. It proxies HTTP requests from port 8080 to the workers.
|
|
5. When you edit a source file, it rebuilds and restarts the workers
|
|
automatically.
|
|
6. If a worker crashes, it restarts automatically.
|
|
|
|
There is no separate "dev server" or "hot module replacement." The master
|
|
process is the only way to run the application.
|
|
|
|
## Project structure
|
|
|
|
A diachron project looks like this:
|
|
|
|
```
|
|
.
|
|
├── DIACHRON.md # This file (framework overview for newcomers)
|
|
├── master/ # Go master process (framework-owned)
|
|
├── logger/ # Go logging service (framework-owned)
|
|
├── diachron/ # Managed binaries, shims, framework library
|
|
│ ├── AGENTS.md # Guide for AI coding agents
|
|
│ ├── binaries/ # Downloaded Node.js, pnpm (gitignored)
|
|
│ ├── cmd.d/ # Commands available via ./cmd
|
|
│ ├── shims/ # Wrappers that use managed binaries
|
|
│ └── ...
|
|
├── backend/ # Your application code
|
|
│ ├── app.ts # Entry point
|
|
│ ├── routes.ts # Route definitions
|
|
│ ├── handlers.ts # Route handlers
|
|
│ ├── services.ts # Service layer
|
|
│ ├── types.ts # Application types
|
|
│ ├── config.ts # Application configuration
|
|
│ └── diachron/ # Framework library code (framework-owned)
|
|
├── cmd # Run managed commands (./cmd pnpm install, etc.)
|
|
├── develop # Development-only commands (./develop reset-db, etc.)
|
|
├── mgmt # Management commands safe for production
|
|
├── sync.sh # Install/update all dependencies
|
|
├── master # The compiled master binary (after sync)
|
|
└── docker-compose.yml
|
|
```
|
|
|
|
### File ownership
|
|
|
|
There are two owners of files in a diachron project:
|
|
|
|
- **You own** everything in `backend/` (except `backend/diachron/`), plus
|
|
`docker-compose.yml`, `package.json`, and anything else you create.
|
|
- **The framework owns** `master/`, `logger/`, `diachron/`,
|
|
`backend/diachron/`, and the top-level scripts (`cmd`, `develop`, `mgmt`,
|
|
`sync.sh`, `check.sh`).
|
|
|
|
Don't modify framework-owned files. This separation keeps framework upgrades
|
|
clean.
|
|
|
|
## Getting started
|
|
|
|
```bash
|
|
# Install dependencies and build the master process
|
|
./sync.sh
|
|
|
|
# Start the application
|
|
./master
|
|
```
|
|
|
|
The app will be available at `http://localhost:8080`.
|
|
|
|
You need Linux or macOS on x86_64. For the full stack (database, Redis,
|
|
etc.), you also need `docker compose`.
|
|
|
|
## The command system
|
|
|
|
diachron has three types of commands, separated by intent and safety:
|
|
|
|
- **`./cmd <command>`** -- Run managed tools (node, pnpm, tsx, etc.). These
|
|
use the project's pinned versions, not whatever is installed on your system.
|
|
|
|
```bash
|
|
./cmd pnpm install
|
|
./cmd pnpm test
|
|
```
|
|
|
|
- **`./mgmt <command>`** -- Management commands that are safe to run in
|
|
production. Migrations, user management, that sort of thing.
|
|
|
|
```bash
|
|
./mgmt migrate
|
|
./mgmt add-user
|
|
```
|
|
|
|
- **`./develop <command>`** -- Development commands that may be destructive.
|
|
Database resets, fixture loading, etc. These are gated in production.
|
|
|
|
```bash
|
|
./develop reset-db
|
|
./develop db # Open a database shell
|
|
```
|
|
|
|
The rule of thumb: if you'd run it at 3am while tired and worried, it's a
|
|
`mgmt` command. If it destroys data on purpose, it's a `develop` command.
|
|
|
|
## Key concepts
|
|
|
|
### Call and Result
|
|
|
|
diachron wraps Express's `Request` and `Response` in its own types called
|
|
`Call` and `Result`. This avoids shadowing and keeps the framework's
|
|
interface distinct from Express internals. Your handlers receive a `Call`
|
|
and return a `Result`.
|
|
|
|
### Routes
|
|
|
|
Routes are defined as data (arrays of `Route` objects in `routes.ts`), not
|
|
through decorators or method chaining. The framework processes them into
|
|
Express handlers.
|
|
|
|
### No environment variables for behavior
|
|
|
|
There is no `NODE_ENV`, no `DEBUG`, no mode switching. Configuration that
|
|
must vary between deployments (database URLs, secrets) lives in
|
|
configuration files, but the application's behavior doesn't branch on
|
|
environment.
|
|
|
|
## Further reading
|
|
|
|
- `README.md` -- Project introduction and requirements
|
|
- `diachron/AGENTS.md` -- Guide for AI coding agents
|
|
- `docs/` -- Design documents and philosophy
|
|
- `docs/commands.md` -- Detailed explanation of the command system
|
|
- `docs/concentric-circles.md` -- What diachron is (and isn't) designed for
|