Definition
Foundation building is the software engineering discipline of constructing the base layers of a system with deliberate care, establishing the architectural patterns, dependency structures, quality standards, and operational infrastructure that all subsequent development will build upon. A well-built foundation enables rapid feature development, straightforward maintenance, and confident scaling. A poorly built foundation creates compounding technical debt that eventually slows development to a crawl, introduces fragility, and makes the system resistant to change.
In the context of the Prismatic Platform, foundation building encompasses the design of the umbrella application structure, the OTP supervision hierarchy, the storage abstraction layer, the quality gate infrastructure, and the agent orchestration framework. These foundational elements were established early in the platform's evolution and have supported 19 generations of development, growing from a handful of applications to 115 umbrella apps with 530+ agents and approximately 2.8 million lines of code.
Overview
The metaphor of a building foundation translates directly to software: just as a skyscraper requires deeper and more robust foundations than a single-story house, a platform intended to grow to millions of lines of code requires more careful foundational work than a simple web application. The investment in foundation building is front-loaded -- it appears to slow initial development -- but the returns compound over time as every subsequent feature benefits from the established patterns, abstractions, and quality infrastructure.
Foundation building is not a one-time activity. As a system evolves, its foundations must evolve with it. The Prismatic Platform has gone through multiple foundation-level changes across its 19 generations: the introduction of the storage trait system, the migration to compositional supervision with PrismaticSupervisor, the establishment of the AIAD agent standard, and the deployment of the Trinity Gate verification framework. Each of these changes required careful planning, incremental migration, and validation at every step.
The discipline of foundation building stands in contrast to two common anti-patterns. The first is "big design up front" (BDUF), where teams attempt to design the entire system before writing any code, resulting in over-engineered abstractions that don't match real requirements. The second is "code and fix," where teams build features without any architectural planning, resulting in a tangled codebase that becomes increasingly difficult to change. Foundation building occupies the middle ground: establishing enough structure to guide development without over-constraining it, and evolving the structure as understanding deepens.
The Elixir/OTP ecosystem is particularly well-suited to foundation building because its core abstractions -- supervision trees, GenServers, behaviours, and protocols -- are themselves foundational patterns. An OTP application is structured around a supervision tree that defines the process topology, fault recovery strategy, and dependency ordering. This structure is the application's foundation, and getting it right determines the application's reliability and maintainability for its entire lifecycle.
Technical Details
Architectural Layering
Foundation building begins with establishing clear architectural layers. The Prismatic Platform uses a layered architecture where each layer depends only on layers below it:
Layer 5: Applications (prismatic_web, prismatic_api, prismatic_perimeter)
Layer 4: Orchestration (prismatic_agents, prismatic_claude, prismatic_supervisor)
Layer 3: Business Logic (prismatic, prismatic_safety, prismatic_dark)
Layer 2: Storage (prismatic_storage_core, prismatic_storage_ecto, prismatic_storage_ets)
Layer 1: Core (prismatic_core, prismatic_types, prismatic_telemetry)
Each layer provides a stable API to the layers above it. Changes within a layer do not propagate upward as long as the API contract is maintained. This isolation is the primary benefit of layered architecture and the primary goal of foundation building.
OTP Supervision Foundation
The OTP supervision tree is the runtime foundation of every Elixir application. The Prismatic Platform's supervision foundation is managed by PrismaticSupervisor, which provides dependency-aware startup, domain-based supervision, and pluggable backends:
defmodule PrismaticFoundation.SupervisionArchitecture do
@moduledoc """
Demonstrates the foundational supervision patterns used
throughout the Prismatic Platform.
"""
use Supervisor
@type child_spec :: Supervisor.child_spec()
@type init_result :: {:ok, {Supervisor.sup_flags(), [child_spec()]}}
@spec start_link(keyword()) :: Supervisor.on_start()
def start_link(opts) do
Supervisor.start_link(__MODULE__, opts, name: __MODULE__)
end
@impl Supervisor
@spec init(keyword()) :: init_result()
def init(_opts) do
children = [
# Layer 1: Core services (must start first)
{PrismaticCore.Registry, []},
{PrismaticCore.Telemetry, []},
# Layer 2: Storage services (depend on core)
{PrismaticStorage.Supervisor, []},
# Layer 3: Business logic (depend on storage)
{PrismaticSafety.QualityFloorGuardian, []},
# Layer 4: Orchestration (depend on business logic)
{PrismaticAgents.Pool, []},
# Layer 5: Applications (depend on orchestration)
{PrismaticWeb.Endpoint, []}
]
Supervisor.init(children, strategy: :one_for_one)
end
endQuality Gate Foundation
Quality gates are the verification foundation that ensures code quality does not degrade as the system grows. The Prismatic Platform's quality gate infrastructure was established early and has been progressively strengthened across generations:
defmodule PrismaticFoundation.QualityGateSystem do
@moduledoc """
Foundation-level quality gate system that all code
must pass before integration.
"""
@type gate_name :: :compilation | :credo | :dialyzer | :test | :coverage | :forbidden_patterns
@type gate_result :: :pass | {:fail, String.t()}
@type gate_report :: %{
gate: gate_name(),
result: gate_result(),
duration_ms: non_neg_integer(),
details: map()
}
@spec run_all_gates() :: {:ok, [gate_report()]} | {:blocked, gate_name(), String.t()}
def run_all_gates do
gates = [
{:compilation, &check_compilation/0},
{:credo, &check_credo/0},
{:dialyzer, &check_dialyzer/0},
{:test, &check_tests/0},
{:coverage, &check_coverage/0},
{:forbidden_patterns, &check_forbidden_patterns/0}
]
run_gates_sequentially(gates, [])
end
@spec run_gates_sequentially([{gate_name(), (-> gate_result())}], [gate_report()]) ::
{:ok, [gate_report()]} | {:blocked, gate_name(), String.t()}
defp run_gates_sequentially([], results), do: {:ok, Enum.reverse(results)}
defp run_gates_sequentially([{gate_name, gate_fn} | rest], results) do
start = System.monotonic_time(:millisecond)
result = gate_fn.()
elapsed = System.monotonic_time(:millisecond) - start
report = %{gate: gate_name, result: result, duration_ms: elapsed, details: %{}}
case result do
:pass ->
run_gates_sequentially(rest, [report | results])
{:fail, reason} ->
{:blocked, gate_name, reason}
end
end
@spec check_compilation() :: gate_result()
defp check_compilation do
case System.cmd("mix", ["compile", "--warnings-as-errors", "--force"],
stderr_to_stdout: true) do
{_, 0} -> :pass
{output, _} -> {:fail, "Compilation failed: #{String.slice(, 0, 200)}"}
end
end
@spec check_credo() :: gate_result()
defp check_credo do
case System.cmd("mix", ["credo", "--strict"], stderr_to_stdout: true) do
{_, 0} -> :pass
{output, _} -> {:fail, "Credo violations: #{String.slice(, 0, 200)}"}
end
end
@spec check_dialyzer() :: gate_result()
defp check_dialyzer do
case System.cmd("mix", ["dialyzer"], stderr_to_stdout: true) do
{_, 0} -> :pass
{output, _} -> {:fail, "Dialyzer errors: #{String.slice(, 0, 200)}"}
end
end
@spec check_tests() :: gate_result()
defp check_tests do
case System.cmd("mix", ["test"], stderr_to_stdout: true) do
{_, 0} -> :pass
{output, _} -> {:fail, "Test failures: #{String.slice(, 0, 200)}"}
end
end
@spec check_coverage() :: gate_result()
defp check_coverage, do: :pass
@spec check_forbidden_patterns() :: gate_result()
defp check_forbidden_patterns, do: :pass
endContract-First Design
Foundation building emphasizes contract-first design, where the interfaces between components are defined before the implementations. In the Prismatic Platform, this takes the form of behaviours and protocols:
defmodule PrismaticStorageCore.Adapter do
@moduledoc """
The foundational storage adapter contract.
All storage implementations must satisfy this behaviour.
"""
@type key :: term()
@type value :: term()
@type opts :: keyword()
@callback get(key(), opts()) :: {:ok, value()} | {:error, term()}
@callback put(key(), value(), opts()) :: {:ok, value()} | {:error, term()}
@callback delete(key(), opts()) :: :ok | {:error, term()}
@callback list(opts()) :: {:ok, [value()]} | {:error, term()}
end
This contract has remained stable across all 19 platform generations, even as new storage backends (ETS, Ecto, Meilisearch, KuzuDB) have been added. The stability of foundational contracts is a key indicator of successful foundation building.
Implementation
Foundation Building Methodology
The Prismatic Platform follows a structured methodology for foundation building:
Phase 1 -- Core Abstractions (Weeks 1-2): Define the fundamental types, protocols, and behaviours that all components will use. Establish naming conventions, error handling patterns, and logging standards. Create the supervision tree skeleton.
Phase 2 -- Infrastructure Layer (Weeks 2-4): Implement the storage abstraction, telemetry system, configuration management, and process registry. These are the services that every application component will depend on.
Phase 3 -- Quality Infrastructure (Weeks 3-5): Set up compilation checks, Credo rules, Dialyzer, test infrastructure, and the pre-commit hook system. Establish the quality floor that all subsequent development must meet.
Phase 4 -- Vertical Slice (Weeks 4-6): Implement one complete feature from UI to storage, exercising every layer of the foundation. This validates that the foundational abstractions work in practice and reveals any gaps.
Phase 5 -- Documentation and Standards (Weeks 5-7): Document the architectural decisions, coding standards, and operational procedures. Create templates and generators for common patterns. This is the foundation for team scaling.
Phase 6 -- Evolution Mechanisms (Weeks 6-8): Establish the mechanisms for evolving the foundation itself: migration tools, deprecation policies, and versioning standards. The foundation must be changeable without breaking dependent code.
Anti-Fragile Foundations
The Prismatic Platform's foundation is designed to be anti-fragile -- to become stronger under stress rather than merely resilient. Key anti-fragility mechanisms include:
- Let-it-crash philosophy: Individual process failures strengthen the system by exercising recovery pathways and exposing fragile components.
- Quality floor monitoring: The Quality Floor Guardian continuously monitors quality metrics and triggers auto-evolution when quality dips.
- Regression test accumulation: Every bug fix adds a regression test, making the test suite progressively more comprehensive.
- Generation evolution: Each platform generation builds on the previous one, incorporating lessons learned into the foundation.
Comparison
Foundation Building vs. Big Design Up Front (BDUF)
| Aspect | Foundation Building | BDUF |
|---|---|---|
| Planning horizon | 2-8 weeks | Months to years |
| Abstraction level | Concrete contracts and patterns | Abstract diagrams and documents |
| Validation | Vertical slice validates early | Validation deferred to implementation |
| Flexibility | Evolves with understanding | Fixed before coding begins |
| Risk | Moderate (may need adjustment) | High (may not match reality) |
| Waste | Low (builds working code) | High (produces unused designs) |
Foundation Building vs. Code-and-Fix
| Aspect | Foundation Building | Code-and-Fix |
|---|---|---|
| Initial velocity | Moderate (investment required) | High (no planning overhead) |
| Long-term velocity | High (compounding returns) | Declining (compounding debt) |
| Architecture coherence | Strong (by design) | Weak (emergent chaos) |
| Team scaling | Easy (clear patterns) | Difficult (tribal knowledge) |
| Refactoring cost | Low (clean boundaries) | High (entangled dependencies) |
Foundation Building vs. Microservices-First
| Aspect | Foundation Building | Microservices-First |
|---|---|---|
| Deployment complexity | Low (umbrella app) | High (distributed system) |
| Refactoring ease | High (shared compilation) | Low (network boundaries) |
| Initial overhead | Moderate | Very high |
| Appropriate for | Known domain, growing team | Large organization, mature domain |
Best Practices
-
Invest in the supervision tree early. The OTP supervision tree is the most important architectural decision in an Elixir application. Design it carefully, document it explicitly, and resist the temptation to add ad-hoc processes outside the supervision hierarchy.
-
Define behaviours before implementations. Every cross-cutting concern (storage, logging, metrics, authentication) should have a behaviour that defines its contract. Implementations can be swapped without changing dependent code.
-
Establish quality gates from day one. The first commit should include compilation checks, Credo configuration, and a basic test. Quality standards are much easier to maintain than to retrofit.
-
Build one vertical slice early. Validate the foundation by implementing a complete feature that exercises every layer. This reveals gaps and mismatches that paper designs miss.
-
Document architectural decisions. Record the rationale for foundational choices, not just the choices themselves. Future developers (including your future self) need to understand why the foundation is structured as it is to make informed decisions about evolving it.
-
Plan for evolution. Foundations are not permanent. Design them to be changeable: use indirection (behaviours, protocols), provide migration tools, and version interfaces. The best foundation is one that can evolve without breaking dependent code.
-
Resist premature optimization. Foundational code should prioritize clarity and correctness over performance. Optimize only after profiling reveals actual bottlenecks, and isolate optimizations behind clean interfaces.
-
Automate everything repeatable. Code generation, quality checks, deployment, and monitoring should be automated from the start. Manual processes do not scale and introduce variability.
Common Pitfalls
-
Gold-plating the foundation. Building elaborate abstractions for hypothetical future requirements wastes time and adds complexity. Build for known requirements and extend as needed. The YAGNI principle applies to foundations.
-
Coupling foundation to implementation details. A good foundation hides implementation details behind stable interfaces. If changing a database driver requires modifying application code, the foundation's abstraction is leaking.
-
Neglecting operational foundations. Logging, monitoring, alerting, and deployment are as foundational as code architecture. Systems that are difficult to operate become difficult to maintain regardless of their code quality.
-
Skipping the vertical slice. Without validating the foundation against a real feature, teams build foundations that are theoretically elegant but practically unusable. The vertical slice is the reality check.
-
Making the foundation too rigid. Foundations that cannot evolve become constraints that force workarounds. Overly strict type hierarchies, deeply nested module structures, and inflexible configuration systems are common examples.
-
Ignoring developer experience. The foundation should make common tasks easy and uncommon tasks possible. If developers struggle to implement basic features within the foundational framework, the foundation is working against them rather than for them.
-
Under-investing in test infrastructure. Test helpers, factories, fixtures, and assertion utilities are foundational infrastructure that dramatically affects development velocity. Investing in test infrastructure pays dividends across every feature.
Use Cases
Greenfield Platform Development
Foundation building is most impactful at the start of a new platform. The Prismatic Platform's initial foundation (supervision tree, storage traits, quality gates) was established in the first generation and has supported all subsequent evolution. Teams starting new platforms should invest 4-8 weeks in foundation building before feature development.
Platform Modernization
Legacy systems that need modernization benefit from foundation building as a migration strategy. Rather than rewriting the entire system, teams can build a new foundation alongside the legacy system and incrementally migrate features onto the new foundation. The Prismatic Platform used this approach when migrating from earlier architectural patterns to the current umbrella structure.
Team Scaling
When a team grows, foundation building provides the shared patterns, conventions, and infrastructure that enable new developers to be productive quickly. Without a well-built foundation, onboarding requires extensive tribal knowledge transfer. The Prismatic Platform's CLAUDE.md documentation, AIAD standard, and quality gate system serve as the onboarding foundation for new contributors.
Compliance and Certification
Regulatory compliance often requires demonstrable evidence of architectural controls, quality processes, and operational procedures. A well-built foundation naturally produces this evidence through its quality gates, audit trails, and documentation. The platform's NIS2 and ZKB compliance assessments leverage the foundational quality infrastructure.
Open Source Release
Releasing a platform as open source requires a clean, well-documented foundation that external contributors can understand and extend. The Prismatic Platform's 4 OSS packages (SDK, Plugin Kit, Security, UI) were extracted from the platform's foundation layer, benefiting from the rigorous quality standards and clean interfaces established during foundation building.
Related Concepts
Foundation building connects to many architectural and engineering concepts in the Prismatic Platform:
- Architecture provides the broader context of system design decisions that foundation building establishes
- Supervision Tree is the OTP foundation for process management and fault tolerance
- OTP provides the foundational abstractions (GenServer, Supervisor, Application) that Elixir foundations are built upon
- Quality Gates are the verification foundation ensuring code quality does not degrade
- Technical Debt is the consequence of inadequate foundation building
- Scalability is enabled by well-built foundations that can grow without structural changes
- Modularity is a key property of well-built foundations
- Composability enables foundation components to be combined in flexible ways
- Layered Architecture is the structural pattern most commonly used in foundation building
- System Design Principle provides the guiding principles for foundation decisions
See Also
- Umbrella Application for the Elixir project structure that the Prismatic Platform's foundation uses
- Adapter Pattern for the foundational pattern enabling pluggable implementations
- Behaviour for the Elixir mechanism used to define foundational contracts
- Continuous Integration for the operational foundation of automated quality verification
- Domain-Driven Design for the design methodology that guides domain-level foundation decisions
Connect & Contribute: Created by Tomas Korcak (korczis). This glossary entry is part of the Prismatic Platform documentation. Contributions welcome via pull requests. Licensed under GHL. GitHub | GitLab | LinkedIn | Contact