Define typed module interfaces
Retriever, fusion, reranker, generator, evaluator each get a typed contract. Inputs and outputs are explicit. Modules talk through these contracts, not through each other's internals.
Modular RAG is not a pattern, it is an engineering posture. Build the pipeline from named modules (retriever, fusion, reranker, generator, evaluator) so any one can be swapped without rewriting the rest. The default engineering posture for every Kensink RAG build.
Best for: Any RAG build that is going to live past month six. Modular shape pays back the first time you need to swap a reranker or change the embedding model.
The shape is the same as whatever underlying pattern (Naive, Advanced, Agentic) but every step is a named module with a typed input and output. The pipeline is composed; modules are swappable; tests are written at module boundaries.
Retriever, fusion, reranker, generator, evaluator each get a typed contract. Inputs and outputs are explicit. Modules talk through these contracts, not through each other's internals.
Each module has at least one implementation, often more (Cohere reranker, BGE reranker, no reranker). Production picks one; tests can run against several.
The pipeline is a small piece of glue that composes the modules. Adding a step or replacing one is a glue change, not a refactor.
Stack is whatever the underlying pattern needs; what makes it modular is how the code is organised, not which technology is in use.
Typed contracts at every module boundary. The interface is the unit of stability; implementations come and go.
Modules receive their dependencies through constructors. No service locator, no global registry, no dependency framework. Plain composition.
Each module has its own test suite at the interface level. Pipeline tests run end-to-end. Eval tests run quality against golden sets.
Module choices configurable per environment so we can A/B a new reranker in staging without code changes.
Modular RAG deploys the same as whatever pattern is underneath; the work is in how the code is organised so the patterns can be swapped.
Retriever, reranker, generator, evaluator, etc. Names match the pattern in use; types match the data flow.
Each module's input and output types. Explicit, documented, versioned.
Concrete modules behind the contracts. Pipeline is glue.
Unit tests at module interfaces, integration tests at the pipeline level, eval tests on golden sets.
Module choices flagged per environment so staging can run different shapes from production safely.
Modular RAG does not change accuracy; it changes the cost of changing the system. Pays back when the team needs to swap a component, which is almost always.
Modular RAG is evaluated by how cleanly the modules swap, not by accuracy. We measure component-swap cost (how long does it take to replace the reranker?) as a build-quality metric.
Modular RAG is the 2024+ industry consensus engineering posture. Every serious production RAG codebase converges on this shape, framework or not.
Practitioners report the same lesson: the patterns and components in 2026 are not the patterns and components in 2024. The reranker model changes. The embedding model changes. The vector DB might change. Builds that designed for swappable modules survive; builds that did not get rewritten. The posture is the win; the framework choice is secondary.
Every Kensink RAG build is modular. We do not advertise it as a feature because it is engineering hygiene, not a product. But it is the engineering posture that keeps RAG builds maintainable past month six.
We have rebuilt RAG systems for customers who shipped on a framework-heavy stack two years ago and got stuck on the migration tax. The pattern we keep recommending: plain composition, typed interfaces, no framework. Swap a reranker in an afternoon, not a quarter.
Not modular only on throwaway prototypes. Anything else, modular by default.
Modular Advanced RAG is what most production builds converge to.
Read playbookRelated patternPer-source retrievers in agentic RAG are textbook modules.
Read playbookRelated patternEven naive RAG benefits from modular boundaries the moment you want to swap an embedding.
Read playbook