Notes

Ibrahim

Recently, I took a system analysis course, which was taught by the guys from the “School of Strong Programmers”. I have two goals:

  1. First, I need to calibrate. I handle a dozen tech stacks that have different tool sets and concepts of beauty. This, of course, is a great development, but it distorts the perspective: I am drawn to solve the next problem, starting with minor technical things, simply because I am good at it. Meanwhile, if you look at the problem a little from above, think about it, and draw some diagrams, you will get a better or at least more meaningful solution. So I want to convince myself to do this more often.
  2. Secondly, I need to learn how to write clear documentation for developers. In practice, I rarely get around to it, and when I do, I don’t have time to keep it up-to-date. In short, time is short, and I would at least like to do the paperwork somehow so that the reader grasps the idea more quickly.

The course itself is made in the shape of iterations: every week you are told about the same project that the main character of the story, Ibrahim, is trying to design (that’s him on the landing page in the link above). Every week, there are more and more problems with this project, and the solutions that dude comes up with become more and more intricate.

In addition, students are regularly assigned homework for another project with similar issues. It needs to be analyzed and designed again and again, taking into account the new knowledge and limitations gained over the past week.

So far, it's going great. The main concern is a lack of time. I read relatively slowly and try to take notes on the most captivating or simply difficult passages. Meanwhile, there is a lot of material in the course, and all of it is interesting, even if you count only the main part of the content.

In addition, if you go to the additional links, you can even take away the saints. It takes six months, no less. I tried to read them at least diagonally, but it turned out to be a damn bad idea. I ended up seeing the main character of the course in a street advertisement :)

7 Jule 2024 meanwhile

Singapore Doll

Is it possible to love your country's currency more than the people of Saudi Arabia? It is a rhetorical question: the answer is no.

I'm currently looking at the website of their central bank. The country's currency is the Saudi riyal, and the Central Bank sets rates for other currencies in relation to it. Consequently, there is no point in asking the bank for the rate of the riyal itself. However, the website calmly suggests choosing it twice:

The selection form

For your information, the first option breaks the website, and the second one pedantically displays “1” for any date.

Another funny thing: colleagues seem to store currency names as 14-character strings. Otherwise, it is difficult to explain why, according to their data, Canada uses not the Canadian dollar but CANADIAN DOLLA, and Romania uses mysterious NEW ROMANIAN L instead of its leu. However, these two are rather lucky: Singapore, for example, conducts payments in SINGAPORE DOLL.

22 June 2024 work

Non-Unique Metadata

Once again, I came across a nasty bug when the platform broke the data history metadata table.

Outwardly, it looks like this: you update the database configuration, and when you try to restructure, the error “The data history metadata table contains duplicate records. Delete the duplicate records” pops up. The platform does not offer any clear way to find such records.

The data history metadata table contains duplicate records. Delete the duplicate records

The problem can be solved at the database level. The table referenced by the error is _DataHistoryMetadata. It contains metadata versions of each object for which data history is being maintained. This allows the platform to understand what attributes the object had at any point in time if the data history is maintained for the object.

How does it work? Well, when the list of an object’s attributes changes (for example, an attribute was added to the catalog), the platform writes its metadata: specifically, it adds a new entry to _DataHistoryMetadata and stores in it the current list of object attributes, as well as the version number of this list (for example, when history is enabled for an object, the first version of metadata is saved, when adding some attribute, the second version is saved, and so on).

The platform also puts a mark in the created record that this particular version of the object is the most actual, and then removes this mark from the version that was marked as actual before.

So, the problem is that the platform sometimes forgets to take the last step, and two versions appear in the table at once, marked as current. The Designer understands this but cannot do anything.

The solution follows from the algorithm above: you need to find conflicting versions and revoke the mark of actuality from the one that is older. It is better to use queries: data history is often enabled for a bunch of objects, and the list of their attributes is constantly changing — in general, there will be so many versions in the table that the devil will break his leg.

If you also encountered this problem and are therefore reading this text, you can use the queries that I wrote:

  1. get-issues.sql checks that there is an issue: it looks for metadata versions that are also marked as actual.
  2. fix-issues.sql removes the actuality mark from those versions that are actually outdated.

Both queries are written for Microsoft SQL Server. If you use PostgreSQL, then here they are for this DBMS.

The queries will require a slight adaptation to a specific database: they use the _fld626 field, which is the data separator. In your _DataHistoryMetadata table, this field may be called differently, so you need to update its name to the current one. It will be difficult to make a mistake — the table has only one field with the _fld prefix.

P.S. Please bear in mind that the license agreement prohibits access to the database, bypassing the platform’s tools, so you can only go for such experiments if there are no other options left.

8 June 2024 PostgreSQL MS SQL

The Main Issue of UUID

I came across a good text about the main problem that UUID brings with itself. It is also relevant for 1C: all platform reference objects (catalog items, documents, and so on) have their own UUID. They are stored in a database, actively used in searches, and, of course, extensively indexed (with all the ensuing consequences).

1C tries to suppress the issue by creating consistent UUIDs. It may not be perfect, but overall, this thing works, and the indexes turn out to be more or less compact. In general, the community has been talking about this for a long time: for example, the old thread on Mista (though the conversation here quickly turned into a chicken coop, and out of six dozen comments, at most one and a half are on point).

P.S. The remark about the probability of creating two identical UUIDs in one database made me laugh:

As an aside, for those worried about collisions: you should take up the lottery, since winning the jackpot twice in a row is a much more likely outcome than your system ever generating two identical random 128 bit numbers.

2 June 2024 PostgreSQL

Screenshot With Sound

Recently, we came up with the idea of dividing the internal ERP into several independent parts and organizing data exchange between them. We discussed the outlines of the task, the exchange model, transport, and roughly agreed on deadlines — in general, we did what we usually do.

I created a task for the stuff. We give them names in English, so I wrote in the first wording that came to mind.

Cut My Life Into Pieces

The title came out with sound. Okay, this one is funny, but what should I call it then? Well, let it be Distributed Internal ERP. Abbreviated... DIE?

I let the first one be. Long live Papa Roach :)

20 May 2024 work meanwhile

Timesheet for Obsidian

I made another Obsidian plugin, this time for daily notes. Draws a nice report: what tasks I worked on, what I did, and how much time I spent. I tried to describe how it works in the repository; will be glad if it is useful to someone else!

Funny thing: for the examples in the README, I used issue numbers FBI-1, FBI-2, and so on. This isn't a reference to the X-Files or Twin Peaks — it's just the first thing that came to mind. The fact is that our internal project for the development of FirstBit ERP is called First Bit Internal, abbreviated as FBI. The main pool of tasks we work on lives in it.

We’re already used to it, but our colleagues outside the company always find our screenshots from JIRA or SonarQube amusing. Did you imagine that you were Agent Cooper? Well, I almost don’t even need to :)

12 May 2024 done TypeScript Obsidian work

One Query More, One Query Less

One query more, one query less — it doesn’t matter, people often say. Like, the main thing is that the query must be cheap: it doesn’t read too much, uses index, and so on.

This point of view makes sense, but mindlessly machine-gunning queries is a dangerous idea. Even if everything looks good at the moment, the system may change slightly in the future. And then a seemingly harmless patch will burn your production server to the ground right on Friday.

Let me tell you about one example from recent practice. There is an ERP that contains a table with payment stages for customer orders. One of these stages is prepayment; until it is received, you cannot create an order for the supplier.

Technically, the purchase order simply stores the customer's order ID; if the field is filled (that is, if the purchase order is created by the customer's order), ERP needs to read the payment stages of the customer's order and understand whether the purchase can be made.

It sounds elementary, but monitoring shows that the operation is slow as hell and eats up memory as if it were the last time.

Welp, let's go find out. We saw something like this:

825701 records

What do we have here? Instead of taking two or three stages of payment for an order, ERP reads almost a million! How this could be possible?

It turned out that the problem was with those purchase orders that were not related to customer orders at all. The developer considered that the logic for them could not be changed: the customer’s order ID is empty and the query will not find payment stages for this ID. This means that the same result will be obtained as if there were no query at all. And an extra query — well... One query more, one query less... Not a big deal.

The deal turned out to be big. The table of payment stages contained data not only for customer orders, but also for other types of documents. Their customer order ID field was empty. As a result, ERP, when trying to find payment stages using an empty customer order ID, unexpectedly found them.

The query read about a gigabyte of data and wrote it to a temporary table. A gigabyte was read, then a gigabyte was written... History hit the disk, the DBMS buffer cache, and other components of the system (even the network, which had to drive this gigabyte back and forth without any benefit).

Do you know what I think? If the result of a query is known, there is probably no need to do it after all.

5 May 2024 optimization

Foodiary for Obsidian

Following my first plugin for Obsidian, I released the second one a couple of weeks ago. Counts calories, proteins, fats, and carbohydrates in food. It helps not to overeat out of nowhere — it’s rather difficult to judge by eye how much you’ve eaten today and whether you can afford that donut.

In short, a useful thing if you:

  1. Fatty (like me)
  2. Want to stop being fatty (like me)
  3. You take notes in Obsidian (like me) 🙂

In fact, there is a lot of software for this task. I tried some of them but was dissatisfied: it either has terrible design, is bugged, or is constantly trying to sell me a monthly subscription. In short, it's more annoying than helpful. I wanted something native, built into the usual routine; so, if routine settles in Obsidian, then the solution seems to suggest itself.

You can install the plugin directly from the program — the developers have already approved it. Otherwise, everything is simple: you write in a daily note what you ate and how much it weighed, and you receive a simple table sorted by calories with numbers by proteins, fats, and carbohydrates.

There are examples in the repository at the link above.

14 April 2024 done TypeScript Obsidian

Fastimer's Look

I just implemented Fastimer's rendering through callouts: this is an Obsidian mechanic that allows you to turn an ordinary quote into a designed block of text that attracts the reader's attention. You've probably seen blocks like “advice” and “pay attention” — these are callouts.

You can read more in Obsidian Help.

As a result, the timer now takes on a different color depending on the state: blue for an active fast, green for completed, and red for a failed one.

In addition, I made the text more compact and worked on styling:

Example

It turned out to be way nicer than the block of preformatted text as it was before.

9 Marth 2024 Obsidian TypeScript

Small Pleasures

I was wasting time at the supermarket checkout: it’s evening, there aren’t many customers anymore, but the elderly cashier is clearly tired and isn’t in too much of a hurry. Standing in front a tall, gray-haired man with a luxurious beard whiles away the time studying the rack of chocolates next to the cash register.

Finally, he takes a Snickers, twirls it in his hands thoughtfully. Then he pushes two more towards himself and, broadly, with visible pleasure, smiles into his mustache :)

6 Marth 2024 meanwhile Georgia

Not Only Everything

This commentary in the documentation for the WriteJSON() method of XDTOSerializer is enviably deep, I would say:

Not only everyhing

Well, yes. The method dumps data into JSON, not XML. So it’s hard to argue that not all value types can be packed into XML using it (to be precise, none). Such a pity that there is an obvious copy-paste from the help for WriteXML() further in the text! I almost decided that it was an Easter egg from the developers :)

25 February 2024

Fastimer for Obsidian

The Obsidian developers recently approved one of my TypeScript pet plugins — Fastimer. It is an intermittent fasting tracker that adds a new code block to your vault: you enter the start date of the fasting interval and get the date of its finish, the time until this moment, and a list of the zones to be passed.

The block shows an up-to-date picture every time Obsidian renders it, which means you can monitor your progress in real time. When a fasting window ends, you can enter the end date, and the code block will show the result: whether you managed to achieve the goal, how much time you fasted beyond the plan, and so on.

I'm thinking of improving the visual part a bit (currently, everything is displayed as plain text without any design). In addition, I want to add functions for calculating statistics so that you can draw cute graphs like Charts and show achievements. I already made the same in implementing the same application in Python, but I’m unlikely to return to it — it’s easier to solve the task in the Obsidian vault than to roll out an additional utility.

In short, check out the plugin! :) You can find it by name (Fastimer) in the Obsidian library, or install it manually from the repository.

13 February 2024 done TypeScript Obsidian

Do? Do Not?

Among our projects, we have one where two systems are communicating with each other: ERP and CRM. Data exchange is done well: a push'n'pull server has been set up, subscriptions to events have been registered, a REST API has been implemented, and so on. There are many other fascinating technical details, but I'm not talking about that now.

The exchange has various logic chains inside. For instance, if a new company appears in CRM, it sends the data to ERP. The other day, a problem appeared: a company was not sent from the CRM, no matter how many times you tried to write it. So we went to investigate, suspecting the worst: CRM is written in PHP (nothing personal; it’s just not our technical stack), and there’s a lot of different legacy stuff there. It's easier to shoot yourself in the foot than to blow your nose.

However, it didn't take much digging. We opened the company’s page in CRM and saw that he had the “Do Not Export To ERP” checkbox, which, in fact, blocked the sending. A manager made an obvious mistake.

Should we uncheck the box and close the ticket?

Well yes, but actually no

This will solve the problem with that particular company, but not the reason it appeared. It is actually in the interface, specifically in the name of the option: “do not” is used, which is advisable to avoid due to the fact that it is more difficult for users to read the wording correctly. By the way, this also applies to a simple “do”.

It is often difficult for programmers to understand why this is so: we are used to instantly calculating Boolean expressions in our heads, and variations like “not (not true)” are commonplace for us. But people with a different background can get confused. Just a little, but sometimes this is enough for them to perceive “do not export” as “do export” in the heat of the day, click the option, and move on.

To sum up, the solution is to rename the checkbox. “Disable Export” or “Stop Export” are both fine, for example. “Prohibit Export” also comes to mind, but it’s more about interpersonal relationships, and in general, a ban on doing something does not mean that it won’t be done :)

14 January 2024 work English

Last Meth

I'm digging into the code of an external component for 1C platform, published by its developers as an example. The good thing is: well, it can be compiled and if you tweak it a little — it does the job.

As for other things, there are a lot of bruh moments. For example, the project can't be opened in modern Visual Studio (you need to specify CMake manually). The code is quite sloppy; there is no documentation, comments, or formatting. Long story short: I believe, it can be difficult for a developer without solid experience in C++ to get the hang of this.

Was a bit amused by the naming in the code below:

long CAddInNative::FindMethod(const WCHAR_T* wsMethodName)
{ 
    long plMethodNum = -1;
    wchar_t* name = 0;

    ::convFromShortWchar(&name, wsMethodName);

    plMethodNum = findName(g_MethodNames, name, eMethLast);

    if (plMethodNum == -1)
        plMethodNum = findName(g_MethodNamesRu, name, eMethLast);

    delete[] name;

    return plMethodNum;
}

I see here the inexplicable love for abbreviations. What made the author name the variable “eMethLast”, not “eMethodLast”? They already created "wsMethodName" and "plMethodNum", after all.

Perhaps this is an Easter egg with a reference to Breaking Bad. Then I like it for sure :)

17 December 2023

Wrong Freeway Entrance

Have you ever taken the wrong freeway entrance? You need to drive to the next exit to turn around, but you hate every inch of travel because you're going away from your goal.

― Andy Weir, "The Martian"

Programmers have exactly the same emotions when they spend a long time working on something. They suddenly realize that part of it should be designed differently. Moreover, this is exactly what you have to do since it solves several problems at once. This is where technical debt is born.

However, right now you don’t change anything but continue to work with the part of the code that already exists. After all, you are professional, and you have a release date! You have to make it on time and then pay the debt, but you hate every inch of code you write because you're going away from your goal.

14 November 2023 code smell

Everyday Heroism

Some time ago, I was setting up Swagger for the internal API. While I was fiddling around, it became clear that some functionality did not need to be included in the documentation. I was looking for a way to do this without crutches and came across a funny question on GitHub.

What's funny, you ask? Well, I involuntarily remembered Mista. Among 1C developers, this is synonymous with the word “toxicity”: if you ask anything there, you get a bucket of slop by the collar instead of an answer. Here, of course, everything is not so neglected, but holy crap! These persistent guys who referring to the 14-page manual made me laugh a lot.

One thing is good: by the end of the thread, there appeared a brave rebel who just answered the question. They, like, named the required parameter for a FastAPI method's decorator, which is not supposed to be shown in the documentation. No links — could you imagine?

Not all heroes wear capes, I would say.

24 September 2023 work Swagger

Romania's Feature

Making a password recover function via SMS for our customer portal. Got to the Twilio documentation related to alphanumeric sender ID support in different countries; this feature allows you to send messages so that the recipient sees not the sender's number but something meaningful (a company name, for example).

The feature is regulated differently everywhere: in some countries it just works, but in others registration is possible or even required.

Well, let's take a look:

Screenshot

🤔

  • Portugal: yes
  • Puerto Rico: no
  • Qatar: yes (with registration)
  • Reunion: yes
  • Romania: yes (with registration) (but be afraid of Dracula)

I don’t know how else I can explain this cemetery.

UPD: Found the answer. Grave crosses mean you have to pay $700 to register.

To be frank, I like the explanation about Dracula much more.

30 August 2023 meanwhile work

New Fastimer

Released a new version of my console timer for intermittent fasting. I wrote this app about a year ago, when I was once again upset by the Zero application for Android: some very primitive functions (like viewing a specific interval) did not work. Oh my gosh, guys, you had one job!

The main difference between 1.3.1 and 1.2.3 is that the console menu has been cut to hell in favor of the good old arguments and options. The menu concept looks convenient only if an application has few features. However, as soon as you take a step forward, the need to answer a list of questions each time you need something starts to irritate you.

23 August 2023 done Python

Do you speak English?

The repository login form of the current platform (8.3.22.1923, to be precise) launched with the English interface:

Login Form

You had one job, literally.

I heard some rumors that development of the Designer was stopped largely due to the monstrous amount of technical debt that slows down any new features. But here are interface glitches right on one of the first application windows! Curious how it got through the build tests.

Maybe they don't exist at all.

14 May 2023

Going Postal

Recently, I was working with vocabulary in an attempt to describe my latest adventures to an English-speaking friend and got to know about the colorful idiom “to go postal”. It means something like “go crazy with anger”; appeared somewhere between the 80s and 90s in the USA after a series of rather insane incidents in which postal workers went crazy and attacked people around, including colleagues and visitors.

The expression sounds funny at first glance, but the story behind the scenes is painfully gloomy. I think I will continue to use the good old “to go ballistic”. Literally, “get angry that strong so you become a rocket which lost control”. Or, to simplify, “explode with anger”.

By the way, there are similar rocket-like connotations in Russian, but for some reason they are about a more manageable cases. Mostly, they implies something like "the fire in the ass was so damn strong that the poor guy left Earth and successfully landed on Mars". Sounds a bit better than a simple explosion — you didn't completely waste the precious resource, at least :-)

8 May 2023 English

Earlier Ctrl + ↓