Code Navigation, Design Patterns, and Too Many Functions

Published on 2010-10-12.

When going into extremes in using a specific programming style or a specific design pattern the result is always bad code - even if it looks nice.

In software engineering, a design pattern is a reusable solution to a commonly occurring problem in software design. A design pattern is not a finished design that can be transformed directly into code. It is a description or an idea on how to solve a problem that can be used in many different situations. Object-oriented design patterns typically show relationships and interactions between classes or objects, without specifying the final application classes or objects that are involved.

Sometimes you need to refactor your code and this gives you a great opportunity to consider wether you can benefit from implementing a specific design pattern. Refactoring code means that you change the source code of a program, making it better in some way, but without changing the external function of the program. Some of the advantages of refactoring include improved code readability and reduced complexity to improve the maintainability of the source code, as well as a more expressive internal architecture or object model to improve extensibility. Martin Fowler's book Refactoring: Improving the Design of Existing Code is the "canonical reference" on the issue.

NOTE: As with everything, you need to balance your approach. Some people have read Martin Fowler's book religiously and they have caused a lot of damage to the software industry.

Refactoring is usually motivated by noticing a code smell (something that indicates a deeper problem). For example the function at hand may be very long, or it may be a near duplicate of another nearby function. Once recognized, such problems can be addressed by refactoring the source code, or transforming it into a new form that behaves the same as before but that no longer "smells".

Some typical code smells are:

Experienced programmers understand the problems with, for example, duplicated code. When you update the code in one place you have to remember to update the code everywhere. Using refactoring, and possibly (but not always) a well known design pattern, can help greatly to eliminate code duplication.

When we try to improve ourselves, and in this case more specifically our code, we sometimes get hung up in the philosophy of a particular pattern or idea and tend to forget to think practical. In other words: We go into the extreme.

As with everything in life we have to try to keep a nice balance.

We should experiment with different ways to solve problems and also gain experience by looking at how other more experienced people solve problems.

The other day I was looking trough the code of a couple of the popular PHP frameworks and it was clear that the programmers was following well known design patterns in the design of their code. The code was very readable and it all looked very nice. However, there was one major problem in common with these systems that turned all of them into horrible complex beasts. The philosophy of the design patterns and refactoring principles where religious followed to such an extend that the systems actually ended up being "cluttered" and "messy".

One system contained too many methods. Every single individual piece of code execution was contained in a single method! One method would call another method that again would call another method that once more would depend upon the return value of yet another method. All of this was nested very deep. The result is not only a horrible performance, but it very quickly becomes very tedious to follow the code through the many different function calls in the many different files.

Trying to understand the different parts of such a system is like navigating a huge maze with many different paths that all interconnect. It's a nightmare.

Sure, I can reuse a method that performs some kind of calculation, but if I really only need that calculation performed in one specific place in the code, there is no reason to turn that into a method, just run the code then and there.

It is beneficial to think about what programming paradigms that will make things more readable and easy to navigate, but it is even more important to consider efficiency and simplicity.

If the code is well written and well structured it is sometimes more easy to navigate and understand a single file containing thousand lines of procedural code, than it is to navigate a maze of small nested functions in a thousand files.

When code is working from within a function it is possible to test the function in isolation from the rest of the system, and that is beneficial from a testing point of view, but adding yet another function even though no code duplication exists and no return value is needed more than once, makes the code more complicated to navigate.

TIP: Don't split a function for no other reason than it is "too long". Linear code is always more readable.

We must not get caught up in the philosophy or idea behind a specific pattern or solution. Our main concern is to keep the code easy to read, navigate and understand, and as a result easy to maintain and easy to keep secure and performant.

We must also remember that there exist such a thing as an anti-pattern. It is a design pattern that may be commonly used but is ineffective and/or counterproductive in practice.