Working With AI Tools on a New Library

This is the setup guide for Railway-Oriented TypeScript. If you haven’t read the overview yet, start there.

@railway-ts/pipelines and @railway-ts/use-form are new. That creates a practical problem: AI coding assistants — Claude Code, Cursor, GitHub Copilot — have been trained on millions of examples of Zod, react-hook-form, and neverthrow. They have almost no training data for @railway-ts.

The result is predictable. Ask Claude Code to build a form with @railway-ts/use-form installed and it will write zodResolver, useForm from react-hook-form, and setError/clearErrors — confidently, without being asked, because that’s the pattern it’s seen thousands of times. It’s not hallucinating random code. It’s pattern-matching against the highest-probability answer in its training distribution. The problem is that answer is wrong for your project.

This isn’t a criticism of AI tools. It’s a structural reality about how they work that’s worth understanding and routing around.

Why Docs Shipped With the Package Matter

Both libraries now ship their documentation inside the npm package:

node_modules/@railway-ts/pipelines/docs/
node_modules/@railway-ts/use-form/docs/

This matters because modern AI coding tools — Claude Code in particular — can read your node_modules and reason from types and documentation in context. When you point the tool at the right docs, it stops pattern-matching against training data and starts reasoning from the actual library API. The generated code goes from “confidently wrong” to “reads the types and generates correct usage.”

The difference in output quality is substantial. The bundled docs are the mechanism that makes AI-assisted development on a new library viable.

Setting Up a CLAUDE.md

The most reliable way to redirect an AI tool is a CLAUDE.md (or equivalent context file) in your project root. Claude Code reads this automatically. Create it before you start generating form or pipeline code:

# AI Coding Instructions

This project uses @railway-ts/pipelines and @railway-ts/use-form.

Before generating any form or pipeline code, read the docs shipped with the packages:

- node_modules/@railway-ts/pipelines/docs/
- node_modules/@railway-ts/use-form/docs/

Rules:

- Do NOT use Zod or @hookform/resolvers patterns
- Do NOT use react-hook-form's useForm, setError, or clearErrors
- Schema validation uses @railway-ts/pipelines/schema, not z.object()
- Form state uses useForm from @railway-ts/use-form, not react-hook-form
- Async pipelines use flowAsync/pipeAsync from @railway-ts/pipelines/composition

This does two things: tells the tool what not to do (which matters as much as what to do), and points it to the docs that contain the correct patterns.

For other AI tools:

Cursor — add the same content to .cursorrules or use the @Docs feature to index the bundled docs directly.

GitHub Copilot — less controllable without explicit doc indexing, but keeping a reference file open in your editor with correct usage examples significantly improves suggestion quality.

Verifying the Setup Works

After creating CLAUDE.md, ask your AI tool to build a simple form before touching any real code:

Build a React login form with email and password fields using @railway-ts/use-form. Handle server validation errors.

The output should use useForm from @railway-ts/use-form, a schema built with object/required/chain from @railway-ts/pipelines/schema, and form.setServerErrors() for server errors. If you see zodResolver, @hookform/resolvers, or setError/clearErrors, the tool is still pattern-matching against training data — check that CLAUDE.md is in the project root and that the docs are present in node_modules.

What the AI Gets Right Once Pointed at the Docs

Once the tool is reading from the bundled docs, the things it handles well:

  • Schema composition with chain, object, required, optional
  • fieldValidators for async field-level checks
  • form.setServerErrors() for server-side error injection
  • flowAsync/pipeAsync for composing multi-step async pipelines
  • mapWith, flatMapWith, filterWith, tapWith — the full curried operator set
  • combine, combineAll, partition for batch processing
  • Cross-field validation with refineAt

The things worth double-checking manually:

  • Type inference with InferSchemaType — verify the generated type matches your intent
  • initialValues completeness — the TypeScript error is immediate if a field is missing, but worth confirming
  • Error path strings in setServerErrors — confirm they match your schema field names

Quick Start

mkdir my-project && cd my-project
npm init -y
npm install react react-dom @types/react typescript
npm install @railway-ts/pipelines @railway-ts/use-form

Create CLAUDE.md as shown above, then verify the docs are present:

ls node_modules/@railway-ts/pipelines/docs/
ls node_modules/@railway-ts/use-form/docs/

If you’re migrating an existing project that uses Zod:

npm install @railway-ts/use-form @railway-ts/pipelines
# keep zod — the form hook accepts Zod schemas via Standard Schema v1

You can adopt the form hook without touching your Zod schemas. See Part 3 for the migration path.

The Series

Part 1 — The Glue Code Tax
The Zod + react-hook-form seam counted line by line: the resolver, the async validation wiring, the server error conversion, the type assertions. Then the same form rewritten without any of them.

Part 2 — Composable Async Pipelines
The @railway-ts/pipelines API: Result, curried operators, and flowAsync for multi-step async pipelines where errors short-circuit automatically. The same Result type the form hook uses.

Part 3 — Schema-First React Forms
The 3-layer error priority system in detail, array fields, and the full-stack loop where one schema drives backend pipeline validation and frontend form state without any format conversion between them.

Bonus — Data Processing Pipelines
The pipeline library in an ETL context: batch processing with combine, combineAll, and partition, reusable sub-pipelines, structured error reporting. No React, no UI.

GitHub:

Leave a Reply