Programming Paradigms

Jakob Jenkov
Last update: 2022-11-30

In this article I list the most common programming paradigms I have encountered in my work as a software developer and / or architect. You may not have to be an expert in all of them as a developer, but it can be useful to know about them to some extent.

Programming Paradigm List

Here is the list of the programming paradigms I cover in this text:

  • Structured Programming
  • Functional Programming
  • Object Oriented Programming
  • Data Oriented Programming
  • Composable Programming
    • Dependency Injection
  • Declarative Programming
  • Top Down vs. Bottom Up
  • Minimalist vs. Maximalist
  • Adaptive Programming
  • Evolutionary Software Development
  • Ego Driven Development
    • CV Driven Development
    • Preference Driven Development
    • Because-I-Say-So Driven Development

I will elaborate a bit more on each of these paradigms in the following sections, but since each paradigm could warrant a book of its own, I won't be able to cover every little detail of these paradigms here.

Structured Programming

Structured programming (SP) is the most basic programming paradigm in my opinion. Structured programming is about how to write readable code, such as using saying variable and method names, how to use functions, for + while loops, recursion etc. Structured programming also teaches how to program without the GOTO instruction found in some languages. Structured programming is sometimes referred to as procedural programming. A "procedure" is another word for a function. In some languages (such as Pascal) the distinction between a function and a procedure is that a function returns a value whereas a procedure does not.

Functional Programming

Functional programming (FP) is a paradigm that teaches using functions to structure your application as functions calling other functions. Functions are considered to be first class "objects" - or "data", meaning functions can be passed around as objects / data - and later be called where and when needed. Functions can be composed at runtime from other functions. Functions can have no side effects. Recursion is preferred over iteration when processing collections or arrays of elements. There is a lot more to functional programming than this, but covering it all here will take too much space.

Object Oriented Programming

Object oriented programming (OOP) is a paradigm that groups data and functions that belong together into classes. Functions inside classes are typically referred to as methods. Classes can extend (inherit from) other classes and thereby inherit part or all of their data and functions. Some OOP languages have interfaces too, and other features such as traits, value classes etc.

Data Oriented Programming

Data oriented programming is a paradigm used for applications that need to process large amounts of data at high speeds. For instance, computer games or realtime analytics applications often need to process large amounts of data rapidly. The fastest way to process large amounts of data is to access it sequentially in RAM or on disk (preferably an SSD). Therefore, data oriented programming keeps the large amounts of data in big blocks either in RAM or on disk, so it can be processed sequentially. This is different from object oriented programming where the objects of an application can be scattered all over the RAM, and where you do not typically have any control over exactly where in RAM each object is located.

Composable Programming

Composable programming is a paradigm that emphasizes splitting (decomposing) your software into smaller, composable units. In functional programming those units are functions explicitly designed to be composable with other functions. In object oriented programming those units are objects that are explicitly designed to be composable with other objects. Additionally, compositional OOP tends to break with some of the classical OOP principles, such as encapsulation, the "has-a / is a" rule, or the "the object should know how to XYZ" rule.

Dependency Injection

By the way, dependency injection is a technique very commonly used to achieve dynamic composability of components. Dynamic composability is opposed to static composability. In static composability the total composition is hardwired in the code. In dynamic composability the composition can be configured (and sometimes reconfigured) at runtime - e.g. by injecting different dependencies into components.

Declarative Programming

Declarative programming is a style where the code is written using a syntax that merely states what has to be done - not how it should be done. Declarative programming languages usually have a significantly different syntax than imperative programming languages (such as C, C#, Java, JavaScript etc.).

Declarative programming languages are usually designed for more domain specific logic - such as querying databases (SQL), expressing state (JSON / YAML) or express the composition of a web page (HTML). For those domain specific use cases declarative programming languages are often quite useful.

However, as a general paradigm that should be applied to all programming problems in general, I am more skeptical, as I have expressed in more detail in The Declarative Delusion.

Top Down vs Bottom Up Design

Top down and bottom up design are two different approaches to where you start designing your software. Top down design means starting from the "top" of the total system, breaking the system first into its bigger pieces, and then each of those pieces get broken into smaller pieces. This process is carried out recursively, until the smallest sensible pieces have been reached.

Bottom up design means starting the design process by identifying the smallest sensible pieces first, and then working your way up to the larger pieces recursively, until the top of the component structure has been reached.

In practice you will often end up doing both some top down design and some bottom up design, depending on what part your are designing, and where in the software's lifecycle you are (early / mid / late) .

Minimalist vs. Maximalist Design

Minimalist and maximalist design are two different approaches to how to scope the functionality of an application, framework, toolkit or component. The minimalist approach is to minimize the scope and responsibility of the software, making the software small and focused - typically on a single responsibility. The maximalist approach is to address as many problems as possible, meaning having as many features as possible.

The minimalist approach is often (but not always) used to scope small components, either within a larger software system or within toolkits or frameworks. The maximalist approach is more often used to

Adaptive Programming

Adaptive programming is a paradigm where your design choices are adapted to your concrete needs, and not derived from doctrines such as strict FP or OOP. Your needs drive the design. When a given design meets your needs, that design is good enough. That does not mean that one design is the only design that could meet your needs. Multiple designs could potentially meet your needs. Deciding between them is up to you - to determine which one brings you the most benefits besides just meeting your needs. However, meeting your needs is the most essential requirement in adaptive programming.

Adaptive programming also adapts software design to the context of your project. A small code base does not need nearly as much structure as a large code base. A code base that will only live shortly does not require as much structure as a code base that will live for many years or decades. A code base with a single developer (you) does not need as much structure as a code base with hundreds or thousands of developers. As the context of your project changes, so should the design - to adapt to the current context. This is what I refer to as "design realignment" in Evolutionary Software Development.

Evolutionary Software Development

Evolutionary software development is a paradigm where software is developed one release at a time, starting from a minimum viable product (MVP). From the MVP small feature sets are added one release at a time, as quickly as it makes sense for the project. As the software evolves, the design should be realigned with the current design needs - as also mentioned above in "Adaptive Programming". I have described evolutionary software development in more detail in Evolutionary Software Development.

In evolutionary software development we try not to design for tomorrow - but just to design for today. Obviously, if you know ahead of time that your needs will soon change, and you know exactly how, and you know that changing the design later will be a lot of work, designing a few deliveries ahead is probably not a bad idea. But we don't do speculative design for "future needs we may get". We only design for today, and the near future we know for certain will come.

Ego Driven Development

Ego driven development is a paradigm where the design choices of a software project are made to suit the ego of the developer(s) - more than to suit the needs of the project. There are a few subcategories within ego driven development, such as CV driven development and preference driven development.

Ego driven development rarely leeds to great software design - but sometimes it does - if the chosen designs are actually a good match for the project (sometimes that does happen).

CV Driven Development

In CV driven development the developer(s) choose tools and designs that they believe will look good on their CV. Is event driven architecture and Kafka hot? Cool - we will use event driven architecture and Kafka here too.

Preference Driven Development

Preference driven development is a paradigm where the tools and designs are chosen according to the personal preferences of the developer(s). A developer i love with functional programming might only use functional programming techniques. A developer with a strong interest in performance might insist that all parts of the system be highly performance optimized, even if that is not necessary for the given project.

Because-I-Say-So Driven Development

A few developers have an ego that is so large that they absolutely must always have their fingerprint on the design of almost all the code. They will argue in favor of their ideas, their design, their processes - not because they make a whole lot of sense for the project - but because they have a need to feel "superior" in some way to the rest of their team. Okay, this is perhaps a bit exaggerated - but you get the point. That is what I call because-I-say-so driven development - because once you start drilling into their reasoning - it all seems to end at "because I say so" - and not at sound first principles.

Needless to say, because-I-say-so driven development quite often do not beneficial to a software project, and it usually also creates a negative atmosphere on a team.

Jakob Jenkov

Featured Videos

Core Software Performance Optimization Principles

Thread Congestion in Java - Video Tutorial

Close TOC

All Trails

Trail TOC

Page TOC