<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rudolf Olah</title>
    <description>The latest articles on DEV Community by Rudolf Olah (@rudolfolah).</description>
    <link>https://dev.to/rudolfolah</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F178172%2F90fec551-5bd0-458d-9169-95867e4f9cc8.jpeg</url>
      <title>DEV Community: Rudolf Olah</title>
      <link>https://dev.to/rudolfolah</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rudolfolah"/>
    <language>en</language>
    <item>
      <title>AI for Less Popular Programming Languages</title>
      <dc:creator>Rudolf Olah</dc:creator>
      <pubDate>Sun, 28 Jun 2026 22:57:55 +0000</pubDate>
      <link>https://dev.to/rudolfolah/ai-for-less-popular-programming-languages-4jg</link>
      <guid>https://dev.to/rudolfolah/ai-for-less-popular-programming-languages-4jg</guid>
      <description>&lt;p&gt;AI agents are quite popular, from Claude Code to Cursor to Codex, they all can write code for the most popular programming languages such as TypeScript and Python. However, less popular programming languages have their own nuances that can make an AI agent spin its wheels and burn through far more tokens than necessary.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Agents vs. Emacs Lisp
&lt;/h2&gt;

&lt;p&gt;I had this happen when using OpenCode and Junie to write an Emacs plugin that had only one file. I &lt;a href="https://dev.to/rudolfolah/focused-work-distraction-free-coding-kan"&gt;sometimes use Emacs as text editor for distraction-free coding&lt;/a&gt;, in addition to &lt;a href="https://dev.to/rudolfolah/incredible-updates-in-vs-code-191-june-2024-3p100"&gt;VS Code&lt;/a&gt;, and part of the power of Emacs and other IDEs is in the plugins. Writing new plugins for Emacs or modifying the useful ones requires using the Emacs Lisp programming language.&lt;/p&gt;

&lt;h3&gt;
  
  
  Balancing Parentheses
&lt;/h3&gt;

&lt;p&gt;Where in a language like TypeScript or Python we assign variables and call functions like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;
&lt;span class="nf"&gt;hello_world&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Emacs Lisp we do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;test&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;hello-world&lt;/span&gt; &lt;span class="nv"&gt;test&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the parentheses wrap the variable assignment and the function call? That is what the AI agents kept getting tripped on. It did not matter which AI model I used, they all had issues with unbalanced parenthesis and other syntax errors. As part of the &lt;code&gt;AGENTS.md&lt;/code&gt; prompt, I mention ways to check for this with built-in function, &lt;code&gt;check-parens&lt;/code&gt; and byte compilation. Unfortunately, while the AI agents could check for this issue, they looped and burned tokens on custom scripts to pinpoint exactly where in the file there was a missing or extra bracket. Remember, this is for a one-file plugin, imagine how bad this could get with more files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://opencode.ai/" rel="noopener noreferrer"&gt;OpenCode&lt;/a&gt; with &lt;a href="https://docs.z.ai/guides/llm/glm-5.2" rel="noopener noreferrer"&gt;GLM 5.2&lt;/a&gt; wrote custom Emacs Lisp to pinpoint within the file where the missing or extra bracket could be. It rewrote the custom code to check various parts of the file. Each of those is a tool use and many, many tokens burned. The next step is to turn those custom scripts written by the AI agent into a tool to speed up the process, or a skill that shows how to use other tools to speed up the process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Packages and Dependencies
&lt;/h3&gt;

&lt;p&gt;Another issue is that the AI agents, no matter the model, favoured writing custom code. When pointing out that Emacs packages and dependencies can be added, they needed an addition to the prompt to tell the user of the plugin that the dependency needs to be installed, and to let them know if a fallback is being used.&lt;/p&gt;

&lt;p&gt;This could be helped by adding an MCP or tool that queries the Emacs package repository sources. I worked around it by searching for packages myself and then adding them to the prompt. For instance, I mainly use &lt;code&gt;ansi-term&lt;/code&gt; as the terminal in Emacs. When I asked the OpenCode AI agent to fix terminal rendering, it tried to configure and fix &lt;code&gt;ansi-term&lt;/code&gt; instead of suggesting that more modern terminals such as &lt;code&gt;eat&lt;/code&gt; and &lt;code&gt;vterm&lt;/code&gt; exist.&lt;/p&gt;

&lt;p&gt;For less popular languages that are newer and in rapid active development, the popular packages for them could change from one month to the next. Without a way for an AI model to check for the latest packages to use, the code becomes legacy as soon as it is written.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI for Less Popular Programming Languages
&lt;/h2&gt;

&lt;p&gt;Which brings us back to how do we optimize AI agents for less popular programming languages. If using AI agents to write a plugin for a text editor that has been around for decades with plenty of code examples, documentation, and tutorials runs into issues, will less popular programming languages require much more effort to be usable?&lt;/p&gt;

&lt;p&gt;Here are a few ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pack more context into the prompt.&lt;/li&gt;
&lt;li&gt;Use RAG (Retrieval Augmented Generation) to add relevant examples to the prompt.&lt;/li&gt;
&lt;li&gt;Create tools that AI agents can use to speed up linting, formatting, and compilation instead of having them "re-learn" it every time.&lt;/li&gt;
&lt;li&gt;Create skills that codify common patterns of testing, debugging, and documenting.&lt;/li&gt;
&lt;li&gt;Map one language to another. Instead of using a syntax that can trip up the AI agent, use a different syntax that can then be mapped and transformed into the other language.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>emacs</category>
      <category>programming</category>
    </item>
    <item>
      <title>Focused Work, Distraction-Free Coding</title>
      <dc:creator>Rudolf Olah</dc:creator>
      <pubDate>Sun, 24 May 2026 20:06:00 +0000</pubDate>
      <link>https://dev.to/rudolfolah/focused-work-distraction-free-coding-kan</link>
      <guid>https://dev.to/rudolfolah/focused-work-distraction-free-coding-kan</guid>
      <description>&lt;p&gt;With the age of AI agents giving everyone a heightened sense of speed in shipping code, it is valuable for engineers to focus deeply on technical work. The distraction of mobile phones and social networking have been replaced by the distraction of checking what Claude Code is doing, or asking Grok or ChatGPT questions.&lt;/p&gt;

&lt;p&gt;The idea of "writer decks", a minimalist computer, is making a comeback as a way to create a distraction-free environment to be able to have a focused work session. &lt;a href="https://veronicaexplains.net/my-first-writerdeck/" rel="noopener noreferrer"&gt;One writer set up a laptop with no GUI (no desktop or window manager), and only a text editor with a wiki system and a way to back up the files&lt;/a&gt;. You can create the same by finding a quiet spot (or wearing noise-canceling headphones), disabling the wifi on the laptop, and closing all programs so it is just you and the writing. This applies for coding, though you will need a way to look up API documentation. Having a different browser you do not typically use where every website is logged out can reduce distraction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Distraction-Free Writing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Obsidian

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Maxymillion/zen" rel="noopener noreferrer"&gt;Zen plugin for Obsidian&lt;/a&gt; is a "focus mode" for the Obsidian note-taking app.&lt;/li&gt;
&lt;li&gt;Without a plugin: Close the sidebars and only have one tab open.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Emacs

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/joostkremers/writeroom-mode" rel="noopener noreferrer"&gt;writeroom-mode&lt;/a&gt; activates fullscreen mode, hides all toolbars and the scrollbar, and disables the mode line.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/rnkn/olivetti" rel="noopener noreferrer"&gt;olivetti&lt;/a&gt; that positions the text in the middle of the screen. &lt;code&gt;M-x package-install RET olivetti&lt;/code&gt; then &lt;code&gt;M-x olivetti-mode&lt;/code&gt; to activate it.&lt;/li&gt;
&lt;li&gt;Without a plugin: Only have one buffer open, hide the scrollbar and toolbar. I usually have two buffers open for word-wrap and for taking notes in the second scratch bufer.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;neovim

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/folke/zen-mode.nvim" rel="noopener noreferrer"&gt;zen-mode&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://getfreewrite.com/" rel="noopener noreferrer"&gt;Freewrite&lt;/a&gt; has a collection of devices for distraction-free writing&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Write Code Without the Internet
&lt;/h2&gt;

&lt;p&gt;Programming environments such as Python and Ruby have the ability to print out helpful documentation. &lt;a href="https://docs.python.org/3/library/functions.html#help" rel="noopener noreferrer"&gt;Python's interactive shell has the &lt;code&gt;help()&lt;/code&gt; command to look up the functions and documentation for libraries&lt;/a&gt;. Pry is a Ruby shell that can &lt;a href="https://github.com/pry/pry#code-browsing" rel="noopener noreferrer"&gt;browse the methods in a package&lt;/a&gt; and &lt;a href="https://github.com/pry/pry#documentation-browsing" rel="noopener noreferrer"&gt;the documentation for those methods&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;PyCharm and VS Code can show auto-complete suggestions with documentation and function parameters.&lt;/p&gt;

&lt;h3&gt;
  
  
  eBooks, PDFs, HTML Saved Locally
&lt;/h3&gt;

&lt;p&gt;If you are writing bash or zsh scripts, you can &lt;a href="https://www.cs.mcgill.ca/~guide/help/man.html" rel="noopener noreferrer"&gt;use manpages to look up the documentation&lt;/a&gt;. &lt;a href="https://docs.unity3d.com/6000.3/Documentation/Manual/OfflineDocumentation.html" rel="noopener noreferrer"&gt;For Unity game development, you can download all the documentation locally&lt;/a&gt;. &lt;a href="https://doc.rust-lang.org/cargo/commands/cargo-doc.html" rel="noopener noreferrer"&gt;Rust also has a way of downloading documentation and generating documentation for all packages used in a project&lt;/a&gt;. Many programming libraries offer a way to download HTML or PDF versions of their documentation.&lt;/p&gt;

&lt;p&gt;You can buy and download PDFs and ebooks. While books do not have the full API for a library or package you are using, they contain code snippets that can be built upon.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/EbookFoundation/free-programming-books" rel="noopener noreferrer"&gt;Free Programming Books&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://freecomputerbooks.com/" rel="noopener noreferrer"&gt;Free Computer Books&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.gnu.org/manual/manual.html" rel="noopener noreferrer"&gt;GNU Manuals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leanpub.com/store/books?category=&amp;amp;sort=free_without_membership&amp;amp;lng=en" rel="noopener noreferrer"&gt;Free eBooks from Leanpub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Offline API Viewers
&lt;/h3&gt;

&lt;p&gt;There are offline API documentation viewers that display "docsets":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://kapeli.com/dash" rel="noopener noreferrer"&gt;Dash for MacOS (proprietary, paid)&lt;/a&gt; has the documentation for over 200 APIs and over 100 cheat sheets, and the ability to generate documentation for packages for Swift, Python, Ruby, PHP, Java, Go, Rust, Scala, Dart, Haskell, Hex, Clojure.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://velocity.silverlakesoftware.com/" rel="noopener noreferrer"&gt;Velocity for Windows (proprietary, paid)&lt;/a&gt; does much the same as Dash does.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/zealdocs/zeal/" rel="noopener noreferrer"&gt;Zeal (open source, free)&lt;/a&gt; is a free/libre/open source docset viewer that runs on Windows, Linux, and BSD. While it is possible to &lt;a href="https://github.com/zealdocs/zeal/wiki/Build-Instructions-for-macOS" rel="noopener noreferrer"&gt;build Zeal for MacOS&lt;/a&gt;, the recommendation is to download Dash.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/freeCodeCamp/devdocs" rel="noopener noreferrer"&gt;DevDocs (open source, free)&lt;/a&gt; is a local offline documentation viewer. &lt;a href="https://devdocs.io/" rel="noopener noreferrer"&gt;There is a hosted version that can be used offline in a web browser&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>productivity</category>
      <category>writing</category>
      <category>documentation</category>
    </item>
    <item>
      <title>Engineering Management Tools: My Top Picks</title>
      <dc:creator>Rudolf Olah</dc:creator>
      <pubDate>Sun, 01 Mar 2026 14:35:00 +0000</pubDate>
      <link>https://dev.to/rudolfolah/engineering-management-tools-my-top-picks-1hbh</link>
      <guid>https://dev.to/rudolfolah/engineering-management-tools-my-top-picks-1hbh</guid>
      <description>&lt;p&gt;Engineering managers have a different workflow than ICs (individual contributors), and though we often use the same tools, their layout and integration into our workflow are different.&lt;/p&gt;

&lt;h1&gt;
  
  
  Calendar: Google
&lt;/h1&gt;

&lt;p&gt;The most essential tool is the calendar, and &lt;a href="https://calendar.google.com/" rel="noopener noreferrer"&gt;Google Calendar&lt;/a&gt; is great. As a manager, creating recurring events such as daily standups or bi-weekly sprint planning meetings is standard.&lt;/p&gt;

&lt;p&gt;Creating and subscribing to multiple calendars is a good feature. Depending on the number of projects and teams, creating a separate calendar for recurring events can make sense. Many organizations have a separate calendar for engineering department meetings and presentations. &lt;em&gt;Any meetings affecting multiple departments or teams within a department could be on a shared calendar. Office hours are another candidate for a shared calendar across the organization.&lt;/em&gt; Multiple shared calendars are useful in keeping the main calendar manageable.&lt;/p&gt;

&lt;p&gt;For ICs, setting up “focus time” in the calendar is a good way to let engineering managers know when they can schedule meetings to avoid interrupting focused work time. Subscribing to multiple calendars is also great for keeping track of holidays and out-of-office time, which can affect capacity planning and on-call schedules.&lt;/p&gt;

&lt;h1&gt;
  
  
  Tasks, to-dos: Todoist, Google Tasks
&lt;/h1&gt;

&lt;p&gt;There are many task management and to-do list tools out there. Previously, I used &lt;a href="https://todoist.com/" rel="noopener noreferrer"&gt;Todoist&lt;/a&gt; for task management and tried out a few different tools.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The best to-do list is close to the other tools you use for managing a team.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you live in Google Calendar, then &lt;a href="https://tasks.google.com/" rel="noopener noreferrer"&gt;Google Tasks&lt;/a&gt; is a good choice since it integrates directly into the calendar, and like calendars, you can create multiple to-do lists. You can set tasks to repeat, a good reminder that appears in the calendar without taking up a block of time. &lt;em&gt;Tasks are a good reminder for various preparation work for project planning and roadmapping.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Notes: Notion, Obsidian, Google Docs, Slack
&lt;/h1&gt;

&lt;p&gt;For short-form notes, what I’ve found works best is using whichever tool is available. If I’m in a meeting on Google Meet or looking at the calendar, I can use Google Keep for notes. If I’m in a Slack huddle or see something in a channel or thread that I want to write a note about, I use the “Direct Message to self” feature of Slack as a note-to-self. I use &lt;a href="https://www.notion.so/" rel="noopener noreferrer"&gt;Notion&lt;/a&gt; or &lt;a href="https://obsidian.md/" rel="noopener noreferrer"&gt;Obsidian&lt;/a&gt; for long-form notes or for turning short notes into long-form ones. &lt;em&gt;Writing notes and creating links between them is very useful. The links between notes make it easy to refer to past notes about meetings or additional notes about documents.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One of my workflows involves using Slack and Notion. I use Slack for a summary that can be copied/pasted to various channels and threads and Notion for a longer-form note with more details that I rewrite later for different formats (e.g., a technical spec, engineering brief, a company-wide presentation, or during a 1:1). The same applies to Teams, Google Docs, or other tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Obsidian Plugins
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://obsidian.md/plugins" rel="noopener noreferrer"&gt;There are many plugins for Obsidian&lt;/a&gt; to help you with note-taking and creating a knowledge base:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/delashum/obsidian-checklist-plugin" rel="noopener noreferrer"&gt;Checklist&lt;/a&gt;: This plugin gathers all checklists, to-do lists, and tasks across all pages and puts them in the sidebar. It makes it easy to create checklists in daily notes and keep track of which ones are completed later.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/lynchjames/note-refactor-obsidian" rel="noopener noreferrer"&gt;Note Refactor&lt;/a&gt;: This plugin enables you to select text and extract it into a new page. Do you have a long page with many notes about different topics written while you were in the flow? That’s no problem; you can branch out into new pages with the Note Refactor plugin.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/liamcain/obsidian-periodic-notes" rel="noopener noreferrer"&gt;Periodic Notes&lt;/a&gt;: This plugin will create weekly, monthly, quarterly and yearly notes for you at the click of a button. This makes it easy to summarize your week or month. As an engineering manager, I found that a monthly list of what the team got done last month and what was upcoming was great for communicating the value the team was delivering. The quarterly notes can tie into organizational goals.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://florianwoelki.github.io/obsidian-iconize/" rel="noopener noreferrer"&gt;Iconize&lt;/a&gt;: add icons to files and folders; this plugin is good for highlighting very important pages with a visual cue.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  1:1s and Career Goals: Lattice
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://lattice.com/" rel="noopener noreferrer"&gt;Lattice&lt;/a&gt; is incredible for this. The software formalizes what’s usually in spreadsheets or in document templates. For 1:1s, I encourage ICs to add topics; it’s a checklist to ensure we cover each topic. There’s also a checklist of “action items” for the IC and myself to ensure we follow up with concrete actions based on our conversations. Another great feature is the career track and career goals. The org can set up the templates for the career track, and I can work with an IC to turn those into specific goals. &lt;em&gt;Lattice makes it far easier to collaborate with an IC on their goals and to ensure there’s accountability from myself and the IC on accomplishing things that not only move the company or projects forward, they move the IC forward in their career.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Diagrams: Lucidchart, Drawio
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://www.lucidchart.com/pages/" rel="noopener noreferrer"&gt;Lucidchart&lt;/a&gt; and &lt;a href="https://www.drawio.com/" rel="noopener noreferrer"&gt;Drawio&lt;/a&gt; are great diagramming tools. Lucidchart has many great templates to use as starting points, such as user journeys, use case diagrams, sprint retrospectives, roadmaps, and cloud architecture diagrams. Drawio also contains many great templates and can be downloaded locally. I primarily use Drawio for engineering architecture diagrams.&lt;/p&gt;

&lt;p&gt;Both Lucidchart and Drawio can be used for quickly sketching out diagrams. The user interfaces are well-designed. &lt;em&gt;Lucidchart has a good collaboration mode, where you can share a diagram and build it out with your team.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  SQL Database and Codebase Tools: JetBrains
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://www.jetbrains.com/" rel="noopener noreferrer"&gt;JetBrains&lt;/a&gt; suite for professionals — from &lt;a href="https://www.jetbrains.com/idea/" rel="noopener noreferrer"&gt;IntelliJ IDEA&lt;/a&gt; to &lt;a href="https://www.jetbrains.com/pycharm/" rel="noopener noreferrer"&gt;PyCharm&lt;/a&gt; to &lt;a href="https://www.jetbrains.com/datagrip/" rel="noopener noreferrer"&gt;https://www.jetbrains.com/datagrip/&lt;/a&gt;, each tool makes it easy for engineering managers to explore and understand a code base, data models, and database schema.&lt;/p&gt;

&lt;p&gt;I use IntelliJ IDEA to get up to speed on a codebase by searching the code or clicking through to see the usage and definitions of methods and classes. The integrated debugger works great for Python and Ruby codebases, and the autocomplete and code navigation for TypeScript codebases are fantastic.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The database tools in JetBrains IDEs allow you to connect to various data sources, from DynamoDB to PostgreSQL to ClickHouse to Redis and SQLite. The tool lets you run queries and edit records and rows within the editor, which is good for checking data and modifying it during testing. It’s also possible to sort, filter, and search the data locally and to compare data.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;While there are other database tools, having an integrated database viewer in your IDE that sits beside the code that creates and updates that data is very useful.&lt;/p&gt;

&lt;h1&gt;
  
  
  Password Management: 1Password, Bitwarden
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://1password.com/" rel="noopener noreferrer"&gt;1Password&lt;/a&gt; and &lt;a href="https://bitwarden.com/" rel="noopener noreferrer"&gt;Bitwarden&lt;/a&gt; are solid choices for password management. Both feature vaults that can be shared with team members and across departments. &lt;em&gt;They both feature Single Sign On, which enables your IT department to control access to the vaults.&lt;/em&gt; Bitwarden is also open source, with the core code licensed under the GPL.&lt;/p&gt;

&lt;p&gt;For software engineers, IT, and DevOps, the CLI, and APIs offered by 1Password and Bitwarden allow for the automation of secrets management and vault management.&lt;/p&gt;

</description>
      <category>management</category>
      <category>leadership</category>
    </item>
    <item>
      <title>Learning Coding from High Skilled Professional Programmers</title>
      <dc:creator>Rudolf Olah</dc:creator>
      <pubDate>Sat, 06 Sep 2025 14:29:54 +0000</pubDate>
      <link>https://dev.to/rudolfolah/learning-coding-from-high-skilled-professional-programmers-28cj</link>
      <guid>https://dev.to/rudolfolah/learning-coding-from-high-skilled-professional-programmers-28cj</guid>
      <description>&lt;p&gt;So, to be a prolific programmer, what does it take? In the past, we would have to sit beside the programmer or have them describe their workflow to us. With streaming platforms, we can observe how software engineers approach problem-solving and learn from their approaches, which can help improve our own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Live Stream Programming
&lt;/h2&gt;

&lt;p&gt;I was watching a few videos from &lt;a href="https://www.youtube.com/@geohotarchive/videos?view=0&amp;amp;sort=dd&amp;amp;shelf_id=2" rel="noopener noreferrer"&gt;the George Hotz streaming archives on YouTube&lt;/a&gt;. George Hotz, more popularly known as geohot, is a prolific programmer who has jailbroken the iPhone and PlayStation 3, built open source semi-autonomous car software, and, more recently, founded a machine learning startup. What's interesting is his approach to tackling challenges, such as building a neural net chess engine.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/RFaFmkCEGEs"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Coding Questions
&lt;/h2&gt;

&lt;p&gt;Another interesting selection to watch is from &lt;a href="https://www.youtube.com/c/neetcode" rel="noopener noreferrer"&gt;Neetcode, videos that walk through solutions to LeetCode problems&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For example, in one video, it begins with a review of the question, followed by a diagram to map out the inputs and steps to achieve the expected output, and then the coding is presented.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/fei4bJQdBUQ"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;No matter the question, this is the overall process for coding interviews. Where it gets interesting is the code implementation. As it's walked through, take note of where there's a pause and where the solution is checked and stepped through. The debugger can help step through code.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Pair Programming
&lt;/h2&gt;

&lt;p&gt;Gene Kim paired with Steve Yegge, an incredible engineer and writer, on chat-oriented programming, basically AI pair programming. &lt;a href="https://steve-yegge.medium.com/" rel="noopener noreferrer"&gt;You can find Yegge's writings from 2005 to the present, and they're all good stuff&lt;/a&gt;. Within many essays, there are a few lines that can have a significant impact on how you write code and approach problem-solving. &lt;a href="https://sites.google.com/site/steveyegge2/innovation-101" rel="noopener noreferrer"&gt;In Yegge's essay "Innovation 101," for example, there's a section on why he writes essays&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The video walkthrough effectively demonstrates the process Yegge uses to work with an AI copilot and how knowledge transfer and training occur.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/jpzv-_YQf6k"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Open Source History Class
&lt;/h2&gt;

&lt;p&gt;There is a wealth of open source code out there to learn from. There are even two great books that discuss the architecture, system design, and code for various open-source projects. However, this focuses on the code at a certain point in time.&lt;/p&gt;

&lt;p&gt;An interesting way to learn is to attend history class. By going through the past commits in a project, you can see how the code got into its current shape.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/simonw/llm" rel="noopener noreferrer"&gt;&lt;code&gt;llm&lt;/code&gt; command line tool&lt;/a&gt; provides an instructive example. The code for &lt;code&gt;llm&lt;/code&gt; is written in Python and &lt;a href="https://github.com/simonw/llm/commits/main/?since=2023-04-01&amp;amp;until=2023-04-01" rel="noopener noreferrer"&gt;began with prototyping, with the first commits made on April 1st, 2023&lt;/a&gt;. &lt;a href="https://github.com/simonw/llm/commit/3f89cb9609ee504654692faab4b404427454d1ff" rel="noopener noreferrer"&gt;The first commit sets up a GitHub Action workflow to test and publish the Python package; it also has one command.&lt;/a&gt; From the start, it's possible to use the tool, test it and publish a package to distribute it more widely.&lt;/p&gt;

&lt;p&gt;The initial design of &lt;code&gt;llm&lt;/code&gt; is documented through the open GitHub issues. One of the issues opened was "command for browsing captured logs", and it contains a few ideas on implementation. The expectation is revised and then closed out with a pull request. What's interesting is that this is still the initial part of the journey to building a new command-line tool. The issues are used as documentation and then quickly closed out with an implementation.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/QUXQNi6jQ30"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Let's think about the alternatives to this. If we start a project and create a comprehensive list of tasks using issues, we may reconsider our approach and close many of the issues. What about beginning with no issues listed and only documenting through pull requests? This approach can work; however, the design will already be in place, and we may not see the complete set of techniques that have been tried. By opening issues that are going to be worked on soon (within a day or within a week) and commenting on them, a trail of code and design breadcrumbs is created, along with a place to document the various approaches tried. Commenting on the design and leaving notes for yourself (and your team) about considerations that changed the design helps you in the future when looking back at how and why the implementation is the way it is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Books and Courses
&lt;/h2&gt;

&lt;p&gt;Books and video courses from highly skilled engineers are worthwhile to read and watch, because they can contain insights on how to improve the effectiveness of your problem solving skills.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.informit.com/imprint/series_detail.aspx?ser=3511331" rel="noopener noreferrer"&gt;Zed Shaw has written a few books on learning to program in Ruby, Python, and C&lt;/a&gt;, plus video courses on those.  In &lt;a href="http://learnrubythehardway.org/book/ex7.html" rel="noopener noreferrer"&gt;"Learn Ruby the Hard Way", exercise 7&lt;/a&gt; has a set of study drills: write a comment for each line of code, read it out loud to find errors, keep track of any mistakes you've made in the code, try to avoid repeating the mistakes in the next exercise. This is valuable advice and can help clarify your understanding of the code base and when reading through merge requests and other people's code. Many books and video courses contain these valuable productivity tips and insights about the author's workflow but you may need to dig through to find them.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Your Software Engineer Coworkers
&lt;/h2&gt;

&lt;p&gt;In the above, I've covered the ways you can learn from great developers who develop in open source or stream their coding sessions. You can apply some of that to learning from your coworkers. Read through their code and code commits, read through what they comment on code reviews, and ask them about their workflow. They may utilize many keyboard shortcuts or use specific extensions and plugins for their IDE. When they plan their work out, they may use a TODO comment in the code or a simple list in a notepad or another method. Whatever their strategies, processes and systems thinking they use, you can ask and learn from them.&lt;/p&gt;

</description>
      <category>learning</category>
      <category>productivity</category>
      <category>programming</category>
      <category>resources</category>
    </item>
    <item>
      <title>Debian GNU/Linux in VirtualBox on MacOS M1 to Practice Kubernetes</title>
      <dc:creator>Rudolf Olah</dc:creator>
      <pubDate>Sun, 11 May 2025 16:45:13 +0000</pubDate>
      <link>https://dev.to/rudolfolah/debian-gnulinux-in-virtualbox-on-macos-m1-to-practice-kubernetes-4dad</link>
      <guid>https://dev.to/rudolfolah/debian-gnulinux-in-virtualbox-on-macos-m1-to-practice-kubernetes-4dad</guid>
      <description>&lt;p&gt;When studying for the CKAD (Certified Kubernetes Application Developer) exam, I practiced using the &lt;a href="https://killercoda.com/killer-shell-ckad" rel="noopener noreferrer"&gt;Killer Coda CKAD scenarios&lt;/a&gt; and the &lt;a href="https://killer.sh/ckad" rel="noopener noreferrer"&gt;Killer Shell Exam Simulator&lt;/a&gt;. The exam simulator sets up a remote Linux XFCE desktop environment with a browser and terminal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/rudolfolah/kubernetes-and-docker-desktop-for-fast-local-development-19c3"&gt;When practicing various Kubernetes commands, I used minikube and colima to set up Kubernetes with Docker&lt;/a&gt;. However, this isn’t the same experience as navigating a desktop with a specific resolution, set of tools, and slightly different keyboard shortcuts. To get the same experience, without being limited to a few exam simulator sessions, I looked into VirtualBox and Vagrant. VirtualBox is a virtualization system, and Apple Silicon chips became supported by VirtualBox in September 2024.&lt;/p&gt;

&lt;h2&gt;
  
  
  VirtualBox 7.1
&lt;/h2&gt;

&lt;p&gt;Here are the steps I took to run a Debian desktop environment in VirtualBox:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.virtualbox.org/wiki/Downloads" rel="noopener noreferrer"&gt;Download version 7.1+ of VirtualBox for &lt;code&gt;macOS / Apple Silicon hosts&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cdimage.debian.org/debian-cd/current/arm64/iso-cd/" rel="noopener noreferrer"&gt;Download an ARM64 ISO for the Debian GNU/Linux distribution&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Install VirtualBox&lt;/li&gt;
&lt;li&gt;Start VirtualBox&lt;/li&gt;
&lt;li&gt;Drop the Debian &lt;code&gt;.iso&lt;/code&gt; file onto VirtualBox

&lt;ol&gt;
&lt;li&gt;You can “skip the unattended installation”&lt;/li&gt;
&lt;li&gt;Set the hard disk size, base memory, and number of processors&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;Adjust the amount of storage in the settings for the virtual machine&lt;/li&gt;

&lt;li&gt;Go to Settings &amp;gt; Display for the virtual machine and change the “Scale Factor” from 100% to 200% (or larger)&lt;/li&gt;

&lt;li&gt;Go to Settings &amp;gt; Network and change “Attached to” from NAT to Bridged Adapter, the Adapter Type to “Intel PRO/1000 MT Desktop (82540EM),” and the Promiscuous Mode to “Allow All.” &lt;em&gt;The network settings must be fixed if the network mirrors do not work correctly during the installation process.&lt;/em&gt;
&lt;/li&gt;

&lt;li&gt;Start the virtual machine and &lt;a href="https://www.debian.org/releases/stable/arm64/" rel="noopener noreferrer"&gt;follow the usual steps for a Debian installation&lt;/a&gt;
&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4wpvwihacqmknuaqiiqp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4wpvwihacqmknuaqiiqp.png" alt=" " width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For Kubernetes, you can install kubectl and minikube via apt-get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/#install-using-native-package-management" rel="noopener noreferrer"&gt;https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/#install-using-native-package-management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://minikube.sigs.k8s.io/docs/start/?arch=%2Flinux%2Farm64%2Fstable%2Fdebian+package" rel="noopener noreferrer"&gt;https://minikube.sigs.k8s.io/docs/start/?arch=%2Flinux%2Farm64%2Fstable%2Fdebian+package&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# kubectl&lt;/span&gt;
curl &lt;span class="nt"&gt;-LO&lt;/span&gt; &lt;span class="s2"&gt;"https://dl.k8s.io/release/&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; https://dl.k8s.io/release/stable.txt&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/bin/linux/arm64/kubectl"&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;u+x ./kubectl

&lt;span class="c"&gt;# minikube&lt;/span&gt;
curl &lt;span class="nt"&gt;-LO&lt;/span&gt; https://github.com/kubernetes/minikube/releases/latest/download/minikube_latest_arm64.deb
&lt;span class="nb"&gt;sudo &lt;/span&gt;dpkg &lt;span class="nt"&gt;-i&lt;/span&gt; minikube_latest_arm64.deb

&lt;span class="c"&gt;# test the installation&lt;/span&gt;
minikube start
kubectl get po &lt;span class="nt"&gt;-A&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may need to unset the following VirtualBox global setting because it could cause issues with spinning up a virtual machine on Apple Silicon:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;VBoxManage setextradata global &lt;span class="s2"&gt;"VBoxInternal/Devices/pcbios/0/Config/DebugLevel"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Vagrant
&lt;/h2&gt;

&lt;p&gt;Many of the above steps are manual and can be done through the VirtualBox interface. You can use &lt;a href="https://developer.hashicorp.com/vagrant" rel="noopener noreferrer"&gt;Vagrant&lt;/a&gt; to automate the steps. Vagrant makes it simpler to reproduce a working virtual machine environment.&lt;/p&gt;

&lt;p&gt;Install Vagrant using &lt;a href="https://brew.sh/" rel="noopener noreferrer"&gt;Homebrew&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew tap hashicorp/tap
brew &lt;span class="nb"&gt;install &lt;/span&gt;hashicorp/tap/hashicorp-vagrant
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the manual steps above to change the storage and network settings, this is how you would automate the configuration with Vagrant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"virtualbox"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;customize&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"storagectl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"--name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"VirtIO Controller"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"--hostiocache"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"on"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;customize&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"modifyvm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"--nictype1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"82540EM"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DNS resolution can be provisioned with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;provision&lt;/span&gt; &lt;span class="s2"&gt;"shell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;inline: &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;SHELL&lt;/span&gt;&lt;span class="sh"&gt;
  echo 'DNS=8.8.8.8' &amp;gt;&amp;gt; /etc/systemd/resolved.conf
  systemctl restart systemd-resolved
&lt;/span&gt;&lt;span class="no"&gt;SHELL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>virtualmachine</category>
      <category>virtualbox</category>
      <category>kubernetes</category>
      <category>docker</category>
    </item>
    <item>
      <title>Task Runners for Projects</title>
      <dc:creator>Rudolf Olah</dc:creator>
      <pubDate>Thu, 26 Dec 2024 18:43:04 +0000</pubDate>
      <link>https://dev.to/rudolfolah/task-runners-for-projects-57gi</link>
      <guid>https://dev.to/rudolfolah/task-runners-for-projects-57gi</guid>
      <description>&lt;p&gt;Many projects need to run arbitrary commands and tasks.&lt;/p&gt;

&lt;p&gt;In some projects, this is done by creating commands within an IDE such as &lt;a href="https://www.jetbrains.com/help/idea/running-applications.html" rel="noopener noreferrer"&gt;JetBrains Intellij&lt;/a&gt; or &lt;a href="https://code.visualstudio.com/docs/editor/tasks" rel="noopener noreferrer"&gt;VS Code&lt;/a&gt; for common tasks such as running tests or building a project.&lt;/p&gt;

&lt;p&gt;JavaScript and TypeScript projects can use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gruntjs.com/" rel="noopener noreferrer"&gt;Grunt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gulpjs.com/" rel="noopener noreferrer"&gt;Gulp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.npmjs.com/cli/v8/using-npm/scripts" rel="noopener noreferrer"&gt;package.json &lt;code&gt;scripts&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Depending on familiarity with the tools, the simplest task runners are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;shell scripts&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.gnu.org/software/make/manual/make.html" rel="noopener noreferrer"&gt;Makefiles&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other task runners:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/casey/just" rel="noopener noreferrer"&gt;just&lt;/a&gt;, stores tasks in a file similar to Makefile format, has CLI completion support and loads &lt;code&gt;.env&lt;/code&gt; files&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://mise.jdx.dev/getting-started.html" rel="noopener noreferrer"&gt;mise&lt;/a&gt;, task runner that uses TOML files or standalone files&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>makefile</category>
      <category>just</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Keeping Skills Up-to-Date as a Software Developer</title>
      <dc:creator>Rudolf Olah</dc:creator>
      <pubDate>Thu, 19 Sep 2024 02:34:44 +0000</pubDate>
      <link>https://dev.to/rudolfolah/keeping-skills-up-to-date-as-a-software-developer-l69</link>
      <guid>https://dev.to/rudolfolah/keeping-skills-up-to-date-as-a-software-developer-l69</guid>
      <description>&lt;p&gt;Software developers need to stay up-to-date within their specialization and in the general field. If you’re a frontend web dev, you will want to keep up with how frameworks are evolving. If you’re into devops, you’ll need to be up-to-date on the latest build and deploy tools and IaC.&lt;/p&gt;

&lt;p&gt;These are my recommended sites for keeping your skills up to date:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.oreilly.com/online-learning/" rel="noopener noreferrer"&gt;O’Reilly Online Learning&lt;/a&gt;: features many books, audio books, and video courses along with sandboxes for running code.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.linkedin.com/learning" rel="noopener noreferrer"&gt;LinkedIn Learning&lt;/a&gt;: offers a wide range of video courses and is good for a refresher or for getting started quickly with a new tech. It also has business-oriented content if you need to get up to speed on non-coding subjects.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://codingchallenges.fyi/" rel="noopener noreferrer"&gt;Coding Challenges&lt;/a&gt; features a list of self-directed challenges that you can code in any programming language. Some of the challenges include building your own Memcached server and building a Docker container. There’s a nice variety of challenges and by working through them you can learn about data structures, algorithms, and the specifics of a programming language, library or framework.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://roadmap.sh/" rel="noopener noreferrer"&gt;roadmap.sh - Developer Roadmaps&lt;/a&gt; gives you direction to learn parts of the tech stacks for different positions in the field. They also feature lists of projects you can work on to upgrade your skills.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://learning.acm.org/skills-bundle" rel="noopener noreferrer"&gt;Association for Computing Machinery: ACM members get access to O’Reilly Online Learning, Skillsoft Percipio, and Pluralsight.&lt;/a&gt; You also get access to the ACM Digital Library which contains a wealth of information of computer science and software engineering research.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>learning</category>
      <category>beginners</category>
    </item>
    <item>
      <title>The Power of Tries, Data Structure Optimization in Emacs</title>
      <dc:creator>Rudolf Olah</dc:creator>
      <pubDate>Thu, 19 Sep 2024 02:10:33 +0000</pubDate>
      <link>https://dev.to/rudolfolah/the-power-of-tries-data-structure-optimization-in-emacs-2abb</link>
      <guid>https://dev.to/rudolfolah/the-power-of-tries-data-structure-optimization-in-emacs-2abb</guid>
      <description>&lt;p&gt;&lt;em&gt;The following article, originally published in 2009, was a combination of both prose and code, written in Emacs with a tool called &lt;a href="https://github.com/nrnrnr/noweb" rel="noopener noreferrer"&gt;"noweb"&lt;/a&gt;. I have updated it to not use the special syntax of TeX and noweb. Noweb is a &lt;a href="https://en.wikipedia.org/wiki/Literate_programming" rel="noopener noreferrer"&gt;"literate programming" tool, where the usual interplay between comments and code is changed into a complex layering of code and explanations and deep-dives&lt;/a&gt;. With the noweb tool, you can export the code into an assembled ordering. This prose/code text can be run through the noweb tool and produce an emacs lisp file. The code that's exported was in blocks that begin with &lt;code&gt;&amp;lt;&amp;lt;&amp;gt;&amp;gt;=&lt;/code&gt; and end with &lt;code&gt;@&lt;/code&gt;. This updated version includes the assembled code at the end. &lt;a href="https://www.smashingmagazine.com/2009/11/zen-coding-a-new-way-to-write-html-code/" rel="noopener noreferrer"&gt;Zencoding was a way to rapidly generate HTML using CSS selectors.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  zencoding-trie
&lt;/h2&gt;

&lt;p&gt;To improve the performance of &lt;code&gt;zencoding-mode&lt;/code&gt;, caching/memoization of previously entered input needed to be done. At first, a naive hash-table implementation was chosen and this greatly cut down the speed with which zencoding generated HTML. Then it was realized that a trie data structure would be more efficient as the hash-table was storing partially entered input as well. A trie could store the partially entered data when needed and use less space than a hash-table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Trie" rel="noopener noreferrer"&gt;A trie is a tree where each node represents a character in a string&lt;/a&gt;. When you reach a leaf node, a word or sentence is spelled out. Each node can contain a value.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Important: the key is not stored in the node, but in the parent of that node.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The only operations we require for our trie are retrieve value and insert value.&lt;/p&gt;

&lt;p&gt;A node is a sequence of two elements. The first element is some value, in our case the cached version of zencoding's AST (Abstract Syntax Tree) output. If this is NIL then there is no value stored in this node. The second element is a sequence of branches (more nodes).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;make-zencoding-trie-node&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="s"&gt;"Creates a vector of two elements. The first element is some value, the second element is a character table for storing more branches of the trie. The value is initially NIL."&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;vector&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;make-char-table&lt;/span&gt; &lt;span class="ss"&gt;'trie-table&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need some functions for setting and getting the values of our node type. All of the functions accept a node as an argument which must be a sequence type with at least two elements. The &lt;code&gt;value&lt;/code&gt; argument can be anything, typically something non-NIL. The &lt;code&gt;branch-key&lt;/code&gt; argument must be a character.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;zencoding-trie-node-value&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;aref&lt;/span&gt; &lt;span class="nv"&gt;node&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;zencoding-trie-node-set-value&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;node&lt;/span&gt; &lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;aset&lt;/span&gt; &lt;span class="nv"&gt;node&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;zencoding-trie-node-branches&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;aref&lt;/span&gt; &lt;span class="nv"&gt;node&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;zencoding-trie-node-branch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;node&lt;/span&gt; &lt;span class="nv"&gt;branch-key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;aref&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;zencoding-trie-node-branches&lt;/span&gt; &lt;span class="nv"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;branch-key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need a function to access a particular branch in a node. If the branch does not exist, it will be created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;zencoding-trie-node-create-branch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;node&lt;/span&gt; &lt;span class="nv"&gt;branch-key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;aset&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;zencoding-trie-node-branches&lt;/span&gt; &lt;span class="nv"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;branch-key&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;make-zencoding-trie-node&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;zencoding-trie-node-brancher&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;node&lt;/span&gt; &lt;span class="nv"&gt;branch-key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;branch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;aref&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;zencoding-trie-node-branches&lt;/span&gt; &lt;span class="nv"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;branch-key&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;branch&lt;/span&gt;
    &lt;span class="nv"&gt;branch&lt;/span&gt;
      &lt;span class="c1"&gt;;; branch doesn't exist, so create it and then return it.&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;zencoding-trie-node-create-branch&lt;/span&gt; &lt;span class="nv"&gt;node&lt;/span&gt; &lt;span class="nv"&gt;branch-key&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The retrieval function is given a string &lt;code&gt;s&lt;/code&gt; to search for and the &lt;code&gt;trie&lt;/code&gt; to search within. Where &lt;code&gt;n&lt;/code&gt; is the length of the string &lt;code&gt;s&lt;/code&gt;, the argument &lt;code&gt;i&lt;/code&gt; is bounded like so: &lt;code&gt;0 &amp;lt; i &amp;lt;= n&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We are moving forward through a string till we reach its end. The current character is &lt;code&gt;s[i - 1]&lt;/code&gt;. If the current node we are visiting in the trie contains the current character as one of its branches, then we have to visit it.&lt;/p&gt;

&lt;p&gt;When the end of &lt;code&gt;s&lt;/code&gt; has been reached, &lt;code&gt;i = n&lt;/code&gt;, then we have reached the correct node and can return it (it will be created if it doesn't already exist). Before this, we will have to keep on searching for the right node.&lt;/p&gt;

&lt;p&gt;If we reach the end of the trie before the correct node has been found, we return NIL.&lt;/p&gt;

&lt;p&gt;The traverse function is a generalized version which includes a function argument, &lt;code&gt;f&lt;/code&gt;, telling us how to select the next branch to visit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;zencoding-trie-traverse&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;s&lt;/span&gt; &lt;span class="nv"&gt;trie&lt;/span&gt; &lt;span class="nv"&gt;i&lt;/span&gt; &lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;null&lt;/span&gt; &lt;span class="nv"&gt;trie&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="no"&gt;nil&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;branch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;funcall&lt;/span&gt; &lt;span class="nv"&gt;f&lt;/span&gt; &lt;span class="nv"&gt;trie&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;aref&lt;/span&gt; &lt;span class="nv"&gt;s&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;1-&lt;/span&gt; &lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;null&lt;/span&gt; &lt;span class="nv"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;i&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;length&lt;/span&gt; &lt;span class="nv"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
      &lt;span class="nv"&gt;branch&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;zencoding-trie-traverse&lt;/span&gt; &lt;span class="nv"&gt;s&lt;/span&gt; &lt;span class="nv"&gt;branch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;1+&lt;/span&gt; &lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;zencoding-trie-retrieve&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;s&lt;/span&gt; &lt;span class="nv"&gt;trie&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;zencoding-trie-traverse&lt;/span&gt; &lt;span class="nv"&gt;s&lt;/span&gt; &lt;span class="nv"&gt;trie&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="ss"&gt;'zencoding-trie-node-branch&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we have a function for inserting a string and its value into a trie. The pre-conditions are that &lt;code&gt;s&lt;/code&gt; is a type of string, &lt;code&gt;value&lt;/code&gt; and &lt;code&gt;trie&lt;/code&gt; are non-NIL.&lt;/p&gt;

&lt;p&gt;We use the previously defined retrieve operation to find the final node and then insert the &lt;code&gt;value&lt;/code&gt; there. Fortunately for us, the retrieve operation takes care of creating new branches if necessary.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;zencoding-trie-insert&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;s&lt;/span&gt; &lt;span class="nv"&gt;value&lt;/span&gt; &lt;span class="nv"&gt;trie&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="s"&gt;"`s' is the string used to navigate the trie (each character is a node in the trie). `value' is the value we want to insert. `trie' is the node where we start the insertion."&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;zencoding-trie-node-set-value&lt;/span&gt;
   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;zencoding-trie-traverse&lt;/span&gt; &lt;span class="nv"&gt;s&lt;/span&gt; &lt;span class="nv"&gt;trie&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="ss"&gt;'zencoding-trie-node-brancher&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final file looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="c1"&gt;;;; zencoding-trie.el --- A trie data structure built for zencoding-mode&lt;/span&gt;
&lt;span class="c1"&gt;;;&lt;/span&gt;
&lt;span class="c1"&gt;;; Copyright (C) 2009, Rudolf Olah&lt;/span&gt;
&lt;span class="c1"&gt;;;&lt;/span&gt;
&lt;span class="c1"&gt;;; Author: Rudolf Olah&lt;/span&gt;
&lt;span class="c1"&gt;;;&lt;/span&gt;
&lt;span class="c1"&gt;;; This file is free software; you can redistribute it and/or modify&lt;/span&gt;
&lt;span class="c1"&gt;;; it under the terms of the GNU General Public License as published by&lt;/span&gt;
&lt;span class="c1"&gt;;; the Free Software Foundation; either version 3, or (at your option)&lt;/span&gt;
&lt;span class="c1"&gt;;; any later version.&lt;/span&gt;
&lt;span class="c1"&gt;;;&lt;/span&gt;
&lt;span class="c1"&gt;;; This file is distributed in the hope that it will be useful,&lt;/span&gt;
&lt;span class="c1"&gt;;; but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;/span&gt;
&lt;span class="c1"&gt;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;/span&gt;
&lt;span class="c1"&gt;;; GNU General Public License for more details.&lt;/span&gt;
&lt;span class="c1"&gt;;;&lt;/span&gt;
&lt;span class="c1"&gt;;; You should have received a copy of the GNU General Public License&lt;/span&gt;
&lt;span class="c1"&gt;;; along with GNU Emacs; see the file COPYING.  If not, write to&lt;/span&gt;
&lt;span class="c1"&gt;;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,&lt;/span&gt;
&lt;span class="c1"&gt;;; Boston, MA 02110-1301, USA.&lt;/span&gt;
&lt;span class="c1"&gt;;;&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;make-zencoding-trie-node&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="s"&gt;"Creates a vector of two elements. The first element is some value, the second element is a character table for storing more branches of the trie. The value is initially NIL."&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;vector&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;make-char-table&lt;/span&gt; &lt;span class="ss"&gt;'trie-table&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;zencoding-trie-node-value&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;aref&lt;/span&gt; &lt;span class="nv"&gt;node&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;zencoding-trie-node-set-value&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;node&lt;/span&gt; &lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;aset&lt;/span&gt; &lt;span class="nv"&gt;node&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;zencoding-trie-node-branches&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;aref&lt;/span&gt; &lt;span class="nv"&gt;node&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;zencoding-trie-node-branch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;node&lt;/span&gt; &lt;span class="nv"&gt;branch-key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;aref&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;zencoding-trie-node-branches&lt;/span&gt; &lt;span class="nv"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;branch-key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;zencoding-trie-node-create-branch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;node&lt;/span&gt; &lt;span class="nv"&gt;branch-key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;aset&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;zencoding-trie-node-branches&lt;/span&gt; &lt;span class="nv"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;branch-key&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;make-zencoding-trie-node&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;zencoding-trie-node-brancher&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;node&lt;/span&gt; &lt;span class="nv"&gt;branch-key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;branch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;aref&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;zencoding-trie-node-branches&lt;/span&gt; &lt;span class="nv"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;branch-key&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;branch&lt;/span&gt;
    &lt;span class="nv"&gt;branch&lt;/span&gt;
      &lt;span class="c1"&gt;;; branch doesn't exist, so create it and then return it.&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;zencoding-trie-node-create-branch&lt;/span&gt; &lt;span class="nv"&gt;node&lt;/span&gt; &lt;span class="nv"&gt;branch-key&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;zencoding-trie-traverse&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;s&lt;/span&gt; &lt;span class="nv"&gt;trie&lt;/span&gt; &lt;span class="nv"&gt;i&lt;/span&gt; &lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;null&lt;/span&gt; &lt;span class="nv"&gt;trie&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="no"&gt;nil&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;branch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;funcall&lt;/span&gt; &lt;span class="nv"&gt;f&lt;/span&gt; &lt;span class="nv"&gt;trie&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;aref&lt;/span&gt; &lt;span class="nv"&gt;s&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;1-&lt;/span&gt; &lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;null&lt;/span&gt; &lt;span class="nv"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;i&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;length&lt;/span&gt; &lt;span class="nv"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
      &lt;span class="nv"&gt;branch&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;zencoding-trie-traverse&lt;/span&gt; &lt;span class="nv"&gt;s&lt;/span&gt; &lt;span class="nv"&gt;branch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;1+&lt;/span&gt; &lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;zencoding-trie-retrieve&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;s&lt;/span&gt; &lt;span class="nv"&gt;trie&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;zencoding-trie-traverse&lt;/span&gt; &lt;span class="nv"&gt;s&lt;/span&gt; &lt;span class="nv"&gt;trie&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="ss"&gt;'zencoding-trie-node-branch&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;zencoding-trie-insert&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;s&lt;/span&gt; &lt;span class="nv"&gt;value&lt;/span&gt; &lt;span class="nv"&gt;trie&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="s"&gt;"`s' is the string used to navigate the trie (each character is a node in the trie). `value' is the value we want to insert. `trie' is the node where we start the insertion."&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;zencoding-trie-node-set-value&lt;/span&gt;
   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;zencoding-trie-traverse&lt;/span&gt; &lt;span class="nv"&gt;s&lt;/span&gt; &lt;span class="nv"&gt;trie&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="ss"&gt;'zencoding-trie-node-brancher&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;provide&lt;/span&gt; &lt;span class="ss"&gt;'zencoding-trie&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;;; test code&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;make-zencoding-trie-node&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;zencoding-trie-insert&lt;/span&gt; &lt;span class="s"&gt;"ha"&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;zencoding-trie-insert&lt;/span&gt; &lt;span class="s"&gt;"hi"&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;zencoding-trie-retrieve&lt;/span&gt; &lt;span class="s"&gt;"ha"&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>trie</category>
      <category>datastructures</category>
      <category>emacs</category>
    </item>
    <item>
      <title>Docker Compose Develop Watch and Syncing Files from the Host Container to a Service</title>
      <dc:creator>Rudolf Olah</dc:creator>
      <pubDate>Fri, 12 Jul 2024 14:02:19 +0000</pubDate>
      <link>https://dev.to/rudolfolah/docker-compose-develop-watch-and-syncing-files-from-the-host-container-to-a-service-18hh</link>
      <guid>https://dev.to/rudolfolah/docker-compose-develop-watch-and-syncing-files-from-the-host-container-to-a-service-18hh</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/docker/compose/issues/11102" rel="noopener noreferrer"&gt;This thread has helped me better understand the "develop" behavior with Docker Compose&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/compose/file-watch/#example-1" rel="noopener noreferrer"&gt;The Docker Compose documentation of "Example 1" in "Use Compose Watch"&lt;/a&gt;&amp;nbsp;does a pretty good job explaining things. Perhaps part of it should be added to &lt;a href="https://docs.docker.com/compose/compose-file/develop/" rel="noopener noreferrer"&gt;the&amp;nbsp;Compose File - Develop&amp;nbsp;page&lt;/a&gt; instead of hidden by links?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;After the service is up, the watch mode starts monitoring the target directories and files. Then, whenever a source file in the web/ directory is changed, Compose syncs the file to the corresponding location under /src/web inside the container. For example, ./web/App.jsx is copied to /src/web/App.jsx.&lt;/p&gt;

&lt;p&gt;Once copied, the bundler updates the running application without a restart.&lt;/p&gt;

&lt;p&gt;Unlike source code files, adding a new dependency can’t be done on-the-fly, so whenever package.json is changed, Compose rebuilds the image and recreates the web service container.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The "Rebuild Everything" Method
&lt;/h2&gt;

&lt;p&gt;This appears to be the ideal process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;build the image with the current state of files copied into it
run compose with watch&lt;/li&gt;
&lt;li&gt;whenever a file changes, rebuild the image so it has the updated state of files within it&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The "Sync Files Manually" Approach
&lt;/h2&gt;

&lt;p&gt;Alternatively, outside of Docker Compose for syncing files (and based on the suggestions in the thread):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;build the image with the current state of files copied into it (use a "watch" or "reload" command built into whatever app you're running)&lt;/li&gt;
&lt;li&gt;run compose (do not use develop or watch)&lt;/li&gt;
&lt;li&gt;run &lt;code&gt;docker compose cp ./src app:/src&lt;/code&gt;
this should trigger the watch/reload command within the container&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The "Volume Mounting" Method
&lt;/h2&gt;

&lt;p&gt;Another option is to build a base image that &lt;em&gt;does not&lt;/em&gt; include source code and &lt;em&gt;only&lt;/em&gt; builds an environment in which the source code can be built or run. Then, a volume can be mounted into the container with the latest source code from the host environment, and the code can be run.&lt;/p&gt;

&lt;p&gt;Something like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;build the image with the environment&lt;/li&gt;
&lt;li&gt;run compose with a volume mounted&lt;/li&gt;
&lt;li&gt;run the steps in the container to build/run the source&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feh2844caramnapbiqg5j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feh2844caramnapbiqg5j.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It all depends on your developer workflow and what is understandable and can be used daily with minimal disruption to &lt;a href="https://medium.com/hootsuite-engineering/optimizing-the-feedback-loop-a-key-to-great-developer-productivity-f567c4e80c80" rel="noopener noreferrer"&gt;the developer feedback loop&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Things to consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how often are dependencies changing? should they be re-installed within the container or should the image be rebuilt (since one of the steps is installing dependencies)?&lt;/li&gt;
&lt;li&gt;is there a "watch" or "reload" command that reloads your app based on files changes?&lt;/li&gt;
&lt;li&gt;how much time does it take to rebuild the image? is it acceptable to yourself and to your team to wait for a rebuild on every set of source code changes? does it slow the developer feedback loop too much?&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>docker</category>
      <category>containers</category>
      <category>development</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Incredible Updates in VS Code 1.91 (June 2024)</title>
      <dc:creator>Rudolf Olah</dc:creator>
      <pubDate>Fri, 05 Jul 2024 17:49:07 +0000</pubDate>
      <link>https://dev.to/rudolfolah/incredible-updates-in-vs-code-191-june-2024-3p10</link>
      <guid>https://dev.to/rudolfolah/incredible-updates-in-vs-code-191-june-2024-3p10</guid>
      <description>&lt;p&gt;&lt;a href="https://code.visualstudio.com/updates/v1_91" rel="noopener noreferrer"&gt;Version 1.91 of VS Code is out now and has some incredible updates:&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://code.visualstudio.com/updates/v1_91#_extension-install-options" rel="noopener noreferrer"&gt;You can now install a specific version of extensions without downloading the latest version.&lt;/a&gt; This is useful if there are issues with newer versions or if everyone on the team is running a particular version of an extension.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://code.visualstudio.com/updates/v1_91#_unset-a-theme-color" rel="noopener noreferrer"&gt;Override a theme's color or border if you don't like it.&lt;/a&gt; You can set it back to "default".&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://code.visualstudio.com/updates/v1_91#_typescript-55" rel="noopener noreferrer"&gt;TypeScript 5.5 is included.&lt;/a&gt; It enables regular expression syntax checking in JavaScript and TypeScript.&lt;/li&gt;
&lt;li&gt;Python: VS Code now uses &lt;a href="https://github.com/microsoft/python-environment-tools" rel="noopener noreferrer"&gt;python-environment-tools&lt;/a&gt; to discover all Python installs and virtual environments.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/updates/v1_91#_code-actions-on-save" rel="noopener noreferrer"&gt;Run "code actions" when saving a file, such as for automatically fixing lint issues.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>vscode</category>
      <category>python</category>
    </item>
    <item>
      <title>In-Project Python Virtualenvs in Poetry</title>
      <dc:creator>Rudolf Olah</dc:creator>
      <pubDate>Sat, 01 Jun 2024 17:54:43 +0000</pubDate>
      <link>https://dev.to/rudolfolah/in-project-virtualenvs-in-poetry-30km</link>
      <guid>https://dev.to/rudolfolah/in-project-virtualenvs-in-poetry-30km</guid>
      <description>&lt;p&gt;Poetry is a dependency and package manager for Python.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can list the Poetry configuration with &lt;code&gt;poetry config --list&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;You can set a Poetry configuration option with &lt;code&gt;poetry config $setting $value&lt;/code&gt;, for example &lt;code&gt;poetry config virtualenvs.path /path/to/cache/directory/virtualenvs&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a Poetry project is created, the default configuration will create a virtualenv in another directory.&lt;/p&gt;

&lt;p&gt;Change where the virtualenv is created by adding this &lt;code&gt;poetry.toml&lt;/code&gt; file to your project before you run &lt;code&gt;poetry install&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# poetry.toml
[virtualenvs]
create = true
in-project = true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The documentation for Poetry advises &lt;em&gt;not&lt;/em&gt; to check in the &lt;code&gt;poetry.toml&lt;/code&gt; into version control since it can contain user-specific settings. You can add &lt;code&gt;poetry.toml&lt;/code&gt; to the &lt;code&gt;.gitignore&lt;/code&gt; for a repo.&lt;/p&gt;

</description>
      <category>python</category>
      <category>virtualenv</category>
      <category>poetry</category>
      <category>configuration</category>
    </item>
    <item>
      <title>New shell scripting language, a new tablet, and in-product messaging</title>
      <dc:creator>Rudolf Olah</dc:creator>
      <pubDate>Fri, 24 May 2024 16:15:26 +0000</pubDate>
      <link>https://dev.to/rudolfolah/new-shell-scripting-language-a-new-tablet-and-in-product-messaging-5g5h</link>
      <guid>https://dev.to/rudolfolah/new-shell-scripting-language-a-new-tablet-and-in-product-messaging-5g5h</guid>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amber-lang.com/" rel="noopener noreferrer"&gt;Amber, a programming language that compiles to BASH&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://matthewstrom.com/writing/wayfinding/" rel="noopener noreferrer"&gt;The slippery slope of in-product messaging
&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://daylightcomputer.com/product" rel="noopener noreferrer"&gt;Daylight Computer, an alternative to Kindle and iPad?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Shell scripting times a million
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://amber-lang.com/" rel="noopener noreferrer"&gt;Amber is a new programming language that allows you to write code converted into BASH shell scripts.&lt;/a&gt; The language's syntax is a mix of JavaScript, Python, and something else (Kotlin? Rust?). The idea is to write in a language with a modern syntax, type-safety, and safety features.&lt;/p&gt;

&lt;p&gt;One of those safety features forces you to write code to handle failures whenever you run a shell command.&lt;/p&gt;

&lt;p&gt;The syntax also makes it more apparent that command failures are being handled:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let files = ["config.json", "file.txt", "audio.mp3"]

loop index, file in files {
    $mv {file} {index}{file}$ failed {
        echo "Failed to rename {file}"
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're only occasionally writing shell scripts, Amber may not be a priority for you. In such cases, linting tools like &lt;a href="https://www.shellcheck.net/" rel="noopener noreferrer"&gt;ShellCheck&lt;/a&gt; could be more beneficial. However, if you find yourself frequently writing shell scripts, to the point where you're considering Python or Ruby for better re-usability, then Amber is definitely worth your attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  In-app tour guides are marketing
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://matthewstrom.com/writing/wayfinding/" rel="noopener noreferrer"&gt;In &lt;em&gt;"The Slippery Slope of In-Product Messaging,"&lt;/em&gt; designer Matthew Ström explains that in-product messaging, those product tours you see in web apps, could be a symptom of a "bad" design.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's an interesting take, though I can't entirely agree. In-product messaging helps guide users/customers through new product features, regardless of whether the new feature's user experience is well-designed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdtyt9nzd2g91833pv453.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdtyt9nzd2g91833pv453.png" alt="screenshot of app cues" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In-product messaging is another marketing touchpoint that informs the customer about a feature that may not be part of their typical workflow. Not to mention, once a customer/user has seen them, they won't reappear. Think about it this way: from a B2B SaaS perspective, the customer gets an email about a new feature release, logs into the app, and sees a small notification icon or a banner on the page about the new feature release. They go to some other page and get invited to go through a tour of the feature. This kind of repetition is needed even for well-designed user experiences.&lt;/p&gt;

&lt;h2&gt;
  
  
  A tablet you might use every single day
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://daylightcomputer.com/product" rel="noopener noreferrer"&gt;The Daylight Computer is a mix between the Kindle and the iPad.&lt;/a&gt; The screen resembles the Kindle screen, and it has the form factor of an iPad, including a stylus. It is extremely interesting that the screen's refresh rate is much higher than the typical ebook reader. It's high enough to be a tablet.&lt;/p&gt;

&lt;p&gt;The Daylight Computer, DC-1, runs Android, so you can run any Android tablet app on it with a kinder and gentler screen for the eyes.&lt;/p&gt;

&lt;p&gt;Screen glare is not a problem with the DC-1.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fid2m84khzgvgxj0blx6i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fid2m84khzgvgxj0blx6i.png" alt="screenshot of daylight computer and ipad comparison" width="800" height="612"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've bought a few tablets before and used them for reading, watching movies (and sometimes video games and coding). I use them once in a while. I also have a Kindle and use it every day.&lt;/p&gt;

&lt;p&gt;It will be interesting to see whether the DC-1 lives up to the hype.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>ipad</category>
      <category>product</category>
      <category>marketing</category>
    </item>
  </channel>
</rss>
