"Low coupling, high cohesion", "information hiding": Well known design principles. Nevertheless, they are not taken seriously in many software architectures, especially when it comes to the interaction between the frontend and the backend.
By providing API systems decide which information they expose to the outside world and which information they keep secret (Information hiding).
Why is that important? Because the broader an API is the more expensive it is to maintain. Think about the extreme of exposing every implementation detail. Every change in the system might break the clients which use the code. That's the reason why we strive for having small APIs. I wrote a post about this topic in the context of Event Sourcing.
Regardless of its size an API prevents us from evolving our system in a free manner. We always have to document the API carefully and consider not to break our clients. Changes have to be introduced in a backward compatible way or we have to align the deployments of our systems which quickly ends up in a deployment monolith hell.
But why do systems provide so many APIs, if it's so hard to maintain them? One reason might be that the API is the system's Unique Selling Point. The API is the product und you earn money by providing this API. In this case it's worth the efforts to document it, to use techniques like hypermedia and care about backward compatibility. Your clients will appreciate it and your API product shines.
I argue that those API products are rare. Way more often the existence of a broad API is a sign for badly chosen boundaries - a violation of the "Low coupling - High cohesion" principle.
While this investigation applies to backend-to-backend communication, too, I would like to focus on the frontend. I question that we need an API to connect the backend and the frontend.
It's natural that the backend and the frontend are highly cohesent, because whenever some new data needs to be displayed on the frontend the backend needs to be adjusted. So why do we try to decouple them by putting a wall (an API) in between them?
Most of the time such a design is driven by organizational or technical circumstances. There are dedicated backend and frontend teams, but I wonder if the overhead of maintaining an API is always taken into account when deciding to separate the teams.
But... using such a library does not necessitate an API! Think about delivering the service as a self-contained system, including the backend AND the frontend. Sure, you have a technology and environment break as the backend code is executed server-side and the frontend code is executed in the user's browser. This break requires the usage of REST or GraphQL in order to synchronize both environments.
I call this a bridge rather than an API as you can evolve the backend services hand in hand with the frontend. There is no need to document an API carefully or to deal with breaking changes, because there is just one client out there. And this client is well-known, because it's located in the very same system. The consequence is that the backend and frontend are always deployed as a whole and those documentation and compatibility challenges disappear.
Technology should not be the driver for system design. Regarding the interaction between the backend and the frontend take the requirements into account and decide if you want to go for a monolithic frontend or self-contained systems. "Low coupling - high cohesion" is a powerful guideline!
If you decide for a self-contained system it's up to you to choose a server-side or client-side rendering approach. Although CSR and SPA (Single Page Application) often are lumped together you can use CSR while still having a per-module frontend.
I had the idea of writing this post while listening to the SoftwareArchitekTOUR Podcast - Episode 82 (German) with Stefan Tilkov and Eberhard Wolff. Thanks for the inspiration!