<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: szabi</title>
    <description>The latest articles on DEV Community by szabi (@szabi).</description>
    <link>https://dev.to/szabi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F151594%2Fc4ccb58f-1fba-4761-9487-fa83851dfba0.png</url>
      <title>DEV Community: szabi</title>
      <link>https://dev.to/szabi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/szabi"/>
    <language>en</language>
    <item>
      <title>Responsible programming</title>
      <dc:creator>szabi</dc:creator>
      <pubDate>Sun, 22 May 2022 10:01:00 +0000</pubDate>
      <link>https://dev.to/szabi/responsible-programming-k4o</link>
      <guid>https://dev.to/szabi/responsible-programming-k4o</guid>
      <description>&lt;p&gt;As programmers we come a across quite often a question: “Is the application safe to deploy?”. I had to ask myself as well sometimes: should I deploy X or Y feature even if there is an edge-case bug in there? Of course later those edge cases are the bugs, that the users find. Even though they aren’t looking for them, but this just shows sometimes how far we get from the way our users would expect an application to work.&lt;/p&gt;

&lt;p&gt;Long time ago when someone would say: &lt;em&gt;We can only deploy an application when it is safe to do open-heart surgery with it!&lt;/em&gt; I would smile a bit to myself thinking that I’m not coding medical equipment nor aviation software. But as years past and I got more experience working with spaghetti code and legacy systems that I inherited from here and there, my view started to change on this. It changed to the point, where today I would say the same to fellow programmers.&lt;/p&gt;

&lt;p&gt;Save time today by cutting corners (e.g. cutting on writing tests for a fix or a small feature) and you will waste 10x more trying to fix it later or add a new feature to it. But &lt;em&gt;if&lt;/em&gt; it would only be time that is wasted… but its not just time, it is never only time…&lt;/p&gt;

&lt;p&gt;With bad code you will waste credit, clients will not trust your work and will have a fear from either deployment (what will break this time) or from not getting what they asked for (we got A and B, but C will only work if you sacrifice a chicken at a blood moon).&lt;/p&gt;

&lt;p&gt;With bad code you will also waste the time of the users, if that user is trying to use your app to actually do their job, then you’re wasting their employer’s money as well.&lt;/p&gt;

&lt;p&gt;With bad code you can cause your user to miss their monthly bonus, if you do an app that is used by employers to track employee performance.&lt;/p&gt;

&lt;p&gt;With bad code you can also waste money of your client directly. Even if you do a small webshop and if any steps of the checkout or placing the product in the cart is acting badly, then your client will lose their clients.&lt;/p&gt;

&lt;p&gt;With bad code you can get someone unemployed - doing an accounting application that doesn’t sum up everything correctly, or misses to add some taxes.&lt;/p&gt;

&lt;p&gt;With bad code you can get someone imprisoned, as above bad tax reports can be seen as tax fraud in front of court.&lt;/p&gt;

&lt;p&gt;With bad code you can kill someone even if you don’t do aviation or other safety-critical software.&lt;/p&gt;

&lt;p&gt;There are lots of things that can go bad in software and while I enjoy making spaghetti code better, adding tests, breaking them down and adding meaning full naming. Every time I look at an app that is 100% spaghetti and I know that there are thousands of users using it daily, can’t help it but some questions comes to my head: “How did this make it out to production? Who thought this is okay to do? How is this still running?”&lt;/p&gt;

&lt;p&gt;If you write code that someone will use, then please read the following three articles and take it to your heart. Think of them every time you code something! Even an accounting app can kill someone!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.dailymail.co.uk/news/article-9502925/Post-Office-scandal-Dozens-staff-today-convictions-quashed.html"&gt;https://www.dailymail.co.uk/news/article-9502925/Post-Office-scandal-Dozens-staff-today-convictions-quashed.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.theatlantic.com/technology/archive/2017/09/saving-the-world-from-code/540393/"&gt;https://www.theatlantic.com/technology/archive/2017/09/saving-the-world-from-code/540393/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/Volkswagen_emissions_scandal"&gt;https://en.wikipedia.org/wiki/Volkswagen_emissions_scandal&lt;/a&gt; - this one is an exception. Here not the code quality, but the quality of the person who done the code knowingly is the issue.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Is your code good enough to do an open heart surgery with it? Would you bet your life on your code?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>development</category>
      <category>management</category>
      <category>codequality</category>
    </item>
    <item>
      <title>TrueNAS &amp; Nextcloud</title>
      <dc:creator>szabi</dc:creator>
      <pubDate>Sat, 07 May 2022 08:42:00 +0000</pubDate>
      <link>https://dev.to/szabi/truenas-nextcloud-3mo7</link>
      <guid>https://dev.to/szabi/truenas-nextcloud-3mo7</guid>
      <description>&lt;p&gt;Awhile back I wrote about my experience in running &lt;a href="https://dev.to/szabi/time-travel-and-privacy-16b0"&gt;TrueNAS and Nextcloud&lt;/a&gt; server at home. Thought I would give a small update how is that after 1.5 years.&lt;/p&gt;

&lt;p&gt;I’m still very happy with the whole setup, even extended storage in the server. Had some interesting experiences when I tried to upgrade Plex and Nextcloud jails. There I just gave up, I still update and upgrade them, but not via TrueNAS’ update functionality, but by logging into the instance and issuing the update commands in the container. Also before updating I always make backups of them using the &lt;a href="https://www.truenas.com/docs/core/storage/snapshots/"&gt;snapshot feature&lt;/a&gt; and when I managed to fubar my Nextcloud instance it was just ~4 clicks to restore it to normal without losing data or settings or anything. It is quite fiddly, still didn’t manage to figure out how am I going to upgrade the MySQL in that container. Tried to just use the system’s package manager, but that didn’t turn out well.. So might gonna re-create a new instance and just migrate settings and mounts over.&lt;/p&gt;

&lt;p&gt;Managed to discover a &lt;a href="https://apps.nextcloud.com/apps/news"&gt;plugin&lt;/a&gt; for Nextcloud recently that manages RSS feeds and has also an &lt;a href="https://play.google.com/store/apps/details?id=de.luhmer.owncloudnewsreader"&gt;Android app&lt;/a&gt;, so that it can keep track of read and unread news no matter where the reading happened. Pretty much like feedly (used that service before finding this), so I can read some articles on the computer and those will be also marked as read on the mobile. Writing it down it feels like a laughable feature, but its really convenient! (one less google owned service that I use)&lt;/p&gt;

&lt;p&gt;My only grievance with Nextcloud is a &lt;a href="https://github.com/nextcloud/android/pull/9981"&gt;regression in their mobile app&lt;/a&gt; that they are really slow on fixing it. The issue is about that once you sync photos from Nextcloud to your phone, then the mobile’s gallery app won’t see them properly, so you cannot use those photos in other apps. E.g. would like to crop and only send the cropped image to someone on discord, well then you’re out of luck.. have to edit it on the computer.. and since I’m on the computer, might as well send it from there.. Not a deal breaker bug, but it is annoying. Like this Nextcloud for my wife is only about backup and easy access on the computer.&lt;/p&gt;

&lt;p&gt;Over to backup feature of TrueNAS. Those files that are important to me and they are not in Nextcloud (e.g. full family photo collection) I setup TrueNAS to do daily cloud backups. I’m using &lt;a href="https://www.backblaze.com/b2/cloud-storage.html"&gt;Backblaze&lt;/a&gt; for it and like this I have an offsite backup for ~7 USD/month for all those files that are truly important. Also TrueNAS can do encrypted backups, so if the worst would happen and Backblaze would get hacked, then still nobody would have to see pictures of me sipping beer in my underwear. :D&lt;/p&gt;

&lt;p&gt;So in this post I have mentioned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nextcloud 11 times and&lt;/li&gt;
&lt;li&gt;TrueNAS 7 number of times&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Safe to say that I’m very happy with this setup and that I’ve managed to repurposed an old PC that wouldn’t really get otherwise any use.&lt;/p&gt;

</description>
      <category>nextcloud</category>
      <category>truenas</category>
      <category>backup</category>
      <category>server</category>
    </item>
    <item>
      <title>Rewrite, refactor, knee-deep in legacy - part two</title>
      <dc:creator>szabi</dc:creator>
      <pubDate>Sat, 30 Apr 2022 10:07:00 +0000</pubDate>
      <link>https://dev.to/szabi/rewrite-refactor-knee-deep-in-legacy-part-two-1o49</link>
      <guid>https://dev.to/szabi/rewrite-refactor-knee-deep-in-legacy-part-two-1o49</guid>
      <description>&lt;p&gt;I really don’t take on extra work apart of my full time job. At work I do get to try many different things. We get chance to work on both legacy and greenfield projects. So really don’t feel that I would need extra projects on the side.. :D&lt;/p&gt;

&lt;p&gt;This project that I’m gonna write about is a webshop, that sort of fallen into my lap. Back in 2010, when I started out with programming I’ve been doing webhops and inventory sites, using PHP, MySQL. So it didn’t feel too alien to me to help out every now and then, when the person running it needed help. Even though I really don’t like webhops and always turned them away, this was the first and last exception.&lt;/p&gt;

&lt;p&gt;I got to be a second dev on this project as the original author had less and less time to deal with it. Eventually the situation got to the point that I had to take over the maintenance fully (to much of my dislike).&lt;/p&gt;

&lt;h2&gt;
  
  
  State
&lt;/h2&gt;

&lt;p&gt;The site was written in PHP using MySQL and NextJS for the front-end. The backend was producing normal JSONs, so nothing too exotic there. The issues started when one started to look under the hood. The original author also didn’t have much time to actually do the site and this was reflected in the code as well.&lt;/p&gt;

&lt;p&gt;It was really chaotic and as I didn’t do programming in PHP for long it was hard for me to change it in a good way. Since the site was simple there was no framework used and as it was a rushed job the most common mistake was also made: no tests were written.&lt;/p&gt;

&lt;p&gt;Front-end was also similar, with the exception that there NextJS gave a nice frame to it, but also zero tests to see if everything is working.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kickoff
&lt;/h2&gt;

&lt;p&gt;When I had to take over the maintenance including running the server, it was clear that I won’t be able to support the PHP backend specially in its current form. Wanted to fix that up and stick with it, but really couldn’t get a handle on it.&lt;/p&gt;

&lt;p&gt;At that time I started to get to know NestJS as at my company we started to migrate services to this framework. So for me it was an easy choice to go with NestJS. The plan was to rewrite the backend using NestJS while keeping the database schema and HTTP API the same.&lt;/p&gt;

&lt;p&gt;This time I’ve made sure to do it in TDD, so that every new route and service was fully tested. Some logic issues came out that were also present in the PHP version. It was an interesting journey, but at times it did feel monotone, doing the same over and over. But on the end all worked out and managed to deploy the backend with nearly zero change to the front-end.&lt;/p&gt;

&lt;p&gt;With NestJS the default database ORM is TypeORM, it supports many different databases. In general I don’t trust libraries that can do anything while providing the same interface. But here it actually worked out nicely, as when I was looking for database providers I couldn’t find cheap MySQL provider while I managed to find a really cheap (actually free plan) for PostgreSQL. This was the first time where I actually migrated from one database to a different one with about 10 minutes job thanks to the ORM. I did concentrate on using the ORM’s methods for building queries and had zero raw SQL statements in the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frontend
&lt;/h2&gt;

&lt;p&gt;The frontend wasn’t in the best of shape either, but hey it is React thanks to NextJS, so that help a bit. It wasn’t in such a bad shape that I would want to rewrite from scratch, but I needed some proof after each and every change that all is working.&lt;/p&gt;

&lt;p&gt;Tried to write tests using Jest, but the react part wasn’t written with the best practices, so it brought out all timing issues. This way proved to be really slow going. Here I decided to do an end-to-end testing using Cypress instead unit and functional tests with Jest.&lt;/p&gt;

&lt;p&gt;Had zero experience with Cypress, just seen some PRs and nice comments at work from colleges. Before jumping into Cypress tests I crunched through a course from udemy, with double playback speed, think it took me day and a half. Sorry, don’t want to brag here but still makes me smile thinking about the instructor’s voice in double speed :D&lt;/p&gt;

&lt;p&gt;After this I covered all the pages and all the features that I came across. With cypress it was truly a breeze, timing issues were still present, but manage do put some &lt;code&gt;cy.wait&lt;/code&gt; commands and TODO comments for a later time.&lt;/p&gt;

&lt;p&gt;Once cypress covered everything, refactoring the front-end and getting rid of all the timing issues was a dream. Okay maybe not a dream, but went with good speed and managed to do frequent deploys, knowing all is working.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outro
&lt;/h2&gt;

&lt;p&gt;Overall I’m happy that I chose rewrite, with the old code without tests I would still be having nightmares. Since then I managed to extend on the features of the site and also automated a lot of tasks on it. All these new features were done with tests, so at all time I know that the site is working and that the person relying on it won’t lose sales due to bugs.&lt;/p&gt;

&lt;p&gt;Also this project gave me a good chance to deepen my knowledge with NestJS, Cypress, React and NextJS. Did make me a fan of NestJS and NextJS!&lt;/p&gt;

&lt;p&gt;However I did put a &lt;strong&gt;lot&lt;/strong&gt; of work into it. Looking back on it, eventually I rewrote the front-end as well. Via refactors think there is no line that I didn’t touch there. So in hind site for a webshop I would pick a provider rather than hand rolling it.&lt;/p&gt;

</description>
      <category>node</category>
      <category>nestjs</category>
      <category>nextjs</category>
      <category>codequality</category>
    </item>
    <item>
      <title>Rewrite, refactor, knee-deep in legacy - part one</title>
      <dc:creator>szabi</dc:creator>
      <pubDate>Sun, 24 Apr 2022 16:37:00 +0000</pubDate>
      <link>https://dev.to/szabi/rewrite-refactor-knee-deep-in-legacy-part-one-5d6k</link>
      <guid>https://dev.to/szabi/rewrite-refactor-knee-deep-in-legacy-part-one-5d6k</guid>
      <description>&lt;p&gt;From time-to-time we encounter some legacy projects - or the braver developers even work on them day after day. :) When working on such projects we do come across the exclamation that: “Starting from scratch would be quicker than trying to fix this mess!”. This normally comes from the frustration that legacy projects can cause.&lt;/p&gt;

&lt;p&gt;There are already lots of articles out on the interwebs about refactor vs rewrite. Failure and success stories and would encourage those who don’t have experience in deciding this question to read many articles, pro and con. This article includes a few good one:&lt;a href="https://medium.com/@herbcaudill/lessons-from-6-software-rewrite-stories-635e4c8f7c22"&gt;https://medium.com/@herbcaudill/lessons-from-6-software-rewrite-stories-635e4c8f7c22&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So where do I stand with this question? Personally I like to work on legacy systems, one can feel like Indiana Jones exploring hidden gems, funny comments and many odd solutions. Apart of this archaeological work when you just correct a small bit on legacy code that can sometimes yield huge performance improvements that makes work on them quite rewarding. So from this you can guess that I’m more towards the refactor end of the scope. In my experience rewrite from scratch doesn’t work on larger systems.&lt;/p&gt;

&lt;p&gt;Got two rewrite stories to share. Long-long time ago I had the chance to partake in a full rewrite of a large system that turned out to be the text book example of the struggle and up hill battles. Second is a smaller project but was a good reminder for me why I don’t like rewrites.&lt;/p&gt;

&lt;h2&gt;
  
  
  The System
&lt;/h2&gt;

&lt;p&gt;The system that I have had the chance to contribute in rewriting was like this: had a legacy code base in python using Django with a MySQL database, then there was a proof-of-concept app written next to it in NodeJS using MongoDB. This PoC app supposed to be temporary. Yes, this is the point where you can already start to laugh, like that ever happens… temporary…, of course it went to production and the temp PoC solution hunted the company for 3+ years.&lt;/p&gt;

&lt;p&gt;We tried to fix up the temp solution to get it into shape and avoid throwing ever growing amount of money on CPU/memory/DB and network resources. But the beast always managed to grow another head. This was the point where the lead at that time decided to start over: clean slate.&lt;/p&gt;

&lt;p&gt;Lesson #1: when you create a PoC or temp solution it still need to adhere to the highest quality standards. In our case it was a rushed steaming pile of spaghetti, straight from the stow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rewrite
&lt;/h2&gt;

&lt;p&gt;Now we have an instable PoC system in production and a stable but painfully slow legacy system. How would you go about the rewrite? One could say divvy up the system and start rewriting small portions of it. Yeah, one could definitely take that path, but that wasn’t the one we picked.&lt;/p&gt;

&lt;p&gt;We started to create a parallel system from scratch dreamed up all the microservices ahead of time. Decided to continue to use NodeJS with MongoDB as that was already the hype back then. We created a synchronization service that would actively bring data over from the old python system. The legacy and the PoC was all used in production and we wanted to test early the new microservices as well so we constantly synced data over to the system that nobody used yet.&lt;/p&gt;

&lt;p&gt;The development teams were divided along the new and old systems. There was a larger development team supporting the old system that was actively bringing in money for the company and paying all of our salaries. Another smaller team had the marvellous job of learning about new technologies and write the microservices that will be the new system that will save us all.&lt;/p&gt;

&lt;p&gt;I’ve had the chance to be in both of those parts (supporting the legacy and help with the rewrite) actually neither side is fun. Supporting the old application had the pressure from the clients that they needed newer and newer features, hunting bugs, dealing with downtimes. Reward part here was that when you have done something you got instant feedback on your job. In the rewrite team you get the pressure that everybody is waiting for this saviour system to come. They expect to get a system that will solve all their problems, it will solve their insomnia, it saves their marriage, they will be better parents and the new system will even make them coffee in the morning. Also there is more developers on the old system cranking out newer and newer features that the new system of course will support! :D&lt;/p&gt;

&lt;p&gt;Catching the ever growing features of the system we would like to replace is just like Don Quichotte fighting with the windmills. You have very small chance of winning. We actually tried to push back on the adoption of the new system saying that its just not yet ready. But eventually we all agreed that it will never be fully ready and at one point we need to start using it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adaption of the new system
&lt;/h2&gt;

&lt;p&gt;The whole team saw that we won’t be able to reach feature parity. For the adoption we pick a client that had the least need of custom features and set them up with the new system.&lt;/p&gt;

&lt;p&gt;There were a lot of hick-ups, we were correcting a lot of bugs and mistakes that came from the fact that the new system was also rushed. It didn’t become spaghetti and thanks god we did write &lt;em&gt;some&lt;/em&gt; tests - unlike the mentioned “temp PoC” project that had zero tests. But eventually the new system was working, it started to generate income for the company! To get to this point it took the teams about 2 years!!! Also there was one big challenge still remaining: get all the clients on it.&lt;/p&gt;

&lt;p&gt;Migrating the clients was the next stage of the rewrite. Clients shouldn’t need to know about the changes we make in the background. They have a service that they expect to get regardless if the backend is python or nodejs or assembly. Interesting to note here that the service provided by the system relied on historical data. That data was all in the old system! All that data had to be converted and moved into the new system. This task was the one I actually ended up enjoying maybe the most. Luckily the conversion wasn’t too complex and it was interesting to see that we filled up the new databases through weeks of carefully controlled migrations. Had to be careful not to over load the old system. Then after all that: it worked! The system manage to show the same reports to the users just like the old system! 2-3 years and we managed to get to the same point as where we were before! :D&lt;/p&gt;

&lt;h2&gt;
  
  
  Different way
&lt;/h2&gt;

&lt;p&gt;The temp PoC system was some sort of microservices architecture, sometimes we were joking about it saying its a micro-monolith as 4 services were serving it. The python legacy system was monolith. New system with the planning up front without good experience with microservices was more towards a distributed monolith with ~8 services serving the flow. We managed to change on this over time (without starting from scratch).&lt;/p&gt;

&lt;p&gt;Thinking back of the decisions and what happened during the rewrite, frustrations from both development and customer service teams, if I would have to do the whole thing all over again, then would choose a different path.&lt;/p&gt;

&lt;p&gt;Starting again with a monolith system would look at the processes and would try to move out small parts of the big service to a new microservice. Breaking down the monolith bit by bit, so that all parts are constantly in use and being validated. When we were doing the rewrite and testing we didn’t test the system with high loads. Of course everything was working on low loads and amazing developer computers, but when it went to production and came the load there were lots of performance issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outro
&lt;/h2&gt;

&lt;p&gt;In the above case rewrite was a bad decision in my opinion, but still we managed to get it working. There are cases where you have no other choice than starting from scratch. In another post I’ll share a story where I did a full rewrite and would do the same again in that situation.&lt;/p&gt;

</description>
      <category>node</category>
      <category>legacy</category>
      <category>refactor</category>
      <category>rewrite</category>
    </item>
    <item>
      <title>Time travel and privacy</title>
      <dc:creator>szabi</dc:creator>
      <pubDate>Mon, 22 Feb 2021 17:39:00 +0000</pubDate>
      <link>https://dev.to/szabi/time-travel-and-privacy-16b0</link>
      <guid>https://dev.to/szabi/time-travel-and-privacy-16b0</guid>
      <description>&lt;p&gt;In December of 2020 I read some articles that made me want to go back in time and use tools that I considered to be old. One of these articles was about the productivity score by Microsoft’s O365, another was about how our phones betray our trust (no surprise there).&lt;/p&gt;

&lt;p&gt;The content behind the following three links that made me think:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.theguardian.com/technology/2020/nov/26/microsoft-productivity-score-feature-criticised-workplace-surveillance"&gt;https://www.theguardian.com/technology/2020/nov/26/microsoft-productivity-score-feature-criticised-workplace-surveillance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/WolfieChristl/status/1331221942850949121"&gt;https://twitter.com/WolfieChristl/status/1331221942850949121&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nrkbeta.no/2020/12/03/my-phone-was-spying-on-me-so-i-tracked-down-the-surveillants"&gt;https://nrkbeta.no/2020/12/03/my-phone-was-spying-on-me-so-i-tracked-down-the-surveillants&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Mail and Calendar
&lt;/h2&gt;

&lt;p&gt;After reading the article about O365 and that GSuite also has something similar to that, I decided to go back to some kind of e-mail and calendar application like &lt;a href="https://www.thunderbird.net/"&gt;Thunderbird&lt;/a&gt; or &lt;a href="https://help.gnome.org/users/evolution/stable/"&gt;Evolution&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Yes I did use O365 via the browser before, it worked actually quite well didn’t miss notifications and it overall provided a good interface. But using the browser it also means that they can track:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;where is my mouse on the screen,&lt;/li&gt;
&lt;li&gt;if the tab is actually active on my screen&lt;/li&gt;
&lt;li&gt;what is on my screen&lt;/li&gt;
&lt;li&gt;typing and deleting and typing…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So really waaaay too much information that I would feel comfortable to share.&lt;/p&gt;

&lt;p&gt;Must say it here: I’m not working for a company that collects such information or would do actions based on such information. If the company would change and go in this direction then I would have the choice to change my employer. Still I would rather avoid providing even the possibility for it.&lt;/p&gt;

&lt;p&gt;Looked through possible e-mail clients for GNU/Linux, there were two things that are a must have for me:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;no electron apps - don’t want to run a full browser with nodejs.. just don’t&lt;/li&gt;
&lt;li&gt;to be able to integrate with a calendar app.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Checked &lt;a href="https://bluemail.me/"&gt;BlueMail&lt;/a&gt; briefly, but fall short on &lt;em&gt;a)&lt;/em&gt; being an electron app and &lt;em&gt;b)&lt;/em&gt; not really maintained.&lt;/p&gt;

&lt;p&gt;Looked at Thunderbird, it is still an okay with &lt;a href="https://addons.thunderbird.net/en-US/thunderbird/addon/lightning/"&gt;Lightning&lt;/a&gt; it can also satisfy the calendar and it is still the default e-mail client in Ubuntu. While I don’t use Ubuntu this will still keep maintainers motivated. However Thunderbird just didn’t feel right to me.&lt;/p&gt;

&lt;p&gt;Lastly I tried Evolution. After installing and setting it up, realized that I used to use this client back when I started programming in 2010. Suddenly I’m back in 2010, just have more grey hair than I used to.&lt;/p&gt;

&lt;p&gt;With evolution it was a huge surprise to me that it works with O365 out of the box, syncs calendar, contacts and even the todo items (if I would use them.. org-mode FTW!).&lt;/p&gt;

&lt;p&gt;NOTE: MS did admit this wasn’t a good way and they are going to remove some low level reporting. See their &lt;a href="https://www.microsoft.com/en-us/microsoft-365/blog/2020/12/01/our-commitment-to-privacy-in-microsoft-productivity-score/"&gt;blog post&lt;/a&gt; about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Chat applications
&lt;/h2&gt;

&lt;p&gt;Okay this is bit of different topic than MS and phones tracking all of our moves, but still related to the back in time theme.&lt;/p&gt;

&lt;p&gt;By moving my e-mail and calendar to an app I managed to close two pinned tabs from my browser and also managed to avoid using container tabs to be able to login to my son’s and daughter’s O365 account (they are in lower grades of primary school, so yes I do get to manage their accounts for when we’re in remote tuition mode). With this closing of tabs I noticed that there are other tabs that I also use they are just constantly pinned: whatsapp, facebook chat, discord and sometimes gitter. So this provided a new challenge: can I find an app to unite them all? Yes I can.&lt;/p&gt;

&lt;p&gt;Again didn’t wanted to use something that is just another browser, so went with an old-old friend &lt;a href="https://pidgin.im/"&gt;Pidgin&lt;/a&gt;. All the listed chat platforms have a plugin for it, open source with a clean code base (didn’t review all of the code, but all the files I visited was clean and relatively easy to understand), so very much tempting to contribute. Best of all: Pidgin will only eat up ~70 Mb of memory - opposed to gigabytes - and won’t send statistics about me to the facebook and other data hoarder and dealers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mobile front
&lt;/h2&gt;

&lt;p&gt;All this has triggered the privacy demon in me so started to look for alternatives as well on mobile. First big hoarder to tackle: Google. The app that is actual for me to look at is Google Photos. I loved this app as made it very easy for me to share pictures with my wife or with anyone. Also could access all the photos on my computer quickly and all is backed up and safe.&lt;/p&gt;

&lt;p&gt;All good things must come to an end at some point for me with Google Photos the end meant that - understandably - they don’t provide free storage anymore. In the past if you accepted that they re-compress the images with their algorithms, then they wouldn’t count towards used data - so free and unlimited storage.&lt;/p&gt;

&lt;p&gt;Photos are actually a long story for me, tried a lot of different solutions when dealing with photos from my camera. Nothing work truly easily. A new motivation&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;apart of google - was that my wife started to make photos and videos with&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;the family DSLR. My wife isn’t tech savvy, so whatever solution I try next it has to be seamless to her, other way she won’t use it.&lt;/p&gt;

&lt;p&gt;Now to the solution: &lt;a href="https://www.truenas.com/download-tn-core/"&gt;TrueNAS&lt;/a&gt;. Yes, decided to run my own storage at home. As you may already guess I didn’t go with the most straightforward solution… nah life would be boring if we wouldn’t make is pointlessly over complicated!&lt;/p&gt;

&lt;p&gt;So for NAS my criteria was that I want to be able to extend it later when and if I need more storage. For this you need more than just a two bay NAS, but rather a 4 or 5 bay NAS. Now those bigger NAS setups a for me just ridiculously expensive. Instead of spending big, decided to reuse my desktop PC from 2008 to build a NAS using TrueNAS system. It is really an awesome setup for me, but think I’ll write about that in a dedicated post.&lt;/p&gt;

&lt;p&gt;On this TrueNAS setup I also installed &lt;a href="https://nextcloud.com/"&gt;Nextcloud&lt;/a&gt;, this is able to replace both Google Photos and Dropbox for me. Got a mobile app for it, easy enough to use and there is also a Gnome integration for it. Apart of images and files it can also serve as a calendar and contacts server (another stab at Google).&lt;/p&gt;

&lt;p&gt;For larger files also configured NFS endpoints so that we can handle images from the DSLR. NFS works surprisingly well with Darktable, it is able to read the files quickly enough. If I would deal with images as a profession would probably invest into a better network setup, but it works well enough via WiFi already. Only secret is to keep &lt;a href="https://www.darktable.org/"&gt;Darktable&lt;/a&gt;’s SQLight database files on the local system, having that over NFS renders the app uselessly slow.&lt;/p&gt;

&lt;p&gt;Having the above setup working reliable and well, I’m really considering going back to a privacy oriented Android setup on my phone as well, without all the bloatware and mandatory google applications installed.&lt;/p&gt;

&lt;h2&gt;
  
  
  News sources
&lt;/h2&gt;

&lt;p&gt;RSS is still out there and still lives, I was surprised to find that most news sites will have RSS feeds as well. With RSS again one can use a simple application, even Evolution has RSS reader plugin and have all the news tracking free.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outro
&lt;/h2&gt;

&lt;p&gt;The journey with all those apps and different setups made me realize again that we do share a lot of information about ourselves on the internet. In the background we have no idea how they use all that data. The collected data shouldn’t be connectable the person behind it, but in truth it can be. If you don’t read all the three articles linked in the beginning, just read &lt;a href="https://nrkbeta.no/2020/12/03/my-phone-was-spying-on-me-so-i-tracked-down-the-surveillants/"&gt;Martin Gundersen&lt;/a&gt;’s one.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What gets measured gets managed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Through the data that we submit knowingly or not, voluntarily or not, will enable companies to manage our actions. So now I’m back in 2010, running my own server and using native application instead of browser based solutions.&lt;/p&gt;

</description>
      <category>privacy</category>
      <category>backups</category>
      <category>nas</category>
      <category>nextcloud</category>
    </item>
    <item>
      <title>Bad dependencies and bad interface changes</title>
      <dc:creator>szabi</dc:creator>
      <pubDate>Tue, 19 May 2020 17:29:44 +0000</pubDate>
      <link>https://dev.to/szabi/bad-dependencies-and-bad-interface-changes-pmo</link>
      <guid>https://dev.to/szabi/bad-dependencies-and-bad-interface-changes-pmo</guid>
      <description>&lt;p&gt;Recently one of my colleagues has shared his pain about a nodeJS package that we use to zip files.&lt;/p&gt;

&lt;p&gt;His issue was that the library stopped generating the zip files and just hang. Eventually found, that the package in question has been upgraded,&lt;br&gt;
but with a small issue. The newer version has changed the interface and the same method instead of returning the instance it returns a Promise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Archiver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;finalize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aborted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;finalize: archive was aborted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;finalize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;finalize: archive already finalizing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;finalize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_pending&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;idle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_statQueue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;idle&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_finalize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;span class="src-block-number"&gt;Code Snippet 1&lt;/span&gt;:&lt;br&gt;
  Old version&lt;/p&gt;

&lt;p&gt;&lt;span class="underline"&gt;source:&lt;/span&gt; &lt;a href="https://github.com/archiverjs/node-archiver/blob/0.15.1/lib/core.js#L489-L507"&gt;https://github.com/archiverjs/node-archiver/blob/0.15.1/lib/core.js#L489-L507&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Archiver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;finalize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aborted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ArchiverError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ABORTED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;finalize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ArchiverError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FINALIZING&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;finalize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_pending&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;idle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_statQueue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;idle&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_finalize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;errored&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;end&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;errored&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;errored&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;span class="src-block-number"&gt;Code Snippet 2&lt;/span&gt;:&lt;br&gt;
  New version&lt;/p&gt;

&lt;p&gt;&lt;span class="underline"&gt;source:&lt;/span&gt; &lt;a href="https://github.com/archiverjs/node-archiver/blob/dd7f10d/lib/core.js#L759-L792"&gt;https://github.com/archiverjs/node-archiver/blob/dd7f10d/lib/core.js#L759-L792&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bad interface changes
&lt;/h2&gt;

&lt;p&gt;The most painful thing about the new code is not that it has the same name while returning something new, this can be forgiven, since the upgrade was not minor but &lt;em&gt;major&lt;/em&gt; upgrade. Fair is fair in major upgrade methods can change and retain the same name, might not be the best, but can happen.&lt;/p&gt;

&lt;p&gt;The thing that actually bad in my point of view, that it can return two completely different things. Till there is an &lt;code&gt;if&lt;/code&gt; that is being triggered it returns the instance of the class, &lt;strong&gt;but&lt;/strong&gt; once those are exhausted it returns a &lt;code&gt;Promise&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Icing on the cake that in this case the instance that it returns is a stream, that we were just piping, but now at some point it will turn in to a &lt;code&gt;Promise&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Generally it is a good rule of thumb that a method should always return with the same type, no matter on the logic inside. (Unless your method is a type selector or something like that :D )&lt;/p&gt;

&lt;p&gt;For the above code I could imagine that the original &lt;code&gt;finalize&lt;/code&gt; could have remained the same, always returning a stream. There could have been a new method created that would give back a promise and also retaining backward compatibility. E.g.: introducing an &lt;code&gt;asPromised&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Archiver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;finalize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aborted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ArchiverError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ABORTED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;finalize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ArchiverError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FINALIZING&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;finalize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_pending&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;idle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_statQueue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;idle&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_finalize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;Archiver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;asPromised&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;errored&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;end&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;errored&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;errored&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Bad dependencies
&lt;/h2&gt;

&lt;p&gt;Now over for the necessity of this npm package.&lt;/p&gt;

&lt;p&gt;This package is used in NodeJS, so we don't send it to do magic on the&lt;br&gt;
front-end, but we use it in a controlled environment: a NodeJS app running in a Docker container wrapped in a Kubernetes pod.&lt;/p&gt;

&lt;p&gt;For the docker container we use GNU/Linux, so we can have other programs in there as well, that can be found for Linux.&lt;/p&gt;

&lt;p&gt;The files that we zip are on an NFS drive, so we don't need to stream it, we know the location of the file as well.&lt;/p&gt;

&lt;p&gt;Here comes the idea:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  we have linux ✅&lt;/li&gt;
&lt;li&gt;  we have the files/folders accessible ✅&lt;/li&gt;
&lt;li&gt;  we could have the &lt;code&gt;zip&lt;/code&gt; program on the system ✅&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So in effect we could axe a third party npm package and use &lt;a href="https://nodejs.org/api/child%5Fprocess.html#child%5Fprocess%5Fchild%5Fprocess%5Fexec%5Fcommand%5Foptions%5Fcallback"&gt;child_process.exec&lt;/a&gt; or &lt;a href="https://nodejs.org/api/child%5Fprocess.html#child%5Fprocess%5Fchild%5Fprocess%5Fspawn%5Fcommand%5Fargs%5Foptions"&gt;child_process.spawn&lt;/a&gt; method's from NodeJS itself. The extra 3rd party in this case would be the &lt;code&gt;zip&lt;/code&gt; program, but think it is safe to say that command is a battle tested, fire hardened program. Which is not likely to cause issues or change interface. For me an extra benefit of this approach, that we can offload processing from the main app to a separate CPU thread &lt;em&gt;aaaand&lt;/em&gt; we can also use &lt;a href="https://www.gnu.org/software/coreutils/manual/html%5Fnode/nice-invocation.html"&gt;GNU's nice utility&lt;/a&gt; to set the importance of the task. Like this we can also&lt;br&gt;
ensure that the main app will always have enough CPU time to respond and the zip command won't eat it up from the app as well.&lt;/p&gt;

&lt;p&gt;Gains with this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;zip&lt;/code&gt; is a reliable program that has and is being tested by millions&lt;/li&gt;
&lt;li&gt;  offload processing from the main app, so it can still serve other requests without an issue&lt;/li&gt;
&lt;li&gt;  use built-in NodeJS module that is not likely to change interface and also tested by more than a lonely lib from npmjs.org&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;It is good to think through as what is the goal and not just pick an npm package off the shelf. Most issues have already been solved by someone previously.&lt;/p&gt;

&lt;p&gt;Originally I've posted this on &lt;a href="https://szab.it/posts/bad-dependencies-and-bad-interface-changes/"&gt;https://szab.it/posts/bad-dependencies-and-bad-interface-changes/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>npm</category>
      <category>node</category>
      <category>dependencies</category>
      <category>zip</category>
    </item>
    <item>
      <title>Custom keyboard layout in GNU/Linux</title>
      <dc:creator>szabi</dc:creator>
      <pubDate>Fri, 08 May 2020 15:00:05 +0000</pubDate>
      <link>https://dev.to/szabi/custom-keyboard-layout-in-gnu-linux-i5f</link>
      <guid>https://dev.to/szabi/custom-keyboard-layout-in-gnu-linux-i5f</guid>
      <description>&lt;p&gt;I generally like to use english keyboard layout for programming, makes it easier to get to the special characters. However, since I'm a hungarian, from time-to-time would like to use accented characters as well. E.g. &lt;code&gt;á&lt;/code&gt;, &lt;code&gt;é&lt;/code&gt;, &lt;code&gt;ű&lt;/code&gt;, &lt;code&gt;ú&lt;/code&gt;, &lt;code&gt;ő&lt;/code&gt;...&lt;/p&gt;

&lt;p&gt;In GNU/Linux to change keyboard layout isn't the easiest to do. As far as I remember (from about 10 years ago) in Windows there is a graphical app that helps you with this. In GNU/Linux this is all via text files.&lt;/p&gt;

&lt;p&gt;Here are the steps I've done:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;add a new section in the &lt;code&gt;us&lt;/code&gt; symbol file - &lt;code&gt;/usr/share/X11/xkb/symbols/us&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;partial alphanumeric_keys
xkb_symbols &lt;span class="s2"&gt;"euro-hu"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    include &lt;span class="s2"&gt;"us(basic)"&lt;/span&gt;
    name[Group1]&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"English (US, with Hun)"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    include &lt;span class="s2"&gt;"eurosign(5)"&lt;/span&gt;

    include &lt;span class="s2"&gt;"level3(ralt_switch)"&lt;/span&gt;

    key &amp;lt;AD07&amp;gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;            u,            U,        uacute,           Uacute &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    key &amp;lt;AD03&amp;gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;            e,            E,        eacute,           Eacute &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    key &amp;lt;AD08&amp;gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;            i,            I,        iacute,           Iacute &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    key &amp;lt;AD09&amp;gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;            o,            O,        oacute,           Oacute &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    key &amp;lt;AD10&amp;gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;            p,            P,    odiaeresis,       Odiaeresis &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    key &amp;lt;AC01&amp;gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;            a,            A,        aacute,           Aacute &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    key &amp;lt;AE10&amp;gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;            0,   parenright,    odiaeresis,       Odiaeresis &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; // 0 &lt;span class="o"&gt;)&lt;/span&gt; ö Ö
    key &amp;lt;AE11&amp;gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;        minus,   underscore,    udiaeresis,       Udiaeresis &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; // - _ ü Ü
    key &amp;lt;AE12&amp;gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;        equal,         plus,        oacute,           Oacute &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; // &lt;span class="o"&gt;=&lt;/span&gt; + ó Ó
    key &amp;lt;AD11&amp;gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;  bracketleft,    braceleft,  odoubleacute,     Odoubleacute &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; // &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; ő Ő
    key &amp;lt;AD12&amp;gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; bracketright,   braceright,        uacute,           Uacute &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; // &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt; ú Ú
    key &amp;lt;AC10&amp;gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;    semicolon,        colon,        eacute,           Eacute &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; // &lt;span class="p"&gt;;&lt;/span&gt; : é É
    key &amp;lt;AC11&amp;gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;   apostrophe,     quotedbl,        aacute,           Aacute &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; // &lt;span class="s1"&gt;' " á Á
    key &amp;lt;BKSL&amp;gt; { [    backslash,          bar,  udoubleacute,     Udoubleacute ] }; // \ | ű Ű
    key &amp;lt;LSGT&amp;gt; { [    backslash,          bar,  udoubleacute,     Udoubleacute ] }; // \ | í Í
};
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;the new keyboard variant should also be noted in the following four files as&lt;br&gt;
well:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;/usr/share/X11/xkb/rules/base.xml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;variant&amp;gt;
  &amp;lt;configItem&amp;gt;
    &amp;lt;name&amp;gt;euro&amp;lt;/name&amp;gt;
    &amp;lt;description&amp;gt;English (US, euro on 5)&amp;lt;/description&amp;gt;
  &amp;lt;/configItem&amp;gt;
&amp;lt;/variant&amp;gt;
&amp;lt;variant&amp;gt;
  &amp;lt;configItem&amp;gt;
    &amp;lt;name&amp;gt;euro-hu&amp;lt;/name&amp;gt;
    &amp;lt;description&amp;gt;English (US, with Hun)&amp;lt;/description&amp;gt;
  &amp;lt;/configItem&amp;gt;
&amp;lt;/variant&amp;gt;
&amp;lt;variant&amp;gt;
  &amp;lt;configItem&amp;gt;
    &amp;lt;name&amp;gt;intl&amp;lt;/name&amp;gt;
    &amp;lt;description&amp;gt;English (US, intl., with dead keys)&amp;lt;/description&amp;gt;
  &amp;lt;/configItem&amp;gt;
&amp;lt;/variant&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;/usr/share/X11/xkb/rules/base.lst&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;euro            us: English (US, euro on 5)
euro-hu         us: English (US, with Hun)
intl            us: English (US, intl., with dead keys)
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;/usr/share/X11/xkb/rules/evdev.xml&lt;/code&gt; - same way as &lt;code&gt;base.xml&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;/usr/share/X11/xkb/rules/evdev.lst&lt;/code&gt; - same as &lt;code&gt;base.lst&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;/ol&gt;

&lt;ol&gt;
&lt;li&gt; Restart the system and the new keyboard layout should be available - for gnome - in the &lt;em&gt;Regional &amp;amp; Language&lt;/em&gt; setting's &lt;em&gt;Input Sources&lt;/em&gt; section.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you wish to dig deeper in the topic there is a good documentation about this over on a polish programmer's site &lt;a href="https://michal.kosmulski.org/computing/articles/custom-keyboard-layouts-xkb.html"&gt;Michał Kosmulski's "Creating custom keyboard layouts for X11 using  KB"&lt;/a&gt; article.&lt;/p&gt;

&lt;p&gt;There is also an option to make keyboard layout modifications with Xmodmap, but I've had some bad experiences with it. Namely it was slow to load the config, so rather do it via XKB. Possible that I was doing something bad with Xmodmap... but we will never no that now. :D&lt;/p&gt;

&lt;p&gt;Originally I've posted this on &lt;a href="https://szab.it/posts/custom-keyboard-layout-in-gnu_linux/"&gt;https://szab.it/posts/custom-keyboard-layout-in-gnu_linux/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>keyboardlayout</category>
      <category>gnulinux</category>
      <category>linux</category>
      <category>archlinux</category>
    </item>
    <item>
      <title>MongoDB deprecating "count"</title>
      <dc:creator>szabi</dc:creator>
      <pubDate>Sun, 01 Mar 2020 08:22:00 +0000</pubDate>
      <link>https://dev.to/szabi/mongodb-deprecating-count-5ahk</link>
      <guid>https://dev.to/szabi/mongodb-deprecating-count-5ahk</guid>
      <description>&lt;p&gt;Prior warning: this post may turn into a rant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prologue
&lt;/h2&gt;

&lt;p&gt;In the second half of 2019 at my company we have got an expected notice from our MongoDB provider, Atlas.&lt;/p&gt;

&lt;p&gt;The notice was about the usual pain that they do every now and then: force upgrade for old versions. At that time we were running MongoDB v3.4 and so now we got the notice to ensure that we have a driver that supports v3.6 as all the clusters will be upgraded on the end of January 2020.&lt;/p&gt;

&lt;p&gt;All is well we look at these upgrades as a necessary evil, that causes pain in short therm, but will bring benefits in the long run. The benefits with newer MongoDB versions was the performance. We have tested some of our heavier queries - with which we already have had issues in production - and behold they become 10 times faster. (We were comparing MongoDB v3.4 with v4.2 at that time)&lt;/p&gt;

&lt;p&gt;We thought cool 10x the power! Lets do this!&lt;/p&gt;

&lt;p&gt;So we have started our long journey of upgrades, tests, fixes and further upgrades and tests, tears and cries, laughter and anger.&lt;/p&gt;

&lt;p&gt;Once we were satisfied with the upgrade we have deployed our first services,that was already in need of that performance boost. Cool we thought, surely we gonna have some co-workers coming to us saying: Boys don’t know what has happened, but the service is blazing fast!&lt;/p&gt;

&lt;p&gt;Man, we were wrong! Surely the queries looked fast, but there was a tiny bit of issue: some of our calls to the database started to timeout. Worst of it those calls were actually fast previously. As an icing on the cake this didn’t come to our attention straightaway, but only a week later, when another new service wanted to sync data.&lt;/p&gt;

&lt;p&gt;Once noted, we jumped into debugging. Looking at the database’s real-time operations (&lt;a href="https://docs.mongodb.com/manual/reference/method/db.currentOp/"&gt;&lt;code&gt;db.currentOp()&lt;/code&gt;&lt;/a&gt;) we were seeing &lt;code&gt;aggregation&lt;/code&gt; calls on the biggest collection being called. As we didn’t recall to have used such heavy aggregations on that collection, we searched through our code base to find what could issue this command.&lt;/p&gt;

&lt;p&gt;We have managed to find a couple of places where we have used aggregation, but none of them fit the match that we seen in the operations list.&lt;/p&gt;

&lt;p&gt;Eventually one team member suggested that that aggregation is the way MongoDB does the count. I couldn’t believe it at first, but then we have read a bit more about the new &lt;a href="https://docs.mongodb.com/manual/reference/method/db.collection.countDocuments/"&gt;&lt;code&gt;countDocuments&lt;/code&gt;&lt;/a&gt; method that was suggested method by the documentation to use instead of the &lt;a href="https://docs.mongodb.com/manual/reference/method/db.collection.countDocuments/"&gt;&lt;code&gt;count&lt;/code&gt;&lt;/a&gt; and turned out that it is indeed slower as it is more accurate.&lt;/p&gt;

&lt;p&gt;From MongoDB’s JIRA ticket &lt;a href="https://jira.mongodb.org/browse/NODE-1638"&gt;NODE-1638&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;count has been deprecated for a number of reasons. First, and foremost, the existing count command could not be run within transactions, so an alternative approach needed to be taken. Secondarily, the count command in most cases was not accurate. We have replaced count with countDocuments and estimatedDocumentCount. Please use estimatedDocumentCount for the performance you are looking for, and parity with the original count.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So the reasons against &lt;code&gt;count&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;not giving accurate results and&lt;/li&gt;
&lt;li&gt;not transaction friendly&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;From my point of view those two are not really reasons to deprecate a core command, which think is quite needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;count&lt;/code&gt; is not accurate
&lt;/h2&gt;

&lt;p&gt;Okay it isn’t, but honestly what was accurate in MongoDB before? Like with iterating a &lt;code&gt;cursor&lt;/code&gt; (with mongoose &lt;code&gt;stream&lt;/code&gt;), you could easily miss documents or see others twice in the process. Unless you set read preference to snapshot, but even then if the process is long running and you have inserts in the mean time,then you won’t see the new documents, so it is still a &lt;em&gt;meh&lt;/em&gt; solution.&lt;/p&gt;

&lt;p&gt;For processing all the data in the database, even the ones that didn’t exist when we started the process, we were using a practice where we sorted the &lt;code&gt;_id&lt;/code&gt; in ascending order, retrieving data in batches and using the last _id in the list with a greater than filter: &lt;code&gt;{ _id: { $gt: lastId } }&lt;/code&gt;. Like this we could processes all the documents without duplicates and if there were new documents created while the process run, no problem, still got them.&lt;/p&gt;

&lt;p&gt;Now in case of the count, so far I haven’t seen a case where would have needed pinpoint accuracy. I can imagine that there are case where one needs it, but then just like with the streaming above there is a solution for it. The solution in this case comes in aggregation and I’m sure that before the &lt;code&gt;countDocuments&lt;/code&gt;command developers were using it to get the accurate count that they needed.&lt;/p&gt;

&lt;p&gt;It is nice that now there is a method in mongo, that can give you the accurate count, without fiddling around with aggregation. It is convenient for those who need it. Still in my point it is not a reason to deprecate &lt;code&gt;count&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not transaction safe
&lt;/h2&gt;

&lt;p&gt;Well okay. It isn’t. Don’t know, never tried it. As I tend to work with micro-services, I never missed or wanted to use transactions. It is hard to implement across services. My preference for data consistency is to make operations idempotent and so it is safe to put them into job queues, that guarantee to run it at least once, hence getting &lt;a href="https://en.wikipedia.org/wiki/Eventual_consistency"&gt;eventual consistency&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Just to emphasize it: I do respect that in some cases transactions could be the best or only solutions and it is nice that &lt;code&gt;countDocuments&lt;/code&gt; is transaction safe. Just still not a reason to deprecate &lt;code&gt;count&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;So &lt;code&gt;count&lt;/code&gt; has been marked as deprecated in MongoDB v4.0, it is still well and alive in v4.2. Since the two replacement suggested to be used instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;countDocuments&lt;/code&gt; - way too slow for us&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;estimatedDocumentCount&lt;/code&gt; - cannot provide a query&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;are both unsuitable for us, we have reverted all our calls to use the poor old &lt;code&gt;count&lt;/code&gt; method and we have accepted that our terminal will be showing the deprecation warnings for a while.&lt;/p&gt;

&lt;p&gt;For now we hope that they won’t remove it or they will improve the performance of the new &lt;code&gt;countDocuments&lt;/code&gt; method to be on pair with &lt;code&gt;count&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finale
&lt;/h2&gt;

&lt;p&gt;Okay, this has indeed become a rant, but you have been warned. :D Sorry.&lt;/p&gt;

</description>
      <category>node</category>
      <category>mongodb</category>
      <category>database</category>
      <category>consistency</category>
    </item>
  </channel>
</rss>
