Друкарня від WE.UA

What Makes a Good API? The Design Principles Nobody Talks About Enough

Let me ask you something. When was the last time you used an API and thought — *wow, this just works*?

Not often, right?

Most APIs are functional. They do the job. But truly well-designed APIs are rare. The ones where the naming makes sense, the errors are actually helpful, the docs match reality, and you can get something built without spending half a day in a Slack channel asking "wait, what does this field actually mean?"

That gap — between functional and genuinely good — comes down to design principles. And not the abstract, academic kind. The ones that show up in pull request reviews, in API gateway logs at 2am, and in the DMs of frustrated developers trying to integrate your product.

I've spent a lot of time on both sides of this — building APIs and consuming them. Here's what I've actually learned. And if you want a structured reference to go alongside this, the [API design principles guide from Keploy](https://keploy.io/blog/community/api-design) is a genuinely useful read.

---

## Start With the "Why" Before the "What"

Most API design mistakes don't happen at the code level. They happen before a single line is written, when nobody stopped to ask: *who is actually going to use this, and what do they need to do?*

An internal API used by three backend services has completely different needs from a public API consumed by hundreds of external developers. A mobile-first API needs to think about payload size and latency in ways a server-to-server API doesn't.

Before you design anything, write down:
- Who are the consumers of this API?
- What are the top 5 things they'll actually try to do?
- What data do they need, and in what shape?

Design flows naturally from those answers. Skip them and you're guessing.

---

## RESTful Doesn't Mean Anything Goes

REST is commonly misunderstood. A lot of APIs call themselves RESTful but are really just JSON over HTTP with no real structure.

True REST — or at least, practically good REST — means:

**Resources, not actions.** Your URLs should represent things (nouns), not operations (verbs). `/users/42/orders` is a resource. `/getOrdersForUser?id=42` is not.

**Statelessness.** Every request should contain everything the server needs to fulfill it. No hidden session state on the server side. This makes your API more predictable and much easier to scale.

**Uniform interface.** Every endpoint should behave in a way that's consistent with the others. If you paginate one list endpoint with `?page=`, all list endpoints should do the same.

These aren't just academic rules. They directly affect how easy your API is to understand, cache, and build against.

---

## Naming Things Is Hard, But There Are Rules

There's no perfect naming convention. But there are a few rules that will save you from the most common disasters:

**Use nouns for resources, verbs for actions.** `/invoices` not `/getInvoices`. `/invoices/42/send` is acceptable for an action that doesn't map neatly to CRUD.

**Be plural and consistent.** `/users` not `/user`. Pick plural and use it everywhere.

**Don't leak your implementation.** `/api/v1/mysql_users_table` is not a resource name. It's an implementation detail that will embarrass you in six months.

**Lowercase, hyphen-separated for URLs.** `/user-profiles` not `/UserProfiles` or `/user_profiles`. URLs are case-sensitive in many environments and camelCase in a URL just looks wrong.

---

## HTTP Status Codes: Use Them Correctly

This is one of those things that feels pedantic until you're debugging a production issue and your monitoring tool is treating a `200 OK` with `{ "error": "Not found" }` in the body as a success. Because technically, it is.

Use the right codes:

| Code | When to use it |
|------|----------------|
| `200 OK` | Successful GET, PATCH, DELETE |
| `201 Created` | Successful POST that created a resource |
| `204 No Content` | Success with no response body |
| `400 Bad Request` | Client sent invalid data |
| `401 Unauthorized` | Not authenticated |
| `403 Forbidden` | Authenticated but not allowed |
| `404 Not Found` | Resource doesn't exist |
| `422 Unprocessable Entity` | Validation failed |
| `429 Too Many Requests` | Rate limit hit |
| `500 Internal Server Error` | Your problem, not theirs |

The difference between `401` and `403` alone has confused more developers than it should. `401` means "tell me who you are." `403` means "I know who you are, and the answer is no."

---

## Error Responses Deserve as Much Thought as Success Responses

When I review API designs, I spend more time looking at the error responses than the happy path. Anyone can return a 200 with some JSON. The real design shows up when things go wrong.

A good error response has:
- The correct HTTP status code
- A stable, machine-readable error code string (not a number, those change)
- A clear human-readable message
- Context about which field or parameter caused the problem, if applicable

Here's what that looks like in practice:

```json
{
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "One or more fields failed validation.",
    "details": [
      {
        "field": "email",
        "issue": "Must be a valid email address."
      },
      {
        "field": "date_of_birth",
        "issue": "Cannot be a future date."
      }
    ]
  }
}
```

Compare that to `{ "error": 1042 }`. One of those saves a developer twenty minutes. The other makes them file a support ticket.

---

## Idempotency Is a Gift You Give Your Clients

Idempotency means that making the same request multiple times has the same effect as making it once.

GET requests are naturally idempotent — call them a hundred times, nothing changes.

But what about POST? If a client sends a payment request and the connection drops before they get a response, should they retry? If they do, will they double-charge the customer?

This is where idempotency keys come in. Let clients pass a unique key with their request (`Idempotency-Key: abc-123`). If you've already processed that key, return the cached response instead of executing again.

Stripe does this brilliantly. It's a small thing to implement and it prevents a whole category of nasty bugs in client applications.

---

## Think About Backward Compatibility Before You Merge

Every time you change a public API, you're potentially breaking someone's integration. That someone might be a paying customer. Or a team inside your own company that didn't get the memo.

Changes that break backward compatibility:
- Removing a field from a response
- Renaming a field
- Changing a field's data type
- Making an optional parameter required
- Changing the behavior of an existing endpoint

Safe changes (additive only):
- Adding new optional fields to a response
- Adding new optional request parameters
- Adding new endpoints

When you need to make a breaking change, the answer is versioning — not crossing your fingers and hoping nobody notices.

---

## Security Isn't a Feature, It's a Foundation

A few non-negotiables:

**Always use HTTPS.** There is no excuse for an HTTP-only API in production. None.

**Never put secrets in URLs.** API keys in query parameters get logged in server access logs, browser history, and proxy caches. Use headers.

**Validate everything on the server side.** Client-side validation is for UX. Server-side validation is for correctness and security. Never trust input.

**Scope your tokens.** An API key should only have access to what it actually needs. A read-only integration shouldn't get a token with write permissions.

**Log authentication failures.** If someone is hammering your API with bad credentials, you want to know about it.

---

## Rate Limiting, Throttling, and Being a Good Host

Think of rate limiting like traffic management. Without it, one bad actor — or just one poorly written retry loop — can bring everyone else's experience down.

Beyond just implementing limits, communicate them:

```
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 743
X-RateLimit-Reset: 1716489600
```

These headers let clients manage themselves. A well-behaved client will back off before hitting the limit. A `429` with a `Retry-After` header tells them exactly when to try again.

Also: don't make your limits a secret. Document them prominently. Developers want to know what they're working with.

---

## Documentation Is Part of the API

I'll say that again because it gets glossed over: **documentation is part of the API.**

An API with perfect design but no docs is nearly unusable for anyone who didn't write it. And an API with decent design and great docs will beat it every time.

Good docs include:
- Authentication setup (with a working example)
- Every endpoint, with request and response examples
- All error codes, explained
- Rate limits and quotas
- A changelog with version history
- A quick start guide that gets someone to their first successful call in under 10 minutes

Use OpenAPI/Swagger to generate the reference docs automatically from your spec. That way they stay in sync with the actual API — unlike hand-written docs that go stale the moment someone merges a PR.

---

## The One Question to Ask Before You Ship

Before you publish an API endpoint, ask yourself: *could a developer who has never spoken to me figure out how to use this correctly, just from the docs and the response structure?*

If the answer is no — if it requires tribal knowledge, a Slack conversation, or a support ticket — then it's not done yet.

The best [API design principles](https://keploy.io/blog/community/api-design) all point toward the same north star: the developer on the other end of your API should be able to move fast, build confidently, and not need to reverse-engineer your intent.

That's the standard. It's worth building to.

---

*APIs are products. The people using them are your users. Design accordingly.*

Статті про вітчизняний бізнес та цікавих людей:

Поділись своїми ідеями в новій публікації.
Ми чекаємо саме на твій довгочит!
MK
Marcus Keenton@marcuskeeton

2Довгочити
5Перегляди
На Друкарні з 27 квітня

Більше від автора

Це також може зацікавити:

  • Procurement Management Software At ZYNO By Elite Mindz

    Enhance your business efficiency with advanced Procurement Management Software at ZYNO by Elite Mindz. Simplify supplier management, automate purchase orders.

    Публікація містить описи/фото насилля, еротики або іншого чутливого контенту.

    Теми цього довгочиту:

    Technology
  • How to find the right heating system installer

    A heating device consists of more than just mechanical components. It also contains software that makes it what it is—a highly efficient, complete, and adjustable heating system that can even be controlled remotely.

    Публікація містить описи/фото насилля, еротики або іншого чутливого контенту.

    Теми цього довгочиту:

    Technology

Коментарі (0)

Підтримайте автора першим.
Напишіть коментар!

Це також може зацікавити:

  • Procurement Management Software At ZYNO By Elite Mindz

    Enhance your business efficiency with advanced Procurement Management Software at ZYNO by Elite Mindz. Simplify supplier management, automate purchase orders.

    Публікація містить описи/фото насилля, еротики або іншого чутливого контенту.

    Теми цього довгочиту:

    Technology
  • How to find the right heating system installer

    A heating device consists of more than just mechanical components. It also contains software that makes it what it is—a highly efficient, complete, and adjustable heating system that can even be controlled remotely.

    Публікація містить описи/фото насилля, еротики або іншого чутливого контенту.

    Теми цього довгочиту:

    Technology