📜 ⬆️ ⬇️

How do i write code

I like to think that I am writing good code. Well, or at least I write more good code than bad.

My favorite feature of a good code is its boredom. Predictable expressions, one after another. No surprises, no tricks, no unique cases. No meta programming, of course! Boring code is very easy to debug, read, explain.

The boring code does not use global states, does not generate side effects and tries to reduce its connection with the project in which it lives. Assigning values ​​occurs in the most boring manner, which for each variable happens only once and saves us from the possibility of a fascinating investigation of who and when has changed this state. The boring code does only what should and does not rely on some implicitly expressed assumptions.
')
Code that uses implicit behavior can be based on some undocumented but already implemented functionality. For example, there is a whole bunch of WRONG code written in the world that relies on the filesystem function returning a list of directories to return them in an alphabetically sorted order. This really often works that way, but exactly until the moment when it breaks down for "incomprehensible" reasons. And in fact, just no one ever guaranteed this sorting.

If the code you write is based on some kind of implicit assumptions, you need to keep them in your head, which in itself complicates the support of such code. You just do not want to go back to work on it, because besides reading the written deadline, you will also have to build a big picture in your head (taking into account its implicit parts). It's complicated. And sometimes, implicit assumptions break down due to external factors, and then the torturous process of reconciling the code with a new picture of the world begins.

Unfortunately, the code without implicitness also has its price. This is usually redundancy. We have to repeat some patterns, handle similar situations in a similar way. The code based on implicitness carries fragility and terrible consequences in the long run. Opposite to him, the obvious code conceals less strategic threats, but it requires diligence here and now.

Overdoing with verbosity is easy enough, but I still agree with the Python Zen : "Explicit is better than implicit." Java may go too far in verbosity and every time we need to read all the lines in a file, we have to write the same few lines of code. An alternative to this would be some kind of wrapper that will take on this responsibility, but will deprive us of some flexibility (and if you need to read not all the lines? And if not in order? And if not from the beginning? Etc.).

I'm trying to take the best of both worlds, dividing my API into layers. The "bottom" layer is written in Java-style: small components, simple behavior, but it takes some effort to assemble something really useful from them. The “upper layer” puts human readability and practicality of use at the head: it will be easy for the user to use the API in the right way, since its structure itself is convenient for this.

Two API layers are what you can see in one of my favorite Python libraries, “requests”. It provides an incredibly expressive and human-readable API that covers most of the use cases you may need in practice. But inside the library uses urllib3, where the main work with the HTTP protocol takes place. Yes, it was not easy to design such a system, and in the internal implementation of requests, there may be some redundancy, but what ease of use it gave to its users!

I call the backbone of the mechanic of my code "modules", and pieces of code that perform auxiliary tasks are called "libraries". In addition, I also like to divide the code of my application into components and frameworks.

A component is the place where the business logic of an application or service lives, and the framework is a thin layer of glue that binds together the various components. Proper application decomposition into separate components is not a trivial task: it is necessary not only to break an entity into several separate ones, but also to clearly understand why this should help.

Paraphrasing David Parnas' excellent explanation: the component exists to hide from the rest of the system some difficult decision or solution that is likely to change in the future.

Business logic, even if it is rather complicated, is not necessarily the Gordian knot that should be cut. If you cannot easily replace a component, you may not need to start by dividing it into separate components, because its more important problem is that it does not hide enough from the rest of the system.

Components should hide business logic from each other, modules should hide their implementation, libraries should hide their algorithms, and the framework should not show all the threads that connect it together.

In practice, the boundaries between these parts are not always able to withstand as precisely as I described them. Libraries affect modules, a small part of business logic creeps into frameworks, and some components do not hide their insides well enough.

I try to write code that does not require me to keep it in my head completely when reading or changing. It seems to me that dividing the code into layers and splitting business logic into components helps in this, but in fact, I just try not to write code that would confuse me over time or, worse, make me horrified.

I try to write boring code.

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


All Articles