Designing for Ctrl + Space
Part 1 of Crafting Fluent APIs 15 May 2025Though undoubtedly there are still vi and Notepad users out there, code completion has become the primary way most developers interact with your API. As such, it is worth considering what they see when they hit Ctrl + Space in their IDE of choice.
APIs with a clean, minimal completion surface are easier to use and harder to misuse—especially when designed fluently, using method chaining and step-by-step invocation.
This is Part 1 of a series entitled Crafting Fluent APIs, where I explore various design considerations behind fluent API design. You can find an overview of the series here.
Most developers, when exploring an unfamiliar API, do not start with the documentation. They type an object name, hit Ctrl + Space, and scan the list of suggestions. That list becomes both documentation and user guide.
Each additional option in that list increases cognitive load. The more methods a developer has to scan and distinguish between, the harder it becomes to discover the right one. A key goal in API design, then, is to reduce the number of visible options at any given moment.
When a developer cannot see the forest for the trees, the API is too noisy. Ideally, the completion menu should contain only a few relevant choices at each step.
RestTemplate
Take the RestTemplate
, introduced in Spring Framework 3.
It follows the same template pattern as JdbcTemplate
, JmsTemplate
, and others.
Like those, RestTemplate
exposes a flat surface—dozens of similar-looking methods:
convenience methods per HTTP method (getForObject
, postForEntity
, etc.), as well as generic methods (exchange
and execute
) for more flexibility.
Moreover, each method has three overloaded variants to be able to offer URL template variables as varargs, as a Map
, or to offer the URL as a java.net.URI
.
All of these appear at once, creating an overwhelming set of choices.
To be fair, RestTemplate
did not start with so many methods.
Over time, more were added out of necessity (like patchForObject
), but each new method added surface area and complexity.
Eventually, we made the decision to stop adding features to RestTemplate
.
Every addition meant another overload, and another burden for the user.
WebClient
and RestClient
With Spring Framework 5 and the new reactive stack, we had the opportunity to apply what we had learned.
WebClient
was born, and later, RestClient
for the non-reactive use case.
A key design goal was to limit the number of options offered at each step, using method chaining and narrower return types to guide the developer. The implementation behind this — particularly how return types control visibility — will be covered in a future post.
What used to be a single overloaded method call in RestTemplate
became a chain of calls in RestClient
, each handling one concern in the HTTP exchange.
First, choose the HTTP method:
Then specify the URI.
As with RestTemplate
, multiple variants are supported.
Because the method is POST
, the next step offers options for setting the request body.
If this were GET
, body-related methods would not appear.
Finally, choose how to handle the response:
Compare this to the earlier approach:
String response =
template.postForObject( // method
"https://example.com/post", // URI
"Hello World", // request object
String.class); // response type
Now, using RestClient
:
String response =
client.post() // method
.uri("https://example.com/post") // URI
.body("Hello World") // request object
.retrieve()
.body(String.class); // response type
Each step surfaces only what is relevant at that stage—a much cleaner experience for the developer.
The Broader Lesson
The key difference between RestTemplate and RestClient lies not in what they do, but in how they guide the developer to do it.
Where RestTemplate
exposes a wide, flat surface with dozens of methods and overloads, WebClient
and RestClient
breaks the interaction into smaller, focused steps—each with a limited set of choices.
This narrowing of the completion surface at each stage reduces cognitive load and makes the API easier to explore through code completion alone.
Fluent API design is not just about chaining calls. It is about structuring the journey—helping the developer move through a series of decisions in the right order, with just the information they need, when they need it.
In modern development, code completion is the user’s first interface with your API. Designing that experience deliberately is what separates usable APIs from user-friendly APIs.