Skip to main content
Architectural Update (March 2026)

This ADR was written when the system used Neo4j for entity storage. As of March 2026, Neo4j has been fully removed and replaced by PostgreSQL taxonomy tables (taxonomy_entities, taxonomy_relationships). The decision rationale documented here remains valid; the storage layer has changed.

ADR-0011: Module Inlining

Status: Accepted

Context

The ZOL Intelligent Search system was initially developed as a multi-package architecture, with five separate Python packages each published and versioned independently:

PackageResponsibility
s4u-llm-clientLLM provider abstraction and API client
s4u-llm-evalResponse evaluation framework
s4u-ragRAG pipeline orchestration
s4u-knowledge-graphNeo4j integration and entity management
s4u-document-processorDocument extraction and chunking

This modular design was intended to promote reusability across projects and enforce clean separation of concerns. In practice, it created significant friction.

The Problem

Pain Points

  1. Version coordination: A change in s4u-llm-client required updating its version, publishing it, updating the dependency in s4u-rag, publishing that, and updating the dependency in the application. A single logical change required 3+ repository updates.

  2. Debugging difficulty: Stack traces spanning multiple packages were hard to follow. Setting breakpoints across package boundaries required editable installs of all packages.

  3. CI/CD complexity: Each package needed its own CI pipeline, PyPI publishing step, and version management. The release process for a cross-cutting change involved coordinating 5+ pipelines.

  4. No external consumers: The packages were designed for cross-project reuse, but no other project consumed any of these packages. The reusability benefit never materialized.

  5. Dependency conflicts: Different packages occasionally pinned conflicting versions of shared dependencies (pydantic, httpx), requiring careful coordination.

Decision

Inline all five packages into a monorepo under the application's module structure:

Module Mapping

Original PackageNew LocationNotes
s4u-llm-clientapp/llm/LLM provider abstraction
s4u-llm-evalapp/evaluation/Evaluation metrics
s4u-ragapp/services/rag/RAG pipeline
s4u-knowledge-graphapp/services/graph/Neo4j integration
s4u-document-processorapp/services/ingestion/Document processing

Consequences

Positive

  • Single install: pip install -e . installs everything. No coordination needed.
  • Unified CI/CD: One pipeline builds, tests, and deploys the entire system
  • Simplified debugging: Standard IDE debugging with breakpoints anywhere
  • Atomic changes: A cross-cutting refactor is a single commit, not 5 coordinated releases
  • No dependency conflicts: A single requirements.txt governs all dependencies
  • Faster development: No publish-update-publish cycle for internal changes

Negative

  • No cross-project reuse: If another project needs the LLM client, it cannot pip-install it independently. However, since no such consumer exists, this is a theoretical rather than practical loss.
  • Larger repository: All code in one repo increases clone size and cognitive load
  • Less enforced boundaries: Package boundaries enforced module separation; within a monorepo, discipline must be maintained through convention and code review

Before and After

Lesson Learned

The premature extraction of internal modules into separate packages is a common architectural pitfall. The inlining decision reflects the principle: do not optimize for reuse until reuse is demonstrated. Internal module boundaries (Python packages within a monorepo) provide sufficient separation of concerns without the operational overhead of independent packaging.