A programming paradigm is a fundamental approach to solving computational problems. But what exactly is a computational problem?

In essence, a computational problem involves working with information—how it is stored, processed, and manipulated to achieve a desired outcome. Examples of computational problems include:

  1. Finding specific information within a larger dataset
    • For instance, searching for a particular word across multiple text files.
  2. Deriving new information from existing data
    • This could be as simple as calculating the sum of two numbers.
  3. Organizing a set of information in a specific way
    • For example, sorting files or arranging items alphabetically.

As computational problems grow larger and more complex, so do the solutions to those problems.

The more complex and lengthy a solution becomes, the greater the likelihood it will contain bugs and errors. This is where programming paradigms come into play. A programming paradigm offers a structured approach to tackling computational problems, aiming to minimize the chances of bugs and errors.

Programming paradigms achieve this by imposing certain constraints or guidelines on how solutions are designed and implemented. These restrictions help prevent risky operations that are prone to causing errors, ultimately making the code safer and more robust.


Many modern programming languages support multiple paradigms, blurring the lines between them. This makes it difficult to distinguish between paradigms and to understand the specific types of problems each one is designed to solve.

Listening to or reading debates about object-oriented programming (OOP) versus functional programming (FP) might give the impression that each paradigm represents a radically different approach. In reality, however, they often share more similarities than differences.

Declarative vs Imperative

Declarative and imperative paradigms represent two fundamentally different philosophies for defining how programs operate.

Understanding the distinction between these two paradigms is essential, as most other programming paradigms are either subsets or extensions of them.

The imperative paradigm emphasizes how to achieve a desired outcome by explicitly detailing each step of the process.

In contrast, the declarative paradigm focuses on what the desired outcome is, delegating the details of how to achieve it to the underlying system or framework.

Imperative

Imperative programming is perhaps one of the easiest paradigms to understand and is often the first approach that people learn.

Imperative programming focuses on:

  • A sequence of commands that alter the machine’s state.
  • The step-by-step order of instructions.
  • The flow of control, emphasizing the order of execution.

Early programming languages, such as Assembly, were imperative. A program was a step-by-step list of instructions that altered the state of the machine by setting a specific value in a particular register.

The program executed the code from top to bottom, and the only way to change the order of execution was by using the goto statement.

Early imperative languages lacked functions, objects, if-else statements, for/while loops, and the concepts, such as encapsulation, inheritance or polymorphism.

The lack of these basic features quickly became a problem, which led to the emergence of other paradigms, such as:

  • procedural programming – allowed instructions to be grouped into subroutines (functions). This paradigm introduced key concepts, including scope, encapsulation, and code reusability.
  • structured programming – was a response to the dangerous goto statements. This paradigm reduced program flow composition to just three control structures: sequence, selection (if, else), and iteration (while, for).

Declarative

Imperative languages like C provide a high level of control over the machine (e.g., memory allocation), but they tend to be difficult to write, hard to maintain, and not particularly expressive, making them less readable.

More importantly, sharing and altering the machine’s state across subroutines and threads significantly increases the risk of bugs and errors.

This is where declarative languages come into play. They are designed to express a program’s intention rather than its concrete implementation. These languages often feature a higher level of abstraction or are tailored to a specific domain.

SQL is an example of a declarative language: a high-level, domain-specific language that expresses the program’s intention rather than its implementation.


Second part of this article will look more closely into structured, object-oriented and functional programming.