- Knowledge
- technology
- OOP
- Tips
- Programming
- Tips
- Tutorial
- SEO
- Ranking
- Knowledge
- Special Day
- Seo
- Bug
- Data science
- Seo
- artificial intelligence
- Machine Learning
- Robotics
- happyNewYear2021
- newYearEve
- 2021
- Automation
- Smart Home
- Career
- Best Practices
- Git
- Logging
- Web Fundamentals
- DNS
- HTTPS
- Performance
- AI Tools
- ChatGPT
- Claude
- Gemini
- Laravel
- Eloquent
- MySQL
- HTTPS
- TLS
- Web Security
- Certificates
- Developer Life
- Debugging
- Docker
- DevOps
- Transactions
- Queues
- LLMs
- AI
- AI Coding
- Developer Tools
- React Native
- Expo
- Kate PMS
- Mobile Apps
- Laravel
- Authentication
- Sanctum
- Cookies
- API Design
- Payments
- Idempotency
- DeepSeek
- Open Source AI
- LLMs
- AI News
- Git
- Version Control
- AI Coding
- Prompting
- PHP
- Checklist
- MCP
- AI Agents
- OpenAI
- Architecture
- Microservices
- Modular Monolith
- Estimation
- Developer Life
- Project Planning
- Humour
- OAuth
- OpenID Connect
- Authentication
- Embeddings
- Vector Search
- RAG
- pgvector
- OpenAI
- GPT-4.1
- Codex CLI
- Events
- Testing
- Clean Code
- Maintainability
- Code Review
- Webhooks
- API
- Security
- Claude Code
- Workflow
- AI
- LLM
- Prompt Injection
- Mobile
- React
- Networking
- TCP
- UDP
- HTTP/3
- CLAUDE.md
- AWS
- Cloud Security
- Backups
- PHPUnit
- Software Engineering
- Leadership
- Communication
- RAG
- Embeddings
- AI Engineering
- IT Infrastructure
- Networking
- Access Control
- CI/CD
- GitHub Actions
- Gemini CLI
- Claude Code
- JavaScript
- Async/Await
- Node.js
- Promises
- Security
- Cryptography
- Passwords
- MySQL
- Database
- Vibe Coding
- Software Quality
- DNS
- Code Reading
- Onboarding
- Productivity
- Background Jobs
- Developer Humour
- Estimates
- Dev Life
- JWT
- o3-mini
- DeepSeek R1
- Rate Limiting
- Kate PMS
- E-Signing
- Audit Trail
- REST
- GraphQL
- API Design
- Laravel 12
- Upgrade Guide
- Open Source
- Self-Hosting
- Task Scheduling
- Cron
- Secrets
- CORS
- PHP
- PHP-FPM
- OPcache
- GitHub Copilot
- Software Architecture
- Engineering
- TypeScript
- JavaScript
- Type Safety
- AI Security
- React Native
- Product Design
- AI Agents
- Kiro
- Queues
- Redis
- RabbitMQ
- AWS SQS
- Nginx
- Apache
- GPT-5
- gpt-oss
- Clean Code
- Architecture
- Naming
- Documentation
- Career
- ADR
- Teamwork
- Supply Chain
- Kate HRM
- HR Software
- Permissions
- System Design
- Pagination
- SSH
- Linux
- Big O
- Databases
- Laravel Boost
- MCP
- Developer Skills
- Validation
- Databases
- Indexes
- Code Quality
- Deployment
- Developer Humour
- Feature Flags
- Code Review
- Pull Requests
- Docker
- Cursor
- Authorization
- RBAC
- Gemini
- Long Context
- PHP 8.4
- Caching
- Dependency Injection
- Web Performance
- Browser
- CSS
- Database
- Migrations
- ChatGPT
- AI for Developers
- Monitoring
- On-Call
- REST
- Backend
- SQL
- NoSQL
- Database Design
- Coding Agents
- Claude 4
- API Resources
- REST API
- Load Balancing
- Scaling
- AWS
- AI Tools
- Claude
- Sora 2
- CTE
- 2FA
- TOTP
- Programming Languages
- Prompts
- Developer Workflow
- API Gateway
- APIs
- Passport
- API Auth
- Learning
- Burnout
- Developer Growth
- Web Development
- SEO
- Kate Mall
- ChatGPT Atlas
- Agent Skills
- Middleware
- Laravel 12
- Collections
- Context Window
- Monitoring
- Commit Messages
- Self Review
- Growth
- Regex
- Programming Basics
- Text Processing
- Database Design
- Normalization
- Linux
- Server Security
- Linux Foundation
- Open Standards
- Legacy Code
- Documentation
- AI Workflow
- File Uploads
- Test Data
- Hashing
- Performance
- Caching
- Enums
- Scope Creep
- Estimation
- Codex
- Gemini CLI
- Timezones
- Carbon
- Bugs
- PHP 8.5
- Gemini 3
- GPT-5.1
- Data Integrity
- Event Loop
- Async
- Opus 4.5
- AI Models
- React
- Forms
- Frontend
- Backups
- AI Images
- DALL-E
- Midjourney
- Race Conditions
- Concurrency
- Legacy Code
- Refactoring
- Senior Engineer
- Scope
- LLM
- CDN
- Web
- Sub-Agents
- Soft Deletes
- Audit Log
- Concurrency
- AI Learning
- NestJS
- AI Evals
- Policies
- SPF DKIM DMARC
- Unicode
- UTF-8
- Knowledge Graph
- Value Objects
- Technical Debt
- Feature Flags
- Laravel Pennant
- Deployment
- Copilot
- Composer
- Dependencies
- Artisan
- Automation
- AWS S3
- Object Storage
- Cloud
- Small Language Models
- Ollama
- Production
- Sessions
- HTTP
- Mentoring
- SQL
- Virtual Machines
- Web Development
- HTTP/2
- QUIC
- Web Performance
- AI Integration
- LLM API
- SOLID
- OOP
- Hosting
- Serverless
- Merge Conflicts
- Temperature
- AI Development
- Reverse Proxy
- Nginx
- Infrastructure
- Verification
- Passkeys
- WebAuthn
- Teams
- Communication
- Stakeholders
- Monorepo
- CI/CD
- Versioning
- JSON Schema
- Livewire
- Inertia
- Meetings
- Distributed Systems
- Privacy
- Full-Stack
- T-Shaped Skills
- Money
- Notifications
- Web Security
- HTTP Headers
- CSP
- Function Calling
- Load Testing
- k6
- Data Extraction
- Debugging
- WebSockets
- SSE
- Real-Time
- Laravel Reverb
- Infrastructure as Code
- Terraform
- Side Projects
- Laravel Pint
- OpenAPI
- Swagger
- UX
- Multimodal
- Jest
- Pair Programming
- APIs
- Rate Limiting
- Resilience
- Dev Humour
- Design Tokens
- JWT
- API Keys
- Sessions
- PHPStan
- Rector
- Incidents
- Reporting
- Dashboards
- Zero Trust
- IAM
- Search
- Laravel Scout
- Junior Developers
- Mentoring
- Images
- WebP
- AVIF
- Bug Reports
- Let's Encrypt
- Design Docs
- Software Design
- Observers
- Replication
- Accountability
- Data Structures
- Reliability
- LLM Memory
- Error Handling
- Payments
- Payment Gateway
- Webhooks
- PCI DSS
- Observability
- OpenTelemetry
- Personal Brand
- Writing
- Conventions
- Dates
- Scheduling
- Disaster Recovery
- Compression
- Brotli
- Deadlines
- Developer Habits
- State Machines
- Tech Roles
- UUID
- ULID
- Horizon
- Planning
- Engineering Culture
- Ownership
- Soft Skills
- Socialite
- Cost Control
- Collations
- Unicode
- Octane
- PostgreSQL
API Design Mistakes I See Over and Over (and How to Fix Each One)
About Post
An API is a promise. The moment a mobile app ships against it, every field name, status code and quirk is frozen in somebody's code, often on phones you can't force to update.
That's what makes API mistakes so expensive. A bad internal function can be refactored on a Tuesday afternoon. A bad API response shape lives as long as the oldest app version still installed. Here are the mistakes I see over and over, in reviews, in third-party APIs I've had to integrate with, and in my own early work. For each one: what it looks like, why it hurts, and the fix.
Mistake 1: Inconsistent naming
What it looks like: one endpoint returns created_at, another createdAt, a third creation_date. Some URLs are /tenants, others /getTenantList.
Why it hurts: every consumer has to look up every field, every time. Mapping code fills up with special cases, and bugs hide in them.
The fix: pick conventions and write them down. Plural nouns for collections (/tenants, /tenants/42), HTTP methods for actions instead of verbs in URLs, one casing style for fields, one format for dates (ISO 8601 with a timezone, like 2025-05-14T09:12:43Z). Then enforce it in code review. Consistency matters more than which convention you pick.
Mistake 2: No versioning plan
What it looks like: /api/contracts, with no version anywhere, and a change that renames amount to total. The web app is fine because it deployed at the same time. The mobile app in the store breaks on the spot.
Why it hurts: you can't update every client at once. Mobile apps especially live on old versions for months.
The fix: version from day one (/api/v1/... is the simplest and most visible), and treat breaking changes as a new version. Most changes don't need one: adding a field is safe, removing or renaming one isn't. Prefer additive changes and you'll rarely need a v2 at all.
Mistake 3: Leaking your database through the API
What it looks like:
public function show(Tenant $tenant)
{
return $tenant; // every column, every hidden flag, straight out
}
Why it hurts: your API is now your schema. Rename a column and you break clients. Add a sensitive column (an internal note, a verification token) and it's public by default unless someone remembered $hidden. Auto-increment IDs also leak information: anyone can guess that /invoices/1043 exists and roughly how many invoices you've issued.
The fix: return a deliberate shape, not a model. In Laravel that's an API resource, where every field is chosen on purpose. For public-facing identifiers, consider UUIDs or ULIDs (Laravel's HasUuids and HasUlids traits make this easy). And remember that unguessable IDs are not authorisation: every endpoint still needs a policy check.
Mistake 4: Wrong status codes
What it looks like:
HTTP/1.1 200 OK
{ "success": false, "error": "Contract not found" }
Why it hurts: HTTP clients, retry logic, monitoring and caches all read the status code. A 200 that means "failed" fools every one of them. Your error rate dashboard says everything's fine while users stare at errors.
The fix: use the codes that exist for exactly this. The short list covers most APIs:
| Code | Meaning |
|---|---|
200 / 201 / 204 | OK / created / done, nothing to return |
400 | The request is malformed |
401 | Not authenticated (who are you?) |
403 | Authenticated, but not allowed |
404 | Doesn't exist (or you're not allowed to know it exists) |
409 | Conflicts with the current state (already paid, already signed) |
422 | Validation failed |
429 | Too many requests, slow down |
500 / 503 | Our fault / temporarily unavailable |
The 4xx versus 5xx split matters most: 4xx says "fix your request", 5xx says "try again later". Clients make retry decisions on that difference.
Mistake 5: No pagination
What it looks like: GET /api/payments returns every payment ever recorded. It's fast in development with twenty rows. In production it times out, or worse, quietly loads tens of thousands of models into memory.
Why it hurts: lists only grow. And adding pagination later is a breaking change, because clients assumed they got everything.
The fix: paginate every list endpoint from the first version, with a sensible default and a maximum page size. Laravel's paginate() or cursorPaginate() plus an API resource collection gives you the data, links and meta for free. Cursor pagination is the better fit for long, constantly changing lists such as feeds or activity logs.
Mistake 6: Errors nobody can act on
What it looks like: {"error": "Invalid request"}. Invalid how? Which field? Is it worth retrying?
Why it hurts: the mobile developer can't show a useful message, so the user sees "Something went wrong" and calls support.
The fix: one error format for the whole API, with a human message, a stable machine-readable code, and field-level details for validation:
{
"message": "The move in date field must be a date after today.",
"code": "validation_failed",
"errors": {
"move_in_date": ["The move in date field must be a date after today."]
}
}
Laravel already returns message and errors with a 422 for validation, so you're most of the way there. Make your other errors follow the same shape. If you want a formal standard, RFC 9457 ("Problem Details for HTTP APIs") defines one.
The test I use: could a developer who has never seen your codebase build a client from your responses alone, and handle every error sensibly? If they'd need to ask you questions, the API isn't finished yet.
The quick checklist
- One naming convention, written down and enforced.
- A version in the URL, and additive changes by default.
- Deliberate response shapes, never raw models.
- Honest status codes.
- Pagination on every list, from day one.
- One error format, with codes clients can act on.
Which API mistake has cost you the most time, either in your own API or in one you had to integrate with? The "200 with an error inside" one still haunts me.

Be first to comment it...