Where Does API Complexity Live?

cover
Ahmet Soormally

Ahmet Soormally

min read

TL;DR

Every API architecture handles the same five concerns - data fetching, security, caching, contract management, and governance. None of them disappear. They just move. REST pushes data fetching to the client and keeps security and caching in the infrastructure. GraphQL pulls data fetching to the server and pushes security and caching into application code. Federation distributes the runtime concerns across teams but lands governance on humans. Fission moves governance from humans to design-time tooling. The question you should be asking yourself is where you want each responsibility to live.

In thermodynamics, the first law is non-negotiable: energy cannot be created or destroyed. It can only change form. To heat one thing, you must cool another. The total energy in a closed system remains constant.

Software architecture obeys a similar law. Larry Tesler, while at Xerox PARC in the 1980s, called it the Law of Conservation of Complexity: "Every application has an inherent amount of irreducible complexity. The only question is: Who will have to deal with it?" 1. Fred Brooks drew a complementary line in 1986, distinguishing essential complexity - the irreducible difficulty of the problem itself - from accidental complexity introduced by our tools 2. The essential part cannot be engineered away. It has to live somewhere.

Every API architecture must handle the same set of concerns:

  • Data fetching: who assembles data from multiple sources?
  • Security: where are access controls enforced?
  • Caching: where are responses stored and invalidated?
  • Contract management: who maintains the schema between producer and consumer?
  • Governance: what are the rules for how the system evolves?

These concerns never disappear. They move - between client and server, between infrastructure and application code, between modules in a monolith and services in a microservices architecture, between humans and tooling.

This article traces where each concern lives across four architectural approaches: REST, GraphQL, Federated GraphQL, and an emerging pattern that WunderGraph calls Fission. The goal is not to compare features or declare a winner. It is to map responsibility: where does each concern live, and who owns it?

One important caveat before we begin. REST is often discussed in its idealised form - proper HTTP caching, clean resource modelling, well-maintained OpenAPI specifications. In practice, most REST APIs do not fully achieve this. This is not just my opinion - Postman's State of the API reports consistently show gaps between API design intent and reality 22, and Fielding himself has noted that most APIs calling themselves REST do not actually conform to the architectural constraints he defined 25. That gap is not an indictment of REST. It is a recognition that when any architecture's idealised model meets operational reality, responsibility shifts. Where it shifts to is the question worth asking.


The Scenario

How do API architectures handle data from multiple services?

You lead the engineering team for a product detail page at a large e-commerce company - the single most visited page type on your platform. The page needs product information, pricing, inventory levels, reviews, and shipping availability. This data lives across four backend services, each owned by a different team. You serve two clients: a mobile app showing a condensed view and a web storefront showing everything.

The same five concerns apply regardless of architectural choice. What changes is where each one lives - and who carries it.

We start with REST, where most of these responsibilities begin their journey.


REST

Why does REST push data fetching to the client?

Data fetching lives with the client. The product detail page requires five sequential HTTP calls - one per service. On a slow connection, each round trip adds latency. The mobile app receives 47 fields per response but uses 8. This is the aggregation problem that REST's resource model does not inherently address - what Microsoft's Azure Architecture Centre documents as the "Chatty I/O" antipattern 23.

Responsibility for solving it falls on someone: the client writes orchestration code, the API team builds per-consumer aggregation endpoints, or a Backend-for-Frontend layer absorbs the work. A BFF sits between the client and the backend services - the mobile app makes one call to its BFF, which fans out to all five services server-side, filters the response down to the eight fields the mobile client needs, and returns a single payload. The five round trips collapse into one. But now someone needs to build and maintain that BFF. Usually it falls to the frontend team, who now own a server-side service in addition to their client code. Alternatively, a platform team provides the BFF - but then every consumer change requires a platform team ticket. Either way, you need a BFF per client type, each with its own aggregation logic. And as backend services evolve - new fields, changed response shapes, deprecated endpoints - each BFF becomes brittle, requiring constant maintenance to stay in sync. Phil Calcado formalised the BFF pattern at SoundCloud but warned about its limits: "Trying to derive a single schema that holds a complete-ish model of your data...reminds me too much of an Enterprise Data Model" 5. Martin Fowler echoed: "Beware of a One-Size-Fits-All API in any form" 6. The aggregation work exists. The question is who carries it.

How does REST handle caching and security?

Security lives in the infrastructure layer. WAFs inspect requests by URL pattern. Rate limiting works per endpoint. OAuth scopes map to URL paths. Decades of HTTP security tooling understand this model natively. This is one area where REST's operational reality aligns closely with its idealised form - the infrastructure is mature and widely deployed.

Caching lives in the protocol layer. Roy Fielding made caching a first-class architectural constraint in his 2000 dissertation: "Cache constraints require that the data within a response to a request be implicitly or explicitly labeled as cacheable or non-cacheable" 3. Each REST endpoint carries Cache-Control and ETag headers. CDNs, reverse proxies, and browser caches understand HTTP GET natively. The protocol owns this responsibility.

That is REST at its idealised best. In practice, the picture is less clean. Research reported by Nordic APIs found that 75% of APIs do not conform to their own specifications 4. Cache headers are misconfigured or omitted. Cache lifetimes are guessed at. The idealised story - where infrastructure handles caching automatically - degrades into partial coverage that teams must supplement with application-level caching. When the ideal breaks, caching responsibility shifts from the protocol layer to the application team, partially or entirely.

Why are contract management and governance fragmented in REST?

Contract management lives with the API team, separately from the code. OpenAPI specifications must be written, maintained, and kept in sync with actual server behaviour. When the contract is a separate artifact from the implementation, drift is the default state, not the exception. Keeping them aligned requires governance, tooling, and discipline.

Governance is implicit and fragmented. Each API team sets its own versioning policy, naming conventions, and change processes. There is no central authority deciding how the system evolves - each team governs its own endpoints independently. When aggregation spans teams, governance defaults to whoever builds the aggregation layer.

Gregor Hohpe captures the underlying pattern: "Flexibility brings complexity; decoupling increases latency; distributing components introduces communication overhead" 7.

In REST, responsibility for data fetching, schema accuracy, and governance sits with the teams building and consuming the API. Responsibility for security and caching sits close to the infrastructure. The problems are all present. They live on the client side and with the API team.

Now the responsibility shifts.


GraphQL

How does GraphQL solve over-fetching and shift complexity to the server?

GraphQL moves the centre of gravity from the client to the server by consolidating the API behind a single typed schema. A client describes the data it needs in a single query. The mobile team requests eight fields. The web team requests forty-two. One round trip. No over-fetching. As Lee Byron - co-creator of GraphQL and Executive Director of the GraphQL Foundation - put it: "GraphQL is a trade in the opposite direction - it optimizes for the network" 8.

Data fetching moves to the server. The client's aggregation problem from REST dissolves - but the work does not. The GraphQL server resolves each field independently, calling each backend service per resolver. The N+1 problem migrates: in REST, it manifested as N+1 HTTP round trips on the client. In GraphQL, it manifests as N+1 resolver calls on the server, typically addressed using batching patterns like Facebook's DataLoader 9 10. Responsibility for efficient data fetching has moved from the client to the server team.

Why do caching and security move into the application layer in GraphQL?

Caching moves from the protocol layer to the application layer. GraphQL is protocol agnostic - it can run over HTTP via the GraphQL over HTTP specification , over WebSockets for subscriptions, or over other transports entirely, much like MCP supports both STDIO and HTTP streaming. In practice, most deployments use HTTP POST, which CDNs and reverse proxies cannot cache natively. The network caching infrastructure that REST can leverage becomes unavailable. Caching still happens - normalised client caches, resolver-level caches, persisted queries over GET - but responsibility moves from infrastructure conventions to application developers who build and maintain these layers explicitly 11 12 13.

Most comparisons describe REST caching as automatic and GraphQL caching as absent. In practice, many REST APIs do not properly implement cache headers, and many GraphQL deployments achieve effective caching through persisted queries and normalised client stores. Adobe Experience Manager, for example, serves persisted GraphQL queries over GET - making them cacheable by CDNs and the browser's standard HTTP cache, just like REST endpoints. The operational reality on both sides is more nuanced than the idealised framing suggests.

Security moves from the infrastructure layer to the application layer. REST's per-endpoint rate limiting, WAF pattern matching, and route-level middleware depend on knowing what a request will do based on its URL. GraphQL's single endpoint accepts arbitrary query shapes, which means query depth attacks, batching attacks, and cost analysis all require custom middleware rather than infrastructure configuration 14. Escape's 2024 report found 69% of GraphQL services susceptible to DoS due to lack of resource controls 15. Neither REST nor GraphQL is inherently more or less secure. Security responsibility lives at a different layer.

Why can GraphQL governance become a bottleneck?

Contract management moves from a separate artifact to the implementation itself. The GraphQL specification requires introspection - the schema cannot drift from the implementation because it is the implementation 16. GitHub cited this as a primary motivation for adopting GraphQL: "Type safety, introspection, generated documentation, and predictable responses benefit both the maintainers and consumers" 17. For complex APIs with many consumers, this eliminates an entire category of integration failures. For simple APIs, it introduces more ceremony than REST's implicit contracts.

Governance concentrates on the schema owner. A single team owns the schema - and with it, the rules for how the API evolves. This works until multiple domain teams need to evolve it simultaneously, and the schema owner becomes the bottleneck. As Kin Lane observed, GraphQL "makes some very complex things simpler" but "also makes some very simple things more complex" 18.

No concern disappeared. Each one changed address. Data fetching moved from client to server. Security moved from infrastructure to code. Caching moved from protocol to application. Contract management moved from a separate artifact to the implementation itself. Governance concentrated on a single schema owner.

The problems that lived with the client now live with the server team. But one problem - governance - is about to move again.


Federated GraphQL

Why does GraphQL federation introduce new complexity?

As the schema grows and more teams need to contribute, the single schema owner becomes a bottleneck. Federation restructures governance by distributing schema ownership. Each team owns a subgraph defining their domain types. A gateway/router composes these into a unified schema at build time and routes queries at runtime.

Netflix operates at this scale: 300+ subgraphs, 3,000+ types, powering 50-60 applications with a small gateway/router team. Stephen Spalding explains: federation "removes the business logic from the core aggregator, so that it becomes an appliance, like a reverse proxy. That is something you can scale" 19.

How are data fetching, caching, and security distributed in federation?

Data fetching lives with each subgraph team for their domain, and with the gateway for cross-subgraph orchestration. The gateway decomposes a client query into subgraph fetches, executes them in parallel where possible, and merges results. The orchestration responsibility that lived with the client in REST, and with the monolithic server in GraphQL, now lives with the gateway.

Caching distributes across subgraph boundaries. Each subgraph caches its own resolver responses. The gateway can cache composed responses. Client-side normalised caches must understand entity references that span subgraphs. Cache invalidation becomes a distributed problem.

Entity caching changes this dynamic. When the gateway/router caches resolved entities - a product, a user, a price - by their entity key, caching becomes a platform concern rather than a team-by-team responsibility. Individual subgraph teams no longer need to build and maintain their own caching layers. The platform offers caching as a service, and every team benefits without additional work. This shifts caching from a distributed problem back toward an infrastructure problem - though one that lives at the gateway layer rather than the protocol layer.

Security distributes across layers. Field-level authorisation lives with each subgraph team. Query cost limits, depth restrictions, and authentication live with the gateway. The gateway must trust that dozens of teams enforce policies consistently - or centralise authorisation and accept the governance overhead.

Why does federation create a governance bottleneck?

Contract management lives with each subgraph team for their types, and with the composition system for cross-subgraph compatibility. The schema remains introspectable and self-documenting. But composition introduces a new failure mode: changes in one subgraph can break composition with another. Contract testing and schema validation become essential infrastructure.

Governance - and this is the most significant shift - lives with humans. Discovering who owns which types requires institutional knowledge. Cross-subgraph schema changes require finding owners, scheduling meetings, negotiating entity keys, and tracking approvals. One enterprise with 60+ subgraphs and 25 teams found that a feature spanning three subgraphs took nine weeks from request to production - two days of engineering per team, seven weeks of governance overhead 20. Platform teams in large federations report spending sixty percent of their time as human routers rather than engineers.

The journey continues. Data fetching moved from the client to the server, then to the gateway. Caching distributed across subgraph boundaries. Security distributed across teams. Contract management distributed to subgraph owners with a composition system keeping them aligned. Each of these concerns found a new home.

But governance - the concern that was implicit and fragmented in REST, then concentrated on the schema owner in GraphQL - has now landed on humans. Platform teams, Slack threads, meeting invitations, approval chains. And human governance does not scale linearly.

The responsibility is about to move once more.


Fission

How does Fission change API design from bottom-up to top-down?

An emerging pattern addresses federation's governance bottleneck by inverting the direction of schema design. WunderGraph calls the algorithm Fission - a name borrowed from nuclear physics, where a heavy nucleus splits into smaller parts while releasing energy. The analogy is deliberate: a single consumer-facing schema splits into subgraph responsibilities, and the energy released is engineering velocity previously lost to human governance.

In federation, each team builds a subgraph based on what their service can expose. The supergraph is assembled from below - a side effect of composition, not an intentional design. Field names reflect service internals. Type boundaries match team boundaries. The consumer API is whatever falls out.

Fission starts from the other end. Design the consumer-facing API first - the ideal query a client would write with no technical constraints. Then decompose downward into subgraph responsibilities. The supergraph is the source of truth. Federation's runtime model stays intact; the design process inverts.

This is a return to GraphQL's origins. When Byron and colleagues built GraphQL at Facebook, the premise was consumer-first: the client describes what it needs, the system figures out fulfilment. Federation drifted producer-first over time. Fission recovers the original philosophy at enterprise scale - design like a monolith, implement as microservices.

How does Fission reduce governance overhead?

Governance moves from humans to design-time automation. Fission identifies who owns which types, analyses the impact of proposed changes, propagates entity keys and federation directives, and routes proposals to affected teams. Composition validation happens at design time, before code is written. The platform team stops being human routers.

An eBay architect described the underlying problem: "Ownership, checks, and cross-team negotiations have become one of our biggest velocity bottlenecks" 21. Early adopters report that the barrier to proposing schema changes drops significantly when governance shifts from meetings to tooling 20. The pattern is still emerging - these are early signals, not established benchmarks.

What new complexity does Fission introduce?

Contract management lives in a dual-view system - the supergraph (consumer-facing API) and the subgraphs (what each team implements), kept synchronised by the Fission engine. When a consumer need changes, the tooling derives the subgraph consequences.

Caching, data fetching, and security remain where federation placed them. Fission does not alter the runtime architecture. It addresses the design-time governance layer.

But the conservation law holds. New complexity appears.

Design-time tooling must be maintained, monitored, and trusted. Keeping supergraph and subgraph views synchronised as the graph evolves is a non-trivial engineering challenge. The tooling is young and the pattern is still emerging.

Consumer-first design requires someone to do the design. When APIs are shaped from consumer needs rather than backend structure, someone must understand those needs deeply. This is API product management - a discipline most engineering organisations have not formalised.

Governance must be defined, not just enforced. Naming conventions, design standards, composition policies - automation handles enforcement, but humans decide what to enforce. Automated governance with poorly chosen rules produces consistently bad schemas efficiently.

Responsibility shifted from human governance to design-time tooling, product thinking, and governance definition. Whether this is a favourable trade depends on which of those your organisation is better equipped to handle.

The journey that began with the client - assembling data from five REST endpoints - has passed through the server, the gateway, the organisation, and arrived at design-time automation. At every stage, the same five concerns were present. At every stage, they lived somewhere different.


The Responsibility Map

How do REST, GraphQL, federation, and Fission distribute responsibility?

Across four architectures, the same five concerns appeared. None disappeared. Each one moved.

ConcernRESTGraphQLFederationFission
Data fetchingClientServerGateway + subgraph teamsSame as federation
SecurityInfrastructure layerApplication layerDistributed: gateway + subgraph teamsSame as federation
CachingProtocol layer (idealised); application layer (operational)Application layerDistributed across subgraphs and gatewaySame as federation
Contract managementSeparate artifact (API team)Implementation itselfSubgraph teams + composition systemDual-view system (design-time tooling)
GovernanceImplicit per teamSchema ownerHumans (platform team)Design-time automation

Why doesn't any API architecture eliminate complexity?

If you were to draw each architecture as a shape on a radar chart - one axis per concern, measuring not capability but where responsibility sits - you would see four distinct shapes. REST extends outward on data fetching and contract management, pulls inward on security and caching. GraphQL inverts this. Federation distributes runtime concerns but extends human governance. Fission compresses human governance but extends tooling complexity.

The total area enclosed by each shape is roughly equal. No architecture is smaller. Each one distributes the work differently - across different layers, different teams, different disciplines.


What This Means

Which API architecture is the best choice?

The Postman 2025 State of the API report shows REST at 93% adoption and GraphQL at 33% 22. Those numbers describe coexistence, not competition. Organisations adopt different responsibility distributions for different contexts - often running both simultaneously.

The question was never which technology is better. It was always: given your team's strengths, your consumers' needs, your organisational structure, and your operational maturity - where should each responsibility live?

What is the real question behind REST vs GraphQL?

A reader who finishes this article thinking about REST versus GraphQL has missed the point. The point is: who in your system or organisation is responsible for each problem - and is that where you want the responsibility to be?

Architecture is not about eliminating complexity. It is about deciding where it lives, and who owns it.


How WunderGraph Helps

If you're running federated GraphQL and recognise the governance bottleneck described in this article, WunderGraph builds the platform to address it.

  • WunderGraph Cosmo is the federation platform - the control plane for composing, managing, and operating your federated graph. It handles schema composition, analytics, and the operational concerns that come with running federation at scale.
  • WunderGraph Hub is the design-time governance layer - schema proposals, impact analysis, automated composition validation, and routing changes to the right teams. Hub is where Fission lives, inverting the design process so you start from the use-case and design with intent.

The concerns described in this article don't disappear. WunderGraph moves them to where tooling can manage them - so your engineers can focus on building, not coordinating.


References

[1] Tesler, L. "Law of Conservation of Complexity." (~1984). https://en.wikipedia.org/wiki/Law_of_conservation_of_complexity

[2] Brooks, F.P. "No Silver Bullet - Essence and Accident in Software Engineering." (1986). https://worrydream.com/refs/Brooks_1986_-_No_Silver_Bullet.pdf

[3] Fielding, R.T. "Architectural Styles and the Design of Network-based Software Architectures." PhD dissertation, UC Irvine, Chapter 5. (2000). https://roy.gbiv.com/pubs/dissertation/rest_arch_style.htm

[4] Nordic APIs. "Understanding the Root Causes of API Drift." https://nordicapis.com/understanding-the-root-causes-of-api-drift/

[5] Calcado, P. "Some Thoughts on GraphQL vs. BFF." (2019). https://philcalcado.com/2019/07/12/some_thoughts_graphql_bff.html

[6] Fowler, M. Tweet on BFF and GraphQL. https://x.com/martinfowler/status/1149688467829354501

[7] Hohpe, G. "The Software Architect Elevator." O'Reilly. (2020). https://architectelevator.com/book/

[8] Byron, L. "Interview with GraphQL Co-Creator Lee Byron." Nordic APIs. https://nordicapis.com/interview-with-graphql-co-creator-lee-byron/

[9] Shapton, L. et al. "Solving the N+1 Problem for GraphQL through Batching." Shopify Engineering. https://shopify.engineering/solving-the-n-1-problem-for-graphql-through-batching

[10] Facebook. "GraphQL DataLoader." https://github.com/graphql/dataloader

[11] Sturgeon, P. "GraphQL vs REST: Caching." APIs You Won't Hate. https://apisyouwonthate.com/blog/graphql-vs-rest-caching/

[12] Byron, L. Reactiflux Q&A transcript. https://www.reactiflux.com/transcripts/lee-byron

[13] Giroux, M-A. "GraphQL & Caching: The Elephant in the Room." Apollo GraphQL Blog. https://apollographql.com/blog/backend/caching/graphql-caching-the-elephant-in-the-room

[14] OWASP. "GraphQL Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html โ€” See also: "REST Security Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html

[15] Escape. "The State of GraphQL Security 2024." https://escape.tech/resources/the-state-of-graphql-security-2024

[16] GraphQL Foundation. "GraphQL Specification, Section 4 - Introspection." https://spec.graphql.org/October2021/

[17] GitHub Engineering. "The GitHub GraphQL API." (2016). https://github.blog/developer-skills/github/the-github-graphql-api/

[18] Lane, K. "My GraphQL Thoughts After Almost Two Years." API Evangelist. (2018). https://apievangelist.com/2018/04/16/graphql-thoughts-after-almost-two-years/

[19] Shin, J. & Spalding, S. "How Netflix Scales Its API with GraphQL Federation." InfoQ. https://www.infoq.com/presentations/netflix-api-graphql-federation/

[20] WunderGraph. Customer case study: 60+ subgraphs, 25 teams. 75-87% schema change cycle time reduction, 4x monthly schema proposals after adopting Fission. (2026).

[21] WunderGraph. eBay architect testimonial: "Ownership, checks, and cross-team negotiations have become one of our biggest velocity bottlenecks." (2026).

[22] Postman. "2025 State of the API Report." https://www.postman.com/state-of-api/2025/

[23] Microsoft. "Chatty I/O Antipattern." Azure Architecture Centre. https://learn.microsoft.com/en-us/azure/architecture/antipatterns/chatty-io/

[24] Hamer, N. "The Conservation of Complexity in Software Architecture." Capgemini Engineering. https://capgemini.github.io/architecture/The-Conservation-of-Complexity-in-Software-Architecture/

[25] Fielding, R.T. "REST APIs must be hypertext-driven." (2008). https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven

Ahmet Soormally

Ahmet Soormally

Principal Solutions Engineer at WunderGraph

Ahmet is a Principal Solutions Engineer at WunderGraph, helping teams adopt Cosmo. He leads technical evaluations, builds prototypes, and runs workshops to accelerate adoption, while improving SDKs, documentation, and onboarding to enhance the developer experience.