Why end-to-end types help coding agents, where TypeScript and Go types stop at the network boundary, and how schemas derived from code, generated clients and compile errors close the gap.

A type-safe API means the shape of every request and response is declared once and checked everywhere it is used, from the handler to the service that calls it to the frontend that renders the result. In a single TypeScript or Go program the compiler already does that, and it is one of the main reasons agents do well in typed languages, since a type error after an edit is a precise instruction for what to fix.
The checks stop at the network boundary, because TypeScript erases its types at compile time and a Go struct means nothing to a JavaScript client, so an agent that renames a field in a handler gets a clean build while the frontend and two other services keep sending the old name.
What end-to-end types give an agent:
undefined values at runtime.Static types catch a real share of bugs before anything runs, and a study of JavaScript projects on GitHub found that TypeScript and Flow would each have detected 15 percent of the public bugs it examined, and those are bugs a person would otherwise find in review or in production. For models, the effect is more direct, since a 2025 paper on type-constrained code generation showed that using the TypeScript type system to steer generation cut compilation errors by more than half and significantly improved functional correctness in code synthesis, translation and repair tasks across model families.
Usage data points the same way, since GitHub's 2025 Octoverse reported that TypeScript became the most used language on GitHub by contributor count in August 2025, and described the shift as developers moving toward typed languages that make agent-assisted coding more reliable in production. All of that depends on the types reaching the code the agent is changing, which is the part that breaks once a request crosses from one service to another.
Encore, the safe way for AI agents to ship backend changes to your cloud, combines a TypeScript and Go backend framework with infrastructure declared in the same code, and it treats the types of each endpoint as the API contract. At build time Encore parses every endpoint's request and response types into its application model, and from that model it validates incoming requests, generates API documentation, compiles typed calls between services and produces clients for frontends.
// orders/orders.ts
import { api } from "encore.dev/api";
import { SQLDatabase } from "encore.dev/storage/sqldb";
import { IsEmail, Min } from "encore.dev/validate";
const db = new SQLDatabase("orders", { migrations: "./migrations" });
interface CreateOrder {
email: string & IsEmail;
total: number & Min<1>;
}
interface Order {
id: number;
email: string;
total: number;
}
export const create = api(
{ expose: true, method: "POST", path: "/orders" },
async (req: CreateOrder): Promise<Order> => {
const row = await db.queryRow<{ id: number }>`
INSERT INTO orders (email, total)
VALUES (${req.email}, ${req.total})
RETURNING id`;
return { id: row!.id, email: req.email, total: req.total };
},
);
A billing service calls that endpoint through ~encore/clients, and the call looks like a local function call even though Encore turns it into a network request.
// billing/billing.ts
import { api } from "encore.dev/api";
import { orders } from "~encore/clients";
export const checkout = api(
{ expose: true, method: "POST", path: "/checkout" },
async (req: { email: string; cents: number }): Promise<{ orderId: number }> => {
const order = await orders.create({ email: req.email, total: req.cents });
return { orderId: order.id };
},
);
If an agent writes amount instead of total, tsc fails with 'amount' does not exist in type 'CreateOrder', pointing at the line in the billing service. If it renames total in the orders service instead, the same error shows up in every caller, which is the list of places the agent has to update. A request from outside that sends a malformed email is rejected with a 400 before the handler runs, so the type annotation and the runtime behavior agree.

Go works the same way, where an //encore:api function in the orders package is called from another service as orders.Create(ctx, &orders.CreateParams{...}), and a wrong field fails encore check with the Go compiler's own unknown field Amount in struct literal of type orders.CreateParams.
package billing
import (
"context"
"encore.app/orders"
)
type CheckoutParams struct {
Email string
Cents int
}
type CheckoutResponse struct {
OrderID int64
}
//encore:api public method=POST path=/checkout
func Checkout(ctx context.Context, p *CheckoutParams) (*CheckoutResponse, error) {
o, err := orders.Create(ctx, &orders.CreateParams{Email: p.Email, Total: p.Cents})
if err != nil {
return nil, err
}
return &CheckoutResponse{OrderID: o.ID}, nil
}
For a frontend, encore gen client --output=./client.ts --env=local writes a single file with a class per service and the same request and response types, so client.orders.create(...) in a React app is checked against the backend's current types. The infrastructure declarations follow the same rule, since Encore validates database, topic and bucket declarations during the build, and the local MCP server exposes the API schemas so an agent can inspect them while encore run starts Postgres locally.
~encore/clients and in Go as package function calls.Generated frontend clients are a snapshot, so a client shipped to users keeps calling the old shape after a breaking change, and Encore's docs recommend avoiding breaking changes on APIs that published clients use. Types catch shape mismatches rather than wrong business logic, so the agent still needs tests that run against a real database.
Run in your terminal to get started locally.
OpenAPIAn OpenAPI document describes every endpoint in a language-neutral format, and code generators such as openapi-typescript for TypeScript or oapi-codegen for Go turn it into typed clients and server stubs.
The document becomes a third artifact next to the server types and the client, and the codegen step is one more thing an agent has to know to run. When the spec is written by hand it drifts from the server, and when it is generated from the server the agent still has to regenerate clients before the compiler can see a mismatch.
tRPC shares the TypeScript type of the server router directly with the client, so there is no schema file and no generation step when both live in the same repository.
tRPC only works for TypeScript clients that import the server's types, so Go services, mobile apps and external consumers need a separate API, and it does not provide service-to-service calls between backends or any infrastructure.
Copying an interface into each caller and writing fetch calls by hand is still common, and it is the setup where an agent has the least to go on. The compiler checks each copy against itself, so a renamed field compiles in every service and the first sign of the change is a failing request, which is the kind of "almost right" result that 66 percent of developers in the 2025 Stack Overflow survey named as their biggest frustration with AI tools.
Derive everything from one declaration of the request and response types. The server's runtime validation, the API schema and the clients that call it should all be generated from that declaration, so that when an agent changes a field, every caller that no longer matches fails to compile. Encore does this for TypeScript and Go by parsing the types of each endpoint, validating requests against them, and generating typed service-to-service calls and frontend clients with encore gen client, so tsc or the Go compiler reports a mismatch before the code runs.
A type checker gives the agent a fast, specific error after each change, which it can fix without running the program. Research on TypeScript and Flow found that static types would have caught about 15 percent of public bugs in the JavaScript projects studied, and a 2025 study found that constraining model output with a type system cut compilation errors by more than half.
No. TypeScript types are erased at compile time, so a request that arrives over HTTP is not checked against them unless a validation layer derived from the types, or a separate schema, does it.
Deploy to your cloud on AWS and Google Cloud
without managing infrastructure
Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.