<?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: Dominik</title>
    <description>The latest articles on DEV Community by Dominik (@dominik_alberski).</description>
    <link>https://dev.to/dominik_alberski</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%2F239160%2F3d203814-7fa6-4691-9941-3437b2b0bd63.jpeg</url>
      <title>DEV Community: Dominik</title>
      <link>https://dev.to/dominik_alberski</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dominik_alberski"/>
    <language>en</language>
    <item>
      <title>Forest Radar: planning a walk with Polish forest data and local AI</title>
      <dc:creator>Dominik</dc:creator>
      <pubDate>Sat, 10 Oct 2026 21:12:10 +0000</pubDate>
      <link>https://dev.to/dominik_alberski/forest-radar-planning-a-walk-with-polish-forest-data-and-local-ai-5e4o</link>
      <guid>https://dev.to/dominik_alberski/forest-radar-planning-a-walk-with-polish-forest-data-and-local-ai-5e4o</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week1-2026-10-05"&gt;Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;p&gt;My idea for the Touch Grass challenge was a small application that helps you choose a nearby forest for a walk. Give it a location and a radius, then get information about the forests nearby and suggestions about what you might find there. Save the result or print it, and take it outside.&lt;/p&gt;

&lt;p&gt;I called it Forest Radar, or Leśny Radar in the Polish interface.&lt;/p&gt;

&lt;p&gt;The original idea included mushrooms, berries, animal tracks, and questions like whether you can bring a dog. Those questions sound simple until you start looking at the available data. Forest inventory data can tell you about the trees and habitat. It cannot tell you that a mushroom is there today, or that a particular forest is safe to enter.&lt;/p&gt;

&lt;p&gt;The current application accepts coordinates and a radius between 1 and 20 kilometres. You can also ask the browser to fill in your location. It produces a separate Polish description for each forest range returned by the search, with a Google Maps link and the same safety footer on every card.&lt;/p&gt;

&lt;p&gt;A forest range is an administrative area, not a complete walking route. This version does not provide turn-by-turn navigation, verify dog-access rules, or guarantee a suitable entrance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;The application runs locally. The example in the screenshot uses latitude 52, longitude 21, and a search radius of 5 kilometres. The first card is for Uwieliny. It has an AI-generated forest description, a Maps link, a print button, and a mushroom-safety warning with a link to look for expert help.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2pr0w5d4mgccqlswigrb.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2pr0w5d4mgccqlswigrb.png" alt=" " width="800" height="1081"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Screenshot: the Uwieliny card in Leśny Radar. The description is labelled as AI-generated and potentially inaccurate; the safety footer is fixed application text.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A search at this location produces cards for Uwieliny, Bogatki, Dobiesz, Chojnów, Góra Kalwaria, and Sękocin. One run took about 97 seconds to fetch the data and generate all six descriptions. It is slow enough that I would want to prepare the cards before leaving, rather than wait for them on the trail.&lt;/p&gt;

&lt;p&gt;The app can generate the cards, but the descriptions still contain errors. They need checking before anyone relies on them to plan a walk.&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;The project is written in Ruby with Sinatra and is available under the MIT licence:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/DominikAlberski/forest_radar" rel="noopener noreferrer"&gt;Forest Radar on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I chose Sinatra because the application needs one page, a JSON endpoint, and a printable result. Rails would add more structure than I need here, while writing the HTTP and view layers myself would take time away from the application.&lt;/p&gt;

&lt;p&gt;The interface uses i18n. The Polish translations are complete; the English translation keys exist, but their values are still empty. The README includes instructions for running the app and its tests.&lt;/p&gt;
&lt;h2&gt;
  
  
  How I built it
&lt;/h2&gt;

&lt;p&gt;The main data source is &lt;a href="https://www.bdl.lasy.gov.pl/" rel="noopener noreferrer"&gt;Bank Danych o Lasach&lt;/a&gt;, the Polish Forest Data Bank. The application queries its OGC API Features service for forest administration and inventory data near the requested location.&lt;/p&gt;

&lt;p&gt;The useful fields include tree species, habitat type, stand age, and inventory year. A stand is a unit in the forest inventory. The application groups the relevant stands by forest range and summarizes their attributes before sending them to the model. Each forest gets its own description.&lt;/p&gt;

&lt;p&gt;For Uwieliny, the query returned 431 stands within the search radius, with an inventory year of 2026. Here is part of the species summary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SO (pine):       216 stands
DB (oak):        79 stands
Missing species: 53 stands
BRZ (birch):     39 stands
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are counts of stands grouped by their recorded species code. They are not hectares or percentages, and they do not count individual trees.&lt;/p&gt;

&lt;p&gt;I keep this raw data off the user-facing card because it is hard to interpret, but include it in the model's prompt.&lt;/p&gt;

&lt;p&gt;I chose &lt;a href="https://ollama.com/SpeakLeash" rel="noopener noreferrer"&gt;Bielik&lt;/a&gt; because it is a Polish model and I wanted the forest descriptions in Polish. I run it through Ollama on a separate machine on my local network, using &lt;code&gt;SpeakLeash/bielik-11b-v2.3-instruct:Q4_K_M&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I originally asked Bielik for a structured response with specific keywords and separate sections. It consistently left out the keywords or sections I requested. The forbidden-word checks described below also kept rejecting its answers, so I stopped requiring that format and now display the response as it comes back, with a disclaimer.&lt;/p&gt;

&lt;p&gt;The prompt asks, in Polish, what the BDL data can tell us about the forest's area, accessibility for walkers, and possible animals, plants, and mushrooms. The application supplies numbered data lines, but it does not check whether the generated description accurately reflects them.&lt;/p&gt;

&lt;p&gt;The backend also queries OpenStreetMap through Overpass and includes the retrieved trail information in each forest's prompt. The input distinguishes four cases: a mapped trail crosses the forest, the search found no crossing trail, the data was unavailable, or the forest was not checked. The prompt explains that missing data does not mean there are no trails on the ground. It also explains that the Google Maps link identifies the forest location rather than a verified walking route.&lt;/p&gt;

&lt;p&gt;I added this trail information after reviewing the earlier output shown here. Responses remain unvalidated, so the extra context does not guarantee a correct description.&lt;/p&gt;

&lt;h3&gt;
  
  
  The mushroom rule I changed
&lt;/h3&gt;

&lt;p&gt;I wanted the application to suggest possible mushroom species for a habitat. I did not want it to decide whether a mushroom is edible or poisonous.&lt;/p&gt;

&lt;p&gt;My first approach was strict validation: reject responses containing forbidden words about edibility. Bielik kept using those words when listing mushrooms, and the validator kept rejecting the answers. I removed the word check from the running version.&lt;/p&gt;

&lt;p&gt;The screenshot shows the description mentioning "grzyby jadalne i trujące" (edible and poisonous mushrooms). In a later run, before I added the trail information, the model went further and listed named mushrooms as edible. The original validation would have rejected that output. The current application does not.&lt;/p&gt;

&lt;p&gt;I opted for a disclaimer and the same mushroom-safety footer on every forest card. The description is labelled as AI-generated and potentially wrong. The footer warns against collecting unfamiliar mushrooms and points readers toward a sanitary station, where a qualified expert can check them. It includes a Google Maps search link to help find that service.&lt;/p&gt;

&lt;p&gt;This is a trade-off, not a replacement safety guarantee. A disclaimer does not prevent unsafe output. Forest Radar is not a mushroom-identification or edibility tool, and its generated text is not a reason to eat something found in the forest.&lt;/p&gt;

&lt;h3&gt;
  
  
  What the model got wrong
&lt;/h3&gt;

&lt;p&gt;Before I added the trail information to the prompt, one Uwieliny description included this phrase:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;sosna (216 ha) i świerk (79 ha)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That means "pine (216 hectares) and spruce (79 hectares)." The source summary actually had 216 stands recorded with the pine code and 79 with the oak code. The model changed both the units and the species associated with the second value.&lt;/p&gt;

&lt;p&gt;It also called the forest accessible to walkers without current access evidence. These are errors in the generated description, not facts established by BDL. Animals, berries, and mushrooms mentioned in the text are not verified sightings either.&lt;/p&gt;

&lt;p&gt;I have not yet taken a card outside and compared it with what I found in the forest.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why I removed the entry-ban check
&lt;/h3&gt;

&lt;p&gt;Checking whether a forest was closed was part of the original plan. During development, the entry-ban service returned HTTP 400 errors, so the application could not determine whether a ban was in place. I removed the check rather than keep a feature that always reported an unknown result.&lt;/p&gt;

&lt;p&gt;That means Forest Radar does not establish current entry-ban status. The model can still write an accessibility claim, as the demo shows, but that claim is not a substitute for checking current notices from the relevant forest district.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does open innovation matter?
&lt;/h2&gt;

&lt;p&gt;Public forest data makes this project possible. It gives the application a source for local forest information instead of asking a model to invent it from a place name. It also lets me compare the generated description with the input and identify mistakes such as the stand-count error.&lt;/p&gt;

&lt;p&gt;An open-weight model served through Ollama gives me control over inference. I can choose the model host and change the model without replacing the rest of the application. Running it on hardware I control avoids a proprietary model API and its per-request charges, although the hardware and electricity still have a cost.&lt;/p&gt;

&lt;p&gt;The application still needs internet access. BDL and Overpass receive queries containing a bounding box around the user's location. The model host receives summarized forest data, trail information, and distances, rather than the raw input coordinates. Google Maps receives location information when a user opens one of the links.&lt;/p&gt;

&lt;p&gt;For this project, the benefit is control over the model service and access to the underlying data. Neither makes the generated text automatically correct.&lt;/p&gt;

&lt;p&gt;Forest Radar is a working local prototype for exploring nearby forest options. I want the screen to be the preparation for a walk, not the activity itself. Before relying on the descriptions as a guide, I still need to improve their accuracy and test the cards outside.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>I wanted Claude to delegate to a local model. That became MAF.</title>
      <dc:creator>Dominik</dc:creator>
      <pubDate>Sat, 10 Oct 2026 12:44:29 +0000</pubDate>
      <link>https://dev.to/dominik_alberski/i-wanted-claude-to-delegate-to-a-local-model-that-became-maf-1e2a</link>
      <guid>https://dev.to/dominik_alberski/i-wanted-claude-to-delegate-to-a-local-model-that-became-maf-1e2a</guid>
      <description>&lt;p&gt;I started building MAF because I wanted to make AI more reliable and spend less on it.&lt;/p&gt;

&lt;p&gt;My idea was simple: use a paid claude based model for complex reasoning, planning, and judgment, then use a local model for implementation work. Keep the expensive model involved where it helps and use the local model for work it can handle.&lt;/p&gt;

&lt;p&gt;At the time, I couldn't find an option that fit my workflow easily. My experiments with local models were also disappointing. Simple, well-explained tasks worked, but tool calls or longer agentic workflows would overflow the context window.&lt;/p&gt;

&lt;p&gt;I started using Claude Code, but I still wanted to use a local model through Ollama and OpenCode for some side tasks.&lt;/p&gt;

&lt;p&gt;I tried several approaches: first subagents, then skill bridges that told Claude Code how to delegate simple tasks to OpenCode. Automatic communication between the two harnesses—the tools running the agents—was unreliable or used too many Claude tokens.&lt;/p&gt;

&lt;p&gt;I was making progress, though. I worked out a process where Claude Code wrote a task file with instructions for OpenCode to implement. I still had to point OpenCode to those files myself.&lt;/p&gt;

&lt;p&gt;The shared-board idea came from a conversation at EuRuKo with Maciej Mensfeld. He described his own agentic workflow and how he isolated agents with &lt;a href="https://github.com/coipond/coi" rel="noopener noreferrer"&gt;Coi (Code on Incus)&lt;/a&gt;, a tool that runs coding agents inside isolated Incus system containers. He also mentioned agents coordinating through a Kanban-like task service.&lt;/p&gt;

&lt;p&gt;What stuck with me was the idea of agents using a task board to communicate. I wanted something similar, but local: no external task service and no web requests just to coordinate work. The agents could still use cloud models, but their board and messages would live in the project.&lt;/p&gt;

&lt;p&gt;That gave me a way to move beyond manually passing task files between agents. It became Multi_Agent_Flow (MAF).&lt;/p&gt;

&lt;h2&gt;
  
  
  A shared board instead of another agent runtime
&lt;/h2&gt;

&lt;p&gt;MAF is the glue that lets different coding-agent tools coordinate work on one project. It does not replace Claude Code, OpenCode, Codex, or Hermes. Those tools still run the agents, using the model access you configure for each one.&lt;/p&gt;

&lt;p&gt;You can use Claude Code with your subscription and OpenCode with a local model, or choose another combination of harnesses and models. MAF does not require the whole team to use the same provider. It comes with predefined roles and workflow examples, but you can define your own.&lt;/p&gt;

&lt;p&gt;MAF gives the agents a shared way to coordinate work on one repository:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A project-local Taskwarrior board records goals, tasks, and claims.&lt;/li&gt;
&lt;li&gt;File inboxes carry messages between roles and workers.&lt;/li&gt;
&lt;li&gt;Git worktrees give workers separate checkouts.&lt;/li&gt;
&lt;li&gt;Resource locks let workers serialize access to something they share, such as a local model host.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The common interface is the &lt;code&gt;coord&lt;/code&gt; CLI. An agent does not need a vendor SDK to read the board or send a message. It needs to run a command.&lt;/p&gt;

&lt;p&gt;There is an optional shared-memory layer through graphify and an Obsidian vault. Obsidian makes it easier for you to browse the agents' knowledge.&lt;/p&gt;

&lt;p&gt;There is also a web dashboard, which you can start by running &lt;code&gt;.maf/bin/dashboard&lt;/code&gt; from the project root. It shows worker status, the task board, and coordination activity. It also includes analysis tools that suggest ways to reduce token usage.&lt;/p&gt;

&lt;p&gt;The part I care about most is choosing tools by role. I can keep a cloud model on planning and review while routing scoped implementation work to another harness. MAF provides the coordination; it does not guarantee that a local model will do a task correctly or cheaply.&lt;/p&gt;

&lt;h2&gt;
  
  
  Roles are responsibilities, not model names
&lt;/h2&gt;

&lt;p&gt;An architect decomposes a goal into tasks. Developers implement them. A tester checks behavior. A reviewer examines the result.&lt;/p&gt;

&lt;p&gt;For example, imagine adding CSV export to an app. The architect defines the task and its acceptance criteria. A developer claims it and implements it in a separate worktree. A tester checks the behavior, and a reviewer examines the diff. If something is missing, they send feedback through the inbox instead of relying on me to copy messages between terminals.&lt;/p&gt;

&lt;p&gt;A project manager can sit between me and the architect: I describe the outcome, and the project manager creates the goal and reports back. The lead roles do not claim implementation tasks.&lt;/p&gt;

&lt;p&gt;A harness is the tool running a role. A worker is a running instance of that role. More than one worker can serve the same role.&lt;/p&gt;

&lt;p&gt;This distinction matters because I do not want my project structure to depend on which model I happen to use this month.&lt;/p&gt;

&lt;p&gt;For example, this configures a mixed team:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;maf add claude:architect opencode:backend-developer claude:reviewer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That command installs the flow and generates the role files. It does not start the agents.&lt;/p&gt;

&lt;p&gt;OpenCode is the harness here, not the model. To use it for local implementation, I configure it to use a model served by Ollama.&lt;/p&gt;

&lt;p&gt;You aren't limited to the predefined roles. You could define a writer, editor, researcher, or proofreader and assign each one to whichever harness and model fits best. &lt;code&gt;maf role add NAME&lt;/code&gt; creates a stub in &lt;code&gt;.maf/roles.yml&lt;/code&gt;; you fill in its responsibilities and permissions. You can also write your own workflow in &lt;code&gt;.maf/workflow.md&lt;/code&gt; for the architect to follow.&lt;/p&gt;

&lt;h2&gt;
  
  
  MAF stays out of the project's tracked files
&lt;/h2&gt;

&lt;p&gt;The tool keeps its main installation in &lt;code&gt;.maf/&lt;/code&gt;. It uses &lt;code&gt;.git/info/exclude&lt;/code&gt; to keep those files out of the clone's normal Git status. The knowledge graph lives separately in &lt;code&gt;graphify-out/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;MAF also installs Git hooks and harness-specific files. Those are real changes to the local environment, even though the tool keeps its setup out of the project's tracked source files. A clean Git status does not mean that nothing changed on disk.&lt;/p&gt;

&lt;p&gt;The agents' code and project documentation still go through your normal Git workflow; MAF's local setup stays outside it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Different ways to run MAF
&lt;/h2&gt;

&lt;p&gt;At first, I ran all my harnesses interactively. That let me see what each agent was doing in its own terminal window and micromanage the process when I needed to.&lt;/p&gt;

&lt;p&gt;Now I talk to a single agent in the project manager role. The rest of the team runs in &lt;code&gt;--dispatch&lt;/code&gt; mode, in terminals I start myself. The dispatcher waits for tasks or messages and invokes the agent when there is work. You can also ask the project manager to arrange a team with the architect, within the worker limits and allowed harnesses you configure.&lt;/p&gt;

&lt;p&gt;One important caveat: dispatch mode bypasses approval prompts for Claude Code, Codex, and Hermes; OpenCode uses the permissions in its role file. Separate worktrees help keep changes apart, but they are not a security sandbox. Only use unattended workers with access you are comfortable granting them.&lt;/p&gt;

&lt;p&gt;Try different approaches to see what works for you. I started with interactive sessions because I wanted to watch the coordination before leaving more of it unattended.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it on something small
&lt;/h2&gt;

&lt;p&gt;You don't need to start with the whole team. Begin with an architect and one developer, watch how they coordinate, and add other roles when you need them.&lt;/p&gt;

&lt;p&gt;You need Ruby 3.0 or later, Git, and Taskwarrior for the coordination layer. For actual agent work, you also need an installed coding-agent harness with the model access it requires. Graphify is optional but recommended for shared knowledge.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gem &lt;span class="nb"&gt;install &lt;/span&gt;maf
maf version
maf guide
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then open a coding agent in your project and ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Run &lt;code&gt;maf guide&lt;/code&gt;. Read the output and help me set it up.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;MAF is &lt;a href="https://github.com/DominikAlberski/multi_agent_flow" rel="noopener noreferrer"&gt;open source on GitHub&lt;/a&gt; and &lt;a href="https://rubygems.org/gems/maf" rel="noopener noreferrer"&gt;published on RubyGems&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can ask your agent about MAF as you go. It can guide you through adding custom roles or workflows rather than making you learn every command first.&lt;/p&gt;

&lt;p&gt;I'd like feedback from people whose workflows differ from mine. If you try MAF, tell me which tools you used and where setup or coordination became confusing. If you encounter a bug, please open an issue with a reproducible example.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>ruby</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I had Ruby running on a 2009 iPhone. Then I needed something to run.</title>
      <dc:creator>Dominik</dc:creator>
      <pubDate>Sun, 04 Oct 2026 22:23:12 +0000</pubDate>
      <link>https://dev.to/dominik_alberski/i-had-ruby-running-on-a-2009-iphone-then-i-needed-something-to-run-471o</link>
      <guid>https://dev.to/dominik_alberski/i-had-ruby-running-on-a-2009-iphone-then-i-needed-something-to-run-471o</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01"&gt;Hacktoberfest Weekend Challenge: Build for a Friend&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I had Ruby 4.0.7 running on a 2009 iPhone 3GS. I just didn't know what to run on it.&lt;/p&gt;

&lt;p&gt;Then this challenge gave me an idea: turn the phone into a home network dashboard connected to a local AI model.&lt;/p&gt;

&lt;p&gt;I built it for my best friend: myself. It do not solves any problems but it let me learned some new concepts.&lt;/p&gt;

&lt;p&gt;The Ruby port was already part of a larger side project. The dashboard is the new project I built for this challenge. I wanted something to run on the phone and something to write about here.&lt;/p&gt;

&lt;p&gt;The phone shows devices on my home network. I can type commands or ask questions about the latest device scan. A model on my PC answers the questions. The phone runs the Ruby dashboard; the PC runs the model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iPhone 3GS (Ruby daemon, Safari)
      └── HTTP LAN ──&amp;gt; Mac (Ruby agent: scan / ping / wake / chat)
                            └── HTTP LAN ──&amp;gt; PC (Ollama: Qwen3.8-27B-Ridge)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three machines to ask which devices are online. This is what happens when I choose my own weekend projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;The interface is one HTML page served on the phone by Ruby's &lt;code&gt;TCPServer&lt;/code&gt;. Safari displays the page. An ordinary HTML form sends questions. A refresh link updates the display. There is no JavaScript.&lt;/p&gt;

&lt;p&gt;The page lists registered and discovered devices, their online status, and the model's availability. The latest reply appears above the question box.&lt;/p&gt;

&lt;p&gt;During verification over USB SSH, Ruby on the phone returned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ruby 4.0.7 (2026-09-29 revision fdc00de497) +PRISM [armv7-darwin10]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An HTTP request from the phone to its own dashboard returned 13 device entries. Eleven entries were online and two were offline. A second request from the phone reached the Mac agent successfully. The agent reported the local model as reachable.&lt;/p&gt;

&lt;p&gt;Those two offline entries need a qualification: &lt;code&gt;pc&lt;/code&gt; and &lt;code&gt;printer&lt;/code&gt; still had placeholder records in my configuration. The result proves the dashboard receives data. It does not prove that my actual PC and a real printer were offline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;The dashboard source is available on &lt;a href="https://github.com/DominikAlberski/dashboard_project" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;agent/&lt;/code&gt; runs on the Mac. It scans the network, handles commands, and sends questions to the model.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;client/&lt;/code&gt; runs on the phone. It serves the dashboard and talks to the agent.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;config/&lt;/code&gt; holds device records and agent settings.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deploy/&lt;/code&gt; installs the phone's dashboard service.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agent and client use Ruby's standard library, with no runtime gems. The tests use Minitest.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;The model is &lt;a href="https://huggingface.co/empero-ai/Qwen3.8-27B-Ridge-GGUF" rel="noopener noreferrer"&gt;Qwen3.8-27B-Ridge&lt;/a&gt;, served locally by &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; on my PC. The Mac agent reaches Ollama over HTTP on the LAN.&lt;/p&gt;

&lt;p&gt;The jailbroken phone runs iOS 6.1.6 and my cross-compiled CRuby build. Getting Ruby onto the phone needed changes to CRuby for compatibility with the old iOS SDK. That work belongs to the larger project. This weekend's task was to give Ruby an application.&lt;/p&gt;

&lt;p&gt;I chose a web page for the interface because the phone already has Safari and an onscreen keyboard. Ruby serves the page, and a launchd service starts the dashboard. I had already used this approach for a light-meter application on the same phone.&lt;/p&gt;

&lt;p&gt;The most useful bug appeared after the connections worked. The agent scanned the network. The model answered questions. But the model knew nothing about the scan.&lt;/p&gt;

&lt;p&gt;I asked what devices were on my network. The reply began:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I can’t see your network. On your computer: run &lt;code&gt;arp -a&lt;/code&gt; …&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fair answer. I had connected the model without sending the information the model needed.&lt;/p&gt;

&lt;p&gt;The fix was to attach the latest device snapshot as context:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;system_prompt&lt;/span&gt;
  &lt;span class="s2"&gt;"You are a LAN overseer. Answer from the device list below only. "&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"Keep the answer short. Devices: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;device_summary&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&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;After the fix, the recorded answer began with “10 devices online” and listed addresses from the snapshot. The model now had information about my network instead of only my question.&lt;/p&gt;

&lt;p&gt;Commands such as &lt;code&gt;ping&lt;/code&gt;, &lt;code&gt;scan&lt;/code&gt;, &lt;code&gt;wake&lt;/code&gt;, and &lt;code&gt;ports&lt;/code&gt; go through Ruby command handlers. Free-text questions go to the model. The model explains the supplied device status; it does not choose or execute network commands.&lt;/p&gt;

&lt;p&gt;The prompt does not guarantee accurate answers. One later reply suggested that the placeholder printer might have responded on a printer-related port. The supplied context contained no evidence for that explanation.&lt;/p&gt;

&lt;p&gt;That is a useful limit to show alongside the working demo. Providing context helps, but the configuration and the answers still need checking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;Open source made both parts possible: adapting Ruby for the phone and running a model on hardware I control.&lt;/p&gt;

&lt;p&gt;The phone's Ruby build has no OpenSSL extension. The dashboard uses plain HTTP inside my home LAN. The Mac agent has no authentication, so this prototype belongs on a trusted home network.&lt;/p&gt;

&lt;p&gt;Local inference gives this project three useful properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Local data.&lt;/strong&gt; The application sends device status and questions to my own PC.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No per-token API fees.&lt;/strong&gt; I use existing hardware, although running that hardware still uses electricity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A replaceable model.&lt;/strong&gt; I can install another compatible model in Ollama and change &lt;code&gt;model_name&lt;/code&gt; in &lt;code&gt;config/agent.yml&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The phone does not need to run the model itself. A small Ruby client is enough to use the model through the Mac agent.&lt;/p&gt;

&lt;p&gt;That is enough for this weekend. I started with Ruby running on an old phone and no application in mind. Now the phone has a dashboard, and I have a project to write about.&lt;/p&gt;

&lt;p&gt;Eventually, I want to share the wider Ruby-on-3GS story at a Ruby user group, perhaps as my first lightning talk. This dashboard gives me one working example to show.&lt;/p&gt;

&lt;p&gt;My best friend seems pleased with the result. He is already thinking about what else to run on the phone.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>hf26challenge</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Reading The Pragmatic Programmer in the age of AI</title>
      <dc:creator>Dominik</dc:creator>
      <pubDate>Tue, 07 Jul 2026 13:51:39 +0000</pubDate>
      <link>https://dev.to/dominik_alberski/reading-the-pragmatic-programmer-in-the-age-of-ai-3jnf</link>
      <guid>https://dev.to/dominik_alberski/reading-the-pragmatic-programmer-in-the-age-of-ai-3jnf</guid>
      <description>&lt;p&gt;I bought &lt;em&gt;The Pragmatic Programmer&lt;/em&gt; a few years ago but never finished it. As part of my current effort to sharpen my fundamentals, I decided to finally read it from cover to cover.&lt;/p&gt;

&lt;p&gt;I expected it to feel slightly dated. The first edition was written in 1999. The second edition came out in 2019, just before generative AI reshaped how many of us work. Instead I found the opposite: most of the principles still hold. What changed isn't their validity — it's the level at which they apply.&lt;/p&gt;

&lt;p&gt;The first thing that stopped me was one sentence in chapter 2. The authors note, almost in passing, that there are still no compilers capable of generating software from a concise description of requirements. Reading that struck a strange tone in my mind. We still don't have the compiler they were imagining, but we do have LLMs and AI agents that occupy a surprisingly similar role — turning a few paragraphs of natural language into working software. A later chapter reinforces the feeling: the authors argue that programming can't be reduced to a mechanical process, or CASE tools would have replaced programmers long ago. History spent twenty years appearing to prove them right. Then it stopped being so obvious.&lt;/p&gt;

&lt;p&gt;That became the question I read the rest of the book with: if implementation is finally being automated, which of these ideas become &lt;em&gt;more&lt;/em&gt; important, and which become less?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Knowledge Portfolio, revisited
&lt;/h2&gt;

&lt;p&gt;Take the &lt;strong&gt;Knowledge Portfolio&lt;/strong&gt;. On the surface, AI seems to reduce the need to learn deeply. Why spend months mastering something an assistant can explain or generate in seconds?&lt;/p&gt;

&lt;p&gt;My strongest evidence to the contrary came from a React migration. I'm primarily a backend developer with little frontend experience. With AI's help I produced a UI that matched the mockups and met every functional requirement. To me, the code looked fine. The senior frontend engineers reviewing it disagreed — it ignored several established patterns in the codebase, and I didn't have enough experience to recognize the problems myself.&lt;/p&gt;

&lt;p&gt;Later, on smaller and more clearly defined tasks, I noticed something. AI followed project conventions much more reliably when I could extract and state those conventions explicitly. The better I understood the rules of the system, the more useful the AI became. The lesson wasn't that AI replaced expertise. It was that expertise determined whether I could recognize good output when I saw it.&lt;/p&gt;

&lt;p&gt;So learning and gaining expertise is still essential. Otherwise how will you notice mistakes in AI thinking? For me, the valuable investment is in mental models — functional programming, distributed systems, architecture, security, AI systems themselves. The goal is no longer accumulating syntax knowledge across many languages but understanding how software behaves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Orthogonality, DRY, and the cost of unclear thinking
&lt;/h2&gt;

&lt;p&gt;The idea that gained the most weight for me was &lt;strong&gt;orthogonality&lt;/strong&gt;. Clear boundaries between modules and responsibilities have always been good practice; in the AI era they're closer to a prerequisite. AI increasingly writes the implementation while the engineer defines interfaces, constraints, and responsibilities — and the quality of those boundaries determines how effectively AI can contribute. Vague definitions produce tangled, duplicated, hard-to-verify output. Clear ones produce something you can actually review and trust.&lt;/p&gt;

&lt;p&gt;This sharpens &lt;strong&gt;DRY&lt;/strong&gt;, too. The principle was always about duplicated &lt;em&gt;knowledge&lt;/em&gt;, not duplicated code — but AI scatters business logic across services and modules so reliably when left unsupervised that the distinction becomes impossible to ignore.&lt;/p&gt;

&lt;p&gt;There's one idea I initially thought was new, then reconsidered. I'd written in my notes that AI makes poor thinking &lt;em&gt;more expensive&lt;/em&gt; — a vague prompt burns tokens, time, and iterations. True, but engineers have always paid for unclear thinking. The currency used to be debugging and rework; now it's tokens and review cycles. The cost isn't new. What's new is how fast and how visibly it arrives. AI acts less like a new source of risk and more like a mirror: it doesn't create bad habits, it reflects them back faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Blackboard pattern, reframed
&lt;/h2&gt;

&lt;p&gt;Master your tools, invest in your environment — still valid. But increasingly the tool is operated by an &lt;em&gt;agent&lt;/em&gt;, not typed directly by a developer. Which makes plain text more central, not less: it's the medium through which intent reaches both humans and machines, in prompts, configuration files, and instructions. The skill that used to be "master your tools" is becoming "design workflows that you and an AI can both operate in effectively."&lt;/p&gt;

&lt;p&gt;That reframing shows up most clearly in the &lt;strong&gt;Blackboard pattern&lt;/strong&gt; — one of the book's more academic-feeling ideas, where independent specialists post partial solutions to a shared space until a solution emerges. It suddenly resembles modern multi-agent systems, where specialized agents collaborate through a shared space rather than a shared conversation.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi5ct4683vgwq4ycnbeqz.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi5ct4683vgwq4ycnbeqz.png" alt=" " width="800" height="353"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The reframe I keep coming back to is that what agents need to share isn't conversational context, which gets expensive fast, but structured knowledge: goals, decisions, findings, tasks.&lt;/p&gt;

&lt;p&gt;Shared knowledge rather than shared context.&lt;/p&gt;

&lt;p&gt;The more I experiment with agentic systems, the more I believe preserving useful knowledge matters more than preserving every step of the conversation that produced it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing: don't let AI do the thinking part
&lt;/h2&gt;

&lt;p&gt;One place that caught my interest was testing. The authors frame tests as valuable less for catching bugs than for forcing you to reason about your code before you write it — and that matches what TDD taught me: deciding how to test something is part of designing it.&lt;/p&gt;

&lt;p&gt;Which is why I'm cautious about handing testing entirely to an AI. Ask an agent to "add test coverage" and it will — but if it does the thinking too, you skip the part that was actually useful. You never ask what the code should guarantee, where it might break, or what "correct" means here. The tests exist, but the design reasoning they usually prompt didn't happen. So in practice I let the AI write the tests while I stay responsible for what to test — writing assertions is mechanical; deciding what to verify is where the thinking is.&lt;/p&gt;

&lt;h2&gt;
  
  
  The lizard brain, and where instinct comes from
&lt;/h2&gt;

&lt;p&gt;That worry generalizes. The authors tell you to trust your "lizard brain" — the instinct you develop from years of writing, debugging, and refactoring code yourself. That instinct is &lt;em&gt;earned&lt;/em&gt;, and it's earned specifically by doing the work. So what happens when AI does more and more of that work? Can you build the same intuition reviewing generated code that you'd build writing it yourself? Or will the next generation of developers grow a different instinct — one tuned to architecture, decomposition, and judging AI output rather than to implementation detail?&lt;/p&gt;

&lt;p&gt;I genuinely enjoy working with AI. It automates the repetitive parts and lets me explore ideas faster than before. But every problem it solves for me is a problem I don't solve myself — and solving problems is how experience accumulates. There's a balance to strike between using AI as a productivity tool and protecting the learning that produces judgment in the first place.&lt;/p&gt;

&lt;p&gt;That, in the end, is what rereading &lt;em&gt;The Pragmatic Programmer&lt;/em&gt; left me with. Software engineering is becoming less about writing code and more about making decisions — architecture, domain modeling, contracts, boundaries. You can't reason well about AI-generated code if you don't understand software engineering yourself. Knowledge hasn't become less important; it's shifted from producing code to evaluating and guiding it.&lt;/p&gt;

&lt;p&gt;The book didn't change my mind about AI. It sharpened it. The implementation was never the hardest part of the job. If AI keeps automating the mechanics, the ability to think clearly about software may become the most valuable skill a developer can have — and I'm genuinely unsure how we'll keep teaching it once the machine is doing the reps.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>tdd</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Rails Integration Testing with Fixtures: A Practical Approach for Complex Data Sets</title>
      <dc:creator>Dominik</dc:creator>
      <pubDate>Tue, 07 May 2024 07:47:48 +0000</pubDate>
      <link>https://dev.to/dominik_alberski/rails-integration-testing-with-fixtures-a-practical-approach-for-complex-data-sets-mce</link>
      <guid>https://dev.to/dominik_alberski/rails-integration-testing-with-fixtures-a-practical-approach-for-complex-data-sets-mce</guid>
      <description>&lt;h4&gt;
  
  
  The Challenge of Realistic Test Data
&lt;/h4&gt;

&lt;p&gt;I recently faced a unique challenge in writing integration specs for a Rails application. The goal was to simulate real-world complexity within our test environment, involving interactions across 13 models like orders, line items, and products. The catch? I needed to generate around 10,000 interrelated records, reflecting the kind of volume and complexity we see in production. Typically, I'd reach for the &lt;code&gt;factory_bot&lt;/code&gt; gem for test data generation, but due to the sheer volume and the need for persistent data, this method proved too slow. My ideal solution needed to enable the insertion of a large volume of data into the database efficiently, mirroring production data characteristics as closely as possible.&lt;/p&gt;

&lt;h4&gt;
  
  
  Solution: Tapping into Production DB and Fixtures
&lt;/h4&gt;

&lt;p&gt;After exploring several avenues, I discovered that leveraging Rails' default fixtures, combined with our staging database (which mirrors the last three months of production records), provided an unexpectedly effective solution. Here’s how I transformed real production data into a manageable, anonymized test suite:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fixture_creator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;anonymized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;root&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/spec/fixtures/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;table_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.yml"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"w+"&lt;/span&gt;
  &lt;span class="p"&gt;)&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;f&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;fixture_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;in_batches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;of: &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&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;batch&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&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;record&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;fixture_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;fixture_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
          &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;anonymized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt; &lt;span class="n"&gt;fixture_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_yaml&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Exploring the Code
&lt;/h4&gt;

&lt;p&gt;In this snippet, &lt;code&gt;fixture_creator&lt;/code&gt; function does the heavy lifting:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Batch Processing&lt;/strong&gt;: By processing the data in batches (&lt;code&gt;model.in_batches(of: 100)&lt;/code&gt;), we efficiently handle large volumes of data without overwhelming memory usage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unique Keys for Fixtures&lt;/strong&gt;: We create unique keys for each record (&lt;code&gt;"#{record.id}_#{fixture_data.size + 1}"&lt;/code&gt;), ensuring each entry in our fixtures file is distinct and easily identifiable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anonymization&lt;/strong&gt;: The &lt;code&gt;anonymized&lt;/code&gt; parameter is a hash that overrides sensitive attributes with dummy values, crucial for maintaining data privacy.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This approach significantly speeds up the process compared to individually creating records via &lt;code&gt;factory_bot&lt;/code&gt;. By directly exporting data from a staging environment (a subset of our production data), we ensure our tests are running against data that closely reflects real user interactions, both in volume and complexity.&lt;/p&gt;

&lt;h4&gt;
  
  
  Anonymization and Security
&lt;/h4&gt;

&lt;p&gt;One key aspect of using production data for testing is ensuring all sensitive information is thoroughly anonymized. In my case, the &lt;code&gt;anonymized&lt;/code&gt; hash includes mappings of original attribute names to anonymized values, applied to each record. This step is crucial not just for security and privacy, but also for complying with legal obligations like GDPR.&lt;/p&gt;

&lt;h4&gt;
  
  
  Conclusion
&lt;/h4&gt;

&lt;p&gt;This method of generating fixtures from production data presents a robust way to create realistic, high-volume test environments for Rails applications. It's particularly useful when the test scenarios are complex, and the realism of data interrelations is crucial. While this approach worked well in my case, it's important to tailor the solution to the specific needs and constraints of your project and always prioritize data security in any testing strategy.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>testing</category>
      <category>fixtures</category>
    </item>
    <item>
      <title>Memoization in Modern Ruby: How a Shape-Based Approach to Instance Variables Lookup Impacts Performance.</title>
      <dc:creator>Dominik</dc:creator>
      <pubDate>Thu, 02 Nov 2023 11:32:39 +0000</pubDate>
      <link>https://dev.to/dominik_alberski/memoization-in-modern-ruby-using-a-shape-based-approach-to-instance-variables-lookup-2lok</link>
      <guid>https://dev.to/dominik_alberski/memoization-in-modern-ruby-using-a-shape-based-approach-to-instance-variables-lookup-2lok</guid>
      <description>&lt;p&gt;Memoization is a classic technique in Ruby programming, greatly assisting in boosting performance. With Ruby's ongoing evolution, particularly towards a shape-based method in instance variable lookups, thoughtfully implementing memoization can be beneficial. Inspired by Jean Boussier's insights on RailsAtScale.com, this post highlights some suggested best practices.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why Consider Memoization?
&lt;/h4&gt;

&lt;p&gt;Memoization helps cache results of resource-heavy function calls, aiming to speed up subsequent executions. Ruby’s newer shape-based mechanism for instance variable lookups suggests organizing objects based on their instance variables. This approach influences performance and memory usage, but it's important to note that using memoization indiscriminately could lead to a range of object shapes, which might inadvertently affect performance. Understanding these subtleties can be helpful for developers looking to craft efficient and thoughtful Ruby code.&lt;/p&gt;

&lt;h4&gt;
  
  
  Suggested Best Practices
&lt;/h4&gt;

&lt;p&gt;Aligning with Ruby's shape-based approach and optimizing memoization strategies, developers might consider the following approaches:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Mindful Use of Memoized Variables&lt;/strong&gt;: It’s generally a good idea to keep memoized variables to a minimum. Ideally, aiming for one or two per class helps. If you find yourself needing more, initializing these variables to &lt;code&gt;nil&lt;/code&gt; in the constructor could be a proposed better approach:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt; 
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;     
        &lt;span class="vi"&gt;@expensive_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;   
    &lt;span class="k"&gt;end&lt;/span&gt;    

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;expensive_value&lt;/span&gt;     
        &lt;span class="vi"&gt;@expensive_value&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="n"&gt;compute_expensive_value&lt;/span&gt;   
    &lt;span class="k"&gt;end&lt;/span&gt; 
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Handling Falsy Values with Tokens&lt;/strong&gt;: When dealing with memoized values that might return falsy results, using a token could be a neat way to differentiate between an uninitialized state and a value that's legitimately falsy:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt;   
    &lt;span class="no"&gt;NOT_SET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;   
    &lt;span class="n"&gt;private_constant&lt;/span&gt; &lt;span class="ss"&gt;:NOT_SET&lt;/span&gt;    
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;     
        &lt;span class="vi"&gt;@expensive_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;NOT_SET&lt;/span&gt;   
    &lt;span class="k"&gt;end&lt;/span&gt;    

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;expensive_value&lt;/span&gt;     
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="vi"&gt;@expensive_value&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="vi"&gt;@expensive_value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;NOT_SET&lt;/span&gt;     &lt;span class="vi"&gt;@expensive_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;compute_expensive_value&lt;/span&gt;   
        &lt;span class="k"&gt;end&lt;/span&gt; 
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Wrapping Up
&lt;/h4&gt;

&lt;p&gt;Adjusting to Ruby's shape-based approach for variable lookups, we might consider refining our memoization tactics to help maintain efficient performance and thoughtful memory usage. For those interested in exploring this topic further, Jean Boussier provides a deeper dive in his article at &lt;a href="https://railsatscale.com/2023-10-24-memoization-pattern-and-object-shapes/" rel="noopener noreferrer"&gt;RailsAtScale&lt;/a&gt;. It's a thought-provoking read for anyone interested in evolving their Ruby coding practices in harmony with the latest language developments.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This summary reflects ideas from Jean Boussier's article published on October 24, 2023, on RailsAtScale.com. To broaden your understanding, the original article is well worth a read.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
