Articles

Functional Programming vs Object-Oriented Programming

Functional programming builds programs from pure functions and immutable data; OOP bundles state and behavior into objects. Key differences explained.

The Lycoris Team The Lycoris Team · · 5 min read
Abstract representation of programming languages

Functional programming and object-oriented programming are two different ways of organizing how code models a problem — functional programming builds programs out of pure functions and immutable data, while object-oriented programming bundles state and behavior together into objects that pass messages to each other. Most real-world codebases borrow from both; understanding the distinction helps explain why a piece of code looks the way it does, and why certain bugs are easier or harder to reach in each style.

The core idea behind each paradigm

Object-oriented programming (OOP) models a system as a collection of objects, each owning its own private state and exposing methods that read or mutate that state. A BankAccount object holds a balance field and exposes a withdraw() method that changes it. The organizing question OOP asks is: what are the things in this system, and what can they do?

Functional programming (FP) instead organizes code around functions that take input and return output without touching anything outside themselves — no hidden state, no side effects. A withdraw(balance, amount) function in a functional style doesn’t mutate an account; it returns a new balance, leaving the original untouched. The organizing question FP asks is: what are the transformations in this system, and how do they compose?

Pure functions and immutability

The central discipline in functional programming is the pure function: given the same inputs, it always produces the same output, and it doesn’t modify anything outside its own scope — no writing to a shared variable, no network call, no side effect buried inside otherwise-innocent-looking logic. Data is treated as immutable by default; instead of changing an array in place, a functional style produces a new array with the change applied.

This has a direct, practical payoff: a pure function is trivial to test — call it, check the output, no setup or mocking required — because nothing outside the function can be affected by calling it, and nothing outside the function can affect its result. JavaScript’s map, filter, and reduce are a direct, everyday application of this idea — each returns a new array rather than mutating the original.

Encapsulation and state in OOP

Object-oriented programming takes the opposite bet: state changing over time is normal and necessary, so wrap it in a boundary (encapsulation) that controls how and when it can change, rather than trying to avoid mutation altogether. A well-designed class exposes a narrow set of methods as its only way to touch its internal state, so the object can enforce its own invariants — a BankAccount can refuse to let its balance go negative, no matter which part of the codebase calls withdraw().

The four pillars usually associated with OOP — encapsulation, inheritance, polymorphism, and abstraction — are all about managing that mutable state responsibly as a system grows: hiding internal details, sharing behavior between related types, and letting different objects respond to the same message in their own way.

Comparison

Functional programmingObject-oriented programming
Core unitPure functionObject (state + behavior)
DataImmutable by defaultMutable, encapsulated
State handlingAvoided; passed explicitlyOwned internally, changed via methods
Composition styleFunction composition, higher-order functionsInheritance, composition of objects
Easiest to testPure functions in isolationObjects with mocked collaborators
Common failure modeDeeply nested function composition, hard to traceDeep inheritance hierarchies, tangled shared state

Neither is purely one or the other in practice

Most mainstream languages support both styles, and most production codebases mix them freely. A typical modern application might model its domain with classes and objects for structure, while writing the data-transformation logic inside those classes in a functional style — filtering, mapping, and reducing data without mutating shared state along the way. Component-based UI frameworks, like the ones compared in Vue vs. React, lean heavily functional in how they render — components as pure functions of props to UI — while the surrounding runtime and tooling are often built with object-oriented abstractions underneath.

Closures are a good example of a genuinely functional-programming concept that’s load-bearing even in codebases that otherwise look object-oriented — see JavaScript closures for how a function can capture and carry state from its enclosing scope without needing a class at all. Dependency injection, a pattern strongly associated with OOP frameworks, is really just a way of making a class’s dependencies explicit inputs rather than hidden global lookups — the same instinct toward explicit inputs that functional programming applies to everything. See what dependency injection is for how that pattern works.

Which one should you reach for

Favor a functional style for data transformations, business logic that’s naturally a pipeline of inputs to outputs, and anywhere testability and predictability matter more than raw performance — parsing, validation, calculations. Favor an object-oriented style for modeling long-lived, mutable entities with real invariants to protect — a user session, a game character, a hardware connection — where bundling state with the operations allowed to touch it prevents that state from drifting into an invalid shape.

Recursion is one place the two paradigms visibly diverge in practice: a functional style tends to lean on recursion over explicit loops, since a recursive function calling itself with new arguments is a natural fit for immutable data with no counter variable to mutate. See recursion vs. iteration for the tradeoffs between the two approaches, including why deep recursion without tail-call optimization can exhaust the call stack in ways an equivalent loop never would.

The takeaway

Functional programming organizes code around pure functions and immutable data, optimizing for predictability and easy testing; object-oriented programming organizes code around objects that encapsulate mutable state, optimizing for modeling real-world entities with enforced invariants. Neither paradigm has won out over the other — most modern code is a pragmatic blend, using classes to structure a system’s entities and functional techniques to keep the logic inside them predictable.

The Lycoris Team The Lycoris Team · · 4 min read

What Is an Abstract Syntax Tree (AST)?

An abstract syntax tree represents source code's grammatical structure as nodes a compiler, linter, or formatter can walk and transform programmatically.

#Computer Science #Developer Tools #Programming Languages
The Lycoris Team The Lycoris Team · · 5 min read

What Is JIT Compilation? Just-In-Time Compiling Explained

Just-in-time (JIT) compilation translates code to native machine instructions while a program runs, trading startup time for peak execution speed.

#Computer Science #Performance #Programming Languages
The Lycoris Team The Lycoris Team · · 4 min read

What Is a Monad? A Practical Explanation for Programmers

A monad is a wrapper type with rules for chaining operations that might fail, be async, or carry extra context — like Promise or Optional, generalized.

#Computer Science #Programming Languages #JavaScript