I once got paged at 2 AM because a partner integration broke when we added a new field to a response object. The field was optional, backwards compatible, and documented. But their mobile app crash-looped anyway. That's when I realized something most API designers don't really understand: your API isn't just an interface—it's a contract, and broken contracts cost you money and sleep.
Over the past decade, I've watched companies ship APIs that technically work but are a nightmare to integrate with. They fail not because of missing features, but because of thoughtless decisions made in the first 48 hours of design. The sad part? These are preventable mistakes.
The Hidden Cost of Mediocre API Design
Let's talk numbers. A 2023 study from Postman found that 38% of developers spend more than a week debugging API integration issues that weren't actually bugs—they were design inconsistencies. In Vietnam's booming tech ecosystem, where startup bandwidth is already stretched thin, this tax is especially brutal. I've watched Hanoi-based fintech startups burn through their integration budget because they had to reverse-engineer three different API behaviors across departments.
Here's what most people get wrong: they treat API design as something you do after you've built the backend. Wrong. Good API design happens first, then you build the backend to support it.
The Real Principles, Not the Marketing Version
RESTful doesn't mean using HTTP verbs correctly (though that helps). It means building APIs that are intuitive enough that developers don't need to call your support team.
Consistency Is Non-Negotiable
The biggest red flag I see is inconsistency. One endpoint returns user_id, another returns userId, and somehow the third one returns id with no context. Your developers will hate you—not in an abstract way, but in the specific, personal way that comes from debugging at midnight because the API contract is a lie.
Share this post
Related Posts
Need technology consulting?
The Idflow team is always ready to support your digital transformation journey.
Use a style guide. Enforce it. If you're using snake_case in responses, use snake_case everywhere. If you're returning timestamps as ISO 8601 strings, don't randomly return Unix timestamps in one endpoint. Use tools like Spectacle or Swagger UI to catch these inconsistencies before they ship. Most of the time, nobody notices consistency until it's broken.
Versioning Is About Managing Breakage
I've seen engineers argue that API versioning is "unnecessary if you're backwards compatible." This is the mindset of someone who hasn't maintained an API for five years. You will break things. Not because you're careless, but because requirements change in ways you can't predict.
Version by URL path (/v1/users, /v2/users), not by header. Headers are invisible to proxy layers, load balancers, and half the monitoring tools you'll use. I've debugged production issues for hours because version headers were being stripped somewhere in the infrastructure. Path-based versioning is ugly, but it's honest.
Most importantly: set an explicit sunset date for old versions. If you're still supporting v1 of your API in 2025, you're harboring technical debt that should have died years ago. At Idflow Technology, we've found that deprecating old API versions on a predictable schedule (we use 18-month deprecation windows) actually improves the ecosystem—partners upgrade, you reduce support burden, and everyone benefits.
Pagination Is More Subtle Than It Looks
Cursor-based pagination seems technically pure, but offset-based pagination is more forgiving in real-world scenarios. The trade-off: offset-based gets inconsistent under concurrent modifications (your page might skip records), while cursor-based requires clients to handle encoded cursors and can't jump to arbitrary pages.
Use offset-based pagination for read-heavy operations. Use cursor-based for write-heavy or real-time feeds. And please, include total count in the response when you can—developers will ask for it eventually, and retrofitting it is a breaking change.
Error Responses Should Tell a Story
I've inherited APIs that return {"error": "Invalid request"} for everything. Invalid request because what? The field format? Missing permission? Rate limited? Your API consumers will either call you constantly or build fragile workarounds.
Return consistent error shapes with semantic HTTP status codes. Use 400 for client errors, 401 for auth failures, 403 for permission issues, 429 for rate limiting, and 500 for server errors. Include an error code (INVALID_EMAIL_FORMAT, not just INVALID_REQUEST) and a human-readable message. Include a URL to documentation if possible.
This is the difference between "my integration is broken" and "my integration is broken because of rate limiting and will retry in 45 seconds."
The Things Nobody Teaches You
Default limits matter more than you think. If you ship pagination with a default limit of 1000 records, someone will build a feature that depends on fetching all records at once. Then they'll scale to 100K records and their integration will silently break. Set reasonable defaults (10-50 items) and make the behavior explicit.
Timestamps are harder than they look. Always use UTC, always use ISO 8601, and always include timezone information. Storing 2025-03-24T14:30:00+07:00 is better than 2025-03-24T14:30:00. Half your integrations will be in different timezones, and leap seconds will eventually be your problem.
Batch operations look like optimization but introduce complexity. Yes, bulk endpoints are faster. But they're also harder to version, harder to error-handle, and harder to understand. Most of the time, a well-designed paginated list operation is sufficient. If you need bulk, be explicit about partial failures (some items succeed, some fail) and how clients should retry.
Validation: Your First Line of Defense
Validate early, validate everywhere, and fail fast. When a client sends invalid data, return 400 immediately with specific guidance on what's wrong. Don't accept the request, queue it for async processing, then fail silently. That's how production outages happen.
Use JSON Schema or similar validation frameworks. Document your constraints. If a username must be 3-20 characters and can only contain alphanumerics, say so explicitly and reject requests that violate it at the API boundary.
---
The difference between an API that works and an API that scales is empathy for the people consuming it. Every field, every response code, every edge case is a communication channel. Use it wisely.
Well-designed APIs don't need a large support team. They don't generate stack overflow posts complaining about inconsistencies. They just work, predictably, across a thousand different use cases you'll never anticipate.