I continue the series of articles devoted to some simple rules for developing under Unix “according to
Eric Raymond ”, which, in my deepest conviction, can be extended to any other operating system. I have already told in the first three parts about the rules of
modularity ,
clarity ,
composition ,
separation and
simplicity . Today it came to the sixth rule -
Code savings rule: develop large programs only if there are objective reasons to do it There is a generation of programmers whom I call
write-only programmers . These are people who love to create, but do not have time to comprehend. They can in a very short time write a quite working monster, but after a week they already find it difficult to change
something in it. Especially if the change is not petty. The rule of code-economy echoes Raymond's other rules, moreover, it is inextricably linked with them. A module that solves one well-formulated problem should be small in volume. If this does not work, then you need to think, and is it possible to break one task into several smaller ones? In the original interpretation of this rule there are words 'by demonstration', which means that you should be able to prove to your colleagues in the shop that it is impossible to break further or impractical.
')
The development of a large amount of code immediately in a short time is often caused not only by the investor’s desire to get the product soon (although this is the most common reason). A greater misfortune is excessive perfectionism attached to the wrong place. Programmers who have completed the first version of the product, it becomes “boring” to do routine support and sluggish development and correcting their own mistakes, because they already understand perfectly well that they have done “wrong” from the very beginning. And they start everything from scratch. This is usually presented to investors as a boon. In fact, this is often a path leading to even more problems. Not always giving, but there are many negative examples even among well-known brands. It is known that it is much more difficult to “read” code than to write it. The smaller the person in the team, the better and more consistent the product comes out, because there are fewer communications and, consequently, opinions and “different handwriting”. But to ensure that this product was supported by others, with this approach is not easy. Because “not lordly this business” is to write technical documentation and administration interfaces (
“SARCASM” sign ).
From my own experience, I can recall the development of the object programming language ArtPublishing. We created it in C /
C ++ , instead of the existing implementation of the
Perl template
language . The latter did not suit us in the first place in terms of performance. As a result, the language of the templates turned out to be the language on which fairly complex websites were built -
online shopping , communities, complex corporate websites,
intranet systems and, most interesting, full-fledged content management system. From the very beginning, we understood that the monster would turn out and we should do it right. In developing the interpreter of the language, the most important thing is to lay a reserve for development, because it promises to be very intensive.
Somewhere we managed it,
somewhere - no. For example, the wrong choice of storing strings (“sishny”, from 00 at the end) made a very expensive refinement for using
UTF-8 . On the other hand, the use of external plug-ins and APIs made it possible to develop the system without affecting the so-called. “Core”, since several sites for different clients worked in parallel on it at once.
To quickly put the language into test operation, we first completely repeated the syntax of the Template library for
Perl , and switched the processing of templates to
C ++ . We made sure that everything worked on the example of the project
chernobil.ru , and then began to build up the language with all sorts of useful constructions that were not in
Perl , leaving backward compatibility. In fact, the first system run was on some “plugs”, which, one by one, were replaced with workable modules. If anyone is interested, there is some rather poor documentation for the language
here . By the way, being a little distracted from the topic, they were very close to releasing the compiler in
C ++ . Imagine a website compiled into a binary code in the form of a single executable
exe-shnik and a set of
dll-modules (in Unix, respectively, of the executable file and
so-modules )? That is, an interpreter for debugging and a compiler with a compiler for publishing. For 2001-2002, it would be a rather interesting decision, but, alas, time has already run away. The language, unfortunately, was monstrous in syntax - but this could not be avoided, it was necessary to withstand backward compatibility with the
Perl version . Here is what the functionality of one of the forum modules looked like, for example:
& {../ templates / header ()};
& {init ()};
& {# message_id = param ('id')};
& {# message_parent = "&& {../ papa (#message_id)};"};
& {# tree = "&& {../ tree_message (#message_parent)};"};
<! -% if {: '& {# message_id};' ne '':} ->
<! -% repeat source = "sql_uprate (#message_id)" -> <! -% / repeat ->
<! -% repeat source = "sql_message (# message_id, # approved_check)" ->
& {# message_answer = .. / templates / message_answer ()};
& {# author = "& f {#author};"};
& {# title = "& f {#title};"};
<! -% if {: '& {# user_mode};' eq 'admin':} -> & {# admin_message = .. / templates / admin_message ()}; <! -% / if ->
<! -% if {: '& {# email};' ne '':} -> & {# author = "<a href='mailto:&{#email};'> & {# author}; </a>"}; <! -% / if- ->
<! -% repeat source = "sql_forum (#forum_id)" ->
& {# path = "& {../ templates / path ()};"};
<! -% / repeat ->
& {../ templates / message ()};
<! -% / repeat ->
<! -% / if ->
& {../ templates / footer ()};
But with all, at times, terrible programming style in this language, one important feature was observed, without which it is simply impossible to program on it. This is the need to think modularly. A rare file with logic inside, such as the one shown above, did not exceed the screen page in size. It has always had an interface that allows it to be used elsewhere. If a new person connected to the product support, he could always start a small piece of the module for debugging and see how it works.
As a result, returning to the topic, here are my recommendations for the development of large systems:
- The system should be assembled from “bricks”, each of which should be as “isolated” as possible. “Bricks” should form a hierarchy. Ideally, it should be possible to replace the “bricks” with “plugs” that imitate their work for the earliest possible test run.
- To work on the "building blocks" you can hire a fairly large group of programmers, and on the architecture of the project - the most compact team of professionals. Technical management of the development of "bricks" and responsible acceptance should be on this team. The development of interfaces between the “bricks” and the entire architecture as a whole will definitely take time, which is often saved with other approaches. This is the biggest mistake when developing large systems.
- Documenting the work of "building blocks" should be as compact as possible: what is at the entrance, what is at the exit and how it works in a nutshell, plus a few important points that may not be clear from the code. Of the documents, the scheme of integrating “bricks” into the system is much more important. It should be built so that you can explain in five minutes how the program works.
- Each module should be supplied with a set of tests that demonstrate its operation in accordance with the requirements. The criterion for accepting the module to work — the tests pass, the documentation is short, understandable and corresponds to the tests, satisfactory and “readable” code.