Where Programming Ends and Engineering Starts
Ask a developer if he is developing the same software as in his previous engagements, and most of the time the answer is "no". On first sight it looks reasonable, if you consider that he has been working on a different project, in a different company, or even within a different industry vertical.
But when we analyze the code that is actually developed, this is most peculiar. Code is made up of business logic and "plumbing". Business logic is around 5% of the total code and project specific. The rest is plumbing that is not specific to the requirements of the project. If 95% of the code is not specific to the project, how can it be that we are not writing mostly the same code?
The reason is often simple, the business logic is not isolated from the plumbing. This causes the plumbing code to become project contextual; the plumbing code is full of traces of business logic and thus specific to the project. Thus the potential 95% that should be project agnostic can't be reused in other projects. Not from implementation, design and/or architecture perspective.
Why Programming is not Engineering
In the context of this article we define programming as the activity to implement the logic, first dimension of software, that will meet the requirements of a project. It differs from engineering, that engineering covers not only the first dimension execution logic, but also the other three dimensions, and in specific the second dimension code structuring.
Find more on the dimensions of a software solution here.
The engineer is aiming to build an engine in which the business logic can be run. This allows the 5% business logic to be isolated from the 95% plumbing code. Building the engine is a repeatable task, that independent of the project has the same approach. Reuse of architecture, design, and sometimes even implementations. This implies that the engineer is almost always developing the same software.
Experience has thought that reimplementing the same software leads each iteration to shorter development times, and higher quality (less bugs). Thus this implies that an engineer writes better quality software and is more productive then a programmer.
Programmer or Engineer
The earlier selection criteria if a developer is a programmer or an engineer can be considered subjective. Wouldn't it be great if we can define an objective way to identify the difference between the programmer and the engineer? Especially as it would allow a developer to understand how to become an engineer. And thus write better quality software.
Software is implemented by systems, systems by modules, and modules by units. We try to aim to keep as many systems independent of business logic. And within the systems that are project specific, we try to keep the amount of modules that are specific to business logic to a minimum. And finally we try to keep the units that have business logic, as small as possible.
More information on the structuring of software can be found here.
There are specific techniques how to do this, and we will discuss some of them below.
Divide the software into multiple systems
Most of the projects are only implemented in a single system. Not only does this explicitly prevents code reuse, it also does not provide the simplest form of separating business logic from plumbing. For example lets take the non-functional requirements: we need to have a REST interface and authentication. Those requirements comeback in almost all projects, and thus we should be writing (or using) the same code as the other projects.
Note that a different system is not the same as a different service. The system can be linked directly, or bundled together, inside the same deployment package (archive, library or binary). It will be running in the same process.
Thus an objective measure is: do the projects you work on have separate systems, especially for non-functional requirements. If not, you should be introducing them as that will contribute to the separation of business logic from plumbing code.
Leverage a Logical Data Model
When you are leveraging directly Strings, Byte Arrays, Files, or Maps, then you are, almost always, not on the right path - to separate business logic and plumbing. You should be using a logical data model. What is a logical data model? A logical data model is a set of classes - code - that is used to represent an efficient expression language for business logic to operate on data.
Suppose we are having reports, and reports have multiple fields like "title", "author", and "notes". Using a Map is inefficient. Whenever we need to get the title, we must hope the creator of the map did actually populate the title field. Furthermore, the IDE is not helping you with indicating that the writer of the piece can be found in the field "author".
The amount of plumbing that is needed, to ensure the map is valid and that we can use the actual values inside that map, is significant and will be mixed inside the functions that implement the business logic.
Using Constructional Patterns
When constructing an object, you need to specify the dependencies of that object. The dependencies are determined by the implementation of the object. While how you want to construct the object, is dictated by the context of the business logic you are writing. How do you bridge this semantic gap?
The most popular solution is to do the plumbing directly in the callers code (the business logic). This pollutes the business logic with plumbing concerns.
The second most encounter solution is that the work is hidden in the constructor of the to-be-constructed object. Why this is less popular, because often we need database connections and other significant computations that really don't belong in scope of the responsibility of the new object.
The better solution is the least used one: use a creational pattern. Especially Builder pattern is used to have a different interface (expression language) exposed for the caller, and then convert this to the dependencies needed for the object under construction. This makes your business logic nice and clean, and allows your implementation code to be reused in other scenarios - other projects!
Writing working software is like making food that is edible. Yes, you won't starve out of hunger, but the eater won't be satisfied. We forgot the process that a professional should also make it taste well. This analogy explains why programmers can meet requirements, but businesses are not satisfied with the solutions. It is not as maintainable, extendible and of the quality as desired by the business.
There are clear subjective and objective indicators if software is build towards success or not. Most software developers are not separating business logic from plumbing, for example by leveraging design patterns. This lack of separation will be the main problem of your software to improve.