Tony Marston's Blog About software development, PHP and OOP

Programmer Productivity takes Precedence over Paradigm Purity

Posted on 10th July 2024 by Tony Marston

Amended on 16th August 2024

Introduction
Not-so-best practices
Better practices
Previous experience
Switching Languages
Learning Object Oriented Programming
My implementing of OOP
Adding more reusable components
The results of this volume of reusability
References
Amendment History
Comments

Introduction

Let me start with some universal truths:

The topic of "best practices" is often talked about as if it were a set of rules that is cast in stone and universally accepted by every programmer on the planet, but that is not the case. What some people regard as being best for them others will dismiss as second-best or even nowhere-near-best. In the 1980s and 90s I worked in several teams for different organisations, and each team had its own set of programming standards. The idea of a single set of universal standards simply did not exist, mainly because getting a group of programmers to reach a consensus on the definition of "best practice" is like herding cats, with the likelihood of success being inversely proportional to the size of the herd.

In those decades I was mostly employed by software houses where we designed and built bespoke solutions for different clients, and as we had to compete against other software house for business we had to demonstrate that we were the most cost-effective, that we had higher rates of productivity. By the time I became team leader I had begun to formulate my own set of standards by sifting through what I had encountered previously and filtering out any practices which I saw as getting in the way of productivity. I did not follow a rule as if it was cast in stone, I examined it, and if it did not measure up to my expectations I discarded it in favour of something better. By "better" I mean the most cost-effective. This means that I am results-oriented, not rules-oriented. I am a pragmatist, not a dogmatist.

I have built enterprise applications in COBOL, UNIFACE and PHP, and because they are different what works in one language may not work in another. While the objectives remained the same, the means of achieving those objectives were different in each language. In other words "what needs to be done" was consistent, but "how can it be done" has an infinite number of variations, and each team has its own version of "how" based on their set of experiences. It is one thing to learn the capabilities of the language, but it is another to learn how to utilise those capabilities to best effect. Whenever a new version of the language was released I took the time to examine any new features, and if any looked as if they could add value to the code I would evaluate them to compare the costs against their benefits. Anything which enabled me to replace duplicated code with reusable code, or enabled me to do something with less code, and hence increased my productivity, was always beneficial.

When I switched to PHP with its Object Oriented capabilities at the start of this century I carried on with this habit. After learning the mechanics of Encapsulation, Inheritance and Polymorphism I began writing code which incorporated high cohesion and loose coupling to produce the best results with the least amount of effort, thus contributing to higher levels of productivity. After completing several modules I would often see repeating patterns that could be turned into code that could be reused instead of being duplicated. Another practice I follow to reduce the amount of code which I write is not to write code that is not necessary, to always choose the simplest solution instead of a complex one which invariably ends up by resembling a Rube Goldberg machine.

I didn't follow what was later pointed out to be "best practices" for the simple reason that I didn't know that they existed. Fellow developers began to ridicule my work by saying You are not following *this* principle or *that* principle, therefore your work is wrong and inferior. When I examined these so-called "superior" techniques I detected a huge flaw in their arguments - the results which they achieved were not as good as mine, for reasons such as:

By following only those practices which actively contribute to my high levels of productivity (what used to take me 1 week in COBOL and then 1 day in UNIFACE I reduced to 5 minutes in PHP) I have found myself discarding quite a number of practices and principles which other programmers regard as being sacrosanct. They criticise me for having the audacity to break their precious rules while totally ignoring the results which I have achieved. I single-handedly designed and built the RADICORE framework which I released as open source in 2006, and I used this framework to single-handedly design and build TRANSIX, my first ERP package application, in 2008. This has now grown into the GM-X Application Suite which has been sold on three continents.

Not-so-best practices

Ever since I started publishing articles regarding my experiences with PHP other programmers have told me that the code which I write and the methods which I use are completely wrong simply because I am not following the same practices as them. I refute this claim for the simple reason that my methods cannot be wrong simply because they work, and something that works cannot be wrong just as something which does not work cannot be right. I did not follow these practices for one simple reason - I did not know that they existed. When I came to examine them I quickly realised that by changing my code to conform to this "advice" it would have a negative effect on my productivity by using code that was more complicated than mine, more convoluted than mine and less efficient than mine, so I did the only sensible thing which was to ignore it, to consign it to the dustbin, to flush it down the toilet.

To counter my detractors I have written a number of blog posts which identify the "best practices" which I choose to ignore as well as explain precisely why I ignore them and why I believe that my own practices are better. These are listed below:

  1. Object Relational Mappers are EVIL

    The "need" for an ORM is caused by deliberately using one methodology to design the database and a separate methodology to design the software, thereby causing a mismatch. My solution is not to create this mismatch in the first place. I design the database first, then build the software around that design using my framework.

  2. Dependency Injection is EVIL

    Some programmers insist that I use a complicated mechanism to inject every dependency even when there is not a selection of dependencies to choose from. While I do inject entities into services where there are multiple choices, I do not inject entities into entities as there is never more than a single choice.

  3. Not-so-SOLID OO principles

    I only follow those principles which I consider to be appropriate.

  4. I fail to GRASP these principles

    I only follow those principles which I consider to be appropriate.

  5. Singletons are NOT evil

    Singletons can be implemented in one of two ways - either as a separate method within each class, or as a static method within a single singleton class. The latter choice does not have the problems encountered in the former.

  6. Getters and Setters are EVIL

    In each of my table classes I do NOT have a separate property for each column with its own getter and setter, I pass all the data around, both in and out, in a single array argument which is precisely how it is handled in the HTML front end and the SQL back end. This reduces the amount of code which I have to write, and directly contributes to loose coupling which is supposed to be a good thing.

  7. Object Interfaces are EVIL

    I don't use object interfaces as they were invented for a problem which does not exist in PHP. I get better results from using abstract classes.

  8. Object Associations are EVIL

    The "approved" technique is to handle associations and aggregations using custom code with each entity. I prefer to use standard solutions which I have built into the framework.

  9. Inheritance is NOT evil

    The rule "favour composition over inheritance" was formulated by someone who never learned how to use inheritance properly. Instead of inheriting from one concrete class to create a different concrete class you should only ever inherit from an abstract class. As well as giving instant access to reusable code it also enables the Template Method Pattern which lies at the heart of framework design.

  10. Composition is a procedural technique for code reuse

    As an argument against inheritance some bright spark argued that "Inheritance is a procedural technique for code reuse", but as someone who programmed in COBOL, the most widely used procedural language of all time, I know that this is not true. Instead I contend that it is object composition which deserves that description.

  11. Namespaces are for numpties

    Namespaces were added to PHP to solve a problem when importing third-party libraries, but as RADICORE is a framework and not a library the effort of changing my code would be a complete waste of time.

  12. Autoloaders are abominations

    Autoloaders are the solution to the self-inflicted problem of requiring multiple files to be loaded from multiple locations before a class can be instantiated. I only have a single file for each entity's class and I keep the number of locations to a minimum, thus conforming to the principles of Encapsulation and high cohesion and avoiding the problem completely.

  13. Decoupling is delusional

    The idea that "decoupling" your software is a good idea shows a complete lack of understanding of the term "coupling". If there is a call from one module to another then they are coupled whether you like it or not, and the strength of that coupling is either tight or loose. To "decouple" means to remove the call. To introduce a third module to act as an intermediary between the first two does not remove the coupling, it actually doubles it by replacing one method call with two.

  14. Strict typing is for stick-in-the-muds

    PHP was designed from the outset to be dynamically and weakly typed, which means that type errors are not detected until run time, and values can be automatically coerced into the desired type. Millions of programmers have become used to this approach which is also used by 60% of all programming languages. Despite this there are some stick-in-the-muds whose brains are still fixated on the idea that strict typing is "best", so they have forced the language to be modified to suit their preferences. Despite the fact that the use of strict types is supposed to be entirely optional, those ham-fisted core developers made a cock-up made it obligatory for all internal functions.

  15. Value objects are worthless

    OO purist love to say that "everything is an object", which is why they favour the use of value objects instead of primitives/scalars. However, PHP does not have any native support for value objects, and neither does HTML nor SQL, so to use them in PHP would require a great deal of effort. Having successfully used primitives in PHP for the last 20 years I cannot see any benefit in amending my code to use value objects as there is nothing I can do with them that I cannot already do without them, so I regard them as a complete waste of time.

  16. I don't do Domain Driven Design

    An enterprise application may have to deal with different business areas for that organisation, such as Order Processing, Invoicing, Inventory/Stock Control and Shipments. Although each of these areas handles totally different data and totally different business rules, these differences can all be handled in a similar fashion. All the data is spread across numerous database tables, and each table can be handled in exactly the same way. The business rules will be different, but they can all be handled in the same way. By building these similarities into a separate system with components that can be shared, each business area can then be regarded as a separate sub-domain or sub-system as part of a larger domain or system.

    The RADICORE framework was built to develop database applications as it provides pre-written components to deal with all the similarities. Each subsystem is developed as an extension or add-in to the framework and then run under the control of the framework.


Better practices

In the previous section I identified those so-called "best practices" which I ignore. In this section I will describe my path to a set of practices which have yielded better results.


Previous experience

Everything started with the following basic observations from my previous experience of developing database applications using COBOL and UNIFACE. Note that during this time I had worked with a mixture of hierarchical, network and relational databases.


Switching Languages

Switching to a new language is not just about switching to a different syntax, it sometimes means switching to a different way of writing and structuring your code. With COBOL we used an ordinary text editor, so there was no debugger. The code we put into a single source file had a data division for data definitions, and a procedure division for code. The code itself consisted of statements, sentences (one or more statements terminated with a period), paragraphs and optional sections. The PERFORM statement was for performing a paragraph or section within the current source file while the CALL statement was for passing control to a subprogram/subroutine which was compiled from a separate source file. As a junior developer I had been taught to create a small number of monolithic programs which were large and complex, but later on, while developing my first Role Based Access Control system, I learned that a larger number of smaller but simpler programs were easier to build and maintain.

UNIFACE was totally different. Instead of a text editor we had to use a special piece of proprietary software known as a Graphical Form Painter (GFP) with which we built form components which referenced entities within its Application Model, an internal database. Entities (tables) were first constructed within the Application Model to define their columns, indexes and relationships, then exported to produce CREATE TABLE scripts in the chosen DBMS. Proc code was entered into various pre-named triggers where different triggers could be fired depending on different mouse movements. Each form component was compiled in the GFP and then run via the UNIFACE Runtime Engine. The early versions were 2-tier as the form component combined the user interface and the business logic into a single unit with all database access logic provided by a separate database driver which was provided by UNIFACE, where there was a different version for each DBMS. We never had to write any SQL queries ourselves as they were all generated by the database driver. Unfortunately each entity in a component could only build SQL queries for that particular table, which meant that it was not possible to build queries with JOINs to other tables, thus producing what is known as the N+1 Problem.

UNIFACE v7.2.04 allowed a form component to be split into two by separating the user interface from the business logic which was moved to a separate separate service component, one for each entity, thus converting it from 2-tier into 3-tier. The transfer of data between the outer presentation layer and the middle business layer was via XML streams. This version also introduced the idea of Component Templates.

UNIFACE was originally developed to create desktop applications, but when they saw the growing demand for internet applications they decided to add in the capability to create web pages. I found their approach to be too clumsy and too clunky. I only worked on one project which attempted to use this new capability, and it was a complete and utter disaster. I decided that UNIFACE was the wrong choice when it came to building web applications, so I looked for a different language that was fit for this purpose. That is when I discovered PHP. I played with it, I liked what I saw, (especially the ability to Generate dynamic web pages using XSL and XML), so I decided to build another version of my development framework to see if I could boost my levels of productivity.

PHP uses scripts which can be constructed using a simple text editor, but I prefer to use a full blown Integrated Development Environment (IDE) with colour coding and debugging. PHP is a multi-paradigm language in that you can write code which is completely procedural, or you can use objects if you want to. It does not force any particular structure on the developer, so you can choose whatever structure you like whether that is monolithic or multi-tier. I found PHP very easy to learn due to the quality of the online manual and the sample code which was available in numerous online resources and books. I was particularly impressed with arrays which could be indexed, associative or multi-level and which were much better at handling collections of data items, such as database data, than the records or aggregated data types that I had used before.

I quickly accustomed myself to the fact that a PHP script could be activated either by an HTTP GET request from the browser's address bar, or an HTTP POST from an HTML form. I learned how to overcome PHP's stateless nature by using the session handling functions, and I learned how to activate another script from within the current script using the header('location ...') function.


Learning Object Oriented Programming

While I had heard about this thing called "object oriented programming" during my work with UNIFACE I hadn't a clue what it meant or what made it so special, so I did some research on the interweb thingy. I found a few descriptions which used vague and meaningless terminology, but then I came across the following description:

Object Oriented Programming entails writing programs which are oriented around objects, thus taking advantage of Encapsulation, Inheritance and Polymorphism to increase code reuse and decrease code maintenance.

After looking at the descriptions in the PHP manual I identified the distinguishing features, those which exist in OO languages but not in non-OO languages, as follows:


My implementation of OOP

At this time I was not aware of all those things called "best practices" which I was supposed to follow, so I went ahead to see how I could take advantage of encapsulation and inheritance (with polymorphism still be a mystery) in order to fulfil the aims of OOP which is to increase code reuse and decrease code maintenance. My previous experience had already taught me that the more reusable code I had at my disposal then the less code I had to write, and that being able to achieve results by writing less code was the path to higher productivity.

There were several features I found in UNIFACE which influenced my approach to rebuilding my framework in PHP:

I experimented with ways to implement these ideas in PHP by first building a small sample application as a Proof of Concept (POC) using the following steps:


Adding more reusable components

As this prototype proved that all my ideas worked I then proceeded to build a brand new version of my framework which I called RADICORE. Full details can also be found in Evolution of the RADICORE framework. While building this framework I added to my library of reusable software in the following ways:


The results of this volume of reusability

As a result of these efforts I have managed to create these Levels of reusability.

Having this amount of reusable software at my disposal means that when I am developing an application there is a huge amount of code which I don't have to write, which can be summarised as follows:

  1. I don't need to waste any time with OOD or DDD
  2. I don't need to waste time in designing class hierarchies
  3. I don't need to waste any time choosing which design patterns to use
  4. I don't need to waste time in building each table class (Model) by hand
  5. I don't need to waste time in designing methods and properties for each table class
  6. I don't need to waste time with writing code to validate user input
  7. I don't need to waste time writing code for associations and aggregations
  8. I don't need to waste time with designing Controllers for each of my Model classes
  9. I don't need to waste time designing and building HTML Views for each web page
  10. I don't need to waste time writing code for each of the components on a web page
  11. I don't need to waste time in writing code to perform standard SQL queries
  12. I don't need to waste time with writing a separate Data Access Object (DAO) for each table
  13. I don't need to waste time writing code to build new transactions
  14. I don't need to waste time designing and building any Access Control Lists (ACL)
  15. I don't need to waste time designing and building menu structures
  16. I don't need to waste time writing detailed program specifications
  17. I don't need to waste time changing method signatures after changing a table's structure

References


Amendment History

16 Aug 2024 Added Better practices
27 Jug 2024 Added I don't do Domain Driven Design

counter