The Good, the Bad and the Ugly code


Good code or bad? For me personally, a good code has the following qualities:
  • The code is easy to understand for developers of various skills and well structured
  • Code is easy to modify and maintain.
  • The application performs its functions and has sufficient fault tolerance for the current range of tasks

Despite a short description of how to achieve these three conditions, many thick books have been written.

Why exactly these criteria? Immediately make a reservation, we are now talking about developing software for business (enterprise application) . The code evaluation criteria for real-time systems, aircraft, life support systems and the ISS are different.


Surely everyone or almost everyone knows this triangle.
At the same time, of the three conditions - quickly, efficiently, cheaply - only two can be fulfilled.
When developing business applications, our goal is to develop good enough software for an acceptable period, keeping within the budget. In practice, this means that there may be errors in our application, but in places that are not critical to the system. Let's look at a few examples to better understand what this means.

Case No. 1


On the form there is a text input field (textarea). You can enter 100500 characters there and our application will fail with the exception “the maximum request length has been exceeded”.

Is this our case? Hardly. If this is the public part, it is worth putting a Java-script validator and not allowing such forms to post. Setting up an additional server somehow for those who have disabled java-script is just a waste of time, which means budget. We will not do this. Yes, there is a potential error in our application. Does anyone need to be fixed? Not.

Case No. 2


A request comes from the client: we need a partners page. On this page there will be a brief information about each of them and a link to the page on which we must embed the page from the partner’s site in the iframe.

An interesting case. We probably need to make a change to the database, add the partner entity, we don’t pass the db-entity by design to the View, so we need a DTO, a mapping function from db-entity to dto, and a database migration for an auto deploy. Hmm, we still need a page in the admin panel to add a partner. Не забыть о роутинге для контроллера, чтобы было §Partner§ Довольно много работенки…

Верно для 10.000 партнеров. Let's take an interest: “how many partners will be” ? If the answer is "this year is three." We should reconsider our views. This is again a waste of time and budget. Only 3 partners?


namespace Habrahabr.Models.Partner
{
    public class PartnerViewModel
    {
        public string Name { get; set; }

        public string WebSiteUrl { get; set; }

        public string ShortDescription { get; set; }

        public string FullDescription { get; set; }
    }
}

using System.Web.Mvc;
using Habrahabr.Models.Partner;

namespace Habrahabr.Controllers
{
    public class PartnerController : Controller
    {
        private static PartnerViewModel[] partners = new[]
	 {
		 new PartnerViewModel()
			 {
				 Name = "Coca-Cola",
				 WebSiteUrl = "http://coca-cola.com/partner",
				 ShortDescription = "Coca-cola short description", 
				 FullDescription = "Coca-cola full description"
			 },
		 new PartnerViewModel()
			 {
				 Name = "Ikea",
				 WebSiteUrl = "http://ikea.com/partner",
				 ShortDescription = "Ikea short description", 
				 FullDescription = "Ikea full description"
			 },
		 new PartnerViewModel()
			 {
				 Name = "Yandex",
				 WebSiteUrl = "http://yandex.ru/partner",
				 ShortDescription = "Yandex short description", 
				 FullDescription = "Yandex full description"
			 }
	 };

        public ActionResult Index()
        {
            // TODO: populate partners from database if a lot of partner required
            return View(partners);
        }
        
        // TODO: refactor this if a lot of partner required
        [ActionName("Coca-Cola")]
        public ActionResult CocaCola()
        {
            return View("_PartnerDetail", partners[0]);
        }

        public ActionResult Ikea()
        {
            return View("_PartnerDetail", partners[1]);
        }

        public ActionResult Yandex()
        {
            return View("_PartnerDetail", partners[2]);
        }
    }
}

@model IEnumerable<Habrahabr.Models.Partner.PartnerViewModel>

<ul>
    @foreach (var p in @Model)
    {
        <li>
            <h3>@Html.ActionLink(p.Name, p.Name, "Partner")</h3>
            <div>@p.ShortDescription</div>
            <p class="info">@Html.ActionLink("Partner page", p.Name, "Partner")</p>
        </li>
    }
</ul>

@model Habrahabr.Models.Partner.PartnerViewModel
<h2>@Model.Name</h2>
<div>@Model.FullDescription</div>
@if (!string.IsNullOrEmpty(Model.WebSiteUrl))
{
    <iframe width="100%" height="500" src="@Model.WebSiteUrl"></iframe>
}




For everything about everything - a maximum of an hour of time, taking into account the input of texts. We have developed a fairly good partner page. Does it meet our criteria:
  • Clear and structured - yes
  • Easy to change - yes
  • Reliable enough - yes

Domain, data access and application architecture


Fowler correctly noted in his book Enterprise Patterns of Enterprise Application Architecture that the term "architecture" is incredibly blurred:
Термин «архитектура» пытаются трактовать все, кому не лень, и всяк на свой лад. Впрочем, можно назвать два общих варианта. Первый связан с разделением системы на наиболее крупные составные части; во втором случае имеются в виду некие конструктивные решения, которые после их принятия с трудом поддаются изменению. Также растет понимание того, что существует более одного способа описания архитектуры и степень важности каждого из них меняется в продолжение жизненного цикла системы.

У выше описанного подхода с to partners. есть одно существенное ограничение. You cannot constantly write application code in this style. Sooner or later, the system will grow. In this case, we “sewed up” the application logic. A couple more of these controllers and understand the system will be quite difficult, especially for new developers.
At the moment when it turns out that there will be not 3 partners, but 100,500 and not all should be shown, but only those who paid on the night of Friday to Saturday (from 13 to 14) and sacrificed 6 virgins, and the pages should be rotated in correspondence with the position of Venus regarding Jupiter (in real life, the cases, of course, are different, but it is not for nothing that they say that for a developer there is nothing more illogical than “business logic”, consisting entirely of exceptions and special cases). You need to think about architecture in both senses of the word.

We left the “seams” in the application in advance, we will begin to rip them apart and sew the necessary pieces. We will use ORM , rename PartnerViewModel to Partner and map the partner class in the database. I never used Database-First and did not take Entity Framework seriously until the version with Code-First approach was released. In this case, I do not see the need to map Partner in PartnerViewModel . Yes, semantically these entities perform different tasks and in general, PartnerViewModel may differ, but so far there is not a single reason to write exactly the same class. Map ViewModel it makes sense when you can’t do without it without dragging the logic into the view. Usually, the need for ViewModels arises if the interface needs to show a complex form that operates with several entities of the domain at once, or there is a need to use the attributes from the System.Web assembly for the model properties. In the latter case, I am inclined to drag this dependency into an assembly with a subject area, if I am sure that in the next six months there will not be a new entry point in the application, except for the main web application.

In one project in which I participated, to access the data, it was necessary to call a service that accessed the facade, which in turn turned to The DAL layer that called the Entity Framework . The façade and DAL were in different assemblies, and EF used the Database-First approach . DAL , and the rest of the layers simply remap the results in 90% of cases into absolutely identical DTO . Moreover, the system had no other clients and the service was objectively unnecessary. When I asked "why such an overhead", the developer who worked on the project longer said: "I don’t know, our other project is written like this, we decided to do it by analogy." Yes, this “other project” consisted of 20 applications and it took 2 days to deploy it. The API of this application has many clients. Such a complex structure was a necessity. In our case, it was shooting from a cannon at sparrows.

But back to our rams to partners. It remains to understand where to write the code responsible for choosing the necessary partners for display. I remind you that now we must consider Venus and Jupiter. The worst place is the controller. By doing so, we scatter the logic of the application into its various parts, and new developers will have to collect it (logic), like Harry Potter, who collected the crucifixes .
A good option is to use the specification repository pattern .

IOC / DI


With the growth of the application, there is a danger of making it too tightly connected, and this will make the system slow and will interfere with code changes.

I will not once again describe what IOC / DI is and what it is eaten with, you just need to take it as a rule:
  • Do not explicitly create dependencies
  • Use IOC containers

It is not necessary to create an interface for each class in the project. Often, an interface can be easier to highlight later on. R # does a great job of this. But keep this in mind when you write a class. Think of it as an interface, not a specific implementation. Using DI you can flexibly manage implementations. If necessary, you can throw out one implementation and replace it with another, for example, if for some reason the database stops working for you, you can transparently replace it by replacing the repository implementation. In addition, using IOC / DI is much easier to write test code.

Tests


I am not a fan of 90-100% of the project coverage. I think that more often than not, this slows down development and makes code support a much more dreary undertaking. I prefer to test the behavior of the system, i.e. basic business rules of the application. Thus, the tests not only insure against errors, but also help new developers to quickly understand the logic of the system. Such tests work better than any documentation. Tests using BDD notation are especially effective.

Further development of the system


If the application continues to grow, it’s difficult to give specific advice: there are too many factors to consider. Too much depends on the specifics of the project. A good solution might be SOA . In general, decomposition of any large system is a great idea. Ten projects are uploaded to your IDE or a hundred - the difference is very big. When developing software of this scale, deploy scripts, installers and release management are indispensable, but this is a completely different story ...

Lambdas, aspects, functionality and other holivor


In the end, I decided to leave deliberately controversial moments. I noticed that many developers tend to get used to the stack with which they work. If a programmer is "used" to his stack, he is inclined to drag it into each new project, regardless of its usefulness. So, I have repeatedly heard criticism of C # for "flirting" with a functional style, lambdas that are compiled to "damn know what" and "do not debug" and for other extension methods. Objectively, we see that closures have been added in php, Stream will appear in java8 for stream processing collections, which means many developers like this.

Do not be afraid to combine paradigms, if it is worth it. Ask yourself the questions “how much platform / language / os are suitable for the task?” and "what technologies will help me complete the task as efficiently as possible?" My point is best illustrated by a quote. iliakan :
Я так прикинул, что будет быстрее выучить Erlang или заставить Java работать достаточно быстро и решил выучить Erlang

Books on the topic