Mahamudul Hasan Rubel
HomeAboutProjectsSkillsExperienceBlogPhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • About
  • Projects
  • Skills
  • Experience
  • Blog
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
ArchitectureJune 22, 20264 min read

API Design Schema Evolution: Managing Changes with Field Projection

API Design Schema Evolution is simpler when you use forward-compatible field projection. Learn how to evolve your REST architecture without breaking clients.

API DesignRESTSchema EvolutionMicroservicesSoftware ArchitectureAPIArchitectureBackendSystem Design

Last month, our team spent three days debugging a cascading failure caused by a "minor" field renaming in our core user service. We had assumed that since the field was optional, the impact would be negligible, but our mobile client’s deserializer choked on the unexpected schema change. It was a classic reminder that API Design is less about the code you write today and more about the debt you avoid tomorrow.

When we talk about Schema Evolution, we usually default to URL versioning—/v1/, /v2/—but that approach creates silos that make maintenance a nightmare. Instead, I’ve found that using forward-compatible field projection allows us to evolve our services while keeping the same endpoint stable.

The Problem with Rigid Contracts

Most REST systems rely on strict JSON schemas. When a field changes—say, moving from a single address string to an address_object—you’re forced to either break your consumers or maintain two separate code paths. I previously explored how API Versioning Strategies: Maintaining Backward Compatibility at Scale can mitigate this, but even with good strategies, you eventually hit a wall where the payload itself becomes the bottleneck.

We first tried simple field aliasing. We kept the old field name in the JSON output, mapping it to the new internal structure. It worked for about two weeks, until we realized that different consumers expected different shapes for the same resource. Our internal dashboard needed the full object, while our public SDKs were still hardcoded for the string.

Implementing Forward-Compatible Field Projection

The solution isn't to version the URL, but to negotiate the schema through field projection. By allowing clients to request exactly what they need, you decouple the internal database model from the external API representation.

In a REST Architecture, this is often implemented via a fields query parameter. Here is a simplified example of how we handle this in a Go-based microservice:

Go
// Simplified projection logic
type UserResponse struct {
    ID      string `json:"id"`
    Email   string `json:"email"`
    Address string `json:"address,omitempty"`
    Details *Addr  `json:"details,omitempty"`
}

// Projection layer
func (u *User) Project(fields []string) map[string]interface{} {
    res := make(map[string]interface{})
    // Logic to map fields dynamically
    // If 'address' is requested, check version or client type
    return res
}

This approach shifts the burden of schema knowledge from the server’s static definition to a dynamic projection layer. When you need to introduce a breaking change, you simply add the new field to the projection logic while keeping the old field available for legacy clients.

Managing Forward Compatibility

Forward Compatibility is the art of ensuring that your API can handle requests from future clients without breaking today's implementation. If a client sends a new, unknown field, your service should ignore it rather than throwing a 400 Bad Request.

I often refer back to REST API Design: Mastering Header-Based Versioning for Clean Evolution when deciding how to signal these changes to the client. By using a custom header—like X-API-Schema-Version—you can inform the server which projection logic to apply without cluttering your URI space.

Why this works:

  1. No Breaking Changes: Old clients keep receiving the "old" schema because they don't request the new fields.
  2. Reduced Payload Size: Clients only fetch what they need, which is a massive win for mobile performance.
  3. Graceful Migration: You can monitor which clients are still requesting legacy fields in your logs and reach out to those specific teams to upgrade.

Common Pitfalls to Avoid

Don't over-engineer the projection layer. If you find yourself writing complex SQL joins for every possible field combination, you've gone too far. We found that limiting projections to top-level fields—and using a caching layer like Redis for the resulting JSON fragments—kept our latency increase to roughly 15ms per request.

Also, be wary of "field explosion." If your API supports 50+ projection options, your testing surface area becomes impossible to manage. We limit our supported projections to a whitelist defined in our API contract.

FAQ

Does field projection make caching harder? Yes, it does. You must include the Vary: X-API-Fields header (or whatever your projection key is) in your HTTP responses to ensure downstream CDNs don't serve the wrong schema to different clients.

Should I use this for every endpoint? Absolutely not. For simple read-only resources, static DTOs are fine. Reserve field projection for high-traffic, rapidly evolving resources where the cost of a breaking change outweighs the complexity of the implementation.

How do I handle nested objects? Keep it flat if possible. If you need nested objects, use a dot-notation in your query parameter (e.g., ?fields=id,user.address.city).

Final Thoughts

I’m still not entirely convinced we have the perfect balance. We still occasionally see edge cases where a client expects a field that we’ve deprecated, even when they’ve explicitly requested the "new" schema. Next time, I’d probably implement a strict deprecation header that warns clients in the Warning HTTP header when they request fields marked for removal.

Ultimately, API Design is a series of compromises. By prioritizing forward compatibility through field projection, you buy yourself the time needed to evolve your system without forcing your users into a constant cycle of breaking migrations.

Back to Blog

Similar Posts

A woman writes 'Use APIs' on a whiteboard, focusing on software planning and strategy.
ArchitectureJune 21, 20264 min read

API Versioning Strategies: Maintaining Backward Compatibility at Scale

Master API versioning and maintain backward compatibility in your distributed systems. Learn pragmatic strategies to evolve your services without breaking clients.

Read more
ArchitectureJune 22, 2026
4 min read

API Idempotency: Implementing Deterministic Correlation IDs for Safety

API idempotency prevents duplicate side effects in distributed systems. Learn how to use deterministic correlation IDs to ensure state consistency during retries.

Read more
A close-up view of PHP code displayed on a computer screen, highlighting programming and development concepts.
ArchitectureJune 21, 20264 min read

REST API Design: Mastering Header-Based Versioning for Clean Evolution

REST API design is often cluttered by versioned URLs. Learn how to use content negotiation to manage API versioning effectively and keep your code clean.

Read more