The technical content in this article is intended for software developers who have written at least one significant application. It does not need to be in the realm of GUI programming, but that is my own expertise and this article will reflect that.
In this article, we will:
- Briefly summarize the difference between principles and rules
- Discuss the principle of when it is actually better to not follow a principle (ironic, no?)
- Refine this principle for programmers using some specific examples
Principles And Rules
Before we proceed, let me briefly define these two words for the purposes of this article:
- A principle is an instruction (or set of instructions) which when followed, almost always leads to a better outcome than if one does not follow said instruction(s) most of the time
- A rule is an escapable reality which remains constant in all situations, whether you follow it or not
It is not important that you agree with these definitions outside of the context of this article. Words are not the concepts they point to, so please pay attention to what I am pointing instead.
Here is a principle we observe in building just about anything:
Start with the most general details, and gradually add more details from there
To apply this example to buildings instead of programs for a moment, it looks something like this:
- The idea: I want to build a condominium (like an apartment but you buy the living space instead of renting)
- The model: I create a blueprint of the condominium
- The foundation: I carve out the earth, pour the concrete, and build the frame of this structure
- The body: I build walls, stairs, ceilings, doors, windows, electrical, plumbing, etc…
- The finishing touches: I paint, install appliances, add decorations, furniture, carpets, etc…
To apply this to programming for a moment:
- The idea: I want to build a social media application
- The model: I create user stories/problem statements, UML diagrams, and UI designs/mock-ups of this application
- The domain: I create the most fundamental classes/source files, interfaces/protocols, constants, enums, and variables as a foundation to build upon
- The implementation: I begin to build all required classes/source files, wire them together, and write code necessary to interact with the various Input/Output devices (network adapters like REST, databases, system services)
- The quality assurance: I test the application to ensure it is bug free, responsive, efficient, stable, and looks good on a variety of different devices before it goes to production
Now, obviously there are differences of opinions about which of those steps should be followed and in what order; concerning both buildings and programs.
However, I suggest to you that if your contractors are waiting to begin pouring concrete, but you are holding them up until you decide whether to go with white or brown leather sofas in the lobby, then you are horribly confused about the order of operations in building a large and complicated structure.
Similarly, imagine that you have designed your application around the details of some particular cloud management service, and ninety precent of the way through building it, that cloud management service announces that it will be dismantled in less than a year (Parse API anyone?). Then you will learn why implementation details should be built around foundational classes/source files and not the other way around.
We can keep this simple and move on quickly. As a rule, if you do not have a sufficient amount of building materials to build your structure, then you will not be able to build your structure.
In terms of computation, devices have fixed limits on physical memory space and processesing power. There might be ways to work around these limits using brilliant data structures, algorithms, low level programming, or some good old fashioned hacks, but the limits remain invariant.
Principles & Project Requirements
To begin with a general definition of when not to follow a particular principle, I would phrase it like this:
Principles should be followed until one arrives in a situation or environment, where it makes more sense or causes less harm to not follow the principle.
To take an extreme example, I really do not like violence between human beings. In fact, I would be deeply happy to never end up in a situation again where any form of violence or confrontation was necessary. This is my principle, and it means that I will do whatever I can to avoid violence even in situations where it is a distinct possibility.
However, as just about anyone who has lived in a rough neighborhood understands, if find myself in a situation where an irrational person harms my friends, family, or an innocent person, and I conclude that physically intervening is the best way to stop things from getting worse, then I will do so without hesitation.
This is a rather extreme example, but it illustrates the point I am trying to make. Life is not always ideal, and blindly clinging to principles in all situations can be immoral and wrong. We must balance principles with wisdom and understanding of various situations.
Returning To Programs
One of the most important concepts for a developer to understand is simple in the general sense, but it typically changes for each project at least slightly. Project requirements refers to environmental and situational factors such as:
- Operating systems and platforms
- Languages, libraries, and frameworks
- Physical and virtual architecture
- Device limitations
- Project manager, developer, stakeholder, and customer preferences
- Resource constraints for staff, time, and money
That is just a brief list, but most of you reading this are probably working with small to medium scale projects where the main concern has to do with your own preferences and skill level.
We will now look at a few examples, some of which have popped up in recent discussions from my fellow students of programming.
Example #1: Model-View-ViewModel
Some of you may not be familiar with this pattern, so I will include a more general example next.
To pick the low hanging fruit first, I am very quick to point out that Model-View-ViewModel (MVVM) is a relatively inconsistent pattern. In principle, there are two approaches two this pattern which you will find emphasized by different teachers, often in exclusion of the other:
- ViewModels are decoupled from the details of any particular View and are thus reusable across many Views
- ViewModels contain a field/variable/property for every widget (individual piece of the UI) and are thus not reusable
To mention a technical point, in no case may your ViewModel reference (depend on) any particular View even if you take the second approach. The View must observe, subscribe, or bind to the ViewModel instead.
Some proponents of MVVM architecture strongly emphasize the reusability of ViewModels. Beginners to the pattern can then become confused (as I was initially) about what to do when a View has a lot of complex presentation logic and/or user interaction logic.
Prioritizing reusability of a ViewModel, is achieved by decoupling it from the details of a particular view (presentation logic and knowledge of the View’s widgets). This principle of reusability has the side effect of loosing fine-grained control over any particular View.
Also, filling a View full of logic is a breakage of separation of concerns, and I do believe that to be the single most important principle in the design of good software.
Therefore, I suggest two simple questions to ask yourself regarding project requirements and this particular pattern:
- Do you actually need to reuse that particular ViewModel?
- Is it more important to have fine-grained control of the View?
If the answers are no and yes respectively, then you can happily ignore the principle of reusability and once again pull the presentation logic and user interaction logic out of the View quite happily using the second approach.
Example #2: Program/Code To An Interface
To take a more generalized example, I am a big proponent of the principle commonly called “code to an interface.” This principle advises you to connect the different components of your applications together not by having them refer to each other through concrete class names, but rather through interfaces, protocols, or abstract classes.
Again, a mistake I made early on which is quite common, was to apply this principle in situations where it really was just a pointless extra layer of abstraction. These days, when I teach this principle to beginners, I try to emphasize the idea that you must examine your project requirements (including the requirements of your individual classes/components) in order to determine whether following this principle is actually going to help you:
- Does this component represent a significant architectural boundary of the application, such as the bridge/interface between the front end and the back end?
- Do you expect this component to change frequently?
- Does adding an interface allow you to build and test (verify functionality) different components in isolation of each other?
- Do you find that writing the interface first is actually useful as a means of designing classes and their interactions before getting bogged down in implementation details?
- Is the project you are working on large and complex?
- Are you deliberately trying to build a small and simple application just to practice this principle for when you write large and complex applications?
If the answer to all of those questions is no, then I do not see any point in installing an interface over a concrete class. Unless you happen to be wrong in your assessment (everyone is wrong sometimes, even the geniuses), it really is just a typing exercise.
I know that this particular article is devoid of code examples, but I wanted to keep it as language/platform agnostic as possible. Maybe none of the examples I gave you were relevant to your particular situation, but the point of examining project requirements in order to determine when to follow or deviate from principles, remains constant.
If you are an absolute beginner and have not written any significant applications, it is too early for you to be worrying about this stuff. I suggest you to ground your understanding of project requirements with reference experiences, and do not be afraid to carry principles too far if for no other reason than to figure what too far means in practice. That is generally how I did it, and it leads to a far deeper understanding that simply reading about these things.
Social Media | Support
This article was written by Ryan Michael Kay. I am a self-taught programmer/engineer who creates educational content for on a wide variety of topics, on a wide variety of platforms. The best way to support me is to follow me on various platforms and join in with my developer community (we have hundreds of members!):
Tutorials & Courses:
Free Tutorials, Live Q&A, Live Coding:
Java Desktop Programming w/ JavaFX (Intermediate) — https://skl.sh/31pzCa1
Complete Beginner Introduction To Java Programming (Beginner — Intermediate) — https://skl.sh/3fZbjos
Android Apps With Kotlin & Android Studio (Beginner) — https://skl.sh/2ZU6ZT9
Material Design Android Programming w/ Kotlin (Intermediate) — https://skl.sh/2OrwrYZ