Receber por email
← Voltar para o catálogo

functional-core-imperative-shell

Use when writing or refactoring code, before creating files - enforces separation of pure business logic (Functional Core) from side effects (Imperative Shell) using FCIS pattern with mandatory file classification

235estrelas
Atualizado há 4 meses

Ver no GitHub ↗

Como adicionar

/plugin marketplace add ed3dai/ed3d-plugins

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/functional-core-imperative-shell/svg)](https://www.skillteca.com.br/skills/functional-core-imperative-shell)

Skills relacionadas

Ver todas de Pesquisa e Web →

Alerta por categoria

Receba novas skills de Pesquisa e Web toda segunda

1 email curto, só com as skills novas de Pesquisa e Web. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

Functional Core, Imperative Shell (FCIS)

Overview

Core principle: Separate pure business logic (Functional Core) from side effects (Imperative Shell). Pure functions go in one file, I/O operations in another.

Why this matters: Pure functions are trivial to test (no mocks needed). I/O code is isolated to thin shells. Bugs become structurally impossible when business logic has no side effects.

When to Use

Use FCIS when:

  • Writing any new code file
  • Refactoring existing code
  • Reviewing code for architectural decisions
  • Deciding where logic belongs

Trigger symptoms:

  • "Where should this function go?"
  • Creating a new file
  • Adding database calls to logic
  • Adding file I/O to calculations
  • Writing tests that need complex mocking

MANDATORY: File Classification

YOU MUST add pattern comment to every file containing runtime behavior:

// pattern: Functional Core
// pattern: Imperative Shell
// pattern: Mixed (needs refactoring)

If file genuinely cannot be separated (rare), document why:

// pattern: Mixed (unavoidable)
// Reason: [specific technical justification]
// Example: Performance-critical path where separating I/O causes unacceptable overhead

No file with runtime behavior without classification. If you create a file that contains functions, classes with methods, or orchestration logic without this comment, you have violated the requirement.

Exempt: Files Without Runtime Behavior

DO NOT add pattern comments to:

  • Type-only files - files exporting only types, interfaces, or type aliases (no runtime code)
  • Constants/enum-like files - static data declarations, no functions
  • Barrel/index files - re-exports only (export * from './foo')
  • Test files - tests exercise core/shell code but aren't themselves core or shell
  • Generated files - machine-generated code
  • Bash/shell scripts (.sh, .bash) - inherently imperative
  • Configuration files (eslint.config.js, tsconfig.json, .env, etc.)
  • Markdown documentation (.md)
  • HTML files (.html)
  • Task runner files (justfile, Makefile, etc.)
  • Package manifests (package.json, pyproject.toml, etc.)
  • Data files (JSON, YAML, CSV, etc.)

Note: If an exempt file grows to include runtime logic (e.g., a "types" file gains helper functions, or a constants file gains factory functions), it crosses the threshold and MUST be classified.

Classification applies to application source files containing runtime behavior (functions with logic, classes with methods, I/O orchestration).

File Type Definitions

Functional Core Files

Contains ONLY:

  • Pure functions (same input -> same output, always)
  • Business logic, validations, calculations, transformations
  • Data structure operations
  • Logging (EXCEPTION: loggers are permitted in Functional Core)

NEVER contains:

  • File I/O (reading, writing files)
  • Database operations (queries, updates, connections)
  • HTTP requests or responses
  • Environment variable access
  • Date.now(), Math.random(), or other non-deterministic functions
  • State mutations outside function scope

Logging exception: Functions MAY accept and use loggers. For unit tests, pass no-op loggers. This is the ONLY permitted side effect in Functional Core.

Test signature: Simple assertions, no mocks except logger (if used).

Imperative Shell Files

Contains ONLY:

  • I/O operations: file system, database, HTTP, environment
  • Orchestration: gather data -> call Functional Core -> persist results
  • Error handling for I/O failures
  • Minimal business logic (coordination only)

NEVER contains:

  • Complex calculations
  • Business rule validations
  • Data transformations beyond format conversion

Test signature: Integration tests with real dependencies or test doubles.

Code Flow Pattern

1. GATHER (Shell):  Collect data from external sources
2. PROCESS (Core):  Transform input to output (pure)
3. PERSIST (Shell): Save results externally

Every operation follows this sequence. No exceptions.

Decision Framework

Before writing a function, ask:

digraph fcis_decision {
    "Writing a function" [shape=ellipse];
    "Can run without external dependencies?" [shape=diamond];
    "Does it coordinate I/O?" [shape=diamond];
    "Functional Core" [shape=box, style=filled, fillcolor=lightblue];
    "Imperative Shell" [shape=box, style=filled, fillcolor=lightgreen];
    "STOP: Refactor or escalate" [shape=octagon, style=filled, fillcolor=red, fontcolor=white];

    "Writing a function" -> "Can run without external dependencies?";
    "Can run without external dependencies?" -> "Functional Core" [label="yes"];
    "Can run without external dependencies?" -> "Does it coordinate I/O?" [label="no"];
    "Does it coordinate I/O?" -> "Imperative Shell" [label="yes"];
    "Does it coordinate I/O?" -> "STOP: Refactor or escalate" [label="no"];
}

Questions to ask:

  • Can this logic run without file system, database, network, or environment?
  • YES -> Functional Core
  • NO -> Does it coordinate I/O or contain business logic?
  • I/O coordination -> Imperative Shell
  • Business logic + I/O -> STOP. Refactor or escalate to user.

Common Mistakes and Rationalizations

| Excuse/Thought Pattern | Reality | What To Do | |------------------------|---------|------------| | "Just one file read in this calculation" | File I/O = side effect. Not Functional Core. | Extract to Shell. Pass data as parameter. | | "Database is passed as parameter, so it's pure" | Database operations are I/O. Not pure. | Move to Shell. Core receives data, not DB connection. | | "This validation needs to check if file exists" | File system check = I/O. Not Functional Core. | Shell checks file, passes boolean to Core validation. | | "Small HTTP call, won't hurt" | HTTP = side effect. Breaks purity guarantee. | Shell makes request, Core processes response data. | | "Need Date.now() for timestamp calculation" | Non-deterministic. Not pure. | Shell passes timestamp as parameter. | | "Logging is a side effect, should remove" | WRONG. Logging is explicitly permitted. | Keep logger. This is the exception. | | "This function does both logic and I/O, but it's simpler" | Mixed concerns = untestable without mocks. | Split into Core (logic) + Shell (I/O). Test Core simply. | | "File classification is overhead" | Prevents entire classes of bugs. Non-negotiable. | Add classification comment. Takes 10 seconds. | | "I'll refactor later" | Later never comes. Do it now. | Classify and separate now. | | "Performance requires mixing" | Prove it with benchmarks. Usually wrong. | Separate first. Optimize with evidence. Mark Mixed (unavoidable) with justification. |

Red Flags - STOP and Refactor

If you catch yourself doing ANY of these, STOP:

  • File I/O in a "pure" function (open, read, write, exists checks)
  • Database passed as parameter to Functional Core (queries, updates, connections)
  • HTTP requests in business logic (fetch, axios, requests)
  • Environment variables in calculations (process.env, os.getenv)
  • Math.random() or Date.now() in Functional Core (non-deterministic)
  • Creating a file with runtime behavior without pattern classification comment
  • Thinking "just this once" about mixing concerns

All of these mean: Extract I/O to Shell. Pass data to Core. Classify file correctly.

Implementation Patterns

Functional Core Pattern

# pattern: Functional Core

def calculate_total_with_tax(items, tax_rate, logger=None):
    """Pure calculation: same inputs always produce same output."""
    if logger:
        logger.debug(f"Calculating total for {len(items)} items")

    subtotal = sum(item['price'] * item['quantity'] for item in items)
    tax = subtotal * tax_rate
    total = subtotal + tax

    return {
        'subtotal': subtotal,
        'tax': tax,
        'total': total
    }

No I/O. No database. No file system. Only computation.

Compartilhar X LinkedIn