Why clean code and automated testing are so important for software engineering?

In this post I would like to dig a little bit into philosophical topic, trying to use some analogies between classical engineering disciplines and software development to explain why clean code and automated testing became so important and widely used in software industry.

It is very common today to consider software development to be a craft, not engineering. And while I would agree with this statement to some degree, the argument like “there is very little math in programming, hence it can not be systematic and reliable enough, hence it can not be considered as an engineering discipline” seems to be not really valid. The important insight was given in this amazing article, which simply states that code we write is actually a software design. We used to think that software design is done by very smart guys who draw impressive UML or whatever kind of diagrams. And then monkeys in cubicles sit down and perform actual construction of the code without applying much intelligence to the process. The final product, the machine code after compilation, then shipped to the customer. The agile methodologies boom just destroyed this idea and proved that code in some high-level programming language is actually formal design specification. Formal enough to be taken and without any additional creative thought transformed to the real product, which bring value to the customer.

Now, in classical engineering disciplines, they have similar to an agile process. Design phase goes in iterations and some formal description of future product gets created. This description is further used in the factories and plants, sometimes 100% automated to build final products. To me, it looks like the very close match to software development. The only point, which I think people often miss, is the identification of the final product in software development. I believe it is not a compiled code. Because it is conceptually the same thing as source code, but only translated to the language of worker, so to say. And for interpreted languages there is no such a thing as compilation step. So what is the final product then? Well, it is what brings value to the customer – the process which develops under our design (program). And computing machine is just 100% automated factory.

Ok, but why should you care? You should care because your code is DESIGN SPECIFICATION. And in classical engineering disciplines these specifications are done in the very well defined way with a strong focus on understandability. These specs are media in which design is done. It is used to capture design decisions, to simulate and test future products. These specs are communication media for engineering teams. And by the way, craftsmen never did formal specifications of their designs.

Software engineering is not an exception. The only difference is that we tend to make a mess in our designs. This makes it harder to reflect on systems properties and test them. This is why clean code is essential for establishing software development as an engineering discipline. This is why modern professional developers care a lot about clarity of their designs.

Ok, but what about math? Should we also start intensively use math to become engineers? Well, I don’t think so. I was trained as a classical radio engineer in the university. And I remember very well sitting at the computer with my professor and implementing in BASIC differential equations which described the oscillating circuit. And professor asked if I knew why we were doing it. I just stared at him with surprise in my eyes. The answer was simple, it was much cheaper to analyze characteristics of the circuit we were designing using the mathematical model of the circuit and not the real circuit. I was testing my electric circuit without spending money and time to buy all the components and solder them together (not that it was not fun to build real circuit, I still remember this incomparable feeling of the act of creation). It is simply much cheaper to get feedback on what we have designed before we have built the actual product. But still, mathematical models can be wrong, and testing should ultimately be done to ensure that product does what it is expected to do. Have you ever watched Discovery shows where people test new airplanes? This is just the colossal amount of money. But they have to prove with testing that their airplanes can actually take off, fly and land. No mathematical model can eliminate this design step.

Now, how much does it cost to run the software, i.e. build our products and see if they actually work? It is almost free! Why then should industry spend money on adopting complex formal methods? I can’t see any reason for this and industry seems to be supporting my statement. Analytical proof of correctness of the software seems to be redundant, therefore only empirical methods are left, i.e. testing. However, nice code analyzers are always welcome if they don’t require any additional work to be done on developer’s part. But they will hardly be able to prove that user, for example, can actually save the document in Microsoft Word. If one day this kind of requirement will be possible to describe formally, no doubt, there will be tools wich will transform this requirement to executable programs.
So, the conclusion here is that testing is another ingredient, required for our profession to become engineering.

However, there is a little problem with testing in software. And this problem is in “soft” concept. Software changes very often, it evolves like some organism to meet changing environmental requirements. And without ability to test relatively cheap that software system is still ok after every change, we can’t talk about a systematic approach to software development. Hence, simple testing is not enough. Tests should also be formal and repeatable, i.e. automatic.

Conclusion. Developing software ultimately means designing processes, intended to happen in computing machines. Processes are not material and therefore products, created by software developers, are ideal. Math is not so important because of mentioned idealism and cheapness of building a final product from design spec. This makes a huge difference because all other engineering disciplines build material products. It does not mean, however, that software developers can’t engineer their products,  i.e. develop them systematically, rigorously, with repeatable procedures. For this to happen we have to embrace clean coding as well as automated testing – the only practical way to prove software correctness these days. The more systematically we will do this, the closer we will be to software engineering.

Facebooktwittergoogle_pluslinkedinmailFacebooktwittergoogle_pluslinkedinmail

Якими навичками та рисами має володіти сучасний програміст?

Найважливіше розуміти яких навичок від тебе вимагає HR. Маючи ці навички, можна одразу потрапити на співбесіду. Те, що їм потрібно можна почитати в описі вакансії. End of story. Я на напишу якими на мою думку (думку програміста) навичками має володіти людина щоб стати успішним програмістом. Одразу скажу, у мене самого і половини цих навичок немає, але як приємно спостерігати за людьми у кого вони є, бачити з якою легкістю вони справляються зі своїми завданнями.

Гарна пам’ять. Трохи дивно, чи не так? Але що може виробляти програміст з гарно пам’яттю! У мене є кілька таких колег, одна з них пам’ятає шкільні вірші на пам’ять. Вони пам’ятають як система працює в цілому. Пам’ятають як працюють її окремі частини, у чому суть тієї чи іншої функції, де шукати ту чи іншу функціональність. Кожен раз, коли вони торкаються якогось коду, вони пам’ятають його суть. Можна прийти і запитати їх через рік, що той код робить, і отримати відповідь. Я, напевне, ніколи не перестану дивуватися. Коли працюєш з великою і складною системою, цю рису важко переоцінити.

Уміння бігати в мішку. Як пояснити цю якість? По-справжньому її можна лише відчути, але спробую. Це щось типу кладеш цеглинку за цеглинкою в рівненьку стіну, все гаразд, але тоді ти бачиш, що хтось поклав цеглинку поперек, а ще хтось пропустив цеглинку і в стіні дірка, а ще хтось замість цеглинки купку лайна наклав. Людина, що має цю навичку зорієнтується, не розгубиться, не побіжить скаржитися чи просити допомоги. Вона якимось чудодійним способом розрулює ситуацію і далі кладе цеглу, і проект триває. Косяки є на кожному успішному проекті й уміння з ними жити і деліверити – дуже цінне вміння.

Розв’язування задач. Не те щоб ця навичка була дуже критичною, бо в корпоративному програмному забезпеченні рідко трапляються задачі, які потребують вміння розробляти алгоритми та оцінювати їх складність. Але все ж таки трапляються, а в не корпоративному, гадаю, трапляються ще частіше. І важливо тоді, коли це станеться, не завалити проект.

Управління складністю. Програмне забезпечення дуже складне. Людина нічого складнішого не виробляє. І якщо програміст не вміє зробити код і архітектуру максимально простою, робота з системою, що він розробляє, швидко перетворюється на пекло. Ми проводимо 80% часу читаючи код. Якщо зі складністю не змогли впоратися – читаємо 95% і більше часу. А коли ж писати новий код?

Управління залежностями. Залежності можуть приймати найрізноманітніші форми в програмному забезпеченні. Ось лише кілька: залежність класу в ООП від іншого класу; залежність бізнес логіки від GUI (чого бути не повинно) і навпаки; залежність системи від конкретної системи БД; залежність можливості виконання однієї вимоги від іншої вимоги, яка може її заперечувати. Здається, в програмуванні більше проблем із залежностями, ніж з розроблюванням алгоритмів.

Уміння виділити абстракцію. Це моя улюблена навичка. Я дуже тішуся, коли мені вдається виділити вдалу абстракцію в коді, і захоплююсь людьми, які вміють це робити. Річ у тім, що програмування – це цілковито абстрактна форма інженерії. Усі компоненти, що ми створюємо, є абстракціями і у фізичному світі не існують. Точнісінько як математика. Тому вміння вдало виділити абстракції і вдало їх організувати є ключовим для успішного програміста. (Не розумієш що я маю на увазі під абстракцією? Для прикладу, звичайна функція у процедурному програмуванні є процедурною абстракцією.)

Дитяча цікавість. Думаю, ця риса прямо таки критична. Людині постійно має бути цікаво “поколупати” то функцію, то фреймворк, то перевірити що буде якщо викликати функцію так і так. Якщо цю цікавість помножити на гарну пам’ять, отримуємо програміста, що за рік буде як риба в воді навіть у найскладнішій і заплутанішій системі.

Софт скіл. Той самий розмитий і малозрозумілий софт скіл. По суті це визнання того факту, що програмування-це суцільна комунікація. З комп’ютерами, через мову програмування, та з колегами різних спеціалізацій, через звичайну мову. Якщо так можна висловитися, це педагогічно-інженерна діяльність. До софт скілів також відносять вміння управляти своїм часом і організовувати свою роботу. Адже програміста важко проконтролювати та вказати коли і як йому працювати.

Посидючість, готовність тягнути рутину. Робота програміста в основному це сидіння перед монітором. Нерідко трапляються дні, коли немає жодної наради, і доводиться сидіти й фокусуватися 8 годин на день. Це не так складно, якщо завдання дуже цікаве, але не рідко трапляються нецікаві завдання, і тоді… важкувато. Щоб з усім цим впоратися, треба мати рису характеру, що дозволяє людині довго сидіти на одному місці в обстановці, яка не змінюється.

Уміння і бажання постійно вчитися. З точки зору навчання, ІТ одна з найвимогливіших галузей. Людина має постійно навчатися, щоб залишатся на плаву. Але далеко не кожна людина може це робити, якщо немає такої внутрішні потреби, такої риси характеру.

Уміння і бажання брати на себе відповідальність. Це не та риса, відсутність якої може стати show-stopper-ом для програміста. Людина може бути гарним програмістом, але не більше того. Ніякої технічної кар’єри на цьому вона не побудує. Коли ти відмовляєшся брати на себе відповідальність, тобі ніхто не захоче довірити важливий проект. А саме на важливих проектах людина росте як технічно, так і особистісно.

Facebooktwittergoogle_pluslinkedinmailFacebooktwittergoogle_pluslinkedinmail

Partitioning and aligning long code lines

Formatting the code is very important for readability. And it is for a reason that in magazines and newspapers we have few narrow columns on the page. It is much easier to read text when you don’t have to move your eyes back and forth. However, when we code in a statically typed language like C# it is not always easy to make code narrow. Types have to be declared for every variable and parameter, so we have to break statements into several lines. Doing this in a wrong way, however, one can make more harm than good. So, let’s start considering code without breaks at all:

public class MyClass {
    public void MyMethod(LongTypeName1 longParamName1, LongTypeName2 longParamName2, LongTypeName3 longParamName3) {
        //Some logic goes here
        //Some logic goes here
        //Some logic goes here
    }
}

Reading this can be challenging. You have to move your eyes on the right and then back, distracting yourself from an ordinary top-down flow. Sometimes you even have to grab a mouse and scroll. Lame…
Here is what people start doing with this:

public class MyClass {
    public void MyMethod(LongTypeName1 longParamName1,
        LongTypeName2 longParamName2, LongTypeName3 longParamName3) {
        //Some logic goes here
        //Some logic goes here
        //Some logic goes here
    }
}

And this a mistake! Because longParamName1 is now visually detached from other parameters while logically all parameters are related and should be visually grouped together. In fact, this is the fundamental principle: logically related entities should be related visually. Now if you agree with this statement you might fix the problem in the following way:

public class MyClass {
    public void MyMethod(LongTypeName1 longParamName1,
                         LongTypeName2 longParamName2,
                         LongTypeName3 longParamName3) {
        //Some logic goes here
        //Some logic goes here
        //Some logic goes here
    }
}

Now the fundamental principle is met and we can easily see the grouping. However, there is a hidden mistake. What would happen if you rename the method? Most probably longParamName1 will be shifted left or right while all other parameters will keep their previous alignment. This change will lead to the previous problem – visual detachment of logically related entities.
This can be fixed in the following way:

public class MyClass {
    public void MyMethod(
        LongTypeName1 longParamName1, LongTypeName2 longParamName2, LongTypeName3 longParamName3) {
        //Some logic goes here
        //Some logic goes here
        //Some logic goes here
    }
}

This code solves previous visual detachment problems, but if the parameters list is long, we still have the problem. Luckily enough there is formatting which solves most of described problems:

public class MyClass {
    public void MyMethod(
        LongTypeName1 longParamName1,
        LongTypeName2 longParamName2,
        LongTypeName3 longParamName3)
    {
        //Some logic goes here
        //Some logic goes here
        //Some logic goes here
    }
}

Note that method’s opening figure bracket is placed on the new line. This visually detaches logically unrelated entities – code of the function and list of its parameters. This same rule can be applied to other things in code, to LINQ expressions for instance. It is better to format LINQ expression this way:

new[] { 1, 2, 3, 4 }
    .Where(n => n > 3)
    .OrderBy(n => n)
    .Take(3)
    .Select(n => n * n);

than this way:

new[] { 1, 2, 3, 4 }.Where(n => n > 3)
    .OrderBy(n => n)
    .Take(3)
    .Select(n => n * n);

Please refer here if you would like to learn more on the topic.

Instead of conclusion: before analyzing different formattings and selecting better one, my developer’s life was a complete mess. I spent much time formatting and reformatting things and still being unsatisfied with the visual appearance of my C# code. Now I have no hesitations and even legacy code can be a bit beautified, which is always great.

Facebooktwittergoogle_pluslinkedinmailFacebooktwittergoogle_pluslinkedinmail