Logo

dev-resources.site

for different kinds of informations.

Functional Programming: A Misfit for Backend Engineering

Published at
11/21/2024
Categories
typescript
functional
oop
backend
Author
meeshbhoombah
Categories
4 categories in total
typescript
open
functional
open
oop
open
backend
open
Author
13 person written this
meeshbhoombah
open
Functional Programming: A Misfit for Backend Engineering

The road to technological ruin is often paved with good intentions. There are few philosophies as noble as functional programming: immutability to protect against errors, pure functions to enable predictable behavior, and declarative pipelines to elegantly process data. But backend engineering, with its mutable state, ever-changing requirements, and reliance on existing frameworks, is a treacherous terrain. In backend engineering, where pragmatism reigns, the ideals of functional programming often falter under the weight of practical realities.

The Framework Mismatch

Consider the design of frameworks like Express.js, where mutable state and side effects play a critical role. Middleware functions rely on shared, mutable objects like req and res to propagate data across layers. A typical example highlights the simplicity and elegance of this approach:

app.use((req, res, next) => {
  req.user = { id: 1, name: "Jane Doe" }; // Mutates req object
  next();
});
Enter fullscreen mode Exit fullscreen mode

By modifying shared objects and seamlessly passing control via next(), developers achieve concise, intuitive workflows. Functional programming, however, discourages mutability and side effects. Strict adherence to its principles demands creating immutable objects at every step, forcing developers to rewrite middleware like this:

const middleware = (req) => {
  const newReq = { ...req, user: { id: 1, name: "Jane Doe" } }; // Immutable object creation
  return { newReq, next: true }; // Explicit next control
};
Enter fullscreen mode Exit fullscreen mode

This approach introduces verbosity and performance overhead. Recreating new objects at every step disrupts the natural flow of frameworks like Express, obscuring their design simplicity. Developers spend their time adapting FP principles to stateful systems instead of solving actual problems. The issue lies not just in Express.js itself but in the broader misalignment between functional programming and imperative frameworks. Backend systems require direct manipulation of state and sequential execution, making FP’s emphasis on declarative, stateless pipelines a poor fit.

Architectural Tensions: CSR and the FP Lens

Architectural patterns further underscore this divide. The controller-service-repository (CSR) pattern exemplifies clean separation of concerns in backend engineering. Controllers handle HTTP requests, services encapsulate business logic, and repositories manage data access. Each layer is distinct, fostering modularity and maintainability. For instance:

// Service
export class UserService {
  async createUser(data: any): Promise<User> {
    const user = await UserRepository.save(data);
    await EmailService.sendWelcomeEmail(user.email);
    return user;
  }
}

// Controller
export const initializeController = async (req, res) => {
  try {
    const userService = new UserService();
    const user = await userService.createUser(req.body);
    res.status(201).send(user);
  } catch (error) {
    console.error(error);
    res.status(500).send({ error: 'Something went wrong' });
  }
};
Enter fullscreen mode Exit fullscreen mode

This architecture is explicit. The controller orchestrates actions, delegating logic to the service while ensuring proper response handling. Each layer is modular and independently testable, making it easier to reason about and maintain.

Functional programming disrupts this clarity. By encouraging developers to build composable pipelines, it often blurs the boundaries between controllers, services, and repositories:

const createUserPipeline = pipe(
  validateInput,
  saveToRepository,
  sendWelcomeEmail
);

export const initializeController = (req, res) => {
  try {
    const user = createUserPipeline(req.body);
    res.status(201).send(user);
  } catch (error) {
    res.status(500).send({ error: 'Something went wrong' });
  }
};
Enter fullscreen mode Exit fullscreen mode

While superficially concise, this functional implementation collapses under real-world conditions. Introducing conditional logic—e.g., checking for duplicate users—requires breaking the pipeline, forcing developers to retrofit abstractions with complexity. Controllers and services become entangled, undermining the separation of concerns. This lack of clarity ripples across teams, complicating onboarding, testing, and debugging. OOP’s emphasis on modularity and explicit boundaries aligns far better with backend engineering’s need for clear architectural lines.

Pragmatism Over Purity

The cost of functional purity becomes even more apparent in debugging and error handling. Backend systems are inherently stateful, operating within unpredictable environments where failures are inevitable. OOP embraces these realities, offering practical tools for managing them. A centralized error-handling middleware in Express illustrates this pragmatic approach:

app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).send({ error: 'Internal Server Error' });
});
Enter fullscreen mode Exit fullscreen mode

This design ensures consistency and simplicity. Errors are logged and handled predictably, providing clear points of intervention. Functional paradigms, by contrast, require threading error states through every function in the pipeline. This leads to brittle, verbose code, where even small oversights can cause cascading failures. Debugging becomes a Herculean effort, as developers must untangle nested abstractions to identify root causes.

The struggles of functional programming in backend engineering stem from a fundamental philosophical divide. FP values purity, immutability, and abstraction—principles that shine in domains like data transformation and computation. Backend engineering, however, demands pragmatism, modularity, and the ability to manage mutable state effectively. OOP provides tools—encapsulation, separation of concerns, and centralized error handling—that accommodate the messy, stateful realities of backend systems.


In backend engineering, the costs of functional purity outweigh the benefits. Frameworks like Express, architectural patterns like controller-service-repository, and real-world challenges like debugging demand tools that embrace state and imperfection. OOP succeeds in this domain because it meets developers where they are, offering practical solutions to real-world problems. Functional programming, while valuable in other contexts, remains a philosophical misfit for backend systems.

functional Article's
30 articles in total
Favicon
A monad is a monoid in the category of endofunctors...
Favicon
Rust-like Iteration in Lua
Favicon
Transducer: A powerful function composition pattern
Favicon
🏗️ `Is` Methods
Favicon
7.bet’s Bold Move: Play Smarter, Play Safer, Play Better!
Favicon
Harnessing the Power of Object-Oriented and Functional Programming Paradigms in Software Development
Favicon
Lambda vs. Named Functions: Choosing the Right Tool for the Job
Favicon
Object-Oriented vs Functional Programming—Why Not Both?
Favicon
From C# to Haskell and Back Again: My Journey into Functional Programming
Favicon
Comprehensive Guide to Automated Functional Testing
Favicon
Functional Programming in Go with IBM fp-go: Error Handling Made Explicit
Favicon
Razumevanje funkcija višeg reda (Higher-Order Functions) u JavaScript-u
Favicon
What is Functional Programming, and How Can You Do It in JavaScript?
Favicon
Parallel Testing: Best Practice for Load Testing & Functional Testing
Favicon
For loops and comprehensions in Elixir - transforming imperative code
Favicon
Advent of Code and Aesthetics
Favicon
PureScript for Scala developers
Favicon
Clojure REPL-Driven Development with VS Code
Favicon
Combining Object-Oriented and Functional Programming in Large Projects
Favicon
Non-Functional Requirements: A Comprehensive Guide
Favicon
Unpacking Lambda Expressions: What They Are and Why They Matter
Favicon
Functional Programming: A Misfit for Backend Engineering
Favicon
Scope progression
Favicon
JavaScript Functions for Beginners: Quick Guide
Favicon
Tech Watch #2
Favicon
Either Algebraic Data Type
Favicon
Functional Programming in C#: The Practical Parts
Favicon
A 20-liner Drag'n'Drop feat using ObservableTypes
Favicon
On “superiority” of (functional) programming and other labels
Favicon
Top Open Source functional programming technologies

Featured ones: