~/wiki

Interface Consistency

Confiance : high
interface-consistencysoftware-architectureapi-designcontract-programmingrag-systemstype-safetyproduction-systemssystem-reliabilityinterface-contractspolymorphism

Fundamental software architecture principle requiring that components implementing the same interface return compatible data types and follow identical behavioral contracts. Critical for system reliability and maintainability, particularly in complex systems like RAG pipelines where multiple implementations must be interchangeable.

Core Principle

Interface consistency ensures that:

  • All implementations of an interface return the same data types
  • Method signatures are identical across implementations
  • Behavioral contracts are maintained regardless of underlying implementation
  • Downstream consumers can rely on predictable data formats

Critical Failure Mode: Mixed Return Types

A common violation occurs when different implementations of the same interface return incompatible data types:

# Inconsistent interface implementation
class TFIDFRetriever:
    def search(self, query):
        return [(index, score), (index, score)]  # Returns tuples

class PostgresRetriever:  
    def search(self, query):
        return [Chunk(...), Chunk(...)]  # Returns objects

This creates downstream failures when consumers expect consistent types:

# Downstream consumer crashes with tuples
def build_prompt(chunks):
    for chunk in chunks:
        content = chunk.content  # AttributeError if chunk is tuple

Production Impact

Interface inconsistency can cause:

  • Runtime Crashes: Type errors when unexpected data types are processed
  • Silent Data Corruption: Partial processing with wrong assumptions
  • Debugging Complexity: Failures occur far from the source of inconsistency
  • System Brittleness: Adding new implementations breaks existing code

RAG System Implications

In rag-systems, interface consistency is particularly critical because:

Multiple Retriever Implementations

Systems often support multiple retrieval strategies:

  • Vector-based retrieval returning ranked documents
  • Keyword-based retrieval returning scored matches
  • Hybrid systems combining multiple approaches
  • multi-source-rag federating across different backends

Downstream Processing Chains

Retrieved results flow through multiple processing stages:

  • Reranking systems expecting specific document formats
  • Context builders assembling prompts from document content
  • Logging systems recording search results
  • UI components displaying search results

Pluggable Architecture Requirements

Production RAG systems require:

  • Hot-swappable retriever implementations
  • A/B testing with different retrieval strategies
  • Fallback mechanisms when primary retrievers fail
  • Configuration-driven retriever selection

Design Patterns for Consistency

Common Interface Definition

from abc import ABC, abstractmethod
from typing import List

class Retriever(ABC):
    @abstractmethod
    def search(self, query: str, limit: int = 10) -> List[Chunk]:
        """Return list of Chunk objects ranked by relevance"""
        pass

Adapter Pattern for Legacy Systems

class LegacyRetrieverAdapter(Retriever):
    def __init__(self, legacy_retriever):
        self.legacy = legacy_retriever
    
    def search(self, query: str, limit: int = 10) -> List[Chunk]:
        # Convert legacy tuple format to Chunk objects
        raw_results = self.legacy.search(query, limit)
        return [self._tuple_to_chunk(r) for r in raw_results]

Runtime Type Validation

def validate_search_results(results: List[Chunk]) -> List[Chunk]:
    """Validate that all results are proper Chunk objects"""
    for result in results:
        if not isinstance(result, Chunk):
            raise TypeError(f"Expected Chunk, got {type(result)}")
    return results

Testing Strategies

Interface Compliance Tests

def test_retriever_interface_compliance():
    retrievers = [TFIDFRetriever(), PostgresRetriever(), HybridRetriever()]
    
    for retriever in retrievers:
        results = retriever.search("test query")
        
        # Verify return type consistency
        assert isinstance(results, list)
        for result in results:
            assert isinstance(result, Chunk)
            assert hasattr(result, 'content')
            assert hasattr(result, 'score')

Downstream Integration Tests

def test_build_prompt_with_all_retrievers():
    """Test that build_prompt works with results from any retriever"""
    test_query = "sample query"
    
    for retriever in all_retriever_implementations():
        results = retriever.search(test_query)
        prompt = build_prompt(results)  # Should not crash
        assert isinstance(prompt, str)
        assert len(prompt) > 0

See also