Applies Functional Core, Imperative Shell to isolate logic from side effects. Use when business logic is entangled with I/O or unit tests are slow and brittle.
Installs into .claude/skills of the current project.
Are you the author of Architecture Paradigm Functional Core?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/athola-architecture-paradigm-functional-core)
---
name: architecture-paradigm-functional-core
description: Applies Functional Core, Imperative Shell to isolate logic from side effects. Use when business logic is entangled with I/O or unit tests are slow and brittle.
alwaysApply: false
category: architectural-pattern
tags:
- architecture
- functional-core
- imperative-shell
- testability
- business-logic
- side-effects
dependencies: []
tools: []
usage_patterns:
- paradigm-implementation
- refactoring-guidance
- adr-support
- testability-improvement
complexity: intermediate
model_hint: standard
estimated_tokens: 1200
---
# The Functional Core, Imperative Shell Paradigm
## When To Use
- Separating pure business logic from side effects
- Improving testability through immutable domain models
## When NOT To Use
- Performance-critical hot paths where immutability overhead matters
- Purely imperative codebases with no plans to adopt functional patterns
## When to Employ This Paradigm
- When business logic is entangled with I/O operations (e.g., database calls, HTTP requests), making tests brittle and slow.
- When significant development time is spent rewriting adapters or dealing with framework churn.
- When you require a suite of fast, deterministic unit tests that operate on plain data, complemented by a thin integration testing layer.
## Adoption Steps
1. **Inventory Side Effects**: Create a map of all side effects in the system, such as database writes, external API calls, UI events, and filesystem access. Explicitly assign these responsibilities to the "shell."
2. **Model the Core Logic**: Represent business rules and policies as pure functions. These functions should take domain data as input and return decisions or commands as output, avoiding shared mutable state.
3. **Design the Command Schema**: Define a small, explicit set of command objects that the core can return and the shell can interpret (e.g., `PersistOrder`, `PublishEvent`, `NotifyUser`).
4. **Refactor Incrementally**: Begin with high-churn or critical modules. Wrap legacy imperative code behind adapters while progressively extracting pure calculations into the functional core.
5. **Enforce Boundaries**: Use code reviews and automated architecture tests to validate a strict separation. The shell should only handle orchestration, sequencing, and retries, while the core should never call directly into frameworks or I/O libraries.
## Key Deliverables
- An Architecture Decision Record (ADR) detailing why this pattern was chosen, which modules are affected, and the scope of the migration.
- A suite of unit tests for the core with high (>90%) and deterministic code coverage. Where applicable, use property-based or fixture-based testing to cover a wide range of inputs.
- A suite of contract and integration tests for the shell that verify correct command interpretation, retry logic, and telemetry.
- A set of rollout metrics (e.g., deployment lead time, incident rate in the shell layer) to demonstrate the value of the architectural change.
## Risks & Mitigations
- **Logic Drifting Between Core and Shell**:
- **Mitigation**: It's common for business logic to accidentally be duplicated or placed in the shell. Enforce a "core owns all decisions" checklist during code reviews to prevent this.
- **Mismatch with Frameworks**:
- **Mitigation**: The imperative shell may still need to interact with framework-specific lifecycle hooks. Before committing to a large rewrite, build small proof-of-concept adapters to validate the integration strategy.
- **Team Unfamiliarity with the Pattern**:
- **Mitigation**: Introduce the pattern using pair programming and internal "brown-bag" learning sessions. Document common anti-patterns that are discovered during the pilot phase to guide future development.
## Concrete Components
Vocabulary for the tools and abstractions an implementation of this
paradigm tends to carry. Not dependencies, and not ``tools:`` frontmatter.
- ``boundary-validator``: guards inputs to the pure core so the core can stay total
- ``core-test-generator``: generates property-based tests against the deterministic core
- ``shell-adapter-generator``: scaffolds the imperative shell that wires the core into I/O
## Exit Criteria
- [ ] An explicit side-effect inventory exists mapping every I/O operation to the shell layer
before any refactoring begins.
- [ ] Core unit tests run with no I/O mocks (no database, HTTP, or filesystem calls) and achieve
> 90% deterministic coverage.
- [ ] Automated architecture tests (e.g., import analysis or namespace rules) confirm the core
contains zero imports from I/O or framework libraries.
- [ ] Rollout metrics (deployment lead time, incident rate in the shell layer) are baselined
before migration and compared after to demonstrate the pattern's value.