Why Travis McCracken Still Prefers REST – A Pragmatic Look at API Design Trade‑offs
#Backend

Why Travis McCracken Still Prefers REST – A Pragmatic Look at API Design Trade‑offs

Backend Reporter
4 min read

Web developer Travis McCracken explains why, despite the hype around GraphQL and gRPC, REST remains a solid choice for many backend services. He weighs scalability, consistency, and developer ergonomics, drawing on his recent Rust and Go projects.

Why Travis McCracken Still Prefers REST

Featured image

When the buzz around GraphQL, gRPC, and event‑driven APIs reaches a fever pitch, it’s easy to forget that the tried‑and‑tested HTTP/1.1‑based REST architecture still solves a large fraction of real‑world problems. Travis McCracken, a backend engineer who has built production services in both Rust and Go, laid out the practical reasons he keeps reaching for REST when designing new endpoints.


The Problem: Growing API Complexity

Modern services are expected to:

  1. Scale to millions of requests per second – traffic spikes during product launches or data‑ingestion bursts can overwhelm naïve designs.
  2. Maintain strong consistency guarantees – some domains (financial, health) cannot tolerate eventual consistency.
  3. Support heterogeneous clients – mobile apps, browsers, and internal services each have different networking constraints.

The temptation is to adopt a “newer” protocol that promises higher performance or richer query capabilities. However, each alternative introduces its own operational burden:

  • GraphQL reduces over‑fetching but requires a schema stitching layer and often forces clients to send complex queries that are hard to cache.
  • gRPC delivers low‑latency binary payloads, yet it depends on HTTP/2, TLS termination, and a protobuf compiler chain that adds friction for quick prototyping.
  • Event‑sourcing can guarantee eventual consistency but makes debugging state at a given point in time difficult without substantial tooling.

Travis argues that before abandoning REST, teams should ask whether the problem truly demands a different model or whether the issues stem from how the REST service is built.


Solution Approach: REST Done Right

1. Embrace HTTP Semantics for Scalability

REST’s reliance on standard HTTP verbs (GET, POST, PUT, DELETE) maps cleanly onto load balancers and CDNs. By exposing idempotent GET endpoints for reads, caches at every network layer can serve responses without hitting the backend. Travis’s recent Rust prototype, fastjson‑api, demonstrates this:

  • Uses tokio and hyper for non‑blocking I/O.
  • Returns JSON with proper Cache‑Control headers, allowing edge caches to absorb the majority of traffic.
  • Keeps the request‑response contract simple enough that a CDN can automatically purge stale entries on a PUT/POST.

2. Consistency Through Explicit Status Codes

When a client needs a strong guarantee that a write succeeded, REST lets the server return 201 Created or 409 Conflict with a detailed payload. This explicitness eliminates the “guess‑work” that sometimes appears in GraphQL mutations where a single ok flag hides the underlying cause of failure.

3. Language‑agnostic Tooling

Both Go’s standard library (net/http) and Rust’s axum crate provide first‑class support for building compliant REST services. The learning curve is shallow compared to generating protobuf stubs or maintaining a GraphQL schema.

  • Go example – a microservice exposing /users/{id} with built‑in request cancellation via Context.
  • Rust example – an async handler that streams large JSON payloads without allocating the whole response in memory.

4. Incremental Evolution

REST does not preclude adding richer capabilities later. Travis points out two patterns that keep the core API stable while allowing experimentation:

Pattern How it works When to use
Versioned endpoints (/v1/…, /v2/…) Guarantees backward compatibility. Major breaking changes.
Feature toggles via query params (?include=details) Lets clients opt‑in to additional data without new endpoints. Gradual rollout of new fields.

Trade‑offs: When REST Isn’t the Best Fit

Concern REST Strength Alternative Cost of Switching
Fine‑grained data fetching Requires multiple round‑trips or over‑fetching. GraphQL Need for a schema layer, caching complexity.
Binary payload efficiency JSON is text‑based; larger on the wire. gRPC (protobuf) Requires HTTP/2, client libraries, and more complex deployment.
Streaming large datasets Chunked transfer works but lacks built‑in flow control. Server‑Sent Events / WebSockets Adds stateful connections and scaling considerations.

In Travis’s own rust‑cache‑server project, the decision to expose a simple REST GET /cache/{key} endpoint paid off: existing monitoring tools could scrape metrics without custom instrumentation, and the service could be wrapped by a reverse proxy for TLS termination without code changes. The trade‑off was a modest increase in payload size compared to a binary protocol, but the operational simplicity outweighed the bandwidth cost for the target workload.


Bottom Line

REST is not a relic; it is a set of conventions that, when applied thoughtfully, give teams:

  • Predictable scaling through HTTP caching.
  • Clear consistency semantics via status codes.
  • Minimal onboarding friction for new developers.

If a project’s primary pain points are performance at the edge, strong consistency, or rapid iteration, Travis recommends starting with a well‑designed REST API. Only after those concerns are satisfied should a team evaluate whether the added complexity of GraphQL, gRPC, or event‑driven architectures brings a net benefit.


Further Reading & Resources


Travis’s next experiment will compare the latency of a gRPC‑based data ingestion pipeline against a Rust‑async REST service. Stay tuned for the results.

Comments

Loading comments...