For the last ten years I have been looking at programmers in the market, doing great and small feats with them, mainly in the field of web development. But, unfortunately, there are less and less worthy candidates. They work over the years on the same tasks, cloning existing solutions and their shortcomings. You ask about what you have achieved - and in response to routine, banal things. Window automation is what most of these programmers do. And on really difficult tasks, as there were few specialists, it remains to this day.
Unix-programmers stand out against the background of these "window automations". In the world of open source software, everyone can try their hand at developing system software and in complex application solutions. The achievements of such people in the open source world are accessible to everyone, and for me they sometimes replace any recommendations. Because their work is visible.
There are a number of books that, in my opinion, are a kind of “bible” for those who decided to link their future with software development. I would like to start a series of articles with one of them. This is Eric Rhinemond's book, The Art of Unix Programming
. I would recommend this book not only to those who have chosen open operating systems for themselves. It is based on a fairly universal philosophy, suitable for absolutely everyone who linked their profession to programming.
Eric Raymond outlines the 17 rules of this "philosophy." I will dedicate one note to each rule. I will try to present these concepts in the most understandable, simplified and popular form, to the extent possible.
Let's start with the very first rule - Modularity Rules. It sounds like this: “Simple blocks communicate with each other with clear and understandable interfaces” (Rule of Modularity: Write simple parts connected by clean interfaces).
In other words, at the design stage it is necessary to try to break the target system into a set of simple blocks and try not to reinvent the wheel in the interaction between them, to adhere to this uniformity. It is very important that each block should do only one thing and do it well.
Two real-life examples: 1) a classic home PC from a system unit and a monitor 2) a laptop. For example, on both the screen went out. The reason may be anything, as we understand. But in the case of a home PC, it's easy to check the first assumption: does the monitor work on another computer? if not working, the reason is in the monitor. If yes - the reason is in the computer. Then you can try the video card on another computer - and immediately understand whether it is in it or not. The situation is similar with the keyboard, mouse ...
In the case of a laptop, everything is more complicated. For a simple philistine, he has two states: either working or not. If something fails, finding the cause is not so easy. The reason for the failure of the screen can be as a software failure, and mechanical damage. Yes, for the master it is different components, but for you - one laptop. Therefore, it is more expensive to determine the fault than in the previous example. It is necessary, at least, to contact the service workshop for the primary diagnosis. In the first case, you can determine the malfunction yourself and in the event of a monitor failure, only bring it to the service.
The same with software development. Each component must perform one very simple task. And in an understandable way to integrate with other similar components. The more universal such a mechanism, the better.
From our practice: we are now developing a service for selecting accessories and analogues for Svyaznoy products. By itself, this is a rather complicated system, but if it is considered as a separate component, the solution immediately takes on a beautiful and complete look. We will implement it in the call center, in the electronic catalog software, on the Svyaznoy website, in the upsale mailing system. And the clearer and clearer we will make its external interfaces, the easier it will be to introduce it into a new system, about which, perhaps, we have not even thought about it yet.
The simplicity of interfaces allows, for example, to put a component in a test environment, where all use cases will be emulated and where it will be checked whether the calculated result is as expected. For example, again an example from our practice - when calculating the delivery time a table was developed, where for each test case the delivery time was calculated manually, and then for any changes the tests showed deviations or their absence.
Once upon a time, after the institute, I wrote a system for designing air ducts
for the Ryazan company Ekovent. There it was possible to create an arbitrary model of air ducts - pipes with branches in all three dimensions. At some point, the whole program part had to be completely redone, because it simply became too complicated. The individual components responsible for the design change, such as, for example, an element, had a text interface in the form of text commands like "add block width = 100 height = 100 depth = 10". Of course, no one forced the user to type such commands, for this there was a separate graphical window interface, where he entered width, height, depth, but then the form was still converted into a command. This made it very easy to debug complex situations when it was necessary to create hundreds of objects and render them.
In the UNIX world, one unified command line interface has long been invented; it is called a “pipe” (pipe, |). Through the channels you can very easily connect different programs that perform one, very simple task. For example, there is a utility that counts the number of lines, characters (wc), and a utility that displays the contents of a file (cat). Putting them one after another, we can get, for example, the number of lines in the file (cat file | wc -l). And there are already a lot of such utilities. The ability to combine them on the fly helps to solve quite complex tasks in literally seconds.
In the end, a few simple tips are my consequences of the modularity rule:
- Each block, program, module should do only one thing and do it well. If the goal, the purpose of the block begins to "smear", it is worth thinking about its division into parts.
- If possible, choose interfaces from among industry standards (XML, HTTP, RPC, CSV, JSON) and in no case go beyond them.
- If you can simplify the interface by increasing the number of blocks, simplify it. Let the blocks be larger, but it will be possible to test them separately. Of course, it is normal when the blocks are not by themselves, but arranged in a hierarchy.
- If it is possible to simplify the functionality of the block, breaking it into smaller blocks, with a more precise task to be performed, with standard interfaces, without noticeable performance degradation - break. Even if there is a detriment in productivity - think carefully about it, sometimes they are worth sacrificing. "Iron" is now cheaper.
» Continued: clarity rule