The Common Recommendation

Most blog posts and online resources recommend extracting Feign call-related DTOs and clients into a single shared module. This approach appears more convenient for usage and enables easy code reuse across projects.

However, this common practice fundamentally misunderstands Feign's architectural role.

The Core Problem: Feign's True Nature

Feign is fundamentally the caller's infrastructure component, not the service provider's API definition carrier.

This distinction is critical for understanding proper architectural boundaries in microservice systems.

Comparative Analysis: Two Approaches

Practice MethodCore IssueRisk Manifestation
Extract to provider's public moduleBidirectional couplingProvider changes interface, all callers must synchronously upgrade; version management chaos, high maintenance costs
Define within caller internallyClear responsibilitiesCaller autonomously controls client definitions, unaffected by provider changes; complies with microservice autonomy principles

Small Team vs. Large Team Dynamics

Small Team Scenario

In small teams, extracting to a shared module确实 facilitates code reuse. Writing Feign calls becomes more convenient, and when interface changes occur (adding or removing endpoints), the Feign module can be updated conveniently alongside.

This works because:

  • Communication overhead is low
  • Coordination is easy
  • Version synchronization is manageable
  • Team members share context

Large Team Reality

In large teams, this extraction approach transforms the Feign module into infrastructure, creating severe problems:

1. Frequent Packaging and Publishing

When different teams make interface changes, they frequently package and publish, constantly modifying version numbers.

2. Downstream Dependency Churn

Other calling teams must frequently modify pom.xml to keep Feign module dependency versions synchronized.

3. Pipeline Maintenance Overhead

If using CI/CD pipelines, additional dependency maintenance becomes necessary.

4. Version Conflict Regression Issues

This极易 (easily) triggers version conflict regression problems.

5. Inconsistent Team Capabilities

Different teams have varying skill levels, leading to:

  • Cloud private repository dependency issues
  • Maven local cache conflicts
  • IDEA cache and dependency conflict bugs

6. Escalating Maintenance Costs

These problems compound, making dependency management maintenance costs extremely high.

Abstract Perspective: Responsibility Boundaries

Service Provider's Responsibility: Implement APIs

Feign's Role: Caller's client mapping

Placing Feign interfaces in the provider's module confuses the boundary between "API definition" and "client invocation", violating the single responsibility principle for modules.

Concrete Impact: From Voluntary to Forced Changes

The Dependency Chain Problem

This approach creates a "provider → caller" dependency where callers depend on API definitions provided by the provider.

When the provider changes interfaces and releases:

Downstream teams, having introduced the Feign interface module, experience immediate build failures upon pulling Git changes. They cannot even start the project without synchronously changing their code.

This destroys each module service's independence, transforming from "can proactively change code" to "forced to change code along with provider".

The Alternative: Runtime vs. Compile-Time Errors

Without extracting to a public module:

When upstream makes interface changes and downstream pulls code, the Feign interface contract changes—but this doesn't cause compile-time build failures (code doesn't "explode red").

At worst, the changed interface calls will fail at runtime, but the project can still start and development can continue normally.

Key Advantage: Teams can schedule adaptation time during regular sprints rather than being forced into immediate emergency fixes.

Additional Benefit: Each service maintains its own client. Developers choose which interfaces to call by writing only the interfaces they need, without importing all client methods.

When Extraction Becomes Acceptable

There are specific scenarios where extracting to a public module becomes reasonable:

Condition 1: Interface Stability

Only when interfaces have stabilized and no longer change frequently should extraction be considered.

Condition 2: Provider Team Transition

When the provider team has moved on to maintain the next project, and the calling team can assume maintenance responsibility for the public module.

Condition 3: Multi-Caller Sharing

When multiple callers share the same interfaces and the interfaces are stable, extraction into an independent caller-shared module becomes viable.

Critical Constraint

Even in these scenarios, dependencies and versions must be strictly controlled.

Summary and Recommendation

Feign, as the caller's declarative HTTP client, should not be extracted into a public module maintained by the service provider.

Stronger Recommendation: Define Feign clients internally within the calling service.

Exception: Only in scenarios with multiple callers sharing stable interfaces should extraction into an independent caller-shared module be considered, with strict dependency and version control.

Architectural Principles at Stake

This recommendation aligns with several fundamental microservice architecture principles:

1. Service Autonomy

Each microservice should maintain control over its dependencies and integration points. External services shouldn't dictate internal client implementations.

2. Loose Coupling

Minimizing direct dependencies between services reduces the blast radius of changes and enables independent evolution.

3. Consumer-Driven Contracts

The consumer (caller) should define what it needs from a service, not have the provider dictate the client structure.

4. Single Responsibility

Modules should have one reason to change. A Feign client module that changes whenever either the provider OR any caller changes violates this principle.

5. Explicit Dependencies

Dependencies should be explicit and intentional. Hidden transitive dependencies through shared Feign modules create fragile systems.

Practical Implementation Guidance

For Service Providers

  1. Publish API documentation (OpenAPI/Swagger, etc.)
  2. Maintain backward compatibility when possible
  3. Version APIs explicitly when breaking changes are necessary
  4. Communicate changes early to downstream consumers

For Service Consumers

  1. Define Feign clients internally within your service boundary
  2. Map external APIs to internal DTOs to insulate from upstream changes
  3. Implement circuit breakers and fallbacks for resilience
  4. Test integration points thoroughly with contract testing

For Shared Libraries (When Necessary)

If extraction becomes necessary due to many callers:

  1. Create an independent module not owned by the provider
  2. Establish clear versioning policies (semantic versioning)
  3. Implement deprecation cycles for breaking changes
  4. Maintain comprehensive changelogs
  5. Provide migration guides for major versions

Conclusion

The convenience of shared Feign modules comes at a significant architectural cost. While tempting for small teams or early-stage projects, this approach doesn't scale and creates long-term maintenance burdens.

By keeping Feign clients within calling services, teams maintain autonomy, reduce coupling, and enable independent evolution—core principles of successful microservice architectures.

The slight inconvenience of maintaining separate client definitions pays dividends in system stability, team autonomy, and long-term maintainability.