📜 ⬆️ ⬇️

The Good, the Bad and the Ugly code


Good code or bad? For me personally, good code has the following qualities:

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

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


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

Case â„–1


The form has a text entry field (textarea). You can enter 100,500 characters there and our application will crash with the exception "The maximum request length has been exceeded."
')
Is this our case? Hardly. If this is a public part, it is worthwhile to set up a Java-script validator and not to post such forms. Setting up an additional server for those who disabled java-script is simply a waste of time, which means of a budget. We will not do that. Yes, there is a potential error in our application. Does someone need to be corrected? Not.

Case â„–2


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

An interesting case. Probably, we 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 also need the DTO, the mapping function from the db entity to dto, and the database migration for auto deployment. Hmm, I still need an admin page to add a partner. Do not forget about the routing for the controller to be / Partner / <PARTNER_NAME> Quite a lot of work ...

Right for 10,000 partners. Let's take an interest: “how many partners will there be” ? If the answer is "this year 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 - the maximum hour of time, taking into account the input of texts. We have developed a fairly good partner page. Does he meet our criteria:

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:
The term "architecture" is tried to be interpreted by all and sundry, and everyone in their own way. However, there are two common options. The first is related to the division of the system into the largest components; in the second case, some constructive decisions are meant, which after their adoption are difficult to change. There is also a growing understanding that there is more than one way to describe the architecture and the degree of importance of each of them changes during the life cycle of the system.

The above approach with partners has one significant limitation. You can not constantly write application code in this style. Sooner or later the system will grow. In this case, we have “stitched” the application logic. A couple more of these controllers and understand the operation of the system will be quite difficult, especially for new developers.
At the moment when it turns out that the partners will be not 3, but 100,500, and not all should be shown, but only those who paid Friday to Saturday night from 13 to 14 and sacrificed 6 virgins, and the pages should be rotated to compliance with the position of Venus relative to Jupiter (in real life, cases, of course, are different, but for good reason they say that there is nothing illogical for a developer than "business logic", consisting entirely of exceptions and special cases). We need to think about architecture in both senses of the word.

We left the “stitches” in the app beforehand, we will begin to unpick them and sew the necessary pieces. We will use ORM , rename PartnerViewModel to Partner and zapapim partner class in the database. I have never used Database-First and have not taken the Entity Framework seriously until the version with the 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 be different, but for now there is no single reason to write exactly the same class. Map ViewModel makes sense when you can't do without it without dragging logic into the view. Usually, the need for ViewModels arises if the interface requires showing a complex form operating with several domain entities at once, or there is a need to use attributes from the System.Web assembly for model properties. In the latter case, I am inclined to drag this dependency into an assembly with the subject area, if I am sure that in the next six months a new entry point will not appear 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 addressed the DAL layer , which caused the Entity Framework . The facade and DAL were in different assemblies, and EF used the Database-First approach. All the logic was usually performed by DAL , and the remaining layers simply made up the results in 90% of cases in absolutely identical DTO . At the same time, the system did not have any other clients and the service was objectively not needed. When I asked “why such an overhead projector,” the developer, who had worked on the project longer, said: “I don’t know, our other project was written this way, they decided to do it by analogy”. Yes, this “other project” consisted of 20 applications and only took 2 days to deploy it. The API for this application has many clients. Such a complex structure was a must. In our case, it was the shooting of a gun on the sparrows.

But back to our sheep partners. It remains to understand where to write the code responsible for selecting the necessary partners for the show. I remind you that now we have to take into account Venus and Jupiter. The worst place is the controller. Doing so, we will scatter the application logic in different parts of it and new developers will have to assemble it (logic), like Harry Potter, who has been collecting talisers .
A good option to use the pattern repository specification .

IOC / DI


As the application grows, there is a danger of making it too tightly bound, and this will make the system slow and will prevent the code from changing.

I will not describe once again what IOC / DI is and what it is eaten with, I just need to make it a rule:
It is not necessary to create an interface for each class in the project. Often the interface is easier to allocate in the future. R # does an excellent job with this task. But keep this in mind when writing a class. Think of it as an interface, not a concrete 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 ceases to suit you, you can replace it transparently, 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% project coverage. I think that more often than not, this slows down development and makes code support a much more dreary task. I prefer to test the behavior of the system, i.e. basic business rules of the application. Thus, 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. Especially effective are tests using BDD notation.

Further development of the system


If the application continues to grow, it is difficult to give specific advice: too many factors to consider. Too much depends on the specifics of the project. A good solution might be SOA . In general, the decomposition of any large system is a great idea. You have ten projects uploaded to your IDE or one hundred - the difference is very big. When developing software of such a scale, it is impossible to do without deploy-scripts, installers and release management, but that's another story ...

Lambda, aspects, functional and other holivor


Finally, I decided to leave the obviously controversial points. I noticed that many developers tend to get used to the stack they work with. If the programmer is “used” to his stack, he is inclined to drag him into each new project, regardless of expediency. So, I repeatedly heard criticism of C # for “flirting” with a functional style, lambdas, which are compiled into “damn-knows what” and “not debugged” and for other extension-methods. Objectively, we see that in php they added closures, in java8 there will be a Stream for stream processing of collections, which means that many developers like it.

Do not be afraid to combine paradigms, if it is worth it. Ask yourself the questions “how much platform / language / OS is suitable for the task?” And “what technologies will help me to complete the task as efficiently as possible?”. Best of all, my point of view is illustrated by the quotation iliakan :
I figured that it would be faster to learn Erlang or make Java run fast enough and decided to learn Erlang

Books on the topic


Source: https://habr.com/ru/post/195732/


All Articles