<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Notes about web development from personal blog of Artem Riasnianskyi</title>
  <subtitle>Javascript, React, CSS, browsers, etc</subtitle>
  <link href="https://artm.dev/feed.xml" rel="self"/>
  <link href="https://artm.dev/"/>
  <updated>2026-01-18T00:00:00Z</updated>
  <id>https://artm.dev/</id>
  <author>
    <name>Artem Riasnianskyi</name>
  </author>
  
  <entry>
    <title>Rasterize HTML in Browser</title>
    <link href="https://artm.dev/notes/rasterizing-html-client-side/"/>
    <updated>2021-01-02T00:00:00Z</updated>
    <id>https://artm.dev/notes/rasterizing-html-client-side/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://artm.dev/&quot;&gt;← Back Home&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Rasterize HTML in Browser: A Guide to Local Client-Side Solutions&lt;/h1&gt;
&lt;p&gt;Exploring how to transform HTML content into images locally using browser extensions.&lt;/p&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Rasterization, the process of converting HTML structure into an image, is akin to taking a screenshot of a web page. While there are services and libraries that enable webpage screenshots for purposes like testing and visual diffing, they often rely on server-side solutions. This article delves into client-side alternatives, offering cost-effective and user-specific rasterization techniques.&lt;/p&gt;
&lt;h2&gt;Why Client-Side Rasterization?&lt;/h2&gt;
&lt;p&gt;Client-side rasterization offers two key benefits: cost savings and user-specific rendering. Unlike server-side solutions, it avoids the need for expensive server infrastructure, especially for specific OS requirements like macOS. Moreover, it provides access to user-specific resources like fonts and authenticated pages, ensuring the rasterized image closely matches what users see on their screens.&lt;/p&gt;
&lt;h3&gt;The Challenges with Screenshots&lt;/h3&gt;
&lt;p&gt;Screenshots are the most straightforward method of rasterization, but they fall short in flexibility. They can&#39;t capture specific page elements conveniently, lack transparency support, and don&#39;t adapt well to different screen resolutions, such as retina displays.&lt;/p&gt;
&lt;h2&gt;Approaches to Rasterization&lt;/h2&gt;
&lt;h3&gt;Canvas Method&lt;/h3&gt;
&lt;p&gt;Canvas-based solutions render HTML within a canvas element. This method&#39;s limitations include browser-dependent font rendering and antialiasing, and the inability to interact with iframe content due to security restrictions. Example: &lt;a href=&quot;https://html2canvas.hertzen.com/&quot;&gt;html2canvas&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;WebGL Approach&lt;/h3&gt;
&lt;p&gt;Similar to Canvas, WebGL renders HTML with more intensive GPU effects, offering enhanced visual capabilities. However, it shares similar limitations regarding security and content rendering. Demo: &lt;a href=&quot;http://pixelscommander.com/polygon/htmlgl/demo/filters.html#.X-m3AWT0lqs&quot;&gt;WebGL Filters&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;SVG Rendering&lt;/h3&gt;
&lt;p&gt;This technique involves rendering HTML within an SVG as a foreign object, then using SVG for image creation. It&#39;s effective but struggles with external images and styles, and like the other methods, can&#39;t bypass iframe security.&lt;/p&gt;
&lt;h3&gt;Browser Extension Method&lt;/h3&gt;
&lt;p&gt;Browser extensions can rasterize HTML while addressing color profile and transparency issues. They work by communicating with a client page that requests rasterization and a renderer page that performs the actual rendering.&lt;/p&gt;
&lt;h4&gt;Communication Steps&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Inject a content script to send messages to the extension.&lt;/li&gt;
&lt;li&gt;Use the content script to request rasterization.&lt;/li&gt;
&lt;li&gt;The extension creates a new window and tab to render and capture the HTML content.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;Rasterization Techniques&lt;/h4&gt;
&lt;h5&gt;Tab Screenshot Method&lt;/h5&gt;
&lt;p&gt;This standard method involves:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Adjusting for screen resolution.&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;chrome.tabs.captureVisibleTab&lt;/code&gt; for image capture.&lt;/li&gt;
&lt;li&gt;Tackling larger-than-screen images by tiling and scrolling.&lt;/li&gt;
&lt;/ol&gt;
&lt;h5&gt;Debugger Protocol Method&lt;/h5&gt;
&lt;p&gt;A more advanced technique using the Chrome Debugger Protocol, offering straightforward transparent background captures. However, it requires exclusive access to the DevTools and prompts a security banner in the browser.&lt;/p&gt;
&lt;h4&gt;Limitations&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Transparency handling is complex.&lt;/li&gt;
&lt;li&gt;The Debugger Protocol method impacts other browser functions and displays a prominent security banner.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Saving the Image&lt;/h4&gt;
&lt;p&gt;Once captured, images can be saved using the &lt;code&gt;chrome.downloads.download&lt;/code&gt; function, making the process seamless for end-users.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Client-side rasterization offers a cost-effective, user-specific alternative to server-side solutions. Each method discussed has its own strengths and ideal use cases, with Canvas and WebGL being suitable for most general purposes, and the Debugger Protocol excelling in scenarios requiring transparency.&lt;/p&gt;
&lt;div style=&quot;width: 100%; text-align: center&quot;&gt;
  &lt;open-likes icon=&quot;heart&quot; color=&quot;#e91e1e&quot; counter=&quot;bottom&quot;&gt;&lt;/open-likes&gt;
&lt;/div&gt;
</content>
  </entry>
  
  <entry>
    <title>How to quickly see what was merged in a timeframe (with CLI)</title>
    <link href="https://artm.dev/notes/git-merge/"/>
    <updated>2021-10-09T00:00:00Z</updated>
    <id>https://artm.dev/notes/git-merge/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://artm.dev/&quot;&gt;← Back Home&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;How to quickly see what was merged in a timeframe (with CLI)&lt;/h1&gt;
&lt;p&gt;Let&#39;s say you see some change in chart of your app performance on specific date, and want to know what might be the cause. You can go to Github and check manually merged pull requests sorted by date but you can also speed it up by simple script.&lt;/p&gt;
&lt;p&gt;For that you&#39;ll need to use &lt;a href=&quot;https://cli.github.com/&quot;&gt;Github CLI&lt;/a&gt; client. Install it with your favorite package manager and then autorize by running &lt;code&gt;gh auth login&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;After this go to the folder with repo with you want to check for merges. Then run following command to fetch all merged pull requests in JSON format:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;gh &lt;span class=&quot;token function&quot;&gt;pr&lt;/span&gt; list --state merged --json mergedAt,title,url --limit &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; prs.json&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It will create &lt;code&gt;prs.json&lt;/code&gt; file that we will use to filter using &lt;code&gt;jq&lt;/code&gt;. If you don&#39;t have it installed find how to install it on &lt;a href=&quot;https://stedolan.github.io/jq/download/&quot;&gt;this page&lt;/a&gt; To do so run the following command replacing dates with the timeframe you&#39;re looking for:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;jq --arg s &lt;span class=&quot;token string&quot;&gt;&#39;2021-10-15&#39;&lt;/span&gt; --arg e &lt;span class=&quot;token string&quot;&gt;&#39;2021-10-19&#39;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&lt;br /&gt;[($s, $e) | strptime(&quot;%Y-%m-%d&quot;)[0:3]] as $r&lt;br /&gt;  | map(select(&lt;br /&gt;        (.mergedAt[:19] | strptime(&quot;%Y-%m-%dT%H:%M:%S&quot;)[0:3]) as $d&lt;br /&gt;          | $d &gt;= $r[0] and $d &amp;lt;= $r[1]&lt;br /&gt;    ))&lt;br /&gt;&#39;&lt;/span&gt; prs.json&lt;/code&gt;&lt;/pre&gt;
&lt;small&gt;
Source: &lt;a href=&quot;https://stackoverflow.com/questions/40210276/how-to-select-a-date-range-from-a-json-string-by-using-jq&quot;&gt;Stackoverflow&lt;/a&gt;
&lt;/small&gt;
&lt;div style=&quot;width: 100%; text-align: center&quot;&gt;
  &lt;open-likes icon=&quot;heart&quot; color=&quot;#e91e1e&quot; counter=&quot;bottom&quot;&gt;&lt;/open-likes&gt;
&lt;/div&gt;
</content>
  </entry>
  
  <entry>
    <title>Use case for :has() CSS selector</title>
    <link href="https://artm.dev/notes/use-case-for-css-has-selector/"/>
    <updated>2022-01-02T00:00:00Z</updated>
    <id>https://artm.dev/notes/use-case-for-css-has-selector/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://artm.dev/&quot;&gt;← Back Home&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Use case for :has() CSS selector&lt;/h1&gt;
&lt;p&gt;To highlight internal interactive elements of some bigger interactive element. For example post card that opens post, but button on that card will add post to favorites.&lt;/p&gt;
&lt;img width=&quot;565&quot; height=&quot;106&quot; src=&quot;https://artm.dev/assets/images/use-case-example.gif&quot; /&gt;
&lt;p&gt;Previously for this was either using javascript or more complex layout trick with sibling layer overlaying bigger element.&lt;/p&gt;
&lt;p&gt;Here is how to do it with few lines of CSS&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.outer,&lt;br /&gt;.outer:hover:has(.inner:hover)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; white&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.outer:hover&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #eee&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.inner:hover&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #eee&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Although click event handler still should be captured and stop propagation.&lt;/p&gt;
&lt;p&gt;Selector already available in Safari TP and should be added to Firefox and Chromium based browsers this year.&lt;/p&gt;
&lt;div style=&quot;width: 100%; text-align: center&quot;&gt;
  &lt;open-likes icon=&quot;heart&quot; color=&quot;#e91e1e&quot; counter=&quot;bottom&quot;&gt;&lt;/open-likes&gt;
&lt;/div&gt;
</content>
  </entry>
  
  <entry>
    <title>When to use SVG</title>
    <link href="https://artm.dev/notes/when-to-use-svg/"/>
    <updated>2022-02-15T00:00:00Z</updated>
    <id>https://artm.dev/notes/when-to-use-svg/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://artm.dev/&quot;&gt;← Back Home&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;When to use SVG&lt;/h1&gt;
&lt;p&gt;SVG are great choose for UI elements like icons and graphic elements that will scale. But if you need to use it as illustration with known dimensions — consider first trying PNG with doubled dimensions (for retina screens) after OxiPNG compression using &lt;a href=&quot;https://squoosh.app/&quot;&gt;https://squoosh.app/&lt;/a&gt;. You can also reduce amount of colors if it makes sense. The app will show you difference in percentage and if it&#39;s not bigger then 25% increase good changes that PNG will perform better. The more elements you have in SVG the more changes that PNG will be better for that image.&lt;/p&gt;
&lt;p&gt;The reason for this is performance of huge SVG elements might be not good — SVG adds a lot of complexity to the render pipeline and DOM structure.&lt;/p&gt;
&lt;p&gt;If you need to animate or alter parts of SVG, consider extracting only those parts and render them over PNG.&lt;/p&gt;
&lt;div style=&quot;width: 100%; text-align: center&quot;&gt;
  &lt;open-likes icon=&quot;heart&quot; color=&quot;#e91e1e&quot; counter=&quot;bottom&quot;&gt;&lt;/open-likes&gt;
&lt;/div&gt;
</content>
  </entry>
  
  <entry>
    <title>How to programmatically get highlights from Apple Books</title>
    <link href="https://artm.dev/notes/how-to-programmatically-get-highlights-from-apple-books/"/>
    <updated>2022-02-20T00:00:00Z</updated>
    <id>https://artm.dev/notes/how-to-programmatically-get-highlights-from-apple-books/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://artm.dev/&quot;&gt;← Back Home&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;How to programmatically get highlights from Apple Books&lt;/h1&gt;
&lt;p&gt;If you, like me using Apple Books for reading and making notes you may notice that getting highlights is not easy — you can’t just export them using Share Sheet.&lt;/p&gt;
&lt;p&gt;Without using third party apps there only one option I know — open Apple Books on desktop, open book highlights in UI and using Command key select highlights and then in context menu use Copy. It will copy highlights in text format with book name, date and copyright info.&lt;/p&gt;
&lt;p&gt;It is possible to parse that text data, but there is still manual step — you need to copy highlights from each book. Not cool. Let’s automate it using Javascript and Node.js (well, actually using Typescript and &lt;a href=&quot;https://www.npmjs.com/package/tsm&quot;&gt;tsm&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;First of all let’s create types that we will operate with:&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Book, but could be a sample or an article in PDF&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  title&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  author&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Annotation&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  assetId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// pointer to Book.id&lt;/span&gt;&lt;br /&gt;  quote&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  comment&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  chapter&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  colorCode&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  modifiedAt&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  createdAt&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next step is to extract raw data from origin. It is relatively easy — Apple stores highlights from Apple Books in SQLite DB, so we can access files and read from them everything we want.&lt;/p&gt;
&lt;p&gt;We would need to read from 2 sources — one for annotations and another for info about book. We well produce denormalized data — each highlight item will contain book name and author, so it would be easier to process. But in some cases normalized data might be better, feel free to edit.&lt;/p&gt;
&lt;p&gt;Let’s prepare out source files:&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; glob &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;glob&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; os &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;os&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; username &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;userInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;username&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ANNOTATION_DB_PATH&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/users/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;username&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/Library/Containers/com.apple.iBooksX/Data/Documents/AEAnnotation/&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;BOOK_DB_PATH&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/users/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;username&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/Library/Containers/com.apple.iBooksX/Data/Documents/BKLibrary/&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; annotationsFiles &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; glob&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ANNOTATION_DB_PATH&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/*.sqlite&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; booksFiles &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; glob&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;BOOK_DB_PATH&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/*.sqlite&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Don’t forget to install dependency &lt;code&gt;glob&lt;/code&gt; with&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;npm i glob&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;it will help to get all file names in the folders&lt;/p&gt;
&lt;p&gt;Next, create SQL query for fetching data in the format we defined earlier. This one will produce &lt;code&gt;Annotation&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;SELECT_ALL_ANNOTATIONS_QUERY&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;select &lt;br /&gt;  ZANNOTATIONASSETID as assetId,&lt;br /&gt;  ZANNOTATIONSELECTEDTEXT as quote,&lt;br /&gt;  ZANNOTATIONNOTE as comment,&lt;br /&gt;  ZFUTUREPROOFING5 as chapter,&lt;br /&gt;  ZANNOTATIONSTYLE as colorCode,&lt;br /&gt;  ZANNOTATIONMODIFICATIONDATE as modifiedAt,&lt;br /&gt;  ZANNOTATIONCREATIONDATE as createdAt&lt;br /&gt;from ZAEANNOTATION&lt;br /&gt;where ZANNOTATIONDELETED = 0 &lt;br /&gt;  and ZANNOTATIONSELECTEDTEXT is not null &lt;br /&gt;  and ZANNOTATIONSELECTEDTEXT &amp;lt;&gt; &#39;&#39;&lt;br /&gt;order by ZANNOTATIONASSETID, ZPLLOCATIONRANGESTART;&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And another one for &lt;code&gt;Book&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;SELECT_ALL_BOOKS_QUERY&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;select &lt;br /&gt;  ZASSETID as id, &lt;br /&gt;  ZTITLE as title, &lt;br /&gt;  ZAUTHOR as author &lt;br /&gt;from ZBKLIBRARYASSET&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;store them in corresponding variables.&lt;/p&gt;
&lt;p&gt;Now time to install something that will actually run this queries:&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;npm i sqlite sqlite3&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can create few helper functions that will handle connection to DB&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; sqlite3 &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sqlite3&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; open &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sqlite&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createDB&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;filename&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    filename&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; filename&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    driver&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; sqlite3&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Database&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getBooksFromDBFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;filename&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Book&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; db &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createDB&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;filename&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; db&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Book&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SELECT_ALL_BOOKS_QUERY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getBooks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; books &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;booksFiles&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;getBooksFromDBFile&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; books&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;flat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getAnnotationsFromDBFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;filename&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; db &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createDB&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;filename&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; db&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Annotation&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SELECT_ALL_ANNOTATIONS_QUERY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getAnnotations&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; annotations &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;    annotationsFiles&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;getAnnotationsFromDBFile&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; annotations&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;flat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And before we can combine everything in one function we need to create another helper for timestamps. Apple uses weird starting point for date, from 2001-01-01, in order to get more conventional unix epoch timestamp we need to convert them:&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;APPLE_EPOCH_START&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2001-01-01&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;convertAppleTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;appleTime&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;APPLE_EPOCH_START&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; appleTime &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alright, now we’re ready to put everything together. We’ll start with fetching Books, then we’ll get Annotations, iterate over them to enrich with Book info and at the end will write JSON with results to &lt;code&gt;output.json&lt;/code&gt; file.&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; books &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getBooks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; annotations &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getAnnotations&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; booksByAssetId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Record&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Book&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Book&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; output &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; annotations&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; assetId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; modifiedAt&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; createdAt&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;annotation &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;booksByAssetId&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;assetId&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        booksByAssetId&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;assetId&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; books&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; assetId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; book &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; booksByAssetId&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;assetId&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;annotation&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;        modifiedAt&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;convertAppleTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;modifiedAt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;        createdAt&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;convertAppleTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;createdAt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;        author&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; book&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;author &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Unknown Author&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;        title&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; book&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Unknown Title&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeFileSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;output.json&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;output&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Exported&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; output&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;items&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s it.&lt;/p&gt;
&lt;p&gt;You can find full script at &lt;a href=&quot;https://gist.github.com/asci/82ffbe53cf6b1933bb570b67006c88b4&quot;&gt;Github Gist&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Thanks for reading&lt;/p&gt;
&lt;div style=&quot;width: 100%; text-align: center&quot;&gt;
  &lt;open-likes icon=&quot;heart&quot; color=&quot;#e91e1e&quot; counter=&quot;bottom&quot;&gt;&lt;/open-likes&gt;
&lt;/div&gt;
</content>
  </entry>
  
  <entry>
    <title>How I setup NextJS project</title>
    <link href="https://artm.dev/notes/nextjs-setup/"/>
    <updated>2022-05-28T00:00:00Z</updated>
    <id>https://artm.dev/notes/nextjs-setup/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://artm.dev/&quot;&gt;← Back Home&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;How I setup NextJS project&lt;/h1&gt;
&lt;p&gt;Quick guide on decoupling from framework&#39;s nudged approach&lt;/p&gt;
&lt;h2&gt;What&lt;/h2&gt;
&lt;p&gt;I use &lt;a href=&quot;https://nextjs.org/&quot;&gt;NextJS&lt;/a&gt; for building a web application. It is not a blog or set of landing pages — it is more-less interactive. It has some API, client-side requests and so on, potentially can be even an Electron or React Native app.&lt;/p&gt;
&lt;p&gt;NextJS is a remarkable framework to start. It provides countless features out of the box: perfect build system, performance optimization, routing and many others. Plus, using &lt;a href=&quot;https://vercel.com/&quot;&gt;Vercel&lt;/a&gt; as hosting is a delightful experience — it provides many nice features even on the free account.&lt;/p&gt;
&lt;h2&gt;Why&lt;/h2&gt;
&lt;p&gt;For the web application I want to keep some freedom. In case NextJS will not be the perfect match. Currently it&#39;s a full &lt;strong&gt;vendor lock&lt;/strong&gt;. Which is not good for many reasons. Some of them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What if Vercel will rise prices?&lt;/li&gt;
&lt;li&gt;What if Vercel go bankrupt?&lt;/li&gt;
&lt;li&gt;What if Vercel decide to focus on some specific features and drop development of ones my project relies on?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are just a few, but I believe they are important enough to play a bit safer and plan a bit more future-proof architecture for the project.&lt;/p&gt;
&lt;h2&gt;How&lt;/h2&gt;
&lt;p&gt;My approach is simple — &lt;strong&gt;keep pages as small as possible&lt;/strong&gt;. And it is inspired by domain-driven design and &lt;a href=&quot;https://en.wikipedia.org/wiki/Multitier_architecture&quot;&gt;layered architecture&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So, I put in page files only code that should be there. And all other code that contains business logic, goes into outside files and folders.&lt;/p&gt;
&lt;p&gt;Let&#39;s check flow from a data/backend perspective:&lt;/p&gt;
&lt;picture&gt;
  &lt;source srcset=&quot;https://artm.dev/assets/images/backend-dark.webp&quot; media=&quot;(prefers-color-scheme: dark)&quot; /&gt;
  &lt;img src=&quot;https://artm.dev/assets/images/backend-light.webp&quot; /&gt;
&lt;/picture&gt;
&lt;p&gt;With clear boundaries, I can change underlaying technologies with less friction. Not only NextJS, but also DB (currently I use &lt;a href=&quot;https://supabase.com/&quot;&gt;Supabase&lt;/a&gt;) because all communication with DB is limited to &lt;code&gt;models&lt;/code&gt;. And I can even add new clients, like mobile apps.&lt;/p&gt;
&lt;p&gt;From frontend side it is a similar situation:&lt;/p&gt;
&lt;picture&gt;
  &lt;source srcset=&quot;https://artm.dev/assets/images/frontend-dark.webp&quot; media=&quot;(prefers-color-scheme: dark)&quot; /&gt;
  &lt;img src=&quot;https://artm.dev/assets/images/frontend-light.webp&quot; /&gt;
&lt;/picture&gt;
&lt;p&gt;Separating NextJS features (files for &lt;code&gt;Page&lt;/code&gt;, &lt;code&gt;App&lt;/code&gt; and &lt;code&gt;Document&lt;/code&gt;) from the rest of the app.&lt;/p&gt;
&lt;p&gt;Page Component only accepts props that needed for rendering, exposing types and unaware of the parent features. Some components down the line might use React Context created in &lt;code&gt;App&lt;/code&gt; component.&lt;/p&gt;
&lt;p&gt;And I also prefer to separate UI kit layer, so it can be extracted, reused or replaced with less friction.&lt;/p&gt;
&lt;p&gt;And my preferred folder structure is looking similar to this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;backend
  ⎣handlers
  ⎣models
client
  ⎣components
  ⎣pages
  ⎣api
pages
  ⎣_app.tsx
  ⎣posts.tsx
package.json

&lt;/code&gt;&lt;/pre&gt;
&lt;div style=&quot;width: 100%; text-align: center&quot;&gt;
  &lt;open-likes icon=&quot;heart&quot; color=&quot;#e91e1e&quot; counter=&quot;bottom&quot;&gt;&lt;/open-likes&gt;
&lt;/div&gt;
</content>
  </entry>
  
  <entry>
    <title>Using manual diagrams to learn new codebase</title>
    <link href="https://artm.dev/notes/using-diagrams-for-learning/"/>
    <updated>2022-06-21T00:00:00Z</updated>
    <id>https://artm.dev/notes/using-diagrams-for-learning/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://artm.dev/&quot;&gt;← Back Home&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Using manual diagrams to learn new codebase&lt;/h2&gt;
&lt;p&gt;When exploring a new codebase, it is difficult to get all the entities and relations between them at the same time. Everything is new, and with each opened file, the brain gets bombarded with new information. That&#39;s why I think it is important to unload part of the information to an external carrier. Some kind of map, that will help to navigate the codebase.&lt;/p&gt;
&lt;h3&gt;Why not automated?&lt;/h3&gt;
&lt;p&gt;In the past, I was trying to solve this with automated tools. I was looking for something that check all imports/exports and build an interactive graph of dependencies. My primary focus was Javascript (Typescript). And I managed to find tools that partially deliver, but then I realize that they have 3 big downsides.&lt;/p&gt;
&lt;h4&gt;Limited coverage&lt;/h4&gt;
&lt;p&gt;Firstly, tools cover only one part of the codebase. Most of what I found was focused on building just dependencies graph. So If you change language or build system — you need to find another solution.&lt;/p&gt;
&lt;h4&gt;Unidirectional&lt;/h4&gt;
&lt;p&gt;Tools are also limited because they are mostly unidirectional. They only generate something and that&#39;s it — no comments, no questions, no additional connections (the one that are not expressed directly via import/export, but for example calls via HTTP API). Feedback is limited. It is hard to add some additional layer of information.&lt;/p&gt;
&lt;h4&gt;Informational overload&lt;/h4&gt;
&lt;p&gt;Another thing, is that automated generation creates more information than needed. There is simply too much stuff happening on the map, and it&#39;s not helpful to learn or navigate. The connections in our brain cannot build so quickly, so I found it natural to slow down and to use manual diagrams.&lt;/p&gt;
&lt;h3&gt;Manual&lt;/h3&gt;
&lt;p&gt;For each new task or project, I create a new diagram. I try to add some code pointers, like filenames, so I can easily open related files when required. I connect them with labeled lines, that represents call name or relationship status (for example: &amp;quot;extends&amp;quot;, &amp;quot;implements&amp;quot;, &amp;quot;generates&amp;quot;).&lt;/p&gt;
&lt;p&gt;Furthermore, I also like to add actors, or people that solving some problem with this code — is it a customer who wants to buy new car insurance, or is it a designer who would like to share their prototype? Sometime actors are mechanical — timer, or some other external event, but it is still helping to understand what is the starting point in the context of the task. And it also helps to understand what are the data shapes.&lt;/p&gt;
&lt;h3&gt;Tooling&lt;/h3&gt;
&lt;p&gt;For quick start I use &lt;a href=&quot;https://www.tldraw.com/&quot;&gt;tldraw&lt;/a&gt;, it&#39;s a simple and lightweight, but well-designed instrument. The downside — no way to share or embed diagram from website. Storage is limited to browser local storage or to file (which is handy to put in code repository and use it with &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=tldraw-org.tldraw-vscode&quot;&gt;VS Code plugin&lt;/a&gt;). Ah, and sometimes bugs occur during image export, not critical, but a bit annoying.&lt;/p&gt;
&lt;p&gt;For something more sophisticated, I prefer &lt;a href=&quot;https://www.diagrams.net/&quot;&gt;diagrams.net&lt;/a&gt; — it is a powerful, yet adjustable instrument for any kind of diagrams. It&#39;s even supporting SQL schema imports. I use it with &amp;quot;sketch&amp;quot; theme and created a set of elements to match ones from tldraw (basically using &lt;a href=&quot;https://fonts.google.com/specimen/Caveat+Brush&quot;&gt;Caveat Brush&lt;/a&gt; font for all texts).&lt;/p&gt;
&lt;p&gt;I used to make diagrams with &lt;a href=&quot;https://mermaid-js.github.io/mermaid/#/&quot;&gt;mermaid&lt;/a&gt;, but moved away from it, since it&#39;s very limiting expression. It&#39;s nice to be able to move something with mouse during exploration phase, to add some questions and comments. But it might be a good choice for documenting something established and well known, and put it in code repository — its text format is more readable than tldraw&#39;s.&lt;/p&gt;
&lt;h3&gt;Process&lt;/h3&gt;
&lt;p&gt;I don&#39;t follow any strict process, but there are a few ideas I try to follow in each of the diagrams.&lt;/p&gt;
&lt;h4&gt;Start&lt;/h4&gt;
&lt;p&gt;Diagrams should have a start. If I document some process, and it has one or multiple entry points it is crucial to mark the where to start looking at diagram, it is the same place where process starts, or at least the most important part of the process. It can be form submission, it can be a webhook call, template for code generation, message from the message queue, etc.&lt;/p&gt;
&lt;picture&gt;
  &lt;source srcset=&quot;https://artm.dev/assets/images/roles-dark.webp&quot; media=&quot;(prefers-color-scheme: dark)&quot; /&gt;
  &lt;img src=&quot;https://artm.dev/assets/images/roles-light.webp&quot; /&gt;
&lt;/picture&gt;
&lt;h4&gt;Roles&lt;/h4&gt;
&lt;p&gt;Second — show roles or actors. As mentioned above. It&#39;s important to show (or at least imagine) goals of each actor in the process. For simple flows or codebase, it can be only one actor.&lt;/p&gt;
&lt;h4&gt;Questions and answers&lt;/h4&gt;
&lt;picture&gt;
  &lt;source srcset=&quot;https://artm.dev/assets/images/sticky-dark.webp&quot; media=&quot;(prefers-color-scheme: dark)&quot; /&gt;
  &lt;img src=&quot;https://artm.dev/assets/images/sticky-light.webp&quot; /&gt;
&lt;/picture&gt;
&lt;p&gt;Questions are a critical part of learning. If something is not clear, I immediately put a sticky note next to it with a question. I use yellow for questions. Later, when I continue to explore the system, I can find the answer to my question and just put a new sticky note (green one) next to the question with answer. Occasionally, I can&#39;t find a clear answer — then I try to find somebody more knowledgable in the area and setup a meeting to follow all remaining question stickies.&lt;/p&gt;
&lt;div style=&quot;width: 100%; text-align: center&quot;&gt;
  &lt;open-likes icon=&quot;heart&quot; color=&quot;#e91e1e&quot; counter=&quot;bottom&quot;&gt;&lt;/open-likes&gt;
&lt;/div&gt;
</content>
  </entry>
  
  <entry>
    <title>Don&#39;t confuse signals with answers</title>
    <link href="https://artm.dev/notes/signal-answer/"/>
    <updated>2024-07-15T00:00:00Z</updated>
    <id>https://artm.dev/notes/signal-answer/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://artm.dev/&quot;&gt;← Back Home&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Don&#39;t confuse signals with answers&lt;/h1&gt;
&lt;p&gt;Is 100% test coverage an answer that you code has no bugs? No. It&#39;s &lt;em&gt;a signal&lt;/em&gt; that certain parts of the system behave in a way that is not broken. It is not guarantee that system as a whole works, even if you have some integration tests. There are still some edge cases. But having good test coverage is preferrable, because it is &lt;em&gt;usually&lt;/em&gt; a strong signal that codebase is well treated.&lt;/p&gt;
&lt;p&gt;Here is another example. How much code did you ship in the last month? If you didn&#39;t write much, is it an answer that you didn&#39;t work? No. But it&#39;s a signal that shows that you might need more attention — maybe you&#39;re blocked, or maybe you&#39;re working on a big project and most of contributions are to the RFCs.&lt;/p&gt;
&lt;p&gt;When you ask for advice you don&#39;t ask for definitive answer. For example you might ask a friend, do I look good in this jacket? Their answer is a signal. You might agree or disagree with it. It might be possible that 1 friend said the jacked does not suit you, but 5 other said it sits perfectly. It&#39;s up to you to decide for yourself.&lt;/p&gt;
&lt;p&gt;Same with AI answers — it can provide some signal (opinion) to the question asked, but it might be missing some important context details or it might have more context then needed. So it&#39;s again up to you to how you gonna use that signal, if you agree or disagree with it.&lt;/p&gt;
&lt;p&gt;Similar with performance metrics for the website. If we improve it would it result in better sales? Was it only the performance or also the marketing campaign with 50% discounts that happened at the same time? Or maybe it was our competitor&#39;s website that was down for 10 hours? Some metrics are too abstract to give an answer. Having multiple levels metrics helps to identify stronger signal, and A/B testing also helps to improve signal strength.&lt;/p&gt;
&lt;p&gt;For a lot of questions we only have signals. Some strong, some weak, but it&#39;s rarely a definitive answer. It takes time to shift to this mindset, but it helps to understand world better and helps to keep calm in unexpected situations.&lt;/p&gt;
&lt;div style=&quot;width: 100%; text-align: center&quot;&gt;
  &lt;open-likes icon=&quot;heart&quot; color=&quot;#e91e1e&quot; counter=&quot;bottom&quot;&gt;&lt;/open-likes&gt;
&lt;/div&gt;
</content>
  </entry>
  
  <entry>
    <title>On the impact of AI on the software engineering industry</title>
    <link href="https://artm.dev/notes/ai-and-swe/"/>
    <updated>2026-01-18T00:00:00Z</updated>
    <id>https://artm.dev/notes/ai-and-swe/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://artm.dev/&quot;&gt;← Back Home&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;On the impact of AI on the software engineering industry&lt;/h1&gt;
&lt;p&gt;
  &lt;small&gt;
    &lt;a class=&quot;small&quot; href=&quot;https://mastoshare.link/r?compose=I%20recommend%20reading%20http%3A%2F%2Fartm.dev%2Fnotes%2Fai-and-swe&quot;&gt;Share on Mastodon&lt;/a&gt;
  &lt;/small&gt;
&lt;/p&gt;
&lt;p&gt;As someone who used LLMs for coding since 2023 I&#39;ve seen insane progress with that tools. To the point of existential threat to software engineering occupation.&lt;/p&gt;
&lt;p&gt;AI generated code is extremely cheap. And currently it is used to solve problems that are relevant for the time when we didn&#39;t have extremely cheap AI generated code. Some problems won&#39;t be relevant anymore, some approaches to monetization won&#39;t be relevant anymore. If company didn&#39;t have a moat like a network effect, market dominance, huge regulatory compliance, etc — it&#39;s cooked. It feels like we are heading to the market correction in coming years.&lt;/p&gt;
&lt;h2&gt;Hiring&lt;/h2&gt;
&lt;p&gt;New companies starting today don&#39;t need new engineers in the amounts before. The founders think differently, they might need product designers and AI enabled engineers at 1-1 ratio. Designers will create high fidelity prototypes, and engineers will mostly productionize these prototypes (with help of AI of course). I believe 1-10 ratio from the previous times is over. On one hand it seems positive, as it reduces risks of starting a new business, but one the other hand the competition will be severe and many companies might just not get big enough to compete with established giants.&lt;/p&gt;
&lt;p&gt;The established giants will adapt AI coding assistance on unprecedented pace. It will help them to keep and widen their moat. The inertia will help here to keep current jobs, but hiring will be drastically reviewed. In the markets with less job protections layoffs will continue. To hire new engineers teams will have to proof that they need a new problem solver, new AI orchestrator, that currently they are at their capacity and spinning another cloud agent AI will not help.&lt;/p&gt;
&lt;h2&gt;Personal apps&lt;/h2&gt;
&lt;p&gt;Additionally to that we might not even need that many software. Like how many personalized fitness plans apps are out there? How many of them actually adapting to user needs (like traveling or recovering after sickness)? Can it be solved with AI better?&lt;/p&gt;
&lt;p&gt;It might not be the best interface and not best advice, but it is already good enough for many use cases. And critique will only serve as a direction of improvement for AI apps.&lt;/p&gt;
&lt;h2&gt;Productivity&lt;/h2&gt;
&lt;p&gt;My assumption here is that AI tools indeed increase productivity in the medium to long run, not just being useful for flashy demos. Last year I heard a lot on this, and information was a bit skeptical, meaning companies running AI pilots reported improving productivity only by 10% in best case.&lt;/p&gt;
&lt;p&gt;Will see how it will have this year, as I observed tools quality increased dramatically in practical cases (e.g. I don&#39;t care how good it is for solving PhD math problems, but I care how good it is in solving &lt;em&gt;my problems&lt;/em&gt; and it is usually solving them quite well).&lt;/p&gt;
&lt;p&gt;Also I&#39;ve seen how good local models can be, just running Mistrals 8b on my old MacBook Air M1 with 16Gb RAM — quite comparable to state-of-the-art a year ago. Other open source and open weights models also available to run on own hardware.&lt;/p&gt;
&lt;p&gt;This year I&#39;ve already seen change in opinions from the industry heavyweights like Linus Torvalds or DHH. And some other notable people like Kent Beck, Martin Fowler, the whole O&#39;Reily publishing house are already on board. The question now is not if AI tools are useful, but what impact they will have have on the industry.&lt;/p&gt;
&lt;h2&gt;Future&lt;/h2&gt;
&lt;p&gt;And it does not look good. Software industry is reminding agriculture industry before industrial revolution. We definitely need it and it will definitely grow, but we won&#39;t need that many people working there. But the problem that change is happening much faster, and people won&#39;t be able to adopt. I&#39;ve been working as a software engineer for past 17 years, but right now it feels that the industry in the current state have maybe just a couple of years left.&lt;/p&gt;
&lt;div style=&quot;width: 100%; text-align: center&quot;&gt;
  &lt;open-likes icon=&quot;heart&quot; color=&quot;#e91e1e&quot; counter=&quot;bottom&quot;&gt;&lt;/open-likes&gt;
&lt;/div&gt;
</content>
  </entry>
</feed>