<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Sergei Zobov | Blog</title>
    <description>Personal blog</description>
    <link>https://szobov.github.io/</link>
    <atom:link href="https://szobov.github.io/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Fri, 17 Oct 2025 21:30:39 +0000</pubDate>
    <lastBuildDate>Fri, 17 Oct 2025 21:30:39 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>Advance your Robotics Startup: Monorepo and Functions</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#background&quot; id=&quot;markdown-toc-background&quot;&gt;Background&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#monorepo&quot; id=&quot;markdown-toc-monorepo&quot;&gt;Monorepo&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#re-usage-of-packages&quot; id=&quot;markdown-toc-re-usage-of-packages&quot;&gt;Re-usage of packages&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#re-usage-of-static-files&quot; id=&quot;markdown-toc-re-usage-of-static-files&quot;&gt;Re-usage of static files&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#share-configuration&quot; id=&quot;markdown-toc-share-configuration&quot;&gt;Share configuration&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#ci&quot; id=&quot;markdown-toc-ci&quot;&gt;CI&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#integration-tests-get-easier&quot; id=&quot;markdown-toc-integration-tests-get-easier&quot;&gt;Integration tests get easier&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#docker&quot; id=&quot;markdown-toc-docker&quot;&gt;Docker&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#versioning&quot; id=&quot;markdown-toc-versioning&quot;&gt;Versioning&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#ease-transition&quot; id=&quot;markdown-toc-ease-transition&quot;&gt;Ease transition&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#stop-using-classes&quot; id=&quot;markdown-toc-stop-using-classes&quot;&gt;Stop using Classes&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#use-functions&quot; id=&quot;markdown-toc-use-functions&quot;&gt;Use functions&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#conclusion&quot; id=&quot;markdown-toc-conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;background&quot;&gt;Background&lt;/h2&gt;

&lt;p&gt;Over a decade, I worked in various &lt;strong&gt;small&lt;/strong&gt; high-tech and robotics startups that often shared the same patterns: numerous repositories resembling “micro-services” architecture, OOP, and classes here and there.&lt;/p&gt;

&lt;p&gt;None of these things is inherently bad, but they introduce unnecessary complexity that slows down small teams. Unfortunately, in robotics startups, it can kill your company.&lt;/p&gt;

&lt;p&gt;Below, you’ll find a couple of low-hanging fruits that will help you advance your team.&lt;/p&gt;

&lt;h2 id=&quot;monorepo&quot;&gt;Monorepo&lt;/h2&gt;

&lt;p&gt;“Microservices” became a trend when I was a junior developer. Almost every company I know brought it because it’s “cool”. For some reason, people consider the term “microservices” complete only if these “microservices” are split into different repositories. Additionally, people put mobile or frontend applications separately.&lt;/p&gt;

&lt;p&gt;Cool, yes, but only if you’re a big company with many teams working on the same system.&lt;/p&gt;

&lt;p&gt;If you’re not, a monorepo should be your go-to solution.
Here is why:&lt;/p&gt;

&lt;h3 id=&quot;re-usage-of-packages&quot;&gt;Re-usage of packages&lt;/h3&gt;

&lt;p&gt;Suppose you have several private repositories and you want to reuse the code between them.&lt;/p&gt;

&lt;p&gt;What are your options?&lt;/p&gt;

&lt;p&gt;Set up &lt;a href=&quot;https://web.archive.org/web/20250814042034/https://www.sonatype.com/products/sonatype-nexus-repository&quot;&gt;Nexus&lt;/a&gt;/&lt;a href=&quot;https://web.archive.org/web/20250904182025/https://pypi.org/&quot;&gt;PyPI&lt;/a&gt;/&lt;a href=&quot;https://web.archive.org/web/20250904154814/https://www.npmjs.com/&quot;&gt;npm&lt;/a&gt;/etc Then you have to build your package and upload it to the local registry. Are you done? Not yet: now you need to configure package managers everywhere to point to your local registry. Do not forget, that you can f*ck up &lt;a href=&quot;https://web.archive.org/web/20250901190557/https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610&quot;&gt;configuration&lt;/a&gt;. Finally done? Not yet, because now you also need to figure out versioning.&lt;/p&gt;

&lt;p&gt;The other option is using submodules, but you can easily find on the internet why it’s not the &lt;a href=&quot;https://web.archive.org/web/20250205194457/https://news.ycombinator.com/item?id=31792303&quot;&gt;best idea&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Finally, you can use a monorepo!&lt;/p&gt;

&lt;p&gt;Literally every package manager supports local imports. No more need to copy constants across several modules, just put them in the module and export them everywhere.
Do you want something fancier, or does your codebase consist of too many different languages? Well, use &lt;a href=&quot;https://web.archive.org/web/20250901201054/https://buck2.build/&quot;&gt;buck2&lt;/a&gt; or &lt;a href=&quot;https://web.archive.org/web/20250904083730/https://bazel.build/&quot;&gt;bazel&lt;/a&gt; (but more likely a few bash scripts will be enough in your case).&lt;/p&gt;

&lt;h3 id=&quot;re-usage-of-static-files&quot;&gt;Re-usage of static files&lt;/h3&gt;

&lt;p&gt;Remember, I mentioned robotics?
What do we like most? Big bags of triangles to represent our tools and robots? What do we like even more? Big &lt;a href=&quot;https://web.archive.org/web/20250831003626/https://onnx.ai/&quot;&gt;ONNX&lt;/a&gt; files right into our repositories because we’re too lazy to properly set up S3-like storage.&lt;/p&gt;

&lt;p&gt;Git was not meant to store big binary blobs, so companies made ad hoc solutions like &lt;a href=&quot;https://web.archive.org/web/20250904051259/https://git-lfs.com/&quot;&gt;GitLFS&lt;/a&gt; to deal with it. If you want to go this way now, you have to set it up everywhere you want to reuse your shiny big mesh!&lt;/p&gt;

&lt;p&gt;With a &lt;strong&gt;monorepo&lt;/strong&gt;, do it once, reuse it everywhere.&lt;/p&gt;

&lt;h3 id=&quot;share-configuration&quot;&gt;Share configuration&lt;/h3&gt;

&lt;p&gt;Suppose you have a bunch of repositories with C++ or Python packages.
You have matured to the point where you want to use production technologies such as static analyzers and code formatters.
You wrote your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.clang-tidy&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.clang-format&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ruff.toml&lt;/code&gt;. What to do next? Of course, copy-paste them across all repositories! Now you want to change something and apply changes to all configurations, he-he…&lt;/p&gt;

&lt;p&gt;Solution? Monorepo.&lt;/p&gt;

&lt;p&gt;Put the configuration once in the root directory, and that’s it.&lt;/p&gt;

&lt;h3 id=&quot;ci&quot;&gt;CI&lt;/h3&gt;

&lt;figure class=&quot;image&quot;&gt;
  &lt;img src=&quot;/assets/images/monorepo-functions/reddit_comment.png&quot; alt=&quot;well framed&quot; /&gt;
  &lt;figcaption&gt;&lt;em&gt;well framed&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Wanna run tests and set up a GitHub action for that? Don’t forget to set it up for all your repositories. Oh, now you want to trigger tests/builds in the dependent repositories when you push changes to the common one? If you have ever tried to do that, you know it’s not the easiest task.&lt;/p&gt;

&lt;p&gt;Do you still want to do it, but don’t want to deal with all this hustle?
Use a monorepo. All your dependencies are at hand. Moreover, most likely, you want to run all of your unit tests when you make a change to ensure you didn’t break other parts.&lt;/p&gt;

&lt;h3 id=&quot;integration-tests-get-easier&quot;&gt;Integration tests get easier&lt;/h3&gt;

&lt;p&gt;Since I mentioned CI and tests.&lt;/p&gt;

&lt;p&gt;An extra benefit of using a monorepo is simplified writing of integration tests.
Put your mobile/frontend application in the same repository as your motion planner and enjoy catching bugs in displaying your metrics when the planner fails to plan all trajectories!&lt;/p&gt;

&lt;h3 id=&quot;docker&quot;&gt;Docker&lt;/h3&gt;

&lt;p&gt;Monorepo doesn’t prevent you from using microservices. It just makes building them even easier. Instead of dealing with uploading Docker images to the in-house Docker registry and making sure you run the recent version, just build it right here.&lt;/p&gt;

&lt;p&gt;More to this, you likely want to copy some of the files across multiple containers/images. With a monorepo, you are an enjoyer of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY shared_src/...&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;versioning&quot;&gt;Versioning&lt;/h3&gt;

&lt;p&gt;Versioning among different interdependent packages is hard and requires effort to make it right.&lt;/p&gt;

&lt;p&gt;The first question you should ask yourself: Do I need per-component versioning at all?&lt;/p&gt;

&lt;p&gt;Likely, the codebase represents your entire system. Monorepo perfectly matches it: you only need to put one tag when you do a release, and you’re good to go.&lt;/p&gt;

&lt;h3 id=&quot;ease-transition&quot;&gt;Ease transition&lt;/h3&gt;

&lt;p&gt;Imagine I convinced you to switch to the monorepo and merge all of your seven or eight others into one. How to make it simpler?&lt;/p&gt;

&lt;p&gt;Do it iteratively; you don’t need to change all at once. Choose the first one: “repo A”.
Make sure you have all in-work branches merged into “repo A”. After you merge “repo A” into the monorepo to merge these branches, it will require you to manually cherry-pick changes. Then, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git subtree add&lt;/code&gt; (without the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--squash&lt;/code&gt; option to preserve the history).&lt;/p&gt;

&lt;h2 id=&quot;stop-using-classes&quot;&gt;Stop using Classes&lt;/h2&gt;

&lt;p&gt;Wherever I worked, I often saw people use classes everywhere in their code.
I think it comes from the point that &lt;a href=&quot;https://web.archive.org/web/20250902181930/https://www.java.com/en/&quot;&gt;Java&lt;/a&gt; and C++ are widely studied in universities, and folks just get used to it.&lt;/p&gt;

&lt;p&gt;I have no feelings about classes, but they often make writing tests much harder since the constructors become huge and objects require too many dependencies for initialization.&lt;/p&gt;

&lt;p&gt;This is well described in the book &lt;a href=&quot;https://archive.org/details/working-effectively-with-legacy-code&quot;&gt;“Working Effectively with Legacy Code”&lt;/a&gt; by Michael Feathers, which I highly recommend every software engineer to read.&lt;/p&gt;

&lt;p&gt;We all know that tests are a must-have and they can drastically increase your development speed, but as with anything else, as hard as it is to do, we likely avoid them.&lt;/p&gt;

&lt;p&gt;So, what’s the solution?&lt;/p&gt;

&lt;h3 id=&quot;use-functions&quot;&gt;Use functions&lt;/h3&gt;

&lt;p&gt;Function, and as clean as possible.&lt;/p&gt;

&lt;p&gt;It may sound obvious, but the beauty of a function is the interface: parameter and return type.
No implicit dependencies as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;this-&amp;gt;&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;self.&lt;/code&gt;. Shit in - shit out.&lt;/p&gt;

&lt;p&gt;On the contrary, several times I heard that people don’t like to split into functions, but having everything in one huge spaghetti, motivating that it’s “easier to grasp”. Well, god judge them (and to me, since I have a strong desire to punch in this case). The good thing about functions is that you can start integrating and testing them in places where it feels impossible.&lt;/p&gt;

&lt;p&gt;Suppose you have a class &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MoneyCutter&lt;/code&gt; with a method &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;execute&lt;/code&gt; and this method is around 400 loc. The class has a bunch of dependencies, such as ROS’ action clients, services and subscribers. Nobody ever dared to cover it with tests. You have to extend this method to support choosing the nearest &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;money_disposal_box&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You have an option to just puke another 40 loc inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;execute&lt;/code&gt; without any tests and be done. Let the person who will run the code deal with bugs.&lt;/p&gt;

&lt;p&gt;The other option is to add a function: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;select_nearest_money_disposal_box(money_position, disposal_boxes) -&amp;gt; disposal_box_index&lt;/code&gt; and write a test for it.
The choice is yours, but you know what will make the world a better place.&lt;/p&gt;

&lt;p&gt;An illustrative example:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MoneyCutter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;action_client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;money_detector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;disposal_boxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action_client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;action_client&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;money_detector&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;money_detector&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;disposal_boxes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;disposal_boxes&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# 400 LOC
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;disposal_box_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;select_nearest_disposal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;money_position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;disposal_boxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;select_nearest_disposal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;money_position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;boxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;money_position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_select_nearest_disposal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;money&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boxes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;select_nearest_disposal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;money&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;tldr;&lt;/p&gt;

&lt;p&gt;Default to a monorepo: pull services, apps, and shared assets into one place, keep linters/formatters at the root, build Docker right here, and tag the repo—not every component. Run the full test suite on each change. Push behavior into small, testable functions; keep classes thin and at the edges. Migrate steadily with git subtree add (no –squash) so history comes with you. Do these few things and your robotics team will spend less time wrangling toolchains and more time shipping real robots.&lt;/p&gt;

&lt;p&gt;Do this and you’ll be alright.&lt;/p&gt;
</description>
        <pubDate>Sun, 07 Sep 2025 00:00:00 +0000</pubDate>
        <link>https://szobov.github.io/2025/09/07/few-tricks-to-use-in-robotics-startups-code-base/</link>
        <guid isPermaLink="true">https://szobov.github.io/2025/09/07/few-tricks-to-use-in-robotics-startups-code-base/</guid>
        
        <category>code organization</category>
        
        <category>management</category>
        
        <category>ci</category>
        
        <category>monorepo</category>
        
        <category>knowledge</category>
        
        <category>functions</category>
        
        
        <category>software</category>
        


        
        
        
           
        
           
              
              
              
              
                 
                     
                     <image>
                         <url>https://szobov.github.io/assets/images/monorepo-functions/reddit_comment.png</url>
                         <title>Advance your Robotics Startup: Monorepo and Functions</title>
                         <link>https://szobov.github.io</link>
                     </image>
                 
              
                 
              
                 
              
              
            
        
        


      </item>
    
      <item>
        <title>When Recycling is a Challenge: Practical Tips to Make World Cleaner</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#disclaimer&quot; id=&quot;markdown-toc-disclaimer&quot;&gt;Disclaimer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#background&quot; id=&quot;markdown-toc-background&quot;&gt;Background&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#trash-grabber&quot; id=&quot;markdown-toc-trash-grabber&quot;&gt;Trash grabber&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#kids&quot; id=&quot;markdown-toc-kids&quot;&gt;Kids&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#recycling-collectors-places&quot; id=&quot;markdown-toc-recycling-collectors-places&quot;&gt;Recycling collectors places&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#switch-to-glass&quot; id=&quot;markdown-toc-switch-to-glass&quot;&gt;Switch to glass&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;disclaimer&quot;&gt;Disclaimer&lt;/h2&gt;

&lt;p&gt;It is a non-technical blog post.
I wrote it in English because it’s very easy to translate something from English nowadays.&lt;/p&gt;

&lt;h2 id=&quot;background&quot;&gt;Background&lt;/h2&gt;

&lt;p&gt;We never choose the country where we were born.&lt;/p&gt;

&lt;p&gt;If you were lucky enough to be born in a country where it’s easy to re/upcycle, then it’s amazing!&lt;/p&gt;

&lt;p&gt;I was not that lucky.&lt;/p&gt;

&lt;p&gt;Unfortunately, recycling is not the easiest task in my country, and administrative people spend little (relatively) time cleaning the environment and land fields.
It forced me to find ways on what I can do with it.&lt;/p&gt;

&lt;p&gt;Since I was visiting my family and friends during my summer vacation, I compiled a list of things that I’ll describe in this article.&lt;/p&gt;

&lt;h2 id=&quot;trash-grabber&quot;&gt;Trash grabber&lt;/h2&gt;

&lt;p&gt;One of my best investments was buying Trash Grabber, aka &lt;a href=&quot;https://en.wikipedia.org/wiki/Reach_extender&quot;&gt;Reach Extender&lt;/a&gt;.&lt;/p&gt;

&lt;figure class=&quot;image&quot;&gt;
  &lt;img src=&quot;/assets/images/greener-place/my_trash_grabber.jpg&quot; alt=&quot;my trash grabber&quot; /&gt;
  &lt;figcaption&gt;&lt;em&gt;my trash grabber&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I tried to collect litter with my hands before, but somehow, it raised the bar, so I quickly lost motivation.
On the other hand, Trash Grabber made it fun and easy again!
I use it in the following way: when I go outside in nature, I take it with me along with a trash bag. Then, I start to collect the litter around me.
When all the trash bags are full, I try to find the nearest waste container or just put one under the tree so I can pick it up on my way home.&lt;/p&gt;

&lt;p&gt;Easy and nice, right?&lt;/p&gt;

&lt;p&gt;Well, it is. But do not do it very often.
If you keep it seldom, it doesn’t become a heavy burden.
Remember, getting burnt out is easy, so try to do it slowly but steadily.&lt;/p&gt;

&lt;p&gt;One of the interesting side effects is that when people see you doing this with Trash Grabber, they start to get interested. A few people asked where to buy this thing.
It may not be very sustainable, but you can prepare a paper printer QR code with a list of your local stores or online marketplaces where people can buy it.&lt;/p&gt;

&lt;p&gt;Of course, just collecting and throwing waste in the bin has nothing to do with recycling. It’s not a sustainable solution. But it’s already a big thing. It’s much &lt;a href=&quot;https://web.archive.org/web/20240715014806/https://en.wikipedia.org/wiki/Broken_windows_theory&quot;&gt;more psychologically easier&lt;/a&gt; to throw waste in nature if it’s already littered there.&lt;/p&gt;

&lt;p&gt;I had a funny situation when I was collecting litter around two resting alkies, and they proudly proclaimed that they would collect all the waste they produced. It made me smile.&lt;/p&gt;

&lt;h3 id=&quot;kids&quot;&gt;Kids&lt;/h3&gt;

&lt;p&gt;Kids like Trash Grabbers.&lt;/p&gt;

&lt;p&gt;My nephew joins me every time I go out with Grabber, and he is around. This tool feels like something robotic.
The pace of collecting slows a little, but I’m sure my nephew will overtake me with time. :)&lt;/p&gt;

&lt;figure class=&quot;image&quot;&gt;
  &lt;img src=&quot;/assets/images/greener-place/collecting_waste_together_1.jpg&quot; alt=&quot;My nephew and I collecting litter&quot; /&gt;
  &lt;figcaption&gt;&lt;em&gt;My nephew and I collecting litter&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure class=&quot;image&quot;&gt;
  &lt;img src=&quot;/assets/images/greener-place/collecting_waste_together_2.jpg&quot; alt=&quot;My nephew and I collecting litter 2&quot; /&gt;
  &lt;figcaption&gt;&lt;em&gt;My nephew and I collecting litter 2&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Also, if you’re this type of person, you can make a &lt;a href=&quot;https://web.archive.org/web/20240706064108/https://www.instructables.com/Green-Garbage-Grabber,-Trash-Tongs,-pick-up-tool/&quot;&gt;Trash Grabber yourself&lt;/a&gt; with kids.&lt;/p&gt;

&lt;h2 id=&quot;recycling-collectors-places&quot;&gt;Recycling collectors places&lt;/h2&gt;

&lt;p&gt;Even if it’s not widely spread, there are already places where people can bring their waste for recycling. These could be containers for only glass, a specific type of plastic, or batteries.&lt;/p&gt;

&lt;p&gt;Unfortunately, many such places are obscured and not easily found.&lt;/p&gt;

&lt;p&gt;Let me give you a brief example from my experience.&lt;/p&gt;

&lt;p&gt;I wanted to recycle glass bottles. I opened my home country’s most popular map application, &lt;a href=&quot;https://yandex.com/maps/&quot;&gt;Yandex.Maps&lt;/a&gt;, and tried to search for nearby glass recycling containers. The nearest one was about an hour’s walk from where I stayed. It’s not that far, honestly, but it requires a commitment from my side.&lt;/p&gt;

&lt;p&gt;I decided to look in the search engine with the query something like “&lt;Name of=&quot;&quot; the=&quot;&quot; city=&quot;&quot;&gt; glass recycling containers&quot; (translated into my mother tongue). To my surprise, there were many results, and I quickly found a container that lasted about 5 minutes of walking. I was suspicious because who knows if the information was correct, but it&apos;s not a big deal to check it and, _voilà!_, it was right there. Moreover, there were four different containers to collect paper and various plastics.&lt;/Name&gt;&lt;/p&gt;

&lt;p&gt;I checked if I could quickly add my findings to the “Yandex.Map.” After clicking a few buttons, I submitted a new item to the map’s moderation. It was added in seven days!&lt;/p&gt;

&lt;figure class=&quot;image&quot;&gt;
  &lt;img src=&quot;/assets/images/greener-place/maps_accept_my_correction.png&quot; alt=&quot;Yandex.maps accepted my correction&quot; /&gt;
  &lt;figcaption&gt;&lt;em&gt;Yandex.maps accepted my correction&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure class=&quot;image&quot;&gt;
  &lt;img src=&quot;/assets/images/greener-place/item_added.jpg&quot; alt=&quot;Containers&quot; /&gt;
  &lt;figcaption&gt;&lt;em&gt;Containers&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Why did I tell you this story? Well, in my opinion, it will be easier to recycle if more people do it.
On my next trip back home, I plan to collect more items and check if they all are easily foundable.&lt;/p&gt;

&lt;h2 id=&quot;switch-to-glass&quot;&gt;Switch to glass&lt;/h2&gt;

&lt;p&gt;This paragraph is very obvious, but I had never considered it before living in Germany.
I was visiting my home country when it was hot there. I constantly need to stay hydrated.
When I first walked into the grocery store, I had two options:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;A more comfortable one – a plastic bottle of water.&lt;/li&gt;
  &lt;li&gt;Less comfortable one – a glass bottle of water.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The choice I made was, as you may already know, a glass bottle.&lt;/p&gt;

&lt;p&gt;Glass is a material that people have been able to recycle, at least from the last century.
Plastic, on the other hand, requires more technically developed infrastructure.
Even if glass is not recycled and is left in the fields, it causes less damage to nature.&lt;/p&gt;

&lt;p&gt;As soon as you start do it yourself people around you eventually will give it a try.
Something very simple, but also powerful, when there are no better choices.&lt;/p&gt;
</description>
        <pubDate>Sun, 04 Aug 2024 00:00:00 +0000</pubDate>
        <link>https://szobov.github.io/2024/08/04/what-to-do-if-recycling-is-not-developed/</link>
        <guid isPermaLink="true">https://szobov.github.io/2024/08/04/what-to-do-if-recycling-is-not-developed/</guid>
        
        <category>recycling</category>
        
        <category>environment</category>
        
        <category>non-technical</category>
        
        <category>people</category>
        
        
        <category>human</category>
        


        
        
        
           
        
           
              
              
              
              
                 
                     
                     <image>
                         <url>https://szobov.github.io/assets/images/greener-place/my_trash_grabber.jpg</url>
                         <title>When Recycling is a Challenge: Practical Tips to Make World Cleaner</title>
                         <link>https://szobov.github.io</link>
                     </image>
                 
              
                 
              
                 
              
                 
              
              
            
        
        
           
              
        
        
           
              
        
        
           
              
        
        
           
              
        
        


      </item>
    
      <item>
        <title>Debugging Machine Learning Model with Netron</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#background&quot; id=&quot;markdown-toc-background&quot;&gt;Background&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#an-issue&quot; id=&quot;markdown-toc-an-issue&quot;&gt;An issue&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#bad-guess&quot; id=&quot;markdown-toc-bad-guess&quot;&gt;Bad guess&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#working-sample&quot; id=&quot;markdown-toc-working-sample&quot;&gt;Working sample&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#look-at-a-structure&quot; id=&quot;markdown-toc-look-at-a-structure&quot;&gt;Look at a structure&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#source-of-the-bug&quot; id=&quot;markdown-toc-source-of-the-bug&quot;&gt;Source of the bug&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#conclusion&quot; id=&quot;markdown-toc-conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;background&quot;&gt;Background&lt;/h1&gt;

&lt;p&gt;This article a part of a story I wrote in &lt;a href=&quot;/2024/02/03/unexpected-performance-of-openvino/&quot;&gt;“Goodbye CUDA, Hello OpenVINO: Unexpected Benefits of Upgrading Python”&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;During this upgrade, I faced an issue that initially felt like a show-stopper but became an easy-to-fix bug.&lt;/p&gt;

&lt;p&gt;Let me tell you the details.&lt;/p&gt;

&lt;h1 id=&quot;an-issue&quot;&gt;An issue&lt;/h1&gt;

&lt;p&gt;I had a machine learning model exported as &lt;a href=&quot;https://onnxruntime.ai&quot;&gt;ONNX model&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;If I run an inference of this model using &lt;a href=&quot;https://onnxruntime.ai/docs/execution-providers/CUDA-ExecutionProvider.html&quot;&gt;CUDA Execution Provider&lt;/a&gt;, it runs.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;If  I run an inference using &lt;a href=&quot;https://onnxruntime.ai/docs/execution-providers/OpenVINO-ExecutionProvider.html&quot;&gt;OpenVINO Execution Provider&lt;/a&gt; it fails with the following error:&lt;/p&gt;
    &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;onnxruntime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;capi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;onnxruntime_pybind11_state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ONNXRuntimeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RUNTIME_EXCEPTION&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;during&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;initialization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;home&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;onnxruntimedev&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;onnxruntime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;onnxruntime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;providers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;openvino&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ov_interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;53&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;onnxruntime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;openvino_ep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OVExeNetwork&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;onnxruntime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;openvino_ep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OVCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LoadNetwork&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ov&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AnyMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpenVINO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;  &lt;span class=&quot;nb&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Loading&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Network&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;graph&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OpenVINOExecutionProvider_OpenVINO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subgraph_2_0Exception&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inference&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;core&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cpp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;149&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frontends&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;onnx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;frontend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;graph_cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cpp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;found&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;graph&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;bad-guess&quot;&gt;Bad guess&lt;/h1&gt;

&lt;p&gt;First, I tried to understand if I correctly configured an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ExecutionProvider&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since the error contains the word “&lt;strong&gt;cache&lt;/strong&gt;,” I searched for a solution to clear this cache.
I found that &lt;a href=&quot;https://onnxruntime.ai/docs/execution-providers/OpenVINO-ExecutionProvider.html#summary-of-options&quot;&gt;OpenVINOExecutionProvider has an option “cache_dir”&lt;/a&gt;, which sounded like a good candidate for fixing. 
I used this option and re-started an inference, and surprisingly, it worked, so I described it in the &lt;a href=&quot;https://github.com/microsoft/onnxruntime/issues/18042&quot;&gt;GitHub Issues&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Unfortunately, it was wrong. I misstructured the way parameters passed, and it turned out the inference was silently switching to the default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CPUExecutionProvider&lt;/code&gt;. It was a big pity, so I needed to look for other solutions.&lt;/p&gt;

&lt;h1 id=&quot;working-sample&quot;&gt;Working sample&lt;/h1&gt;

&lt;p&gt;Before continuing my search, I decided to check if this error was happening on all the files or on a particular one.
Luckily, the following sample of the trained model I took worked without errors on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OpenVINOExecutionProvider&lt;/code&gt;.
That’s already something!&lt;/p&gt;

&lt;h1 id=&quot;look-at-a-structure&quot;&gt;Look at a structure&lt;/h1&gt;

&lt;p&gt;The error tells us something is missing in the graph, so let’s look at the graph.&lt;/p&gt;

&lt;p&gt;To visualize the underlying graph structure of our model, I used &lt;a href=&quot;https://github.com/lutzroeder/netron&quot;&gt;Netron&lt;/a&gt;: an open-source visualizer for neural networks.&lt;/p&gt;

&lt;p&gt;Lucky me, &lt;strong&gt;Netron&lt;/strong&gt; supports &lt;strong&gt;ONNX&lt;/strong&gt; models as an input, so I started with visualizing the graph for the model that was rising an expection:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/netron-debug-openvino/missing_graph.png&quot; alt=&quot;Missing nodes&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Then, I compered it to the working sample:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/netron-debug-openvino/correct_graph.png&quot; alt=&quot;Correct graph&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;voilà!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The graph clearly shows that there are missing nodes.
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OpenVINOExecutionProvider&lt;/code&gt; gave me the legitimate exception!&lt;/p&gt;

&lt;p&gt;Therefore, the fix should be done on the model side.&lt;/p&gt;

&lt;h1 id=&quot;source-of-the-bug&quot;&gt;Source of the bug&lt;/h1&gt;

&lt;p&gt;It turned out the incomplete model was trained on the older version of the code, which used &lt;a href=&quot;https://www.tensorflow.org/api_docs/python/tf/keras/callbacks/TerminateOnNaN&quot;&gt;keras’s TerminateOnNan&lt;/a&gt;, which was &lt;a href=&quot;https://github.com/tensorflow/tensorflow/blob/v2.2.0/tensorflow/python/keras/callbacks.py#L807-L817&quot;&gt;softly&lt;/a&gt; terminating a training in case of NaN values encountered in the losses. When training data was malformed, it sometimes crashed on the first epoch and produced an incomplete model.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;Solving the ONNX model issue with OpenVINO was tough but helpful.&lt;/p&gt;

&lt;p&gt;At first, I thought the problem was about a cache setting, but that was wrong. The real issue was missing nodes in the model’s structure, which I discovered by using &lt;strong&gt;Netron&lt;/strong&gt; to compare the broken model with a working one. This experience showed how important it is to use the right tools for debugging machine learning models.&lt;/p&gt;
</description>
        <pubDate>Tue, 30 Apr 2024 00:00:00 +0000</pubDate>
        <link>https://szobov.github.io/2024/04/30/debugging-onnx-model-with-netron/</link>
        <guid isPermaLink="true">https://szobov.github.io/2024/04/30/debugging-onnx-model-with-netron/</guid>
        
        <category>ONNX</category>
        
        <category>OpenVINO</category>
        
        <category>machine learning</category>
        
        <category>debug</category>
        
        <category>netron</category>
        
        
        <category>software</category>
        


        
        
        
           
              
              
              
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
              
            
        
        
           
              
        
        
           
              
        
        


      </item>
    
      <item>
        <title>RasPBX: connecting with my Grandma</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#background&quot; id=&quot;markdown-toc-background&quot;&gt;Background&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#disclaimer&quot; id=&quot;markdown-toc-disclaimer&quot;&gt;Disclaimer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-equation&quot; id=&quot;markdown-toc-the-equation&quot;&gt;The equation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-solution&quot; id=&quot;markdown-toc-the-solution&quot;&gt;The solution&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-test&quot; id=&quot;markdown-toc-the-test&quot;&gt;The test&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#other-benefits&quot; id=&quot;markdown-toc-other-benefits&quot;&gt;Other benefits&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;background&quot;&gt;Background&lt;/h1&gt;

&lt;p&gt;I have a grandma.&lt;/p&gt;

&lt;p&gt;She is already quite elderly and has some severe health issues.&lt;/p&gt;

&lt;p&gt;I have lived abroad for two years and, due to some conditions, can’t easily travel to my home country.
My grandma is an amazing woman, and I wanted to stay connected with her.
The issue here is that she is unable to use smartphones and can only use &lt;strong&gt;push-button cell phones&lt;/strong&gt;.
Another issue is that it’s expensive for both of us to call using a traditional GSM network due to roaming.&lt;/p&gt;

&lt;p&gt;This article will tell you how I overcame these issues.&lt;/p&gt;

&lt;h1 id=&quot;disclaimer&quot;&gt;Disclaimer&lt;/h1&gt;

&lt;p&gt;I’ve done some research on the available ready-to-use options.
Somehow, I missed Microsoft’s options with &lt;a href=&quot;https://www.skype.com/en/international-calls/Russia&quot;&gt;Skype&lt;/a&gt;.
I would have used this instead of tinkering with my solution if I had known it in advance.
But after I implement mine, I’ll not switch to Skype since mine provides a few benefits that I’ll describe above.&lt;/p&gt;

&lt;h1 id=&quot;the-equation&quot;&gt;The equation&lt;/h1&gt;

&lt;p&gt;(1) I have a SIM card with the local mobile number in my local county. The SIM card is needed to connect with another SIM card.&lt;/p&gt;

&lt;p&gt;(2) I can call using my SIM card via smartphone.&lt;/p&gt;

&lt;p&gt;(3) When I think of my smartphone, I see it as a small-sized computer with a built-in GSM modem.&lt;/p&gt;

&lt;p&gt;If I substitute a smartphone from (2) via a small-sized commuter and a modem from (3), I can use the result to solve (1).
With those thoughts, I started to look for solutions.&lt;/p&gt;

&lt;h1 id=&quot;the-solution&quot;&gt;The solution&lt;/h1&gt;

&lt;p&gt;Once upon a time, I worked with &lt;a href=&quot;https://www.asterisk.org/get-started/&quot;&gt;Asterisk&lt;/a&gt;, so I knew that there are protocols that can connect phones to the Internet.
By using the right keywords, I found &lt;a href=&quot;http://www.raspbx.org/&quot;&gt;RasPBX&lt;/a&gt;: the ready-to-use solution that one can roll out on various system-on-a-chip hardware, like &lt;a href=&quot;https://www.raspberrypi.com/&quot;&gt;RaspberryPI&lt;/a&gt; or &lt;a href=&quot;http://beaglebone-asterisk.raspbx.org/&quot;&gt;BeagleBone Black&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Simply speaking, it’s a &lt;a href=&quot;https://www.raspbian.org/&quot;&gt;Raspbian&lt;/a&gt; image with Asterisk with the admin interface provided through &lt;a href=&quot;https://www.freepbx.org/&quot;&gt;FreePBX&lt;/a&gt;. More importantly, it has the drivers to connect a &lt;a href=&quot;http://www.raspbx.org/documentation/gsm-voip-gateway-with-chan_dongle/&quot;&gt;GSM model USB-dongle&lt;/a&gt;.&lt;/p&gt;

&lt;figure class=&quot;image&quot;&gt;
  &lt;img src=&quot;/assets/images/raspbx-grandma/freepbx_interface.png&quot; alt=&quot;FreePBX interface&quot; /&gt;
  &lt;figcaption&gt;&lt;em&gt;FreePBX interface&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The list of supported dongles can be found &lt;a href=&quot;https://github.com/bg111/asterisk-chan-dongle/wiki/Requirements-and-Limitations&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So, I ordered a second-hand Raspberry 2 and Huawei E169 dongle, which cost me approximately 50 euros.
For the installation and configuration, I followed &lt;a href=&quot;http://www.raspbx.org/documentation/#nextsteps&quot;&gt;these steps&lt;/a&gt; from the documentation.
The installation was bumpy because some steps from the documentation seemed missing. For example, I needed to create a user “asterisk” because the setup assumed it existed. Anyway, the installation took me only a short time.&lt;/p&gt;

&lt;figure class=&quot;image&quot;&gt;
  &lt;img src=&quot;/assets/images/raspbx-grandma/raspberry_dongle.jpg&quot; alt=&quot;RaspberryPI 2 and Huawei E169 connected&quot; /&gt;
  &lt;figcaption&gt;&lt;em&gt;RaspberryPI 2 and Huawei E169 connected&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Moreover, a detailed manual is hosted &lt;a href=&quot;https://github.com/MatejKovacic/RasPBX-install/blob/main/english.md&quot;&gt;here&lt;/a&gt;. It’s rich in images and can be an excellent place to find help.&lt;/p&gt;

&lt;p&gt;Remember to connect your RaspberryPI 2 (if you’re using it) to the router via Ethernet cable since it doesn’t have a built-in WiFi module.&lt;/p&gt;

&lt;p&gt;To simplify the network and security, I connected RaspberryPI to my VPN, so whenever I need to connect to Asterisk, I must first connect to my VPN.&lt;/p&gt;

&lt;p&gt;That’s it.&lt;/p&gt;

&lt;h1 id=&quot;the-test&quot;&gt;The test&lt;/h1&gt;

&lt;p&gt;I installed &lt;a href=&quot;https://www.zoiper.com/&quot;&gt;ZoiPer&lt;/a&gt; on my phone to test the solution and called my mom first.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;voilà!&lt;/em&gt; It worked!
There was some delay in transmitting a sound, but we could clearly communicate (with some mental adjustment to always start speaking slowly to let the other side consider the delay).&lt;/p&gt;

&lt;p&gt;I asked my mom to let my grandma know that I’d call so as not to scare her suddenly.
Then I called her, and we finally could talk: from Berlin to a small village on the border with Kazakhstan with my SIM card physically in Saint Petersburg.&lt;/p&gt;

&lt;figure class=&quot;image&quot;&gt;
  &lt;img src=&quot;/assets/images/raspbx-grandma/zoiper_call.jpg&quot; alt=&quot;Zoiper interface for call&quot; /&gt;
  &lt;figcaption&gt;&lt;em&gt;Zoiper interface for call&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure class=&quot;image&quot;&gt;
  &lt;img src=&quot;/assets/images/raspbx-grandma/zoiper_list.jpg&quot; alt=&quot;Looks like a classic phone calls software&quot; /&gt;
  &lt;figcaption&gt;&lt;em&gt;Looks like a classic phone calls software&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h1 id=&quot;other-benefits&quot;&gt;Other benefits&lt;/h1&gt;

&lt;p&gt;Across the globe, mobile network providers do something that I don’t like: whenever a person is not using a SIM card for a while, it is sold to another person.
Many of us (not except me) used their phone numbers as a second factor for authorization in such critical applications as mobile banks or e-government.&lt;/p&gt;

&lt;p&gt;Also, my other family members still send me SMS with good words on my birthday. It’s nice to give them this ability.&lt;/p&gt;

&lt;p&gt;I hope this article may help you too.&lt;/p&gt;
</description>
        <pubDate>Fri, 22 Mar 2024 00:00:00 +0000</pubDate>
        <link>https://szobov.github.io/2024/03/22/raspbx-connecting-with-my-grandma/</link>
        <guid isPermaLink="true">https://szobov.github.io/2024/03/22/raspbx-connecting-with-my-grandma/</guid>
        
        <category>VoIP</category>
        
        <category>SIP</category>
        
        <category>RaspberryPI</category>
        
        <category>Asterisk</category>
        
        <category>FreePBX</category>
        
        
        <category>software</category>
        
        <category>hardware</category>
        


        
        
        
           
        
           
              
              
              
              
                 
                     
                     <image>
                         <url>https://szobov.github.io/assets/images/raspbx-grandma/freepbx_interface.png</url>
                         <title>RasPBX: connecting with my Grandma</title>
                         <link>https://szobov.github.io</link>
                     </image>
                 
              
                 
              
                 
              
              
            
        
        
           
              
        
        
           
              
        
        
           
              
        
        


      </item>
    
      <item>
        <title>Goodbye CUDA, Hello OpenVINO: Unexpected Benefits of Upgrading Python</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#background&quot; id=&quot;markdown-toc-background&quot;&gt;Background&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#difficulties-of-upgrade&quot; id=&quot;markdown-toc-difficulties-of-upgrade&quot;&gt;Difficulties of upgrade&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#metrics&quot; id=&quot;markdown-toc-metrics&quot;&gt;Metrics&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#old-cuda-vs-new-cpu&quot; id=&quot;markdown-toc-old-cuda-vs-new-cpu&quot;&gt;Old CUDA vs new CPU&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#bigger-model&quot; id=&quot;markdown-toc-bigger-model&quot;&gt;Bigger model&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#openvino&quot; id=&quot;markdown-toc-openvino&quot;&gt;OpenVINO&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#conclusion&quot; id=&quot;markdown-toc-conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;background&quot;&gt;Background&lt;/h1&gt;

&lt;p&gt;At my current workplace I was tired of using an obsolete python &lt;strong&gt;3.6&lt;/strong&gt; and decided to focus on upgrading python to something more recent, &lt;strong&gt;3.10&lt;/strong&gt;.
My main reasoning was to improve dev-experience in using modern tooling and language feature. Also, bug fixes and safity patches.&lt;/p&gt;

&lt;p&gt;It turned out, one of the side-effects of this upgrade was enable non-&lt;a href=&quot;https://en.wikipedia.org/wiki/CUDA&quot;&gt;CUDA&lt;/a&gt; machine learning inference with a noticible perfomance boost.&lt;/p&gt;

&lt;p&gt;This article will tell you about this side-effect and may also help you to convince your collegues on why upgrades are important.&lt;/p&gt;

&lt;h1 id=&quot;difficulties-of-upgrade&quot;&gt;Difficulties of upgrade&lt;/h1&gt;

&lt;p&gt;There is a bunch of problems I faced when I started the transition.&lt;/p&gt;

&lt;p&gt;At this time we used quite an old Ubuntu &lt;strong&gt;16.04&lt;/strong&gt; version and lead to some libraries were not available in public repos.
The most critical ones were &lt;strong&gt;CUDA&lt;/strong&gt; related libraries.&lt;/p&gt;

&lt;p&gt;But why should I bother about &lt;strong&gt;CUDA&lt;/strong&gt; at all? Well, because we’re using &lt;a href=&quot;onnxruntime.ai&quot;&gt;onnxruntime&lt;/a&gt; for machine learning inference. The version of this library is tightly bound to two things: the version of python and the version of &lt;a href=&quot;https://developer.nvidia.com/cuda-toolkit&quot;&gt;CUDA toolkit&lt;/a&gt;. If I want to upgrade &lt;strong&gt;python&lt;/strong&gt; I need to upgrade &lt;strong&gt;onnxruntime&lt;/strong&gt; and inevitably &lt;strong&gt;CUDA&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At the time of the transition the only supported versions of &lt;strong&gt;ubuntu&lt;/strong&gt; for &lt;strong&gt;CUDA&lt;/strong&gt; were &lt;strong&gt;20.04&lt;/strong&gt; and &lt;strong&gt;22.04&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;That’s a bummer.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I was puzzled if I should continue the transition, since one of the most important parts of our system is not available.
The tough thoughts led me to a very dirty, but working solution.&lt;/p&gt;

&lt;p&gt;I opened the instruction on installing &lt;strong&gt;CUDA&lt;/strong&gt; toolit locally and noticed a pattern in how URL to downloading was built.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/onnx-cpu-performance/nvidia-url-download.png&quot; alt=&quot;Ubuntu20.04 nvidia repo download&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I built an URL for ubuntu &lt;strong&gt;16.04&lt;/strong&gt; in the same manner and &lt;em&gt;voilà!&lt;/em&gt;
It worked!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/onnx-cpu-performance/nvidia-16-04-debs.png&quot; alt=&quot;Ubuntu16.04 nvidia repo download&quot; /&gt;&lt;/p&gt;

&lt;p&gt;After some back-and-forth (upgrade &lt;a href=&quot;https://kernel.ubuntu.com/mainline/&quot;&gt;linux kernel&lt;/a&gt; and &lt;a href=&quot;https://gcc.gnu.org/&quot;&gt;gcc&lt;/a&gt;) I was able to upgrade &lt;strong&gt;CUDA&lt;/strong&gt; toolkit and install &lt;strong&gt;onnxruntime&lt;/strong&gt; compatible with new &lt;strong&gt;python&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Show must go on!&lt;/p&gt;

&lt;h1 id=&quot;metrics&quot;&gt;Metrics&lt;/h1&gt;

&lt;p&gt;Let me give a little bit of context regarding metrics.&lt;/p&gt;

&lt;p&gt;Whenever you’re doing anything regarding performance, you first need to find a reasonable way to measure &lt;em&gt;this&lt;/em&gt; performance.&lt;/p&gt;

&lt;p&gt;In our case, we had an exact place where the actual “work” was done. Measuring the time it takes us to run this code would be perfect because, in the end, it’s much easier for me to compare statistics from one place than trying to optimize several ones.&lt;/p&gt;

&lt;p&gt;I was fortunate to have an exceptional engineer, &lt;a href=&quot;https://www.linkedin.com/in/georges-dubus-305a4140/&quot;&gt;Georges Dubus&lt;/a&gt;, who already brought to our project &lt;a href=&quot;https://opentelemetry.io&quot;&gt;Open Telemtry&lt;/a&gt; stack.&lt;/p&gt;

&lt;p&gt;The only thing left to me is to introduce a few “flags” to distinguish metrics from different setups.&lt;/p&gt;

&lt;h1 id=&quot;old-cuda-vs-new-cpu&quot;&gt;Old CUDA vs new CPU&lt;/h1&gt;

&lt;p&gt;To this moment I had two setups: one running old python and one running new.
More over, I made it in the way, so I can quickly switch between different &lt;a href=&quot;https://onnxruntime.ai/docs/execution-providers/&quot;&gt;Execution Providers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After the first banch of benchmarks I was not going to belive it.&lt;/p&gt;

&lt;figure class=&quot;image&quot;&gt;
  &lt;img src=&quot;/assets/images/onnx-cpu-performance/new_cpu_vs_old_cuda.png&quot; alt=&quot;old vs new / GPU vs CPU (smaller is better)&quot; /&gt;
  &lt;figcaption&gt;&lt;em&gt;old vs new / GPU vs CPU (smaller is better)&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The &lt;em&gt;performance&lt;/em&gt; of a default &lt;strong&gt;CPU&lt;/strong&gt; provider with newer version was roughly &lt;em&gt;equal&lt;/em&gt; to the performance of &lt;strong&gt;CUDA&lt;/strong&gt;/&lt;strong&gt;GPU&lt;/strong&gt; provider with the older one.&lt;/p&gt;

&lt;h3 id=&quot;bigger-model&quot;&gt;Bigger model&lt;/h3&gt;

&lt;p&gt;I ran these benchmarks on a different setup, but the numbers were quite similar.&lt;/p&gt;

&lt;p&gt;Sounds good, right? Well, yes, until I tested an inference of a bigger (deeper) machine learning model.
Unfortunately, the performance of &lt;strong&gt;CPU&lt;/strong&gt; Provider was not acceptable. So I begin the research on other options.&lt;/p&gt;

&lt;p&gt;You may also ask me, if the performance of &lt;strong&gt;CUDA&lt;/strong&gt;/&lt;strong&gt;GPU&lt;/strong&gt; on a newer version was good enough, why didn’t I just chose this option.
Unfortunately, as I mentioned above upgrade &lt;strong&gt;CUDA&lt;/strong&gt; was quite a laborious work, required different steps executed in a very precise order. Therefore I didn’t try to fully avoid this option, but left it as a last resort.&lt;/p&gt;

&lt;h1 id=&quot;openvino&quot;&gt;OpenVINO&lt;/h1&gt;

&lt;p&gt;The hardware we using utilizes &lt;strong&gt;Intel CPU&lt;/strong&gt;.
Since after the python’s upgrade this &lt;strong&gt;CPU&lt;/strong&gt; gives us a fair performance, can I squeze more from it?&lt;/p&gt;

&lt;p&gt;The answer is yes.
Intel made an open-source toolkit, called &lt;a href=&quot;https://www.intel.com/content/www/us/en/developer/tools/openvino-toolkit/overview.html&quot;&gt;OpenVINO&lt;/a&gt;.
The idea is very similar to &lt;strong&gt;CUDA&lt;/strong&gt;: optimizing training and inference of machine learning models on vendor-specific hardware.&lt;/p&gt;

&lt;p&gt;Lucky me, Intel already implemented an &lt;a href=&quot;https://onnxruntime.ai/docs/execution-providers/OpenVINO-ExecutionProvider.html&quot;&gt;ONNX’s ExecutionProvider&lt;/a&gt; and even made pre-compiled &lt;a href=&quot;https://pypi.org/project/onnxruntime-openvino/&quot;&gt;wheels&lt;/a&gt;.
The changes I need to implement to benchmark is adding a new provider &lt;a href=&quot;https://onnxruntime.ai/docs/execution-providers/OpenVINO-ExecutionProvider.html#python-api&quot;&gt;“OpenVINOExecutionProvider”&lt;/a&gt; and executing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install onnxruntime-openvino&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The only thing left to me was to run benchmarks and compare results.&lt;/p&gt;

&lt;p&gt;Unfortunately, I don’t have nice plots to show here, but here is as table consiting the statistics for the same metric I described above.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;provider&lt;/th&gt;
      &lt;th&gt;99th percentile&lt;/th&gt;
      &lt;th&gt;Average&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;CPUExecutionProvider&lt;/td&gt;
      &lt;td&gt;65ms&lt;/td&gt;
      &lt;td&gt;49ms&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CUDAExecutionProvider&lt;/td&gt;
      &lt;td&gt;30ms&lt;/td&gt;
      &lt;td&gt;21ms&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;OpenVINOExecutionProvider&lt;/td&gt;
      &lt;td&gt;33ms&lt;/td&gt;
      &lt;td&gt;24ms&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;As you can see, the performance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OpenVINOExecutionProvider&lt;/code&gt; is roughly equal to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CUDAExecutionProvider&lt;/code&gt;.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;As you may already understood, I stick to the &lt;strong&gt;OpenVINO&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It was a bit of a journey and research, but in the end I brought an upgrade of the whole system and simplification of the requirements. &lt;strong&gt;OpenVINO&lt;/strong&gt; runs on &lt;strong&gt;CPU&lt;/strong&gt;, that means our product will run fully on &lt;strong&gt;CPU&lt;/strong&gt; reducing the cost of the product! Something I didn’t expect when I started the upgrade.&lt;/p&gt;
</description>
        <pubDate>Sat, 03 Feb 2024 00:00:00 +0000</pubDate>
        <link>https://szobov.github.io/2024/02/03/unexpected-performance-of-openvino/</link>
        <guid isPermaLink="true">https://szobov.github.io/2024/02/03/unexpected-performance-of-openvino/</guid>
        
        <category>ONNX</category>
        
        <category>OpenVINO</category>
        
        <category>machine learning</category>
        
        <category>benchmarks</category>
        
        <category>CUDA</category>
        
        <category>Ubuntu</category>
        
        <category>Python</category>
        
        
        <category>software</category>
        


        
        
        
           
        
           
              
              
              
              
                 
                     
                     <image>
                         <url>https://szobov.github.io/assets/images/onnx-cpu-performance/nvidia-url-download.png</url>
                         <title>Goodbye CUDA, Hello OpenVINO: Unexpected Benefits of Upgrading Python</title>
                         <link>https://szobov.github.io</link>
                     </image>
                 
              
                 
              
                 
              
                 
              
                 
              
              
            
        
        
           
              
        
        
           
              
        
        


      </item>
    
      <item>
        <title>Share feelings to improve communications</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#disclaimer&quot; id=&quot;markdown-toc-disclaimer&quot;&gt;Disclaimer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#being-a-human&quot; id=&quot;markdown-toc-being-a-human&quot;&gt;Being a human&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#example&quot; id=&quot;markdown-toc-example&quot;&gt;Example&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#my-opinion&quot; id=&quot;markdown-toc-my-opinion&quot;&gt;My opinion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;disclaimer&quot;&gt;Disclaimer&lt;/h2&gt;

&lt;p&gt;A few days ago, I returned from a concise journey to my friend in Belgrade.&lt;/p&gt;

&lt;p&gt;We spent approximately 90% of our time together speaking about different matters.
So, this article will be the outcome of our discussion.&lt;/p&gt;

&lt;p&gt;Non-technical, subjective and short.&lt;/p&gt;

&lt;h2 id=&quot;being-a-human&quot;&gt;Being a human&lt;/h2&gt;

&lt;p&gt;Working for almost a decade in technical companies, I noticed an essential and rather sad thing: people try to be as optimal as machines, showing no feelings.
We try our best to speak concisely, delivering our intention fast and straight. As I see it, the problem is that we’re not machines, we’re human, and we should instead be proud of it and use it as an advantage.&lt;/p&gt;

&lt;p&gt;Let me elaborate on it and provide an example.&lt;/p&gt;

&lt;h2 id=&quot;example&quot;&gt;Example&lt;/h2&gt;

&lt;p&gt;Last week, I woke up in a terrible mood. I had a sleepless night, and I felt broken.&lt;/p&gt;

&lt;p&gt;When I opened our team’s chat, I noticed a few messages in direct messages. People mostly were asking about my opinion.&lt;/p&gt;

&lt;p&gt;I started typing the answers, then realized: “&lt;em&gt;Hold on, you’re definitely providing very negative feedback because you feel broke&lt;/em&gt;”.&lt;/p&gt;

&lt;p&gt;I tried rewriting my message to be more objective, but it didn’t help.&lt;/p&gt;

&lt;p&gt;Then, I decided to leave the message as is but add a disclaimer in the beginning:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;10:03 AM&lt;/p&gt;

  &lt;p&gt;I just woke up in a bad mood&lt;/p&gt;

  &lt;p&gt;please divide any of my claims by a factor of 2 at least&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and I then continued to share my opinion.&lt;/p&gt;

&lt;p&gt;I don’t know precisely how my colleague reacted to it. What I know is that I at least provided this person with the knowledge of my being biased, not because of the topic this person brought up but because of totally unrelated stuff.&lt;/p&gt;

&lt;h2 id=&quot;my-opinion&quot;&gt;My opinion&lt;/h2&gt;

&lt;p&gt;Of course, it’s always better to be unbiased.&lt;/p&gt;

&lt;p&gt;But I’m a human. I have bad and good days, which affects my decisions and communications.
Most of the time, I’m pretty out of control of my mood. (&lt;em&gt;Even tho I’m taking mood stabilizers, haha&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;What I know I can do is:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;identify my mood before I say shitty things&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;let people know my feelings before we start our communications&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;if I failed on both previous points, tell my feelings afterwards&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;be patient and empathetic to other people. They may also feel shitty&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These are the steps that I’m trying to follow. I hope they will improve my communication.&lt;/p&gt;

&lt;p&gt;I wish they could also help you.&lt;/p&gt;
</description>
        <pubDate>Fri, 15 Dec 2023 00:00:00 +0000</pubDate>
        <link>https://szobov.github.io/2023/12/15/share-feelings-to-impove-communication/</link>
        <guid isPermaLink="true">https://szobov.github.io/2023/12/15/share-feelings-to-impove-communication/</guid>
        
        <category>communications</category>
        
        <category>feelings</category>
        
        <category>non-technical</category>
        
        
        <category>human</category>
        


        
        
        
           
        


      </item>
    
      <item>
        <title>Improving CI/CD experience with ChatGPT</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#background&quot; id=&quot;markdown-toc-background&quot;&gt;Background&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#reasons-to-use-ai&quot; id=&quot;markdown-toc-reasons-to-use-ai&quot;&gt;Reasons to use AI&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#source-of-input&quot; id=&quot;markdown-toc-source-of-input&quot;&gt;Source of input&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#results&quot; id=&quot;markdown-toc-results&quot;&gt;Results&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;background&quot;&gt;Background&lt;/h2&gt;

&lt;p&gt;At my current company, before I joined, we had a build system based on &lt;a href=&quot;https://en.wikipedia.org/wiki/Jenkins_(software)&quot;&gt;Jenkins&lt;/a&gt;.
I love Jenkins. It’s powerful, extensible, open-source and self-hosted.&lt;/p&gt;

&lt;p&gt;But great power comes with significant responsibilities. Jenkins allows you to make &lt;em&gt;bad&lt;/em&gt; decisions easily.&lt;/p&gt;

&lt;p&gt;In our case, Jenkins’ Jobs were fully defined in UI. Why is it wrong? Well:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It’s impossible to track it in &lt;a href=&quot;https://en.wikipedia.org/wiki/Version_control&quot;&gt;Version Control System&lt;/a&gt; such as git.&lt;/li&gt;
  &lt;li&gt;It’s easy to screw up. If you accidentally change a step in UI, you can only spot it through your naked eyes.&lt;/li&gt;
  &lt;li&gt;It’s hard to get a whole picture. In our case, it required to scroll a screen 4-6 times to see a whole file.&lt;/li&gt;
  &lt;li&gt;It’s hard to reuse code. (Still possible tho)&lt;/li&gt;
  &lt;li&gt;Restarting the pipeline from the beginning or specific stage is &lt;a href=&quot;https://issues.jenkins.io/browse/JENKINS-45455&quot;&gt;impossible&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I decided to address all the mentioned problems.
This article will tell you how I moved to pipelines defined by &lt;a href=&quot;https://www.jenkins.io/doc/book/pipeline/jenkinsfile/&quot;&gt;Jenkinsfile&lt;/a&gt; with the help of ChatGPT.&lt;/p&gt;

&lt;h2 id=&quot;reasons-to-use-ai&quot;&gt;Reasons to use AI&lt;/h2&gt;

&lt;p&gt;There are no particular reasons to do it.&lt;/p&gt;

&lt;p&gt;It’s an easy but tedious task to structure Jenkinsfile.&lt;/p&gt;

&lt;p&gt;Due to my laziness, I was seeking the most effortless approach. Since I’m paying for ChatGPT, I said, “&lt;em&gt;OK, AI is not so bad at summarizing texts&lt;/em&gt;”. In this case, the problem is summarizing: UI is a wordy source, and Jenkisfile is a short summary.&lt;/p&gt;

&lt;h2 id=&quot;source-of-input&quot;&gt;Source of input&lt;/h2&gt;

&lt;p&gt;When I was implementing the migration, it was impossible to send pics to ChatGPT. Even if possible, I wonder if I can treat it as good input.
Jenkins jobs we defined in UI are stored somewhere, so I started searching for the source of the job’s definitions.&lt;/p&gt;

&lt;p&gt;I was lucky to quickly find the exact question on &lt;a href=&quot;https://stackoverflow.com/questions/71504138/how-to-view-jenkins-job-configuration-as-xml-in-the-browser&quot;&gt;StackOverflow&lt;/a&gt;.
The proposed request to the HTTP handler didn’t work for some reason, so I just ran &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find &amp;lt;jenkins_dir&amp;gt; -name config.xml -type f &lt;/code&gt; and immediately found the configuration for the pipeline defined in XML.&lt;/p&gt;

&lt;p&gt;We’ve got a source! It’s not perfect since it’s a colossal XML, but who cares? Let’s feed it now into GPT!&lt;/p&gt;

&lt;h2 id=&quot;results&quot;&gt;Results&lt;/h2&gt;

&lt;p&gt;After some back and forth, ChatGPT was able to generate something that I wanted.
It didn’t go deep, but I didn’t ask it for. I wanted some skeleton or boilerplate code that I could then extend.&lt;/p&gt;

&lt;p&gt;Of course, I had to go through every command and thoroughly read the documentation to understand what was happening in this script.
But the cool thing is that I know what to search for.&lt;/p&gt;

&lt;p&gt;In my case, it was a perfect example of using ChatGPT. I already had a working job that I wanted to get. The thing that I should have in the end should be a drop-in replacement.&lt;/p&gt;

&lt;p&gt;Few more bonuses:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;It allows me to ask about specific parts or share the names of the plugins it used.&lt;/li&gt;
  &lt;li&gt;I can feed other XML files.&lt;/li&gt;
  &lt;li&gt;It takes less mental power for me to edit something rather than to create from scratch.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the reduced, high-level script I’ve got from ChatGPT so you can see that there is nothing specific, just boilerplate lines:&lt;/p&gt;

&lt;div class=&quot;language-groovy highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;agent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;built-in&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;parameters&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;NAME&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;defaultValue:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
               &lt;span class=&quot;nl&quot;&gt;description:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Feature name&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;booleanParam&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;UPLOAD_BUILD&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;defaultValue:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                     &lt;span class=&quot;nl&quot;&gt;description:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Upload build to cloud&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;BRANCH&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;defaultValue:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;feature_build&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
               &lt;span class=&quot;nl&quot;&gt;description:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Git branch to build&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;environment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;GIT_URL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;git@github.com:...&apos;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;stages&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Clone Repository&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;checkout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;$class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;GitSCM&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;nl&quot;&gt;branches:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${params.BRANCH}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]],&lt;/span&gt;
                          &lt;span class=&quot;nl&quot;&gt;doGenerateSubmoduleConfigurations:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;nl&quot;&gt;extensions:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[],&lt;/span&gt;
                          &lt;span class=&quot;nl&quot;&gt;submoduleCfg:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[],&lt;/span&gt;
                          &lt;span class=&quot;nl&quot;&gt;userRemoteConfigs:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;url:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${env.GIT_URL}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]])&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Run Versions Script&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;script&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Trigger Builds&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;script&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;job:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;...&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;parameters:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;VERSION&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;nl&quot;&gt;value:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${env.VERSION}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;wait:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;job:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;...&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;parameters:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;VERSION&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;nl&quot;&gt;value:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${env.VERSION}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;wait:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;job:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;...&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;parameters:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;VERSION&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;nl&quot;&gt;value:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;${env.VERSION}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;wait:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Copy Artifacts&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;script&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;copyArtifacts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;projectName:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;...&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;filter:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;...&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                                  &lt;span class=&quot;nl&quot;&gt;target:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/builds/&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Run Post-Build Operations&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;script&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileExists&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;./post_build.sh&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;sh&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;./post_build.sh&apos;&lt;/span&gt;
                    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;UPLOAD_BUILD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
                    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Mon, 27 Nov 2023 00:00:00 +0000</pubDate>
        <link>https://szobov.github.io/2023/11/27/using-chat-gpt-to-improve-ci-experience/</link>
        <guid isPermaLink="true">https://szobov.github.io/2023/11/27/using-chat-gpt-to-improve-ci-experience/</guid>
        
        <category>chatgpt</category>
        
        <category>jenkins</category>
        
        <category>pipeline</category>
        
        <category>CI/CD</category>
        
        
        <category>software</category>
        


        
        
        
           
        


      </item>
    
      <item>
        <title>Reverse engineering binary protocol from dev console</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#prerequisites&quot; id=&quot;markdown-toc-prerequisites&quot;&gt;Prerequisites&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#background&quot; id=&quot;markdown-toc-background&quot;&gt;Background&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#promising-beginning&quot; id=&quot;markdown-toc-promising-beginning&quot;&gt;Promising beginning&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#broken-api&quot; id=&quot;markdown-toc-broken-api&quot;&gt;Broken API&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#reverse-engineering&quot; id=&quot;markdown-toc-reverse-engineering&quot;&gt;Reverse engineering&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#result&quot; id=&quot;markdown-toc-result&quot;&gt;Result&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;I wrote this article with the assumption that the reader has some prior knowledge about &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/javascript&quot;&gt;JavaScript&lt;/a&gt;, &lt;a href=&quot;binary data&quot;&gt;bytes&lt;/a&gt;, and &lt;a href=&quot;https://en.wikipedia.org/wiki/Presentation_layer&quot;&gt;Presentation layer&lt;/a&gt;.
If you don’t have it, you may still benefit from reading, but it may be too boring. With prior knowledge, it is still boring, but less. ;)&lt;/p&gt;

&lt;h2 id=&quot;background&quot;&gt;Background&lt;/h2&gt;

&lt;p&gt;As I describe in my previous &lt;a href=&quot;/2023/10/22/towards-better-natural-language-learning/&quot;&gt;blog post&lt;/a&gt; I wrote a &lt;a href=&quot;https://github.com/szobov/anker&quot;&gt;telegram bot&lt;/a&gt; to simplify cards creation in &lt;a href=&quot;https://www.ankiweb.net&quot;&gt;Anki&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this article, I’ll describe some technical issues I faced in integrating with Anki, particularly reverse engineering their communication protocol.&lt;/p&gt;

&lt;h2 id=&quot;promising-beginning&quot;&gt;Promising beginning&lt;/h2&gt;

&lt;p&gt;Anki doesn’t provide a public HTTP API. At least on a date of writing this article.&lt;/p&gt;

&lt;p&gt;When I started my integration by spending a few minutes in &lt;a href=&quot;https://developer.chrome.com/docs/devtools/&quot;&gt;dev-console&lt;/a&gt;, I quickly realized that Ankiweb is working by simply sending &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods&quot;&gt;GET/POST requests&lt;/a&gt; with &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn/Forms&quot;&gt;forms&lt;/a&gt;. Sounds easy, right? Speaking honestly, it was easy. The implementation took me an hour, and the client was fully functional.&lt;/p&gt;

&lt;p&gt;Everything worked smoothly, but then it &lt;em&gt;suddenly&lt;/em&gt; broke.&lt;/p&gt;

&lt;h2 id=&quot;broken-api&quot;&gt;Broken API&lt;/h2&gt;

&lt;p&gt;If there is no public API, there are no promises. I should have expected it.&lt;/p&gt;

&lt;p&gt;My bot stopped adding cards to Anki and could not even log in.
I spent some time figuring it out and understood that the API has changed, and Anki is no longer simply sending forms.&lt;/p&gt;

&lt;p&gt;I still strongly desired to add words to my decks, so my next Journey began.&lt;/p&gt;

&lt;h2 id=&quot;reverse-engineering&quot;&gt;Reverse engineering&lt;/h2&gt;

&lt;p&gt;Ankiweb was still using HTTP requests and cookies to preserve the authentification information.
It means I should be able to reproduce what Ankiweb’s front end is doing.&lt;/p&gt;

&lt;p&gt;My first issue was that I could not see the response to requests in the dev console. It simply showed me:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/reverse-engineering-anki/anki_console_failed_to_load_response.png&quot; alt=&quot;no response&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Ouch! It was a bit confusing because I was sure it should somehow exchange the data. Otherwise, how does it work?&lt;/p&gt;

&lt;p&gt;Then, I looked into the login request’s payload. I also set a password to start from a very particular string so it will be easy to spot it in logs.&lt;/p&gt;

&lt;figure class=&quot;image&quot;&gt;
  &lt;img src=&quot;/assets/images/reverse-engineering-anki/loging_password_symbols.png&quot; alt=&quot;payload of the login request&quot; /&gt;
  &lt;figcaption&gt;&lt;em&gt;payload of the login request&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Nothing very suspicious except for”” and “)” symbols.
It felt like some binary data was transferred around, but what could it be?&lt;/p&gt;

&lt;p&gt;I started to look at the source code of the web page.
Obviously, it was minified and obfuscated. Literally, just a bunch of unreadable JavaScript expressions joined in one infinitely long string.&lt;/p&gt;
&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;charCodeAt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;===&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]){&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`
`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\r&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;	&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;invalid base64 string.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;48&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;invalid base64 string.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;subarray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)},&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;enc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;63&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It is a pile of junk, but can we make use of it?&lt;/p&gt;

&lt;p&gt;Browsers are our friend in reverse engineering frontends. For example, Chromium provides &lt;a href=&quot;https://developer.chrome.com/docs/devtools/javascript/reference/#format&quot;&gt;a pretty-printing option&lt;/a&gt; for minified JavaScript.
After pretty-printing it, we get something like this:&lt;/p&gt;
&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;403&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;href&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/account/login&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Uint8Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;statusText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Oe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Still hard to read since it’s minified, but it gives us two advantages:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;It’s easy to find where requests are made since we can directly search for substrings.&lt;/li&gt;
  &lt;li&gt;The most essential point: it’s now easy to set debugger breakpoints and check all values in runtime.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I set a debugger right into the code where the request happened, and it led me to another request, which was not displayed in the dev console for some reason.
It turned out there are many requests that start with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/svc/&lt;/code&gt; prefix. The particularly interesting one is here:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/reverse-engineering-anki/anki_svc_account.png&quot; alt=&quot;no response&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So, I moved into this function.&lt;/p&gt;
&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toBinary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fromBinary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;What’s happening here is definitely something important for request forming:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;e&lt;/code&gt; contains our login and password information in such format: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{username: &apos;by***&apos;, password: &apos;pass***&apos;}&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Then, using some logic, the code makes a binary array from it and sends it to the server.&lt;/li&gt;
  &lt;li&gt;In case of success, it receives a bunch of bytes and decodes them back to the JavaScript object.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, my problem is figuring out the “&lt;em&gt;logic&lt;/em&gt;” behind encoding and decoding data between requests and responses from the server.
First, I looked into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;e.toBinary()&lt;/code&gt; function since encoding happened here.
By jumping back and forth, I got to the following object:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;0: 
    T: 9
    jsonName: &quot;username&quot;
    kind: &quot;scalar&quot;
    localName: &quot;username&quot;
    name: &quot;username&quot;
    no: 1
    packed: false
    repeated: false
    [[Prototype]]: Object
1: 
    {no: 2, name: &apos;password&apos;, kind: &apos;scalar&apos;, T: 9, localName: &apos;password&apos;, …}
    
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On the bright side, it already gives some insight; on the other side, how this object is formed is still unclear.
I started to dig deeper into the sibling of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;toBinary()&lt;/code&gt; function: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fromBinary()&lt;/code&gt;.
Let’s now look closely at this function:&lt;/p&gt;
&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;nx&quot;&gt;fromBinary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;runtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bin&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;makeReadOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;readMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;readerFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;byteLength&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Where the parameter “&lt;em&gt;e&lt;/em&gt;” contains the binary data received from the server. I also checked that the output of this function is a nicely formed object.&lt;/p&gt;

&lt;p&gt;I was lucky to get a jackpot from the first guess, and I printed the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t.runtime&lt;/code&gt; object:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt; t.runtime
&amp;lt; {syntax: &apos;proto3&apos;, json: {…}, bin: {…}, util: {…}, makeMessageType: ƒ, …}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You may wonder why I was so happy to see this object.&lt;/p&gt;

&lt;p&gt;Because of the “&lt;strong&gt;proto3&lt;/strong&gt;” strings, which immediately clicked in my mind: it’s a &lt;a href=&quot;https://protobuf.dev&quot;&gt;protobuf&lt;/a&gt;! 🔥&lt;/p&gt;

&lt;p&gt;And now the object I mentioned a few lines above makes perfect sense: it’s a message representation from the protobuf!
Armed with this knowledge, I started to craft the protobuf messages from the object I got from the dev console.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A few words about protobuf&lt;/em&gt;:
I already mentioned it &lt;a href=&quot;/2019/02/16/prototext-human-readable-protobuf/&quot;&gt;one&lt;/a&gt; of my old articles. It’s a binary serialization protocol, likely &lt;a href=&quot;https://en.wikipedia.org/wiki/OSI_model&quot;&gt;OSI level&lt;/a&gt; 6, and compiler, which is widely used and a cornerstone of &lt;a href=&quot;https://grpc.io&quot;&gt;gRPC&lt;/a&gt; technology. You describe the message structure, and then it automatically creates a code to handle it.&lt;/p&gt;

&lt;p&gt;So, I formed a first login message and transferred it from my code.
Ta-da! It worked. We cracked this nut.&lt;/p&gt;

&lt;p&gt;Unfortunately, I quickly realised something was still missing in my messages.
They work nicely, but sometimes, the server responds with an error for some requests.&lt;/p&gt;

&lt;p&gt;I looked more closely at the data I de-serialized from the server and noticed some peculiar values for IDs. For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deck_id&lt;/code&gt; has a value &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-143234&lt;/code&gt;, which clearly sounded like a bug, probably &lt;a href=&quot;https://en.wikipedia.org/wiki/Integer_overflow&quot;&gt;interger overflow&lt;/a&gt;. Again, I was lucky to solve this issue from the first guess: I initially used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int32&lt;/code&gt; format for int values, probably because it’s unclear from the object in the console. I switched to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uint64&lt;/code&gt;, and all issues were resolved.&lt;/p&gt;

&lt;h2 id=&quot;result&quot;&gt;Result&lt;/h2&gt;

&lt;p&gt;The resulting implementation of the API client can be found &lt;a href=&quot;https://github.com/szobov/anker/pull/7/files#diff-c5164c8415a8ff32b8b3a13ed081dc65ddb8a26594afd606d7b102dbeb875630&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The interface became more consistent, so I’m very much thankful to Anki’s maintainers for this change. It was an exciting journey, and I didn’t regret any time spent on it.&lt;/p&gt;
</description>
        <pubDate>Tue, 14 Nov 2023 00:00:00 +0000</pubDate>
        <link>https://szobov.github.io/2023/11/14/reverse-engineering-api-from-dev-console/</link>
        <guid isPermaLink="true">https://szobov.github.io/2023/11/14/reverse-engineering-api-from-dev-console/</guid>
        
        <category>reverse engineering</category>
        
        <category>anki</category>
        
        <category>frontend</category>
        
        
        <category>software</category>
        


        
        
        
           
        
           
              
              
              
              
                 
                     
                     <image>
                         <url>https://szobov.github.io/assets/images/reverse-engineering-anki/anki_console_failed_to_load_response.png</url>
                         <title>Reverse engineering binary protocol from dev console</title>
                         <link>https://szobov.github.io</link>
                     </image>
                 
              
                 
              
                 
              
              
            
        
        
           
              
        
        
           
              
        
        


      </item>
    
      <item>
        <title>Towards better natural language learning</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#background&quot; id=&quot;markdown-toc-background&quot;&gt;Background&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#anki&quot; id=&quot;markdown-toc-anki&quot;&gt;Anki&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#source-of-words&quot; id=&quot;markdown-toc-source-of-words&quot;&gt;Source of words&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#messengers-to-the-rescue&quot; id=&quot;markdown-toc-messengers-to-the-rescue&quot;&gt;Messengers to the rescue&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#conclusion&quot; id=&quot;markdown-toc-conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;background&quot;&gt;Background&lt;/h2&gt;

&lt;p&gt;A while ago I started to learn new languages. In the beginning, it was English, then Japanese and now German.&lt;/p&gt;

&lt;p&gt;Since then, I have always tried to optimize this process using technologies. And since we now have a powerful computer in our pockets, I was looking for software to extend my vocabulary in a new language.&lt;/p&gt;

&lt;p&gt;That is how I came to &lt;a href=&quot;https://apps.ankiweb.net&quot;&gt;Anki&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;anki&quot;&gt;Anki&lt;/h2&gt;

&lt;p&gt;Briefly, it’s an application or even a framework, that allows you to create &lt;a href=&quot;https://en.wikipedia.org/wiki/Flashcard&quot;&gt;flashcards&lt;/a&gt; and with a remarkable &lt;a href=&quot;https://en.wikipedia.org/wiki/Spaced_repetition&quot;&gt;spaced repetition&lt;/a&gt; algorithm learn effectively (at least for me) new things.&lt;/p&gt;

&lt;p&gt;I’m sure everyone has their best way to learn new words in a new language, but for me, a classical method of just writing words and trying to learn them all at a time simply doesn’t work. At first, it felt like I had learned, but all the new knowledge swiftly faded.&lt;/p&gt;

&lt;p&gt;I found Anki a fantastic tool to solve the problem mentioned above:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;First, you learn new words in batches. The default setting is only &lt;strong&gt;10&lt;/strong&gt; new words per day for a deck of words and &lt;strong&gt;100&lt;/strong&gt; cards to repeat. The numbers are configurable, but it worked perfectly, so I don’t feel overwhelmed.&lt;/li&gt;
  &lt;li&gt;Second, the spaced repetition algorithm helps words stick to your mind. The first time, you need to repeat a word in &lt;strong&gt;10&lt;/strong&gt; minutes, then in &lt;strong&gt;3&lt;/strong&gt; days and after a few more times, it can be more than &lt;strong&gt;1&lt;/strong&gt; year.&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;image&quot;&gt;
  &lt;img src=&quot;/assets/images/tech-for-language-learning/anki_heatmap.png&quot; alt=&quot;heatmap of my repetition in Anki&quot; /&gt;
  &lt;figcaption&gt;&lt;em&gt;heatmap of my repetition in Anki&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The next puzzle we need to solve is filling Anki with the words we want to learn!&lt;/p&gt;

&lt;h2 id=&quot;source-of-words&quot;&gt;Source of words&lt;/h2&gt;

&lt;p&gt;Many say grammar is important. I say only vocabulary can let you speak freely.&lt;/p&gt;

&lt;p&gt;What could be the best source of new words? For me, the answer is simple: &lt;strong&gt;books&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But the problem with books is that reading the whole book in a language you’re not fluent in can be challenging. It’s also hard for your brain without seeing progress, and it can be even more difficult with books.
You can start with books for children, but if you’re not a kid, it could be not very attractive.&lt;/p&gt;

&lt;p&gt;For me, the answer was Japanese comics called &lt;a href=&quot;https://en.wikipedia.org/wiki/Manga&quot;&gt;manga&lt;/a&gt;.
The beauty of manga is that it’s made for all ages. Also, many genres and beautiful drawings made it very attractive to me.
My choice was &lt;a href=&quot;https://en.wikipedia.org/wiki/Dorohedoro&quot;&gt;“Dorohedoro”&lt;/a&gt; translated into German.
It has everything I need: an exciting story, beautiful drawings and a tons of informal lexicon.&lt;/p&gt;

&lt;figure class=&quot;image&quot;&gt;
  &lt;img src=&quot;/assets/images/tech-for-language-learning/me_reading_manga.jpg&quot; alt=&quot;me reading manga&quot; /&gt;
  &lt;figcaption&gt;&lt;em&gt;me reading manga&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;messengers-to-the-rescue&quot;&gt;Messengers to the rescue&lt;/h2&gt;

&lt;p&gt;Now we have two most important pieces: &lt;strong&gt;Anki&lt;/strong&gt; and &lt;strong&gt;books&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The problem we still have is that adding words from the book to Anki is difficult.&lt;/p&gt;

&lt;p&gt;You can use the Anki mobile application to add new words, but it was not the easiest solution for me. When learning a new language, it’s crucial to make the process as simple as possible. Otherwise, it’s easy to get frustrated and quit.&lt;/p&gt;

&lt;p&gt;What can solve this issue? Something I use every day.&lt;/p&gt;

&lt;p&gt;The answer is messengers, particularly &lt;a href=&quot;https://telegram.org&quot;&gt;Telegram&lt;/a&gt;.
I spent some time designing and developing a telegram bot &lt;a href=&quot;https://github.com/szobov/anker&quot;&gt;Anker&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/tech-for-language-learning/bot_chat.jpg&quot; alt=&quot;bot interface&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The UI of this interface is simple: you choose a language and a deck and send it a word to translate. Then, it creates a card from the word and translation.
After syncing the app with the cloud, it appears as this nicely-looking card:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/tech-for-language-learning/anki_interface.jpg&quot; alt=&quot;anki interface&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now, we have all the pieces to improve our vocabulary drastically.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;I stopped learning Japanese as soon as I decided to relocate to Germany. I mostly forgot all the grammar I knew, but I still can understand the words I learned using Anki.&lt;/p&gt;

&lt;p&gt;After my weekly German lesson, I add new words to my learning deck. Anki made it scalable for me. It gives me confidence that I don’t waste my time in the learn-and-forget loop but have slow, steady progress.&lt;/p&gt;

&lt;p&gt;I understand that the method I describe in this article can only fit some people, but I hope you can try it and it can work for you.&lt;/p&gt;
</description>
        <pubDate>Sun, 22 Oct 2023 00:00:00 +0000</pubDate>
        <link>https://szobov.github.io/2023/10/22/towards-better-natural-language-learning/</link>
        <guid isPermaLink="true">https://szobov.github.io/2023/10/22/towards-better-natural-language-learning/</guid>
        
        <category>learning</category>
        
        <category>anki</category>
        
        <category>telegram</category>
        
        <category>bot</category>
        
        
        <category>software</category>
        


        
        
        
           
        
           
              
              
              
              
                 
                     
                     <image>
                         <url>https://szobov.github.io/assets/images/tech-for-language-learning/anki_heatmap.png</url>
                         <title>Towards better natural language learning</title>
                         <link>https://szobov.github.io</link>
                     </image>
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
                 
              
              
            
        
        
           
              
        
        
           
              
        
        
           
              
        
        


      </item>
    
      <item>
        <title>Black box testing for Android application</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#background&quot; id=&quot;markdown-toc-background&quot;&gt;Background&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#available-options&quot; id=&quot;markdown-toc-available-options&quot;&gt;Available options&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#maestro&quot; id=&quot;markdown-toc-maestro&quot;&gt;Maestro&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#simple-language&quot; id=&quot;markdown-toc-simple-language&quot;&gt;Simple Language&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#decoupled-from-the-application&quot; id=&quot;markdown-toc-decoupled-from-the-application&quot;&gt;Decoupled from the Application&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#decoupled-from-the-hardware&quot; id=&quot;markdown-toc-decoupled-from-the-hardware&quot;&gt;Decoupled from the Hardware&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#click-to-test-ui&quot; id=&quot;markdown-toc-click-to-test-ui&quot;&gt;Click-to-test UI&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#readability&quot; id=&quot;markdown-toc-readability&quot;&gt;Readability&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#downsides&quot; id=&quot;markdown-toc-downsides&quot;&gt;Downsides&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#conclusion&quot; id=&quot;markdown-toc-conclusion&quot;&gt;Conclusion&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#disclaimer&quot; id=&quot;markdown-toc-disclaimer&quot;&gt;Disclaimer&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#acknowledgement&quot; id=&quot;markdown-toc-acknowledgement&quot;&gt;Acknowledgement&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;background&quot;&gt;Background&lt;/h2&gt;

&lt;p&gt;A few months ago, I &lt;a href=&quot;https://www.linkedin.com/posts/szobovdev_android-testing-experience-activity-7052603098246057986-LPam&quot;&gt;asked&lt;/a&gt; a question on LinkedIn:
&lt;img src=&quot;/assets/images/android-black-box-testing/linked_in_post_android_testing.png&quot; alt=&quot;linkedin_post&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, it didn’t attract much attention, and I still wondered.
In my current company, we use an Android application as a frontend to our complex backend system.
This Android application is the first and only user-facing, so we needed a tool to ensure its quality.&lt;/p&gt;

&lt;p&gt;Before I joined, it was almost always manually tested, but the problem of this approach could be more scalable.
We needed automated tests but wanted to avoid getting into the Kotlin/Java code to write them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Our objective was&lt;/strong&gt;: &lt;em&gt;we need a tool to write automated tests which don’t require Android/Java/Kotlin knowledge.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;available-options&quot;&gt;Available options&lt;/h2&gt;

&lt;p&gt;We did initial research on the possible solution.
I’m unsure if it’s a complete list, but these are the things we’ve found:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://facebook.github.io/screenshot-tests-for-android&quot;&gt;screenshot-tests-for-android&lt;/a&gt; – a &lt;a href=&quot;https://en.wikipedia.org/wiki/Software_testing#Output_comparison_testing&quot;&gt;snapshot testing&lt;/a&gt; testing tool, but a little unflexible if UI of your application changes often. Also, it requires integration in the source code of the application.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.browserstack.com/app-live&quot;&gt;BrowserStack&lt;/a&gt; – a cloud-based platform to run tests on real or virtual devices. It seems like a powerful instrument, but vendor-locking, recurrent subscription and complexity make us hesitant to try it. On its own, it doesn’t provide a test framework but supports running many.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.android.com/training/testing/espresso&quot;&gt;Espresso&lt;/a&gt; – Android UI test framework. Again, this requires integration with the source code and Kotlin/Java knowledge.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://appium.io/docs/en/2.1/&quot;&gt;Appium&lt;/a&gt; – as stated in the docs: “is an open-source project and ecosystem of related software, designed to facilitate UI automation of many app platforms”. It is a complex UI test framework that can run on different platforms: physical, virtual devices or BrowserStack. We tried to run a setup, but it didn’t feel easy and required many integration steps.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://maestro.mobile.dev&quot;&gt;Maestro&lt;/a&gt; – The first sentence in the docstrings stated: “is the simplest and most effective mobile UI testing framework.”, which made me a little sceptical, but after we tried it turned out to be our choice. I’ll describe it in detail in the next section.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;maestro&quot;&gt;Maestro&lt;/h2&gt;

&lt;p&gt;It is a rather new and relatively obscure tool.
I found the first mentioning on hackernews &lt;a href=&quot;https://news.ycombinator.com/item?id=32664686&quot;&gt;dates Aug 31, 2022&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We chose this tool over the other since:&lt;/p&gt;

&lt;h3 id=&quot;simple-language&quot;&gt;Simple Language&lt;/h3&gt;

&lt;p&gt;The language of writing tests is simple DSL written in YAML files. The language is simple but capable of expressing complex flows.
This is the first example we wrote for our testing:&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;appId&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;com.mirai.app&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;launchApp&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;extendedWaitUntil&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;visible&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Skill&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Overview&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;70000&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# ms (eq 70 sec)&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;assertVisible&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;skill&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You can see that it’s very descriptive. All the &lt;a href=&quot;https://maestro.mobile.dev/api-reference/commands&quot;&gt;commands&lt;/a&gt; cover what I usually do when I use our application.
Also, it’s possible to express &lt;a href=&quot;https://maestro.mobile.dev/api-reference/commands/repeat&quot;&gt;loops&lt;/a&gt; and &lt;a href=&quot;https://maestro.mobile.dev/api-reference/commands/runflow&quot;&gt;importing&lt;/a&gt; from other files.
You may say it’s relatively too simple, but in my personal feelings, complicated language leads to cumbersome and often hard-to-maintain tests.&lt;/p&gt;

&lt;h3 id=&quot;decoupled-from-the-application&quot;&gt;Decoupled from the Application&lt;/h3&gt;

&lt;p&gt;Maestro setup knows nothing about your application.&lt;/p&gt;

&lt;p&gt;All app-specific logic is expressed only in the tests themselves. Maestro only starts the application as a user does it and then runs a flow of commands against it. You can change the language your mobile app is written in, change your build system, and do whatever you want – as long as the interface is the same, your maestro tests will stay as they are.&lt;/p&gt;

&lt;h3 id=&quot;decoupled-from-the-hardware&quot;&gt;Decoupled from the Hardware&lt;/h3&gt;

&lt;p&gt;You can run your tests against a physical device or emulator.&lt;/p&gt;

&lt;p&gt;For our setup, I built a &lt;a href=&quot;https://hub.docker.com/r/szobov/maestro-android-emulator&quot;&gt;Docker Image&lt;/a&gt; based on &lt;a href=&quot;https://github.com/budtmo/docker-android&quot;&gt;docker-android&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Speaking frankly, it required a small tests update to increase the timings since the application in the emulator works slowly, but I would say it’s expected.&lt;/p&gt;

&lt;p&gt;However, the apparent benefit of wiring up these tools is that it is a perfect setup for your CI pipeline.&lt;/p&gt;

&lt;p&gt;The hardware devices are excellent, but they could be more scalable, or you need to pay cloud providers like the mentioned BrowserStack. But with Docker, you can run as many instances of the simulator as could fit into your CI server.&lt;/p&gt;

&lt;h3 id=&quot;click-to-test-ui&quot;&gt;Click-to-test UI&lt;/h3&gt;

&lt;p&gt;Maestro includes a &lt;a href=&quot;https://maestro.mobile.dev/getting-started/maestro-studio&quot;&gt;browser-based UI&lt;/a&gt; where you can click and select the elements of your app and convert them to the tests.&lt;/p&gt;

&lt;p&gt;I consider it a killer feature since it simplifies the test writing workflow.&lt;/p&gt;

&lt;h3 id=&quot;readability&quot;&gt;Readability&lt;/h3&gt;

&lt;p&gt;The file with UI tests can overgrow, and needs help comprehending.
Maestro support includes code from other files via the &lt;a href=&quot;https://maestro.mobile.dev/api-reference/commands/runflow&quot;&gt;runFlow&lt;/a&gt; command.
You can use &lt;a href=&quot;https://github.com/mobile-dev-inc/maestro/pull/1292&quot;&gt;label&lt;/a&gt; to make tests more self-descriptive with human-readable text.&lt;/p&gt;

&lt;h2 id=&quot;downsides&quot;&gt;Downsides&lt;/h2&gt;

&lt;p&gt;Maestro is a very new tool and is under active development.&lt;/p&gt;

&lt;p&gt;Some features need to be documented, and you need to upgrade your installation occasionally.&lt;/p&gt;

&lt;p&gt;While I was writing this blog post, I realized that the “labels” I mentioned in the previous topic were &lt;a href=&quot;https://github.com/mobile-dev-inc/maestro/pull/1462&quot;&gt;reverted&lt;/a&gt; and are no longer available. I may predict there could be bugs and breaking changes.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;We were pleased to find such a powerful and accessible tool despite all the downsides mentioned.
Emulating devices with Docker and running automated tests gave us many opportunities to reduce manual testing time.
Now, we can run as many devices as we need and pinpoint bugs earlier.&lt;/p&gt;

&lt;h3 id=&quot;disclaimer&quot;&gt;Disclaimer&lt;/h3&gt;

&lt;p&gt;I’m not affiliated with any of the mentioned projects.&lt;/p&gt;

&lt;h3 id=&quot;acknowledgement&quot;&gt;Acknowledgement&lt;/h3&gt;

&lt;p&gt;This post is based on my work together with &lt;a href=&quot;https://www.linkedin.com/in/maria-matyushenko/&quot;&gt;Maria Matyushenko&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Thu, 12 Oct 2023 00:00:00 +0000</pubDate>
        <link>https://szobov.github.io/2023/10/12/black-box-testing-android/</link>
        <guid isPermaLink="true">https://szobov.github.io/2023/10/12/black-box-testing-android/</guid>
        
        <category>testing</category>
        
        <category>mobile</category>
        
        <category>android</category>
        
        <category>maestro</category>
        
        <category>e2e</category>
        
        
        <category>software</category>
        


        
        
        
           
        
           
              
              
              
              
                 
                     
                     <image>
                         <url>https://szobov.github.io/assets/images/android-black-box-testing/linked_in_post_android_testing.png</url>
                         <title>Black box testing for Android application</title>
                         <link>https://szobov.github.io</link>
                     </image>
                 
              
                 
              
              
            
        
        


      </item>
    
  </channel>
</rss>
