Backend Development 24 min read

Best Practices for Designing, Optimizing, Securing, and Maintaining APIs

This guide presents comprehensive best‑practice recommendations for building APIs—defining clear contracts, applying six design principles, optimizing performance through batching, async and caching, enforcing robust security with encryption, signing and rate‑limiting, and ensuring long‑term maintainability via versioning, documentation and structured logging.

Tencent Cloud Developer
Tencent Cloud Developer
Tencent Cloud Developer
Best Practices for Designing, Optimizing, Securing, and Maintaining APIs

Writing a good API may look simple, but the details are often the hardest. This article outlines a complete guide covering what an interface is, how to design it, how to optimise it, how to secure it, and how to maintain it.

1. What is an Interface

An interface is a contract that defines how different systems interact, similar to water pipe standards, USB standards, or API calls between services. Jeff Bezos mandated that all Amazon services communicate via service interfaces, which later evolved into the modern API‑centric cloud architecture.

Key principles from Amazon’s internal policy:

All teams must expose data and functionality through services.

All inter‑team interactions must go through these service interfaces.

No other interaction method (e.g., Excel files) is allowed.

All technologies must obey the above rules.

All service interfaces must be externalised.

Violators are dismissed.

2. Interface Design

Design by Contract (DbC) – define precise pre‑conditions, post‑conditions and invariants for each API. Three guiding questions:

What does the API expect?

What does the API guarantee?

What must remain unchanged?

Six practical design principles :

2.1 Separation of Concerns – keep a single responsibility per endpoint; avoid mixing flags and unrelated parameters. Example of a bad Go interface and a corrected version:

// bad
type UserService interface {
CreateUser(name string, age int) error
GetUser(id int) (User, error)
UpdateUser(id int, name string, age int) error
DeleteUser(id int) error
SendEmail(userID int, message string) error // unrelated
}
// good
type UserService interface {
CreateUser(name string, age int) error
GetUser(id int) (User, error)
UpdateUser(id int, name string, age int) error
DeleteUser(id int) error
}
type EmailService interface {
SendEmail(userID int, message string) error
}

2.2 Self‑expressiveness – clear naming, use common abbreviations (info, del, msg), and make the purpose obvious from the name.

2.3 Mutual Exclusivity (MECE) – avoid overlapping endpoints; each URI should have a single, non‑redundant responsibility.

2.4 Fast Failure – validate inputs early and return errors before deep processing. Example using protobuf validation in Go:

// enum control
enum OperaType {
Get = 0; // get value
Set = 1; // set value
}
OperaType opera_type = 2 [(validate.rules).enum.defined_only = true]; // operation type
// range control
int32 page_num = 3 [(validate.rules).int32.gte = 0]; // page number

2.5 Idempotence – ensure repeated calls have the same effect. Techniques include unique request IDs, state machines, DB unique constraints, optimistic/pessimistic locks, and distributed locks.

2.6 Versioning – use URI prefixes (e.g., /v1/users), custom headers (X‑API‑Version), or query parameters to evolve APIs without breaking existing clients.

3. Interface Optimization

3.1 Batch – use Redis MGET, pipeline, or batch RPC calls to reduce network round‑trips.

3.2 Asynchronous – separate core (synchronous) work from non‑core (asynchronous) work using goroutine pools, message queues, or task schedulers.

3.3 Parallel – split independent tasks and run them concurrently (e.g., two 200 ms tasks become 200 ms total with goroutines).

3.4 Space‑for‑Time – cache frequently accessed data in Redis, use maps for O(1) look‑ups, or add indexes to MySQL.

3.5 Pooling – reuse DB connections, goroutine pools (e.g., ants library), or thread pools.

3.6 Pre‑processing – compute and store heavy results ahead of time for fast retrieval.

3.7 SQL Optimisation – avoid deep pagination, use primary‑key based queries, sub‑queries, deferred joins, or reverse pagination. Example of a deep‑offset query and its optimisation:

// problematic pagination
SELECT * FROM t_records WHERE uid = '{my_uid}' LIMIT X, 30;
// primary‑key based optimisation
SELECT * FROM t_records WHERE id > last_seen_id ORDER BY id ASC LIMIT 30;

3.8 Lock Granularity – lock only the critical section (e.g., directory creation) instead of the whole request.

3.9 Appropriate Storage – switch from MySQL to Redis, Memcached, Elasticsearch, or HBase depending on latency and data size requirements.

3.10 Compression – use protobuf, gzip/deflate/br to shrink payloads.

4. Interface Security

4.1 Encryption – symmetric (AES, DES) for speed, asymmetric (RSA) for key exchange.

4.2 Signing – generate an MD5 (or better, HMAC) signature over request parameters and timestamps to detect tampering.

4.3 Token Authorization – issue JWT or server‑side tokens with expiration; avoid stateful token stores when possible.

4.4 Replay Protection – include timestamp + nonce; reject requests with stale timestamps or repeated nonces.

4.5 Rate Limiting – token bucket, leaky bucket, sliding window, or counter algorithms.

4.6 Black/White List – block malicious IPs/users, whitelist privileged callers.

4.7 Data Masking – hash passwords, mask phone numbers/ID numbers before storage or display.

4.8 SQL Injection Prevention – use prepared statements, ORM, input validation, least‑privilege DB accounts, stored procedures, and hide detailed error messages.

4.9 Permission Control – RBAC checks at login, endpoint, and data‑level granularity.

4.10 HTTPS – enforce TLS for confidentiality, integrity, and server authentication.

5. Interface Maintenance

5.1 Documentation – keep docs in sync with code; generate automatically with tools like Swagger.

5.2 Logging – use appropriate levels (error, warn, info, debug, trace), log method, input, output, traceId, avoid verbose debug in production, and consider asynchronous logging for performance.

By following these guidelines, developers can build APIs that are performant, secure, stable, and easy to maintain.

performance optimizationbackend developmentbest practicessecurityAPI design
Tencent Cloud Developer
Written by

Tencent Cloud Developer

Official Tencent Cloud community account that brings together developers, shares practical tech insights, and fosters an influential tech exchange community.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.