What is Procedural Programming?

Procedural programming focuses on separating code into reusable blocks of code called procedures. The goal of procedural programming is to decompose projects into modules and procedures, and enable teams of developers to easily work in parallel on large projects.

While often viewed as a superset of structured programming, procedural programming was actually developed before structured programming. The many overlapping characteristics of the two has however made it known as an evolution of structured programming.

Characteristics of Procedural Programming

Prodedures

A procedure is the basic unit of work in the procedural paradigm. However, you may know procedures better as functions, methods, or subroutines.

While we covered much of this when learning about the structured paradigm, we'll review for clarity. Typically, a procedure will contain parameters that allow the programmer to specify inputs into the procedure. The function can then use those to modify data and return, or output the result of the function.

function get_first_element(array) returns number {
 return array[0];
}

> An important note when comparing the procedural paradigm to the structured paradigm is that there is no explicit discouragement towards jump statements like goto's, where structural programming often prohibits jumps in favor of code that executes sequentially. > > This isn't to say that procedural programming encourages the practice, but that it merely doesn't take a stance on the matter.

Many programming languages, especially "C-like" languages reserve a special function called main(). The purpose of the main function is to act as the starting point of the program:

// A typical main function in C++
int main()
{
 std::cout << "Hello, world!" << std::endl;
 return 0;
}

The ultimate goal of a procedure is to break a large task into smaller sub-tasks that are easier to understand. This is where the next characteristic of the procedural paradigm comes into play: modularity.

Modularity

Taking a modular approach to development means separating your code into modules containing procedures that can be combined to create new functionality. For example, in Python, a module is a single .py file which can contain classes and methods. If done properly, you can build up a set of tools that can be used in many projects.

In fact, there are large databases entirely devoted to cataloging modules of code made and maintained by people all around the world. In Python, you might use Pip to download these modules, whereas in JavaScript (specifically Node.js), you can use Npm. All of this sharing and re-use of modular code libraries has collectively saved centuries, if not millennia of development time.

When a program is highly modular, new features can often be created by pairing a combination of existing features with a handful of new code. This is often a desirable practice -especially in large projects- for the many benefits that can come from it like:

  • Procedures with less code are generally easier to understand, which leads to fewer code defects (bugs).
  • Fixing a bug in a module fixes the bug everywhere the module is used, as opposed to everywhere a function was copied-and-pasted.
    • Similarly, improving the performance of code within a module improves the performance of every program that uses your module!
  • Development is simplified on larger projects with many people working on different modules at the same time.
  • Well-written modules can (ideally) be re-used in future projects, saving time and development costs.
  • Breaking code into modules can save on program compilation time.

Variables and Variable Scope

Lastly, and also similarly to structural programming, procedural programming often makes use of variables and variable scope. Variable scoping is the practice of preventing blocks of code from modifying variables that exist outside the context of the current executing block.

What this means for the procedural paradigm is that procedures can contain local-variables that cannot be modified by other procedures. This is very helpful for reasoning and logic, and making sure two procedures don't step on each other's toes, so to speak. For example, consider the following:

var y = 100;

function foo() {
 var x = "hello world!";
 return x;
}

function bar() {
 var x = 42;
 return x + y;
}

Even though both of these functions have a variable called x, they're not the same x variable. Additionally, the variable x doesn't exist outside the context of the two functions in any capacity. This allows procedures to live in their own bubble, although they do still have the ability to access global variables defined within the module, as we see with the bar() procedure referencing the global y variable.

> Generally speaking, you want to avoid global variables unless you have a good use-case for them. Using them too-often can lead to code that is difficult to understand!