Skip to main content

Command Palette

Search for a command to run...

Your API Can Work Perfectly and Still Deliver a Bad Developer Experience

What building, testing, documenting, and troubleshooting a small API taught me about Developer Experience.

Updated
•8 min read•View as Markdown
Your API Can Work Perfectly and Still Deliver a Bad Developer Experience
V
Full-stack & AI engineer. I write about multi-agent systems, LLM reliability, and what actually breaks when a model call meets production.

An API can return the correct response.

The tests can pass.

The server can be running.

And the developer can still have a terrible experience.

That was the surprising part of building my latest project.

I started with a simple goal: build a small Express API.

But instead of stopping when the API worked, I decided to approach it from a different perspective:

What happens if I pretend I have never seen this project before?

I followed my own README.

I ran the commands.

I copied the examples.

I tried valid requests.

I tried invalid requests.

I looked at the errors.

And I tested whether the documentation actually matched reality.

That changed the project completely.

It stopped being just an API project.

It became a Developer Experience experiment.


The API Wasn't the Real Problem

The API itself was intentionally small.

It exposes:

GET  /hello
POST /hello

A successful request to GET /hello returns:

{
  "message": "Hello, Developer!"
}

The POST endpoint accepts:

{
  "name": "Virginia"
}

and returns:

{
  "message": "Hello, Virginia!"
}

If the developer sends an invalid request, the API responds with:

{
  "error": "Name is required"
}

Nothing complicated.

And that's exactly why it was useful.

With a small API, it becomes much easier to notice the friction around the API itself.


Then I Tried to Use My Own Documentation

This was where things became interesting.

I stopped thinking like the person who built the project.

Instead, I tried to think like someone discovering it for the first time.

The first question was simple:

Can I get from a fresh clone to a working API without guessing?

The answer was initially: not quite.

I found several problems.

1. The documented development command didn't exist

The README instructed developers to run:

npm run dev

But the project didn't initially have a dev script.

The documentation was telling the truth about what I wanted developers to do.

It just wasn't telling the truth about what the project could actually do.

That distinction matters.

A documentation page can look perfectly reasonable while describing a workflow that fails the moment someone follows it.

I fixed this by adding the development script:

{
  "scripts": {
    "dev": "tsx watch src/server.ts",
    "test": "vitest"
  }
}

Now the documented command and the actual project behavior matched.


2. The API worked, but the example didn't work for my environment

The original API example used Unix-style curl syntax.

I was testing the project in Windows PowerShell.

The API wasn't broken.

The request example was.

That sounds like a small distinction.

For a developer trying to get started, it isn't.

I replaced the example with a PowerShell-friendly request:

Invoke-RestMethod -Uri "http://localhost:3000/hello" -Method Post -ContentType "application/json" -Body '{"name":"Virginia"}'

This taught me something I hadn't fully appreciated before:

Documentation is experienced through an environment.

A technically correct command is not automatically a useful command.

The shell matters.

The operating system matters.

The developer's context matters.


3. The failure path mattered just as much as the happy path

It is easy to document the successful request:

{
  "name": "Virginia"
}

But what happens when the developer doesn't send name?

Or sends the wrong type?

For example:

{}

The API responds:

{
  "error": "Name is required"
}

That response is part of the API's developer experience.

It tells the developer:

  • something went wrong
  • which field caused the problem
  • what the API expects

This changed how I think about documentation.

I used to think of documentation as something that sits beside the product.

Now I see it as something that works together with the product.

The API response explains one part.

The documentation explains another.

The troubleshooting guide handles another.

Together, they create the developer journey.


Tests Became Documentation Insurance

Manual testing is useful.

But I didn't want to rely on:

"I tried it once and it worked."

So I added automated API tests with Vitest and Supertest.

The final suite checks four scenarios:

  1. Successful GET /hello
  2. Successful POST /hello
  3. Invalid name
  4. Missing name

The result:

Test Files  1 passed
Tests       4 passed

That gave me something important.

The documentation wasn't just describing what I thought the API did.

I had automated checks verifying the behavior.

This led me to a principle I want to carry into future documentation work:

Tests can protect documentation from silently becoming fiction.

If the API changes and the documented behavior is no longer true, something needs to catch that drift.


Then I Added OpenAPI

Once the API behavior was stable, I documented the API using OpenAPI.

The specification describes:

  • GET /hello
  • POST /hello
  • request body requirements
  • successful responses
  • validation errors
  • operation IDs
  • the development server

I then validated the specification with Redocly.

The result was more than a Markdown page.

The API had a machine-readable contract.

That matters because API documentation has different audiences.

A developer might want a quick example.

A documentation engineer might want a structured specification.

A tooling system might want machine-readable API information.

An API specification can support all three.


Documentation Became Part of the Development Workflow

At this point, the project had moved beyond:

Build an API.

It had become:

Build an API and make the experience around that API trustworthy.

I added:

  • a QuickStart
  • API usage examples
  • validation documentation
  • troubleshooting guidance
  • a Developer Experience audit
  • automated tests
  • OpenAPI documentation
  • Redocly validation
  • GitHub Actions CI
  • a case study documenting the investigation

The documentation itself became part of the engineering workflow.

Changes could be reviewed through pull requests.

The OpenAPI specification could be validated in CI.

The documentation could be compared against actual API behavior.

That is where docs-as-code started becoming much more real to me.

It isn't simply putting Markdown files in Git.

It is treating developer-facing information as something that should be:

written → reviewed → tested → maintained → verified


The Developer Experience Loop

The biggest lesson from this project wasn't a particular tool.

It was a workflow.

I started thinking about Developer Experience as a continuous loop:

Build
  ↓
Document
  ↓
Use
  ↓
Find friction
  ↓
Improve
  ↓
Test
  ↓
Verify
  ↓
Repeat

This changes the question you ask.

Instead of:

"Did I write the documentation?"

You start asking:

"Can a developer actually make progress with it?"

That's a much harder question.

And a much more useful one.


What I Would Check Now Before Calling Documentation "Done"

For a developer-facing API, I would now ask:

Setup

Can someone clone the project and understand what to do next?

QuickStart

Do the documented commands actually work?

Examples

Can developers copy and run the examples in their environment?

API behavior

Do the examples match the actual API?

Errors

Can developers understand what went wrong?

Troubleshooting

If something fails, is there a clear path forward?

Tests

Is important documented behavior protected by automated tests?

API specification

Does the OpenAPI definition match the implementation?

CI

Can changes be validated automatically?

Verification

Has someone actually followed the documentation from the developer's perspective?

These questions are simple.

But together, they reveal a lot.


The Most Important Shift

The project changed how I think about technical documentation.

I don't want to only ask:

"Is this documentation accurate?"

I want to ask:

"Does this documentation help the developer succeed?"

Those are related questions.

But they aren't the same question.

A page can be technically accurate and still be difficult to use.

A command can be correct but fail in someone's shell.

An API can return the right status code but provide an unhelpful error.

A README can explain everything and still leave a new developer wondering:

"Okay... what do I do now?"

That gap is where Developer Experience lives.


What This Project Proved to Me

This small API gave me a practical way to explore something much bigger.

Developer Experience isn't one feature.

It isn't just documentation.

It isn't just error messages.

It isn't just onboarding.

It is the combination of all the small interactions that determine whether a developer can move forward.

And sometimes the best way to discover those problems is surprisingly simple:

Become the developer.

Clone the project.

Read the README.

Run the command.

Copy the example.

Break the request.

Read the error.

Try again.

Then fix what made the journey harder than it needed to be.


Final Takeaway

I started this project thinking I was building an API.

I ended up learning that I was really building an experience around an API.

The code has to work.

The documentation has to work.

The examples have to work.

The errors have to help.

The tests have to verify.

And the whole journey has to make sense.

That is the standard I want to carry into future work with APIs, SDKs, AI integrations, and developer tools.

Because developers don't experience your codebase directly.

They experience the path you create around it.

And that path is part of the product.

Build it. Use it. Break it. Document it. Improve it. Verify it.