<?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: Luka Jioshvili</title>
    <description>The latest articles on DEV Community by Luka Jioshvili (@lazydoomslayer).</description>
    <link>https://dev.to/lazydoomslayer</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%2F3290294%2F8e5e1879-5e33-45e1-897f-7e08fb78030c.jpg</url>
      <title>DEV Community: Luka Jioshvili</title>
      <link>https://dev.to/lazydoomslayer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lazydoomslayer"/>
    <language>en</language>
    <item>
      <title>How I Build Vue 3 Applications (Part 4): Where API Logic Belongs</title>
      <dc:creator>Luka Jioshvili</dc:creator>
      <pubDate>Sat, 18 Jul 2026 11:47:09 +0000</pubDate>
      <link>https://dev.to/lazydoomslayer/how-i-build-vue-3-applications-part-4-where-api-logic-belongs-db9</link>
      <guid>https://dev.to/lazydoomslayer/how-i-build-vue-3-applications-part-4-where-api-logic-belongs-db9</guid>
      <description>&lt;p&gt;After deciding where components and composables belong, the next question naturally becomes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Where should API communication live?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is one of those decisions that seems unimportant when a project is small.&lt;/p&gt;

&lt;p&gt;Fetching data directly inside a component works.&lt;/p&gt;

&lt;p&gt;Putting API requests inside composables, stores, or utility files works too.&lt;/p&gt;

&lt;p&gt;The problem isn't whether those approaches work.&lt;/p&gt;

&lt;p&gt;The problem is consistency.&lt;/p&gt;

&lt;p&gt;As an application grows, API requests slowly become scattered throughout the codebase. Some live in components, others in composables, stores, or helper functions. Eventually, finding where a request actually happens becomes harder than writing it.&lt;/p&gt;

&lt;p&gt;Over the years, I've settled on one simple principle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Services own external communication.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Everything else builds on top of that.&lt;/p&gt;

&lt;p&gt;In this article, I'll explain why I keep API logic inside services, what I believe belongs there, and how that separation keeps large Vue applications easier to understand, test, and maintain.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why do API requests become scattered?
&lt;/h2&gt;

&lt;p&gt;Just like &lt;code&gt;components/&lt;/code&gt; and &lt;code&gt;composables/&lt;/code&gt;, API communication rarely becomes messy overnight.&lt;/p&gt;

&lt;p&gt;It usually starts with a single request inside a component.&lt;/p&gt;

&lt;p&gt;Eventually another component needs the same data.&lt;/p&gt;

&lt;p&gt;The request gets copied.&lt;/p&gt;

&lt;p&gt;Later a composable needs it.&lt;/p&gt;

&lt;p&gt;Then a store.&lt;/p&gt;

&lt;p&gt;Before long, the same responsibility exists in multiple places.&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%2Fyeqhurjwu464ss3dkmvt.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%2Fyeqhurjwu464ss3dkmvt.png" alt="Scatter Requests" width="800" height="774"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The problem is that &lt;strong&gt;communication no longer has a clear owner&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;One endpoint is called from a component.&lt;/p&gt;

&lt;p&gt;Another lives inside a composable.&lt;/p&gt;

&lt;p&gt;A third is buried inside a store.&lt;/p&gt;

&lt;p&gt;Maybe there's even a helper somewhere making requests too.&lt;/p&gt;

&lt;p&gt;Finding where a request happens becomes harder than writing it.&lt;/p&gt;

&lt;p&gt;Changing an endpoint, updating authentication, or debugging an issue now means searching through the project instead of going directly to one obvious place.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h3&gt;
  
  
  The real problem
&lt;/h3&gt;

&lt;p&gt;This isn't an HTTP problem.&lt;/p&gt;

&lt;p&gt;It's an &lt;strong&gt;ownership problem&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Just like components and composables, API communication should have one clear owner.&lt;/p&gt;

&lt;p&gt;For me, that owner is always the service.&lt;/p&gt;




&lt;p&gt;So before I create any new request, I don't ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Where can I call this API?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead, I ask something much more important:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Who owns communication with this external system?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That single question determines almost every decision I make.&lt;/p&gt;

&lt;p&gt;Instead of thinking about convenience, I think about responsibility.&lt;/p&gt;

&lt;p&gt;Every API request should have one clear owner.&lt;/p&gt;

&lt;p&gt;For me, that owner is always the service.&lt;/p&gt;

&lt;p&gt;Once that responsibility is clear, deciding where a request belongs becomes almost automatic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why components shouldn't fetch
&lt;/h2&gt;

&lt;p&gt;A component has one primary responsibility:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Present the UI and respond to user interactions.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Making HTTP requests is a completely different responsibility.&lt;/p&gt;

&lt;p&gt;When a component owns both presentation and communication, those concerns become tightly coupled.&lt;/p&gt;

&lt;p&gt;Instead of simply describing &lt;em&gt;how the interface behaves&lt;/em&gt;, the component now also knows &lt;em&gt;where the data comes from&lt;/em&gt; and &lt;em&gt;how to retrieve it&lt;/em&gt;.&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%2Fpnpia9vqx80ouyyrf0fk.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%2Fpnpia9vqx80ouyyrf0fk.png" alt="Component vs Service" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That might not seem like a problem when there's only one request.&lt;/p&gt;

&lt;p&gt;But applications rarely stay that small.&lt;/p&gt;

&lt;p&gt;Eventually, another component needs the same data.&lt;/p&gt;

&lt;p&gt;Now you have two choices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;duplicate the request&lt;/li&gt;
&lt;li&gt;extract it somewhere else&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of starting with a clear architecture, you're now refactoring because of duplication.&lt;/p&gt;

&lt;p&gt;By keeping API communication inside a service from the beginning, every component talks to the same abstraction.&lt;/p&gt;

&lt;p&gt;The component doesn't care whether the data comes from a REST API, GraphQL endpoint, local cache, or something else.&lt;/p&gt;

&lt;p&gt;Its job is simply to render the result.&lt;/p&gt;

&lt;p&gt;Here's what that separation looks like in practice:&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%2Ftxcxpdfxkbyc8sgspsjv.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%2Ftxcxpdfxkbyc8sgspsjv.png" alt="SFC EXAMPLE" width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that the component doesn't know anything about endpoints, Axios, authentication, or request configuration.&lt;/p&gt;

&lt;p&gt;It simply asks the service for the data it needs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why composables shouldn't fetch directly
&lt;/h2&gt;

&lt;p&gt;Unlike components, composables are designed to encapsulate reusable reactive behavior.&lt;/p&gt;

&lt;p&gt;Managing state, coordinating multiple pieces of logic, or exposing a reusable API to components are all great reasons to create one.&lt;/p&gt;

&lt;p&gt;But making HTTP requests is still a different responsibility.&lt;/p&gt;

&lt;p&gt;A composable shouldn't need to know &lt;em&gt;how&lt;/em&gt; data is retrieved.&lt;/p&gt;

&lt;p&gt;It should only know &lt;em&gt;when&lt;/em&gt; it needs data and &lt;em&gt;what&lt;/em&gt; to do with it.&lt;/p&gt;

&lt;p&gt;Instead of communicating with an API directly, a composable should delegate that responsibility to a service.&lt;/p&gt;




&lt;p&gt;The component owns the UI and decides &lt;strong&gt;when&lt;/strong&gt; the data is needed.&lt;/p&gt;

&lt;p&gt;The service owns &lt;strong&gt;how&lt;/strong&gt; that data is retrieved.&lt;/p&gt;

&lt;p&gt;Each layer stays focused on a single responsibility.&lt;/p&gt;

&lt;p&gt;That's exactly the same principle we've followed throughout this series.&lt;/p&gt;

&lt;p&gt;The same idea also applies to composables.&lt;/p&gt;

&lt;p&gt;Even though they're designed to encapsulate reusable logic, I don't believe they should communicate with APIs directly either.&lt;/p&gt;




&lt;h2&gt;
  
  
  What belongs in a service?
&lt;/h2&gt;

&lt;p&gt;For me, a service has one responsibility:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Communicate with external systems.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌────────────────────────────┐    ┌────────────────────────────┐
│       Service Owns.        │    │        Doesn't Own.        │
├────────────────────────────┤    ├────────────────────────────┤
│ HTTP Requests              │    │ ref()                      │
│ Endpoints                  │    │ computed()                 │
│ Query Parameters           │    │ Loading  State             │
│ DTO Mapping                │    │ Dialogs                    │
│ Request Configuration      │    │ Router                     │
│ External Communication     │    │ Notifications              │
└────────────────────────────┘    └────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing more.&lt;/p&gt;

&lt;p&gt;That includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;making HTTP requests&lt;/li&gt;
&lt;li&gt;defining endpoints&lt;/li&gt;
&lt;li&gt;configuring request parameters&lt;/li&gt;
&lt;li&gt;transforming responses into application models (or delegating to mappers)&lt;/li&gt;
&lt;li&gt;handling request-specific concerns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What doesn't belong in a service is just as important.&lt;/p&gt;

&lt;p&gt;A service shouldn't know anything about the user interface.&lt;/p&gt;

&lt;p&gt;That means no:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ref()&lt;/li&gt;
&lt;li&gt;computed()&lt;/li&gt;
&lt;li&gt;loading state&lt;/li&gt;
&lt;li&gt;dialogs&lt;/li&gt;
&lt;li&gt;notifications&lt;/li&gt;
&lt;li&gt;router navigation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those belong to the layer that's consuming the service.&lt;/p&gt;

&lt;p&gt;Keeping that boundary clear makes services reusable from components, composables, stores, or even server-side code without bringing UI concerns along with them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Services vs Composables
&lt;/h2&gt;

&lt;p&gt;Services and composables solve different problems.&lt;/p&gt;

&lt;p&gt;A service answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"How do I communicate with an external system?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A composable answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"How do I coordinate reusable reactive behavior?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A composable may call one or more services.&lt;/p&gt;

&lt;p&gt;A service should never know a composable exists.&lt;/p&gt;

&lt;p&gt;Think of it this way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Services communicate.&lt;/li&gt;
&lt;li&gt;Composables coordinate.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The architecture in practice
&lt;/h2&gt;

&lt;p&gt;At this point, every layer has a well-defined responsibility.&lt;/p&gt;

&lt;p&gt;Components focus on rendering the UI.&lt;/p&gt;

&lt;p&gt;Composables coordinate reusable reactive behavior.&lt;/p&gt;

&lt;p&gt;Services communicate with external systems.&lt;/p&gt;

&lt;p&gt;Each layer builds on top of the one below it without taking over its responsibility.&lt;/p&gt;

&lt;p&gt;Instead of every layer knowing how to talk to an API, there is a single, predictable path for data to flow through the application.&lt;/p&gt;

&lt;p&gt;That makes the architecture easier to understand, easier to test, and much easier to extend as the project grows.&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%2Fps29h6kx5qtlxa6h8j3r.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%2Fps29h6kx5qtlxa6h8j3r.png" alt="Decisions" width="800" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Components render. Composables coordinate. Services communicate.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One composable may orchestrate multiple services.&lt;/p&gt;

&lt;p&gt;A service, however, should never know that a composable exists.&lt;/p&gt;




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

&lt;p&gt;Throughout this series, we've been asking the same question from different angles.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where should this file live?&lt;/li&gt;
&lt;li&gt;Who owns this component?&lt;/li&gt;
&lt;li&gt;Who owns this logic?&lt;/li&gt;
&lt;li&gt;Who owns API communication?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The answer has always been the same:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Every responsibility should have a clear owner.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When ownership is clear, architecture becomes predictable.&lt;/p&gt;

&lt;p&gt;And when architecture is predictable, software becomes easier to build, test, and maintain.&lt;/p&gt;

&lt;p&gt;That's the principle I try to follow in every Vue application I build.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>typescript</category>
      <category>vue</category>
      <category>architecture</category>
    </item>
    <item>
      <title>How I Build Vue 3 Applications (Part 3): When Logic Belongs in a Composable</title>
      <dc:creator>Luka Jioshvili</dc:creator>
      <pubDate>Tue, 14 Jul 2026 17:43:18 +0000</pubDate>
      <link>https://dev.to/lazydoomslayer/how-i-build-vue-3-applications-part-3-when-logic-belongs-in-a-composable-2bpe</link>
      <guid>https://dev.to/lazydoomslayer/how-i-build-vue-3-applications-part-3-when-logic-belongs-in-a-composable-2bpe</guid>
      <description>&lt;p&gt;After deciding where components belong, the next question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Should this logic become a composable?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Vue makes extracting logic incredibly easy.&lt;/p&gt;

&lt;p&gt;Unfortunately, that also makes it easy to extract &lt;strong&gt;too much&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Before long, &lt;code&gt;composables/&lt;/code&gt; becomes another dumping ground full of &lt;code&gt;useSomething.ts&lt;/code&gt; files with unclear responsibilities.&lt;/p&gt;

&lt;p&gt;Over the years, I've settled on a simple decision process that helps me avoid that.&lt;/p&gt;

&lt;p&gt;This article isn't about using composables.&lt;/p&gt;

&lt;p&gt;It's about knowing &lt;strong&gt;when&lt;/strong&gt; to create one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why does &lt;code&gt;composables/&lt;/code&gt; become a dumping ground?
&lt;/h2&gt;

&lt;p&gt;Just like &lt;code&gt;components/&lt;/code&gt;, the &lt;code&gt;composables/&lt;/code&gt; folder usually starts out clean.&lt;/p&gt;

&lt;p&gt;A few composables solve obvious problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;useDebounce&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;useMediaQuery&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;useClipboard&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything feels organized.&lt;/p&gt;

&lt;p&gt;As the project grows, though, another pattern starts to appear.&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%2Fv0b6wmsmiooe49lbrdnr.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%2Fv0b6wmsmiooe49lbrdnr.png" alt="Composable Structure" width="800" height="705"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nothing looks technically wrong.&lt;/p&gt;

&lt;p&gt;But eventually, every piece of extracted logic becomes another &lt;code&gt;useSomething.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The folder grows, responsibilities become blurred, and it's no longer obvious what deserves to be a composable.&lt;/p&gt;

&lt;p&gt;The problem isn't having a lot of composables.&lt;/p&gt;

&lt;p&gt;It's extracting logic without a clear reason.&lt;/p&gt;

&lt;p&gt;Many developers start with a simple idea:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"This component is getting big."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So they move code into a composable.&lt;/p&gt;

&lt;p&gt;The component becomes shorter.&lt;/p&gt;

&lt;p&gt;But the application doesn't necessarily become simpler.&lt;/p&gt;

&lt;p&gt;Instead of moving responsibility, they only moved lines of code.&lt;/p&gt;

&lt;p&gt;That's an important distinction.&lt;/p&gt;

&lt;p&gt;A composable should exist because it owns a single, well-defined behavior, not because the component reached 300 lines.&lt;/p&gt;

&lt;p&gt;Every composable should own one clear behavior.&lt;/p&gt;

&lt;p&gt;If I can't describe that behavior in a single sentence, it's usually a sign that the composable is doing too much.&lt;/p&gt;




&lt;p&gt;So before I extract any logic, I don't ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Can I move this into a composable?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead, I ask something much more important:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Does this logic have its own responsibility?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's the question that drives almost every decision I make.&lt;/p&gt;

&lt;p&gt;A composable shouldn't exist simply because a component is getting bigger.&lt;/p&gt;

&lt;p&gt;It should exist because it owns &lt;strong&gt;one well-defined behavior&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For me, that's where the &lt;strong&gt;Single Responsibility Principle&lt;/strong&gt; naturally applies.&lt;/p&gt;

&lt;p&gt;Every composable should be focused, predictable, and easy to describe in a single sentence.&lt;/p&gt;

&lt;p&gt;Once I can clearly explain what it owns, deciding whether it deserves its own composable becomes much easier.&lt;/p&gt;

&lt;p&gt;That simple shift changes the question from:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Can I extract this?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;to&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Should this behavior have its own home?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What logic should stay inside the component?
&lt;/h2&gt;

&lt;p&gt;Not every piece of logic deserves its own composable.&lt;/p&gt;

&lt;p&gt;In fact, most of it doesn't.&lt;/p&gt;

&lt;p&gt;A common mistake is assuming that every &lt;code&gt;ref&lt;/code&gt;, &lt;code&gt;computed&lt;/code&gt;, or &lt;code&gt;watch&lt;/code&gt; should be extracted.&lt;/p&gt;

&lt;p&gt;I don't see composables as a way to reduce line count.&lt;/p&gt;

&lt;p&gt;I see them as a way to separate &lt;strong&gt;responsibility&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If the logic only exists to support a single piece of UI, I usually leave it where it is.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is a dialog open?&lt;/li&gt;
&lt;li&gt;Which tab is selected?&lt;/li&gt;
&lt;li&gt;Which row is hovered?&lt;/li&gt;
&lt;li&gt;Which accordion is expanded?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The component is the owner of that state.&lt;/p&gt;

&lt;p&gt;Here's a typical example:&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%2Fryvgd1qkuyx02537qqtc.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%2Fryvgd1qkuyx02537qqtc.png" alt="Local UI state that remains owned by the UserSettings component" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Those values belong to the component because the component owns that interaction.&lt;/p&gt;

&lt;p&gt;They don't have their own responsibility.&lt;/p&gt;

&lt;p&gt;They're simply part of how this component behaves.&lt;/p&gt;




&lt;h2&gt;
  
  
  When does logic deserve a composable?
&lt;/h2&gt;

&lt;p&gt;If the logic shouldn't stay inside the component, I ask another question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Does this logic have its own responsibility?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is where I stop thinking about file size.&lt;/p&gt;

&lt;p&gt;I start thinking about behavior.&lt;/p&gt;

&lt;p&gt;A composable shouldn't exist because the component became bigger.&lt;/p&gt;

&lt;p&gt;It should exist because the behavior deserves its own home.&lt;/p&gt;

&lt;p&gt;That's the decision process I follow:&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%2F4mqgyh5cjrt952zrbkco.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%2F4mqgyh5cjrt952zrbkco.png" alt="Decision Tree" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The number of lines isn't the deciding factor.&lt;/p&gt;

&lt;p&gt;A small piece of logic may deserve a composable if it owns a clear responsibility, while a much larger block of tightly coupled UI logic may still belong inside the component.&lt;/p&gt;

&lt;p&gt;The goal isn't to make components shorter.&lt;/p&gt;

&lt;p&gt;It's to make responsibilities easier to understand.&lt;/p&gt;




&lt;h2&gt;
  
  
  Generic vs Feature Composables
&lt;/h2&gt;

&lt;p&gt;Once I've decided that the behavior deserves its own composable, the next question becomes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Who owns it?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not every composable has the same owner.&lt;/p&gt;

&lt;p&gt;Some are generic and reusable across the entire application.&lt;/p&gt;

&lt;p&gt;Others exist because of one specific business feature.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generic composables
&lt;/h3&gt;

&lt;p&gt;Generic composables are business-agnostic.&lt;/p&gt;

&lt;p&gt;They solve reusable technical problems such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;useDebounce&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;useClipboard&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;useMediaQuery&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;useInterval&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;useWindowSize&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They should be easy to move into another Vue project without changing their behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  Feature composables
&lt;/h3&gt;

&lt;p&gt;Feature composables understand one business domain.&lt;/p&gt;

&lt;p&gt;Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;useBillingFilters&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;useInvoiceValidation&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;useProductSearch&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;useSessionTimer&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They may depend on domain types, services, stores, or business rules because that feature owns them.&lt;/p&gt;

&lt;p&gt;The distinction is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Generic composables own reusable behavior.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feature composables own domain-specific behavior.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Both are valid.&lt;/p&gt;

&lt;p&gt;The important part is that their ownership is clear.&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%2Fvu96uln4thed7cx0bvxc.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%2Fvu96uln4thed7cx0bvxc.png" alt="Tree" width="800" height="661"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Services vs Composables
&lt;/h2&gt;

&lt;p&gt;Creating a composable doesn't mean every piece of logic belongs inside it.&lt;/p&gt;

&lt;p&gt;One convention I follow consistently is keeping network requests inside services.&lt;/p&gt;

&lt;p&gt;A composable may coordinate reactive state such as loading, errors, or pagination, but the actual request belongs somewhere else.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌────────────┬──────────────────────────────────────┐
│ Component  │ Render the UI                        │
├────────────┼──────────────────────────────────────┤
│ Composable │ Manage reactive behaviour            │
├────────────┼──────────────────────────────────────┤
│ Service    │ Perform API / business operations    │
└────────────┴──────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps responsibilities clear.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Components&lt;/strong&gt; render the UI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composables&lt;/strong&gt; manage reactive behavior.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Services&lt;/strong&gt; perform operations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Knowing where to look for each responsibility makes the codebase much easier to navigate.&lt;/p&gt;




&lt;h2&gt;
  
  
  Composable or Store?
&lt;/h2&gt;

&lt;p&gt;A question I often ask is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Does this state belong to one workflow or the entire application?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the behavior belongs to one component or one feature, a composable is usually enough.&lt;/p&gt;

&lt;p&gt;If unrelated parts of the application need to share the same state, it probably belongs in a store.&lt;/p&gt;

&lt;p&gt;The distinction isn't about complexity.&lt;/p&gt;

&lt;p&gt;It's about ownership.&lt;/p&gt;

&lt;p&gt;A composable coordinates behavior.&lt;/p&gt;

&lt;p&gt;A store coordinates shared application state.&lt;/p&gt;




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

&lt;p&gt;There isn't a magic number of lines that tells you when to create a composable.&lt;/p&gt;

&lt;p&gt;What matters is responsibility.&lt;/p&gt;

&lt;p&gt;Some logic belongs inside the component.&lt;/p&gt;

&lt;p&gt;Some deserves its own composable.&lt;/p&gt;

&lt;p&gt;Some belongs in a service.&lt;/p&gt;

&lt;p&gt;Some belongs in a store.&lt;/p&gt;

&lt;p&gt;The important part is knowing &lt;strong&gt;why&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The goal isn't to create more abstractions.&lt;/p&gt;

&lt;p&gt;It's to make every piece of your application have a clear owner.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Extract responsibility, not lines of code.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's the rule I come back to whenever I'm deciding where new logic belongs.&lt;/p&gt;

</description>
      <category>vue</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>architecture</category>
    </item>
    <item>
      <title>How I Build Vue 3 Applications (Part 2): How I Decide Where Every Component Belongs</title>
      <dc:creator>Luka Jioshvili</dc:creator>
      <pubDate>Fri, 10 Jul 2026 14:31:44 +0000</pubDate>
      <link>https://dev.to/lazydoomslayer/how-i-build-vue-3-applications-part-2-how-i-decide-where-every-component-belongs-26ac</link>
      <guid>https://dev.to/lazydoomslayer/how-i-build-vue-3-applications-part-2-how-i-decide-where-every-component-belongs-26ac</guid>
      <description>&lt;p&gt;One of the hardest decisions in a growing Vue application isn't writing components.&lt;/p&gt;

&lt;p&gt;It's deciding &lt;strong&gt;who owns them&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Most projects don't struggle with this on day one.&lt;/p&gt;

&lt;p&gt;They struggle six months later, when hundreds of components exist and every new file raises the same question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Where should this component live?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Over the years, I've settled on a simple decision process that helps me answer that question consistently.&lt;/p&gt;

&lt;p&gt;In this article, I'll walk through the questions I ask before creating any new component and explain how those answers determine where it belongs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why does the &lt;code&gt;components/&lt;/code&gt; folder eventually become a mess?
&lt;/h2&gt;

&lt;p&gt;At the beginning of a project, almost &lt;strong&gt;any component organization&lt;/strong&gt; feels reasonable.&lt;/p&gt;

&lt;p&gt;With only a handful of components, it's easy to remember where everything lives because &lt;strong&gt;your mental map is still small&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;As the application grows, that changes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;New features introduce dialogs, cards, tables, forms, widgets, and other supporting components.&lt;/p&gt;

&lt;p&gt;Eventually, the &lt;code&gt;components/&lt;/code&gt; directory becomes a long list of unrelated files.&lt;/p&gt;

&lt;p&gt;Nothing is technically wrong.&lt;/p&gt;

&lt;p&gt;But every new component becomes a small architectural decision.&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%2Fp9zuwotwhyugz4nydp6j.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%2Fp9zuwotwhyugz4nydp6j.png" alt="Component Folder Overview" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The problem isn't the number of components.&lt;/p&gt;

&lt;p&gt;It's what happens to &lt;strong&gt;your mental model&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of immediately knowing where something belongs, you start relying on search. Related components become scattered, and the business context begins to disappear.&lt;/p&gt;

&lt;p&gt;You're no longer thinking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"I'm working on Billing."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead, you're jumping between dialogs, tables, widgets, and cards just to understand a single feature.&lt;/p&gt;

&lt;p&gt;As the project grows, understanding the impact of a change becomes harder, making accidental regressions more likely.&lt;/p&gt;

&lt;p&gt;For me, this isn't just a folder problem.&lt;/p&gt;

&lt;p&gt;It's a &lt;strong&gt;cognitive problem&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As I mentioned in &lt;strong&gt;Part 1&lt;/strong&gt;, I want to understand &lt;strong&gt;what I'm looking at before opening a file&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The same principle applies to components.&lt;/p&gt;

&lt;p&gt;So before I create a new component, I don't ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Which folder should I put this in?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead, I ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Who owns this component?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once I can answer that question, choosing where it belongs becomes almost obvious.&lt;/p&gt;




&lt;h2&gt;
  
  
  What makes a component a UI component?
&lt;/h2&gt;

&lt;p&gt;The first question I ask whenever I create a new component is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Can another feature reuse it?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the answer is &lt;strong&gt;yes&lt;/strong&gt;, it belongs in &lt;code&gt;components/ui&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;UI components are &lt;strong&gt;business-agnostic&lt;/strong&gt;. They shouldn't know anything about billing, users, products, sessions, or any other feature.&lt;/p&gt;

&lt;p&gt;Instead, they provide reusable building blocks that every feature can compose.&lt;/p&gt;

&lt;p&gt;Common examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Buttons&lt;/li&gt;
&lt;li&gt;Inputs&lt;/li&gt;
&lt;li&gt;Dialogs&lt;/li&gt;
&lt;li&gt;Tooltips&lt;/li&gt;
&lt;li&gt;Avatars&lt;/li&gt;
&lt;li&gt;Checkboxes&lt;/li&gt;
&lt;li&gt;Badges&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A good rule of thumb is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If I could copy this component into another project without changing its behavior, it's probably a UI component.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the decision tree I mentally follow whenever I create a component.&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%2Ffzyxb2jbbvskvtv1o07u.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%2Ffzyxb2jbbvskvtv1o07u.png" alt="Decisitons" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once that decision is made, the folder structure becomes almost obvious.&lt;/p&gt;

&lt;p&gt;Here's what that looks like:&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%2Fy71dgmw0k73iov0lwqjo.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%2Fy71dgmw0k73iov0lwqjo.png" alt="Structure" width="800" height="638"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that the folders are organized by &lt;strong&gt;UI responsibility&lt;/strong&gt;, not by business domain.&lt;/p&gt;

&lt;p&gt;Buttons live together.&lt;/p&gt;

&lt;p&gt;Inputs live together.&lt;/p&gt;

&lt;p&gt;Dialogs live together.&lt;/p&gt;

&lt;p&gt;Icons live together.&lt;/p&gt;

&lt;p&gt;Every category answers the same question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What kind of UI element is this?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The goal isn't to create the perfect hierarchy, it's to make new components obvious to place and existing ones easy to discover.&lt;/p&gt;




&lt;h2&gt;
  
  
  Feature Components
&lt;/h2&gt;

&lt;p&gt;If the component isn't reusable across the application, I ask another question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Is it owned by a single business feature?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the answer is &lt;strong&gt;yes&lt;/strong&gt;, it belongs in &lt;code&gt;components/features&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Feature components represent &lt;strong&gt;actual product functionality&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Unlike UI components, they understand the business they're built for.&lt;/p&gt;

&lt;p&gt;That means they're free to depend on services, stores, composables, domain models, and business rules, because &lt;strong&gt;that feature owns them&lt;/strong&gt;.&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%2Fnybjjigj9yzqi2rghsjo.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%2Fnybjjigj9yzqi2rghsjo.png" alt="Feature Components" width="800" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These components aren't designed to be shared across unrelated parts of the application.&lt;/p&gt;

&lt;p&gt;They're designed to solve &lt;strong&gt;one business problem&lt;/strong&gt; well.&lt;/p&gt;

&lt;p&gt;A good rule I follow is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If another feature needs this component, I don't immediately move it to &lt;code&gt;ui&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Duplicating a small component is often cheaper than introducing a premature abstraction.&lt;/p&gt;

&lt;p&gt;Only after a component proves it's truly shared do I consider promoting it into &lt;code&gt;components/ui&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Layout Components
&lt;/h2&gt;

&lt;p&gt;Not every component belongs to a business feature.&lt;/p&gt;

&lt;p&gt;Some components define &lt;strong&gt;the application's shell&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Business features live &lt;strong&gt;inside&lt;/strong&gt; them not the other way around.&lt;/p&gt;

&lt;p&gt;A simple rule I use is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If removing every business feature still leaves this component useful, it's probably a layout component.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here's what that relationship looks like:&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%2Fjsfiw42pjd4rwinc5umd.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%2Fjsfiw42pjd4rwinc5umd.png" alt="Application Shell" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Layout components aren't owned by Billing, Products, or Users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;They're owned by the application itself.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Transition Components
&lt;/h2&gt;

&lt;p&gt;Some components exist for one reason only:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Animation.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;They shouldn't contain business logic.&lt;/p&gt;

&lt;p&gt;They shouldn't know anything about billing, users, products, or sessions.&lt;/p&gt;

&lt;p&gt;Their only responsibility is providing reusable animations on top of Vue's &lt;code&gt;&amp;lt;Transition&amp;gt;&lt;/code&gt; component.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;FadeTransition&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ExpandYTransition&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ExpandXTransition&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Separating them keeps animations consistent throughout the application while avoiding duplicated transition code.&lt;/p&gt;

&lt;p&gt;More importantly, it creates &lt;strong&gt;one source of truth&lt;/strong&gt; for how things move.&lt;/p&gt;

&lt;p&gt;I don't want five slightly different fade animations scattered throughout the project.&lt;/p&gt;

&lt;p&gt;I want one.&lt;/p&gt;

&lt;p&gt;If I decide to tweak the animation timing, easing, or behavior, I change it once and every feature benefits.&lt;/p&gt;

&lt;p&gt;Here's what that looks like in practice.&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%2Fwel977fdhs2ex24ddcbg.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%2Fwel977fdhs2ex24ddcbg.png" alt="Transition Components" width="799" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's why transition components have their own home.&lt;/p&gt;

&lt;p&gt;Their responsibility isn't business logic.&lt;/p&gt;

&lt;p&gt;It's making the entire application feel consistent.&lt;/p&gt;




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

&lt;p&gt;There is no single "correct" way to organize components.&lt;/p&gt;

&lt;p&gt;Every application grows differently, every team develops its own conventions, and every architecture evolves over time.&lt;/p&gt;

&lt;p&gt;The approach I've shared isn't about finding the perfect folder structure.&lt;/p&gt;

&lt;p&gt;It's about reducing the number of decisions you have to make every day.&lt;/p&gt;

&lt;p&gt;For me, that starts with a single question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Who owns this component?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once I can answer that, where it belongs is usually obvious.&lt;/p&gt;

&lt;p&gt;That simple shift has made my Vue applications easier to navigate, easier to maintain, and easier to scale as they grow.&lt;/p&gt;

&lt;p&gt;Like the hybrid folder structure from Part 1, this isn't a universal rule, it's simply the approach I've refined over the years, and it continues to evolve with every project.&lt;/p&gt;

</description>
      <category>vue</category>
      <category>typescript</category>
      <category>architecture</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How I Build Vue 3 Applications (Part 1): Why I Use a Hybrid Folder Structure</title>
      <dc:creator>Luka Jioshvili</dc:creator>
      <pubDate>Mon, 06 Jul 2026 13:05:23 +0000</pubDate>
      <link>https://dev.to/lazydoomslayer/how-i-build-vue-3-applications-part-1-why-i-use-a-hybrid-folder-structure-48c2</link>
      <guid>https://dev.to/lazydoomslayer/how-i-build-vue-3-applications-part-1-why-i-use-a-hybrid-folder-structure-48c2</guid>
      <description>&lt;p&gt;When a Vue project starts, almost any folder structure works. With only a handful of components and pages, it’s easy to find what you’re looking for.&lt;/p&gt;

&lt;p&gt;The challenge begins as the application grows. More features are added, more developers join the project, and suddenly finding the right file takes longer than writing it.&lt;/p&gt;

&lt;p&gt;Over the years, I’ve experimented with different ways of organizing Vue applications. Instead of choosing between a &lt;strong&gt;responsibility-first&lt;/strong&gt; structure (&lt;code&gt;components&lt;/code&gt;, &lt;code&gt;services&lt;/code&gt;, &lt;code&gt;stores&lt;/code&gt;…) and a &lt;strong&gt;feature-first&lt;/strong&gt; structure (&lt;code&gt;billing&lt;/code&gt;, &lt;code&gt;users&lt;/code&gt;, &lt;code&gt;products&lt;/code&gt;…), I settled on combining both.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My approach is simple: organize the project by responsibility at the root, then organize business-specific code by feature inside those responsibilities.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I call this a &lt;strong&gt;hybrid folder structure&lt;/strong&gt;, and in this article I’ll explain why it has worked well for me.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Two Common Approaches
&lt;/h2&gt;

&lt;p&gt;In my experience, most Vue project structures fall into &lt;strong&gt;one of two categories&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Some teams organize everything by &lt;strong&gt;responsibility&lt;/strong&gt; (or file type), while others organize everything by &lt;strong&gt;feature&lt;/strong&gt; (or domain). Both approaches have their strengths, and both have trade-offs.&lt;/p&gt;

&lt;p&gt;Let's look at each one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Organizing by Responsibility
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;responsibility-first&lt;/strong&gt; structure groups files by &lt;strong&gt;what they do&lt;/strong&gt;.&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%2Fig8z9d5jk1p02pzc6xcb.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%2Fig8z9d5jk1p02pzc6xcb.png" alt="Responsibility Overview" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is probably the most common structure you'll see in Vue projects. It's &lt;strong&gt;simple&lt;/strong&gt;, &lt;strong&gt;familiar&lt;/strong&gt;, and easy for new developers to navigate. If you're looking for a component, you open &lt;code&gt;components/&lt;/code&gt;. If you need a Pinia store, you know exactly where to find it.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;small and medium-sized projects&lt;/strong&gt;, this works extremely well.&lt;/p&gt;

&lt;p&gt;However, as the application grows, a &lt;strong&gt;single feature&lt;/strong&gt; becomes spread across multiple directories. Implementing or modifying one feature often means jumping between &lt;code&gt;components/&lt;/code&gt;, &lt;code&gt;services/&lt;/code&gt;, &lt;code&gt;store/&lt;/code&gt;, &lt;code&gt;types/&lt;/code&gt;, and &lt;code&gt;composables/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The structure is &lt;strong&gt;predictable&lt;/strong&gt;, but the feature itself becomes &lt;strong&gt;fragmented&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Trade-off&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Finding a specific &lt;strong&gt;type of file&lt;/strong&gt; is easy.&lt;br&gt;
Working on a specific &lt;strong&gt;business feature&lt;/strong&gt; becomes harder.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Organizing by Feature
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;feature-first&lt;/strong&gt; structure takes the opposite approach.&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%2F8c8y2qi3qtx11vuyzts1.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%2F8c8y2qi3qtx11vuyzts1.png" alt="Feature Overfiew" width="799" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Everything related to a &lt;strong&gt;business feature&lt;/strong&gt; lives together. Components, composables, services, stores, and types are colocated inside the same directory.&lt;/p&gt;

&lt;p&gt;This makes working on a &lt;strong&gt;single feature&lt;/strong&gt; very convenient because almost everything you need is in one place.&lt;/p&gt;

&lt;p&gt;However, I personally found that this approach introduces a different challenge. &lt;strong&gt;Not everything in an application belongs to a single business feature.&lt;/strong&gt; Shared UI components, layouts, transitions, global composables, application configuration, and other &lt;strong&gt;cross-cutting concerns&lt;/strong&gt; still need their own place.&lt;/p&gt;

&lt;p&gt;For the kinds of applications I build, a &lt;strong&gt;purely feature-based&lt;/strong&gt; structure felt like solving one problem while creating another.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Trade-off&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Working on a specific &lt;strong&gt;feature&lt;/strong&gt; is easy.&lt;br&gt;
Organizing &lt;strong&gt;shared application code&lt;/strong&gt; becomes more challenging.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;After working with both approaches, I found myself naturally combining them.&lt;/p&gt;

&lt;p&gt;I wanted the &lt;strong&gt;predictability&lt;/strong&gt; of a &lt;strong&gt;responsibility-first&lt;/strong&gt; structure while keeping &lt;strong&gt;feature-specific code&lt;/strong&gt; grouped together.&lt;/p&gt;

&lt;p&gt;That eventually led me to the hybrid structure I use today.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hybrid Structure I Use
&lt;/h2&gt;

&lt;p&gt;My approach follows one simple principle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Responsibility first. Feature second.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At the root of the project, I organize files by &lt;strong&gt;responsibility&lt;/strong&gt;.&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%2Fxd1e8l3s7dwzc0b1t0a1.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%2Fxd1e8l3s7dwzc0b1t0a1.png" alt="Project Sturcture Overview" width="800" height="602"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every top-level folder answers the first question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What kind of file is this?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;components/&lt;/code&gt; contains UI components.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;composables/&lt;/code&gt; contains reusable reactive logic.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;services/&lt;/code&gt; contains business operations.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;store/&lt;/code&gt; contains Pinia stores.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;types/&lt;/code&gt; contains TypeScript contracts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, organizing by responsibility only solves &lt;strong&gt;half of the problem&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It tells me &lt;strong&gt;what&lt;/strong&gt; a file is, but not &lt;strong&gt;which feature&lt;/strong&gt; it belongs to.&lt;/p&gt;

&lt;p&gt;That's where the second part of my approach comes in.&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%2Fa3jpzykd3qkcjmpdtpiq.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%2Fa3jpzykd3qkcjmpdtpiq.png" alt="Src overview" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inside each responsibility, I organize code by &lt;strong&gt;feature&lt;/strong&gt; or &lt;strong&gt;business domain&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, components related to billing stay together, pricing services stay together, and product composables stay together.&lt;/p&gt;

&lt;p&gt;This keeps feature-specific code close together without sacrificing the predictability of the project's top-level structure.&lt;/p&gt;

&lt;p&gt;The result is neither a purely &lt;strong&gt;responsibility-first&lt;/strong&gt; nor a purely &lt;strong&gt;feature-first&lt;/strong&gt; architecture.&lt;/p&gt;

&lt;p&gt;Instead, it follows one simple principle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Responsibility first. Feature second.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  File Names Are Part of the Architecture
&lt;/h2&gt;

&lt;p&gt;One thing I care about a lot is file naming.&lt;/p&gt;

&lt;p&gt;I use suffixes like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pinia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;helper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vue&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This may look like a small detail, but it improves searchability a lot.&lt;/p&gt;

&lt;p&gt;When I search for product, I can quickly understand what each file does:&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%2F5k6s51ruggs12c0f5moq.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%2F5k6s51ruggs12c0f5moq.png" alt="Searching into" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The goal is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I should be able to understand what a file probably does before opening it.&lt;/p&gt;
&lt;/blockquote&gt;




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

&lt;p&gt;There is no single "correct" way to organize a Vue application.&lt;/p&gt;

&lt;p&gt;Every team, product, and codebase has different requirements.&lt;/p&gt;

&lt;p&gt;This hybrid approach has worked well for me because it keeps the project predictable while making feature-specific code easy to find and maintain. More importantly, it gives me a simple mental model that scales as the application grows.&lt;/p&gt;

&lt;p&gt;This is simply the approach I've refined over the years, and it continues to evolve with every project.&lt;/p&gt;

</description>
      <category>vue</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Building Software Beyond the Browser</title>
      <dc:creator>Luka Jioshvili</dc:creator>
      <pubDate>Thu, 14 May 2026 07:53:02 +0000</pubDate>
      <link>https://dev.to/lazydoomslayer/building-software-beyond-the-browser-175j</link>
      <guid>https://dev.to/lazydoomslayer/building-software-beyond-the-browser-175j</guid>
      <description>&lt;p&gt;For the last 3 years, most of my work lived inside the browser.&lt;/p&gt;

&lt;p&gt;Vue.js SPA development, Nuxt applications, frontend architecture, SEO optimizations the usual frontend ecosystem.&lt;/p&gt;

&lt;p&gt;And honestly, after some time, I started feeling limited.&lt;/p&gt;

&lt;p&gt;Not because frontend development became bad or boring, but because I felt like I was mostly moving inside the same boundaries over and over again.&lt;/p&gt;

&lt;p&gt;The only thing that still looked extremely interesting to me on the frontend side was probably Three.js, but I never really found a practical place where I could meaningfully use it.&lt;/p&gt;

&lt;p&gt;At some point, I realized I wanted to build software that could do more than a browser tab allows.&lt;/p&gt;

&lt;p&gt;I wanted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;direct operating system interaction&lt;/li&gt;
&lt;li&gt;process management&lt;/li&gt;
&lt;li&gt;local-first tooling&lt;/li&gt;
&lt;li&gt;background tasks&lt;/li&gt;
&lt;li&gt;hardware communication&lt;/li&gt;
&lt;li&gt;system utilities&lt;/li&gt;
&lt;li&gt;applications that continue running outside the browser lifecycle&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That curiosity slowly pushed me toward Rust.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem That Started Harbor Sweep
&lt;/h2&gt;

&lt;p&gt;Ironically, Harbor Sweep started because of a very small and annoying workflow issue.&lt;/p&gt;

&lt;p&gt;I was dealing with a Windows/JetBrains-related bug where ports sometimes remained occupied even after shutting down services or closing development environments.&lt;/p&gt;

&lt;p&gt;Technically, solving it in PowerShell was easy.&lt;/p&gt;

&lt;p&gt;But doing it repeatedly every day became annoying enough that I decided to build a small utility for myself.&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%2Fv9ia03jbmggsumtx6o30.jpg" 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%2Fv9ia03jbmggsumtx6o30.jpg" alt="Gui version of application" width="800" height="612"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At first, it was just meant to save me a few seconds.&lt;/p&gt;

&lt;p&gt;Nothing more.&lt;/p&gt;

&lt;p&gt;But eventually, that small utility became my Rust playground.&lt;/p&gt;

&lt;p&gt;I kept adding features, improving workflows, experimenting with architecture, and trying ideas I normally could not explore inside traditional SPA development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discovering TUI Applications
&lt;/h2&gt;

&lt;p&gt;Around that same period, I attended my first RustMeet in Gliwice.&lt;/p&gt;

&lt;p&gt;That event changed my perspective a lot.&lt;/p&gt;

&lt;p&gt;Until then, Rust still felt somewhat “distant” to me interesting, but maybe overhyped.&lt;/p&gt;

&lt;p&gt;The meetup made me realize people were actually building real systems, tools, and infrastructure with it.&lt;/p&gt;

&lt;p&gt;At that point, Harbor Sweep already had a GUI version.&lt;/p&gt;

&lt;p&gt;Then Orhun Parmaksız casually suggested:&lt;br&gt;
“Why not build it as a terminal UI application?”&lt;/p&gt;

&lt;p&gt;That idea immediately clicked for me.&lt;/p&gt;

&lt;p&gt;So I started experimenting with Ratatui.&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%2Fdqq984ew3kdxvdl8y5gb.jpg" 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%2Fdqq984ew3kdxvdl8y5gb.jpg" alt="Screenshot Basic Application TUi" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And honestly, building terminal applications felt surprisingly refreshing.&lt;/p&gt;

&lt;p&gt;You start thinking differently when the entire interface becomes keyboard-driven and event-based.&lt;/p&gt;

&lt;p&gt;The focus shifts from visual browser components to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;application state&lt;/li&gt;
&lt;li&gt;rendering loops&lt;/li&gt;
&lt;li&gt;event handling&lt;/li&gt;
&lt;li&gt;terminal constraints&lt;/li&gt;
&lt;li&gt;responsiveness&lt;/li&gt;
&lt;li&gt;workflow efficiency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It felt much closer to the operating system itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Desktop Software Changed My Engineering Mindset
&lt;/h2&gt;

&lt;p&gt;The deeper I went into Rust and desktop development, the more I realized how different the engineering mindset becomes outside the browser.&lt;/p&gt;

&lt;p&gt;In frontend development, many low-level details are abstracted away.&lt;/p&gt;

&lt;p&gt;With desktop and systems-oriented software, suddenly you start thinking about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;threads&lt;/li&gt;
&lt;li&gt;async workflows&lt;/li&gt;
&lt;li&gt;long-running processes&lt;/li&gt;
&lt;li&gt;local persistence&lt;/li&gt;
&lt;li&gt;OS APIs&lt;/li&gt;
&lt;li&gt;notifications&lt;/li&gt;
&lt;li&gt;background schedulers&lt;/li&gt;
&lt;li&gt;process management&lt;/li&gt;
&lt;li&gt;direct system interaction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The browser sandbox disappears.&lt;/p&gt;

&lt;p&gt;And with that freedom comes a completely different type of responsibility.&lt;/p&gt;

&lt;p&gt;What surprised me most is that learning Rust itself was not the hardest part for me.&lt;/p&gt;

&lt;p&gt;The documentation is genuinely excellent.&lt;/p&gt;

&lt;p&gt;And modern AI tooling helps a lot when you need practical explanations instead of spending hours watching crash courses.&lt;/p&gt;

&lt;p&gt;Async Rust is still something I continue learning, but overall, the language itself felt much more approachable than I originally expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Was Actually Hard
&lt;/h2&gt;

&lt;p&gt;The hardest part was not ownership or the borrow checker.&lt;/p&gt;

&lt;p&gt;It was shipping software properly.&lt;/p&gt;

&lt;p&gt;Packaging, installers, release pipelines, platform-specific behavior, distribution systems like Chocolatey or Snapcraft that was the part that forced me to adjust the most.&lt;/p&gt;

&lt;p&gt;Debugging also feels very different outside frontend environments.&lt;/p&gt;

&lt;p&gt;In browser development, the tooling is incredibly mature and immediate.&lt;/p&gt;

&lt;p&gt;Desktop applications force you to think closer to the operating system itself, and debugging often becomes much more low-level and environment-dependent.&lt;/p&gt;

&lt;p&gt;That transition took far more adaptation than the language syntax ever did.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond the Browser
&lt;/h2&gt;

&lt;p&gt;Looking back now, Harbor Sweep started as a lazy workaround for an annoying workflow problem.&lt;/p&gt;

&lt;p&gt;But over time, it slowly changed how I think about software engineering entirely.&lt;/p&gt;

&lt;p&gt;What originally started as:&lt;br&gt;
“I want to stop manually killing processes.”&lt;/p&gt;

&lt;p&gt;Eventually became:&lt;br&gt;
“I want to build software that works closer to the machine.”&lt;/p&gt;

&lt;p&gt;And honestly, that journey has been one of the most interesting parts of my engineering career so far.&lt;/p&gt;

&lt;h2&gt;
  
  
  References &amp;amp; Related Projects
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Project List in Linkdin: &lt;a href="https://www.linkedin.com/in/luka-jioshvili/details/projects/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/luka-jioshvili/details/projects/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Harbor Sweep GUI Version — &lt;a href="https://github.com/LazyDoomSlayer/harboor-sweep" rel="noopener noreferrer"&gt;https://github.com/LazyDoomSlayer/harboor-sweep&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Harbor Sweep TUI Version — &lt;a href="https://github.com/LazyDoomSlayer/harboor-sweep-tui" rel="noopener noreferrer"&gt;https://github.com/LazyDoomSlayer/harboor-sweep-tui&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Ratatui — &lt;a href="https://github.com/ratatui/ratatui" rel="noopener noreferrer"&gt;https://github.com/ratatui/ratatui&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Tauri v2 — &lt;a href="https://tauri.app" rel="noopener noreferrer"&gt;https://tauri.app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;RustMeet Gliwice — &lt;a href="https://www.linkedin.com/company/rustmeet" rel="noopener noreferrer"&gt;https://www.linkedin.com/company/rustmeet&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Orhun Parmaksız — &lt;a href="https://github.com/orhun" rel="noopener noreferrer"&gt;https://github.com/orhun&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rust</category>
      <category>tauri</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
    <item>
      <title>The 80% You Don’t See: What Tracking My Work Taught Me</title>
      <dc:creator>Luka Jioshvili</dc:creator>
      <pubDate>Fri, 24 Apr 2026 07:49:53 +0000</pubDate>
      <link>https://dev.to/lazydoomslayer/the-80-you-dont-see-what-tracking-my-work-taught-me-260k</link>
      <guid>https://dev.to/lazydoomslayer/the-80-you-dont-see-what-tracking-my-work-taught-me-260k</guid>
      <description>&lt;p&gt;I started tracking my work recently.&lt;br&gt;
Not with some system.&lt;br&gt;
Not with some productivity guru method.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Just… writing things down.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Here’s what came out of it.
&lt;/h2&gt;

&lt;p&gt;It wasn’t some big realization.&lt;/p&gt;

&lt;p&gt;More like small things… stacking up.&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%2F46jy2qtslulz81cisoxt.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%2F46jy2qtslulz81cisoxt.png" alt="my thoughts about tracking" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Q: Why did you start tracking your work in the first place?&lt;/p&gt;

&lt;p&gt;A:&lt;br&gt;
I think it’s needed.&lt;/p&gt;

&lt;p&gt;You need to appreciate your own progress even if nobody else sees it.&lt;br&gt;
Also, I wanted to understand:&lt;/p&gt;

&lt;p&gt;At this pace… where am I actually going?&lt;/p&gt;




&lt;p&gt;Q: What did you think you were doing well before tracking?&lt;/p&gt;

&lt;p&gt;A:&lt;br&gt;
Nothing specific.&lt;/p&gt;

&lt;p&gt;But now I think more.&lt;br&gt;
I compare more.&lt;br&gt;
And that makes it easier to decide what’s actually worth doing.&lt;/p&gt;




&lt;p&gt;Q: What did tracking immediately prove wrong?&lt;/p&gt;

&lt;p&gt;A:&lt;br&gt;
That tracking is some “online coach” thing.&lt;/p&gt;

&lt;p&gt;It’s not.&lt;/p&gt;

&lt;p&gt;It’s just:&lt;/p&gt;

&lt;p&gt;evaluating your own steps without judging yourself.&lt;/p&gt;

&lt;p&gt;Just understanding why you made certain decisions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Friction &amp;amp; Reality
&lt;/h3&gt;

&lt;p&gt;It started to feel like a 20/80 kind of thing.&lt;/p&gt;

&lt;p&gt;You see the 20%.&lt;/p&gt;

&lt;p&gt;But most of the work lives in the 80%.&lt;/p&gt;

&lt;p&gt;Ever had that feeling?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You think you worked all day…&lt;br&gt;
but can’t explain what you actually did.&lt;/p&gt;
&lt;/blockquote&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%2Fffivahd5zm70mb4nq7io.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%2Fffivahd5zm70mb4nq7io.png" alt="overview my idea" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Q: When you say “I worked all day” what does that actually mean?&lt;/p&gt;

&lt;p&gt;A:&lt;br&gt;
Depends.&lt;/p&gt;

&lt;p&gt;Sometimes “all day” == a few hours of real productivity.&lt;br&gt;
Other times, I can go 8 hours straight on my own projects.&lt;/p&gt;

&lt;p&gt;So yeah… “all day” doesn’t mean much by itself.&lt;/p&gt;




&lt;p&gt;Q: How much of your time is real output?&lt;/p&gt;

&lt;p&gt;A:&lt;br&gt;
At work -&amp;gt; a few hours of focused, real output.&lt;br&gt;
On my own stuff -&amp;gt; I can lock in for much longer.&lt;/p&gt;

&lt;p&gt;Different energy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Patterns I Didn’t Expect
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Not in a dramatic way.&lt;br&gt;
Just… quietly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Q: What pattern kept repeating?&lt;/p&gt;

&lt;p&gt;A:&lt;br&gt;
I kept asking myself:&lt;/p&gt;

&lt;p&gt;“Why am I not exploring things more?”&lt;/p&gt;

&lt;p&gt;I do other hobbies gaming, exploring music, random stuff&lt;br&gt;
but sometimes I skip the kind of work I actually enjoy&lt;br&gt;
(side projects, deeper exploration).&lt;/p&gt;




&lt;p&gt;Q: What surprised you about your behavior?&lt;/p&gt;

&lt;p&gt;A:&lt;br&gt;
I started questioning how I push myself:&lt;/p&gt;

&lt;p&gt;“Am I pushing in the right direction… or just pushing?”&lt;/p&gt;

&lt;p&gt;Also had this thought:&lt;/p&gt;

&lt;p&gt;maybe I could present something at a Rust conference one day.&lt;/p&gt;

&lt;p&gt;And these posts… might actually help with that.&lt;/p&gt;




&lt;p&gt;Q: What did you not want to admit?&lt;/p&gt;

&lt;p&gt;A:&lt;br&gt;
That I actually have experience.&lt;/p&gt;

&lt;p&gt;I’m not some “big” developer.&lt;br&gt;
But I do have things worth sharing.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Quiet Realization
&lt;/h3&gt;

&lt;p&gt;Q: Did tracking ever “hit” you hard?&lt;/p&gt;

&lt;p&gt;A:&lt;br&gt;
Not really.&lt;/p&gt;

&lt;p&gt;It’s more like a background process.&lt;br&gt;
I don’t think about it daily.&lt;/p&gt;

&lt;p&gt;Only when I sit down to reflect like writing this.&lt;/p&gt;




&lt;p&gt;What Actually Got Better&lt;/p&gt;

&lt;p&gt;Q: What did tracking help you understand?&lt;/p&gt;

&lt;p&gt;A:&lt;br&gt;
My habits.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what’s actually bad&lt;/li&gt;
&lt;li&gt;what’s just human&lt;/li&gt;
&lt;li&gt;what’s needed to not burn out&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not everything unproductive is “wrong”.&lt;/p&gt;

&lt;p&gt;Some of it is just… necessary.&lt;/p&gt;




&lt;p&gt;Q: Did anything become clearer?&lt;/p&gt;

&lt;p&gt;A:&lt;br&gt;
Yes.&lt;/p&gt;

&lt;p&gt;I started focusing on things that actually have an effect.&lt;/p&gt;

&lt;p&gt;Small things. Real things.&lt;/p&gt;

&lt;p&gt;Not “big ideas influenced by media”.&lt;/p&gt;

&lt;p&gt;I see people around me chasing huge things.&lt;br&gt;
I’m building something simple but useful.&lt;/p&gt;

&lt;p&gt;No AI hype.&lt;br&gt;
Just coding and solving problems.&lt;/p&gt;

&lt;p&gt;And honestly… that feels better.&lt;/p&gt;




&lt;h3&gt;
  
  
  Final Thought
&lt;/h3&gt;

&lt;p&gt;Q: Tracking my work didn’t make me more productive. It made me realize…&lt;/p&gt;

&lt;p&gt;A:&lt;br&gt;
…my habits.&lt;/p&gt;

&lt;p&gt;My progress.&lt;/p&gt;

&lt;p&gt;And that I should actually appreciate my own effort.&lt;/p&gt;

&lt;p&gt;Even small wins.&lt;br&gt;
Even messy ones.&lt;/p&gt;

&lt;p&gt;Because they add up.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What’s one thing you noticed about yourself?&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>productivity</category>
      <category>career</category>
      <category>programming</category>
    </item>
    <item>
      <title>I Don’t Blog to Teach: I Blog to Track My Own Journey</title>
      <dc:creator>Luka Jioshvili</dc:creator>
      <pubDate>Tue, 07 Apr 2026 18:30:41 +0000</pubDate>
      <link>https://dev.to/lazydoomslayer/i-dont-blog-to-teach-i-blog-to-track-my-own-journey-3mm</link>
      <guid>https://dev.to/lazydoomslayer/i-dont-blog-to-teach-i-blog-to-track-my-own-journey-3mm</guid>
      <description>&lt;p&gt;You see a lot of advice around blogging as a developer.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Build your personal brand.&lt;br&gt;
Grow an audience.&lt;br&gt;
Share knowledge.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s not why I started.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real reason
&lt;/h2&gt;

&lt;p&gt;I didn’t start blogging because I forget things.&lt;br&gt;
Actually, I don’t.&lt;/p&gt;

&lt;p&gt;But that’s not the point.&lt;/p&gt;

&lt;p&gt;What I do want is a clear view of my own roadmap, what I learned, when I learned it, and how I was thinking at that moment.&lt;/p&gt;

&lt;p&gt;Not just final solutions.&lt;/p&gt;

&lt;p&gt;But:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the confusion&lt;/li&gt;
&lt;li&gt;the decisions&lt;/li&gt;
&lt;li&gt;the small realizations&lt;/li&gt;
&lt;li&gt;the progress over time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s the part you lose if you don’t write things down.&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%2F8nchfgtwk6i2jwv47wos.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%2F8nchfgtwk6i2jwv47wos.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The question people asked me
&lt;/h2&gt;

&lt;p&gt;When I started posting, some friends asked me:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Why are you even writing blogs? Are you trying to impress someone?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And honestly, I get it.&lt;/p&gt;

&lt;p&gt;From the outside, it can look like that.&lt;/p&gt;

&lt;p&gt;But no.&lt;/p&gt;

&lt;p&gt;I’m not trying to impress anyone.&lt;/p&gt;

&lt;h2&gt;
  
  
  What blogging actually is for me
&lt;/h2&gt;

&lt;p&gt;For me, blogging is simple.&lt;/p&gt;

&lt;p&gt;It’s a way to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;track my progress&lt;/li&gt;
&lt;li&gt;document how my thinking evolves&lt;/li&gt;
&lt;li&gt;keep a record of what I’m building and learning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s basically a timeline of my developer journey.&lt;/p&gt;

&lt;p&gt;Something I can revisit later and understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;where I was&lt;/li&gt;
&lt;li&gt;what I struggled with&lt;/li&gt;
&lt;li&gt;what eventually clicked&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;As developers, we move fast.&lt;/p&gt;

&lt;p&gt;We jump between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tools&lt;/li&gt;
&lt;li&gt;frameworks&lt;/li&gt;
&lt;li&gt;problems&lt;/li&gt;
&lt;li&gt;ideas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even if you don’t forget things, you lose context.&lt;/p&gt;

&lt;p&gt;You forget:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;why you chose a specific approach&lt;/li&gt;
&lt;li&gt;what confused you at the time&lt;/li&gt;
&lt;li&gt;what changed your understanding&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That context is valuable.&lt;/p&gt;

&lt;p&gt;And that’s exactly what I want to keep.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this will help
&lt;/h2&gt;

&lt;p&gt;Right now, I’m just getting started.&lt;/p&gt;

&lt;p&gt;But I already know this will matter when I document things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;my homelab setup&lt;/li&gt;
&lt;li&gt;infrastructure experiments&lt;/li&gt;
&lt;li&gt;my shift from frontend (Vue) into Rust and systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because later, I won’t just care about what I did.&lt;/p&gt;

&lt;p&gt;I’ll care about how I got there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finally
&lt;/h2&gt;

&lt;p&gt;So no, I’m not blogging to teach.&lt;/p&gt;

&lt;p&gt;I’m blogging to track my own journey.&lt;/p&gt;

&lt;p&gt;To see how I evolve over time.&lt;/p&gt;

&lt;p&gt;If someone finds it useful along the way, that’s just a bonus.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>rust</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>From Vue to Rust: Why a Frontend Developer Started Writing Systems Tools</title>
      <dc:creator>Luka Jioshvili</dc:creator>
      <pubDate>Fri, 13 Mar 2026 18:46:56 +0000</pubDate>
      <link>https://dev.to/lazydoomslayer/from-vue-to-rust-why-a-frontend-developer-started-writing-systems-tools-2204</link>
      <guid>https://dev.to/lazydoomslayer/from-vue-to-rust-why-a-frontend-developer-started-writing-systems-tools-2204</guid>
      <description>&lt;h2&gt;
  
  
  Hook
&lt;/h2&gt;

&lt;p&gt;After spending several years working primarily with Vue and TypeScript, I started paying closer attention to the tools that power the developer ecosystem.&lt;/p&gt;

&lt;p&gt;While exploring modern terminal workflows and developer tooling, I kept encountering the same language again and again: &lt;strong&gt;Rust&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Tools like &lt;strong&gt;ripgrep&lt;/strong&gt; (used for fast searching in editors like Neovim), &lt;strong&gt;zoxide&lt;/strong&gt;, &lt;strong&gt;exa&lt;/strong&gt;, terminal applications like &lt;strong&gt;lazygit&lt;/strong&gt;, GPU-accelerated terminals like &lt;strong&gt;Alacritty&lt;/strong&gt;, and TUI frameworks such as &lt;strong&gt;Ratatui&lt;/strong&gt; are all part of the growing Rust ecosystem.&lt;/p&gt;

&lt;p&gt;Beyond terminal tools, Rust is also powering projects like the &lt;strong&gt;Zed editor&lt;/strong&gt;, the &lt;strong&gt;Biome JavaScript toolchain&lt;/strong&gt;, and desktop frameworks like &lt;strong&gt;Tauri&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Seeing Rust appear across so many modern developer tools made me curious:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What would it be like for a frontend developer to start building tools in Rust?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  My Background as a Frontend Developer
&lt;/h2&gt;

&lt;p&gt;My professional work is mostly in the frontend ecosystem. Over the past few years I’ve been working with &lt;strong&gt;Vue and TypeScript&lt;/strong&gt;, building web applications and focusing on the user-facing side of software.&lt;/p&gt;

&lt;p&gt;Like many frontend developers, most of my tooling and scripting traditionally lived in the &lt;strong&gt;Node.js ecosystem&lt;/strong&gt;. From build tools to small automation scripts, JavaScript was usually the default choice.&lt;/p&gt;

&lt;p&gt;But over time I started becoming more interested in developer tooling itself things like CLI utilities, terminal workflows, and small system-level tools that improve everyday development.&lt;/p&gt;

&lt;p&gt;That curiosity is what eventually led me to start exploring Rust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Decided to Try Rust
&lt;/h2&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%2Fwedxhzm8cifocsckqcpn.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%2Fwedxhzm8cifocsckqcpn.png" alt="Why I Decided to Try Rust" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My first real reason to try Rust came from experimenting with &lt;strong&gt;desktop applications using Tauri&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As someone coming from the frontend world, Tauri felt like a natural bridge. I could still use web technologies for the interface, while Rust handled the system-level parts of the application. That combination made Rust feel much more approachable than jumping straight into systems programming.&lt;/p&gt;

&lt;p&gt;After that first step, curiosity started taking over.&lt;/p&gt;

&lt;p&gt;I began exploring other areas where Rust is commonly used building &lt;strong&gt;CLI tools&lt;/strong&gt;, experimenting with &lt;strong&gt;terminal user interfaces using Ratatui&lt;/strong&gt;, and generally trying to understand how Rust applications interact more closely with the system.&lt;/p&gt;

&lt;p&gt;Several things about Rust were appealing at the same time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Memory safety&lt;/strong&gt; without a garbage collector&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt; of compiled binaries&lt;/li&gt;
&lt;li&gt;The ability to produce a &lt;strong&gt;single standalone executable&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;And honestly, the challenge of learning something very different from the JavaScript ecosystem&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rust felt like a language that encouraged thinking differently about how software is structured, especially compared to the object-oriented patterns I was used to from other languages.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Surprised Me When Learning Rust
&lt;/h2&gt;

&lt;p&gt;Starting with Rust was not as smooth as learning another JavaScript framework or library.&lt;/p&gt;

&lt;p&gt;Coming from a background where most of my experience was with object-oriented patterns and higher-level abstractions, Rust initially felt very different. Concepts like &lt;strong&gt;ownership&lt;/strong&gt;, &lt;strong&gt;borrowing&lt;/strong&gt;, and strict compile-time checks were confusing at first.&lt;/p&gt;

&lt;p&gt;The shift in thinking was probably the biggest challenge. Instead of relying on runtime behavior or flexible patterns, Rust encourages you to be explicit about how data moves through your program.&lt;/p&gt;

&lt;p&gt;At the beginning this can feel frustrating, but something interesting happens over time: the compiler starts to feel less like an obstacle and more like a guide.&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%2Fh9z3m44awah8uxhadpk1.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%2Fh9z3m44awah8uxhadpk1.png" alt="Rust compiler as teacher" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Rust’s compiler is extremely strict, but it also provides detailed feedback. Many mistakes that would normally show up as runtime bugs are caught much earlier during compilation.&lt;/p&gt;

&lt;p&gt;That experience changes how you think about writing software.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Rust Makes Sense (and When It Doesn't)
&lt;/h2&gt;

&lt;p&gt;One thing I learned while exploring Rust is that it isn’t meant to replace every tool or language in your workflow.&lt;/p&gt;

&lt;p&gt;For example, JavaScript and TypeScript are still excellent choices for building web applications, especially when rapid development and a large ecosystem are important. In frontend development, the productivity and flexibility of the JavaScript ecosystem are hard to beat.&lt;/p&gt;

&lt;p&gt;Rust, however, shines in slightly different areas.&lt;/p&gt;

&lt;p&gt;Because Rust compiles to a standalone binary and focuses heavily on performance and safety, it works extremely well for things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CLI tools&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;terminal applications&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;developer tooling&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;system-level utilities&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;desktop applications with frameworks like Tauri&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These kinds of programs often benefit from fast startup times, efficient resource usage, and strong guarantees around memory safety.&lt;/p&gt;

&lt;p&gt;For me, Rust didn’t replace the tools I was already using it simply opened a new category of problems that I could explore and build solutions for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where My Rust Journey Is Going Next
&lt;/h2&gt;

&lt;p&gt;Learning Rust has changed the way I think about building software.&lt;/p&gt;

&lt;p&gt;Even though my primary work is still focused on frontend development with Vue and TypeScript, Rust introduced me to a different layer of the software stack one that is closer to the system and focused on performance, reliability, and tooling.&lt;/p&gt;

&lt;p&gt;Right now I’m continuing to experiment with Rust by building small tools, exploring &lt;strong&gt;Tauri for desktop applications&lt;/strong&gt;, and learning more about &lt;strong&gt;terminal user interfaces using Ratatui&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For a frontend developer, Rust might feel unfamiliar at first, but that unfamiliarity is exactly what makes the journey interesting.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Stay Rusty 🦀&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>rust</category>
      <category>webdev</category>
      <category>tauri</category>
      <category>ratatui</category>
    </item>
    <item>
      <title>run.sh Diaries #5: Make It Yours – Forking, Tweaking &amp; Extending the Setup</title>
      <dc:creator>Luka Jioshvili</dc:creator>
      <pubDate>Thu, 12 Mar 2026 10:40:29 +0000</pubDate>
      <link>https://dev.to/lazydoomslayer/runsh-diaries-5-make-it-yours-forking-tweaking-extending-the-setup-40g4</link>
      <guid>https://dev.to/lazydoomslayer/runsh-diaries-5-make-it-yours-forking-tweaking-extending-the-setup-40g4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;TL;DR&lt;br&gt;
In this final post, I’ll show you how to take my Ubuntu WSL bootstrap and make it your own.&lt;/p&gt;

&lt;p&gt;Whether you want to add dotfiles, set up Git identity, or install completely different tools, this structure makes it easy to fork, extend, and personalize.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why Customization Matters
&lt;/h2&gt;

&lt;p&gt;This project works for me, but &lt;strong&gt;you should own your environment&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;My goal from the beginning was to make this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Modular (each tool gets its own script)&lt;/li&gt;
&lt;li&gt;Transparent (readable Bash, no black-box logic)&lt;/li&gt;
&lt;li&gt;Easy to tweak (just edit configs or scripts)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This isn’t a framework, it’s a blueprint.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Add or Remove Packages
&lt;/h2&gt;

&lt;p&gt;You can edit &lt;a href="https://github.com/LazyDoomSlayer/os-bootstraps/blob/main/ubuntu/packages.conf" rel="noopener noreferrer"&gt;&lt;code&gt;packages.conf&lt;/code&gt;&lt;/a&gt; to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add tools you always install (e.g. &lt;code&gt;jq&lt;/code&gt;, &lt;code&gt;htop&lt;/code&gt;, &lt;code&gt;gh&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Remove things you don’t use (e.g. &lt;code&gt;yazi&lt;/code&gt;, &lt;code&gt;btop&lt;/code&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="nv"&gt;SYSTEM_UTILS_APT&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
  wget curl jq htop
&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;DEV_TOOLS_SNAP&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
  nvim
&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These arrays are passed into helper functions from &lt;code&gt;utils.sh&lt;/code&gt;, which handle install logic cleanly.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Add Your Own Scripts
&lt;/h2&gt;

&lt;p&gt;To add new tooling (like Go, Deno, or Rust), follow this pattern:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new file like &lt;code&gt;go-setup.sh&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add install logic using &lt;code&gt;apt&lt;/code&gt;, Snap, curl, or custom methods&lt;/li&gt;
&lt;li&gt;Append it to the &lt;code&gt;SCRIPTS=(...)&lt;/code&gt; array in &lt;code&gt;run.sh&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;readonly &lt;/span&gt;&lt;span class="nv"&gt;SCRIPTS&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
  ...
  &lt;span class="s2"&gt;"go-setup.sh"&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Include Dotfiles or Shell Config
&lt;/h2&gt;

&lt;p&gt;Want to bootstrap your &lt;code&gt;.bashrc&lt;/code&gt;, &lt;code&gt;.tmux.conf&lt;/code&gt;, or &lt;code&gt;.gitconfig&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;You can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clone your dotfiles repo in a new &lt;code&gt;dotfiles-setup.sh&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Symlink them or append to existing configs&lt;/li&gt;
&lt;li&gt;Set aliases, functions, or themes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is also a good place to add default Git config, like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.name &lt;span class="s2"&gt;"Your Name"&lt;/span&gt;
git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.email &lt;span class="s2"&gt;"you@example.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Handle Secrets and Git Identity
&lt;/h2&gt;

&lt;p&gt;Avoid committing private tokens or configs. Instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep a local &lt;code&gt;~/.local-setup.sh&lt;/code&gt; script with personal logic&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;.local-setup.sh&lt;/code&gt; to your &lt;code&gt;.gitignore&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Source it at the end of &lt;code&gt;run.sh&lt;/code&gt; (if it exists)
&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="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.local-setup.sh"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Running local custom setup..."&lt;/span&gt;
  bash &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.local-setup.sh"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. WSL-Specific Optimizations
&lt;/h2&gt;

&lt;p&gt;Some tweaks worth adding if you run WSL full time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a &lt;code&gt;.wslconfig&lt;/code&gt; on Windows for better resource limits&lt;/li&gt;
&lt;li&gt;Add mounts or performance tweaks in &lt;code&gt;/etc/wsl.conf&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Disable unnecessary services that don’t apply in WSL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can even add a WSL check to &lt;code&gt;run.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qi&lt;/span&gt; microsoft /proc/version &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Running under WSL"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Fork It, Extend It, Own It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This whole project is meant to be cloned, forked, and hacked.&lt;/li&gt;
&lt;li&gt;Start with mine, and make it match your exact stack and preferences.&lt;/li&gt;
&lt;li&gt;If you build something cool from this, I’d love to see it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Recap
&lt;/h2&gt;

&lt;p&gt;Over the last 5 posts, you’ve seen how I:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Automated my WSL Ubuntu setup from scratch&lt;/li&gt;
&lt;li&gt;Wrote modular Bash scripts with smart defaults&lt;/li&gt;
&lt;li&gt;Bootstrapped a full terminal dev stack&lt;/li&gt;
&lt;li&gt;Installed Node, Python, and Docker with zero hassle&lt;/li&gt;
&lt;li&gt;Built a foundation that anyone can extend&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Takeaway?&lt;/p&gt;

&lt;p&gt;Automate the boring stuff, personalize the rest, and keep improving it. &lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>linux</category>
    </item>
    <item>
      <title>run.sh Diaries #4: From Zero to Coding-Ready with Node, Python &amp; Docker</title>
      <dc:creator>Luka Jioshvili</dc:creator>
      <pubDate>Tue, 03 Mar 2026 14:00:56 +0000</pubDate>
      <link>https://dev.to/lazydoomslayer/runsh-diaries-4-from-zero-to-coding-ready-with-node-python-docker-h9</link>
      <guid>https://dev.to/lazydoomslayer/runsh-diaries-4-from-zero-to-coding-ready-with-node-python-docker-h9</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;TL;DR&lt;br&gt;
This post covers how my WSL bootstrap sets up Node.js, Python, and Docker and the core tools I use for frontend, backend, scripting, and container work.&lt;br&gt;
Each tool has its own setup script, designed to be modular, idempotent, and fast.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why These Tools?
&lt;/h2&gt;

&lt;p&gt;My day-to-day work (and open source projects) span across:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend apps (Vue, Nuxt, TypeScript)&lt;/li&gt;
&lt;li&gt;Scripts, automation, CLI tools (Python)&lt;/li&gt;
&lt;li&gt;Testing infrastructure, running services, and debugging (Docker)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So my bootstrap includes all of them by default and no more grabbing install commands from old gists or StackOverflow every time I set up WSL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Node.js Setup – &lt;a href="https://github.com/LazyDoomSlayer/os-bootstraps/blob/main/ubuntu/node-setup.sh" rel="noopener noreferrer"&gt;&lt;code&gt;node-setup.sh&lt;/code&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I install &lt;strong&gt;Node.js LTS (v22)&lt;/strong&gt; using &lt;a href="https://github.com/nvm-sh/nvm" rel="noopener noreferrer"&gt;&lt;code&gt;nvm&lt;/code&gt;&lt;/a&gt; because it's the most flexible method for switching versions.&lt;/p&gt;

&lt;h3&gt;
  
  
  What the script does:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Installs or updates NVM&lt;/li&gt;
&lt;li&gt;Loads it into the shell&lt;/li&gt;
&lt;li&gt;Installs Node.js v22 and sets it as default&lt;/li&gt;
&lt;li&gt;Installs global tools: &lt;code&gt;pnpm&lt;/code&gt; and &lt;code&gt;yarn&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Disables Yarn telemetry&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why this approach?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Versioning via NVM makes switching Node versions trivial&lt;/li&gt;
&lt;li&gt;I use &lt;code&gt;pnpm&lt;/code&gt; for speed and monorepos, and &lt;code&gt;yarn&lt;/code&gt; where needed
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nvm &lt;span class="nb"&gt;install &lt;/span&gt;22
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; pnpm yarn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output is printed to verify versions and check everything succeeded.&lt;/p&gt;

&lt;h2&gt;
  
  
  Python Setup – &lt;a href="https://github.com/LazyDoomSlayer/os-bootstraps/blob/main/ubuntu/python-setup.sh" rel="noopener noreferrer"&gt;&lt;code&gt;python-setup.sh&lt;/code&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Python is everywhere and whether I’m scripting quick tools, using CLI apps, or running data scripts.&lt;/p&gt;

&lt;h3&gt;
  
  
  What this setup covers:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Installs Python via APT&lt;/li&gt;
&lt;li&gt;Installs &lt;code&gt;pip&lt;/code&gt;, &lt;code&gt;venv&lt;/code&gt;, and upgrades &lt;code&gt;setuptools&lt;/code&gt;, &lt;code&gt;wheel&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;Prepares the environment for creating isolated Python workspaces&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;This setup is intentionally minimal and  just enough to bootstrap Python for development, automation, and further customization.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Docker Setup – &lt;a href="https://github.com/LazyDoomSlayer/os-bootstraps/blob/main/ubuntu/docker-setup.sh" rel="noopener noreferrer"&gt;&lt;code&gt;docker-setup.sh&lt;/code&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I use Docker for containers when testing services or building isolated environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  What the script does:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Installs Docker via APT (using official repo)&lt;/li&gt;
&lt;li&gt;Adds the current user to the &lt;code&gt;docker&lt;/code&gt; group (so no need for &lt;code&gt;sudo&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Enables the Docker service&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why this matters:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;WSL2 supports Docker with great performance&lt;/li&gt;
&lt;li&gt;You shouldn’t have to use &lt;code&gt;sudo&lt;/code&gt; to run &lt;code&gt;docker&lt;/code&gt; in dev environments&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Docker is optional, but including it makes the bootstrap future-proof if I need containers down the line.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  One Bootstrap to Install Them All
&lt;/h2&gt;

&lt;p&gt;All of these are automatically set up when I run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./run.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I don’t have to worry about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which method to use (&lt;code&gt;nvm&lt;/code&gt;, APT, Snap, curl?)&lt;/li&gt;
&lt;li&gt;Forgetting steps like adding users to Docker groups&lt;/li&gt;
&lt;li&gt;Repeating things across different machines&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Future Improvements
&lt;/h2&gt;

&lt;p&gt;Things I might add later:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Version pinning via &lt;code&gt;.nvmrc&lt;/code&gt; or &lt;code&gt;.tool-versions&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Python virtualenv examples (or Poetry setup)&lt;/li&gt;
&lt;li&gt;Docker Compose install&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next: Make It Yours
&lt;/h2&gt;

&lt;p&gt;In run.sh Diaries #5, I’ll show you how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add your own scripts&lt;/li&gt;
&lt;li&gt;Customize package lists&lt;/li&gt;
&lt;li&gt;Include dotfiles or Git config&lt;/li&gt;
&lt;li&gt;Extend the bootstrap for your own stack&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>linux</category>
    </item>
    <item>
      <title>run.sh Diaries #3: My Terminal Stack – tmux, zoxide, and lazygit</title>
      <dc:creator>Luka Jioshvili</dc:creator>
      <pubDate>Tue, 24 Feb 2026 07:45:26 +0000</pubDate>
      <link>https://dev.to/lazydoomslayer/runsh-diaries-3-my-terminal-stack-tmux-zoxide-and-lazygit-4hmm</link>
      <guid>https://dev.to/lazydoomslayer/runsh-diaries-3-my-terminal-stack-tmux-zoxide-and-lazygit-4hmm</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;TL;DR&lt;br&gt;
This post walks through the terminal tools I bootstrap with my WSL setup: &lt;code&gt;tmux&lt;/code&gt; for multitasking, &lt;code&gt;zoxide&lt;/code&gt; for lightning-fast directory switching, and &lt;code&gt;lazygit&lt;/code&gt; for painless Git management. &lt;br&gt;
These aren’t just cool tools and they’re essentials in my terminal-first workflow.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why a Terminal Stack?
&lt;/h2&gt;

&lt;p&gt;I’ve said it before: I prefer a &lt;strong&gt;terminal-first&lt;/strong&gt; workflow.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Modern IDEs? Too heavy.&lt;/li&gt;
&lt;li&gt;GUI Git clients? Slow and mouse-driven.&lt;/li&gt;
&lt;li&gt;I want &lt;strong&gt;speed&lt;/strong&gt;, &lt;strong&gt;control&lt;/strong&gt;, and tools that work over SSH, WSL, or raw Linux.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I built my environment around powerful CLI tools that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Launch instantly&lt;/li&gt;
&lt;li&gt;Don’t eat memory&lt;/li&gt;
&lt;li&gt;Let me work efficiently in any shell or session&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s break down the tools I install automatically via my bootstrap scripts.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/tmux/tmux" rel="noopener noreferrer"&gt;tmux&lt;/a&gt; – Terminal Multiplexing
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;tmux&lt;/code&gt; lets me split one terminal into multiple panes and windows. I use it to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run multiple shells or logs side-by-side&lt;/li&gt;
&lt;li&gt;Persist sessions even after closing the terminal&lt;/li&gt;
&lt;li&gt;Combine editor + server + shell in one view&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why I love it:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Clean, scriptable config&lt;/li&gt;
&lt;li&gt;Always available in headless setups&lt;/li&gt;
&lt;li&gt;Works beautifully with &lt;code&gt;neovim&lt;/code&gt;, &lt;code&gt;fzf&lt;/code&gt;, and my dotfiles&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/tmux-plugins/tpm" rel="noopener noreferrer"&gt;TPM&lt;/a&gt; – Tmux Plugin Manager
&lt;/h2&gt;

&lt;p&gt;My &lt;code&gt;tpm-setup.sh&lt;/code&gt; script installs &lt;a href="https://github.com/tmux-plugins/tpm" rel="noopener noreferrer"&gt;TPM&lt;/a&gt; (Tmux Plugin Manager) which makes installing and updating tmux plugins painless. It lives in &lt;code&gt;~/.tmux/plugins&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/ajeetdsouza/zoxide" rel="noopener noreferrer"&gt;`zoxide&lt;/a&gt;&lt;code&gt; – Smarter &lt;/code&gt;cd`
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/ajeetdsouza/zoxide" rel="noopener noreferrer"&gt;&lt;code&gt;zoxide&lt;/code&gt;&lt;/a&gt; is a fast, smarter alternative to &lt;code&gt;cd&lt;/code&gt;. You just type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;z GitHub
z os-bootstraps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…and it jumps to your most-used directories.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why I use it:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Tracks recent paths automatically&lt;/li&gt;
&lt;li&gt;Works across shells&lt;/li&gt;
&lt;li&gt;Insanely efficient once you get used to it&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/jesseduffield/lazygit" rel="noopener noreferrer"&gt;lazygit&lt;/a&gt; – Git, But Better
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/jesseduffield/lazygit" rel="noopener noreferrer"&gt;&lt;code&gt;lazygit&lt;/code&gt;&lt;/a&gt; is a TUI-based Git interface I use daily to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Switch branches&lt;/li&gt;
&lt;li&gt;Stage/commit&lt;/li&gt;
&lt;li&gt;Resolve merge conflicts&lt;/li&gt;
&lt;li&gt;Review logs and diffs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s keyboard-first, extremely fast, and integrates perfectly into a &lt;code&gt;tmux&lt;/code&gt; workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  All of These Are Auto-Installed
&lt;/h2&gt;

&lt;p&gt;Once I run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./run.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My terminal stack: &lt;code&gt;tmux&lt;/code&gt;, &lt;code&gt;zoxide&lt;/code&gt;, &lt;code&gt;lazygit&lt;/code&gt;, &lt;code&gt;neovim&lt;/code&gt;, and more is ready to go. No more downloading, aliasing, or reconfiguring things manually.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next: Languages and Containers
&lt;/h2&gt;

&lt;p&gt;In run.sh Diaries #4, I’ll walk you through how I set up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node.js&lt;/strong&gt; with NVM&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python&lt;/strong&gt; environments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker&lt;/strong&gt; with user group access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And how it all ties into real-world frontend + backend dev work.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>linux</category>
    </item>
    <item>
      <title>run.sh Diaries #2: The Bash Behind the Bootstrap</title>
      <dc:creator>Luka Jioshvili</dc:creator>
      <pubDate>Tue, 17 Feb 2026 07:54:15 +0000</pubDate>
      <link>https://dev.to/lazydoomslayer/runsh-diaries-2-the-bash-behind-the-bootstrap-1e9</link>
      <guid>https://dev.to/lazydoomslayer/runsh-diaries-2-the-bash-behind-the-bootstrap-1e9</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;TL;DR&lt;/p&gt;

&lt;p&gt;In this post, I’ll walk through the internal logic of how my WSL bootstrap works and let’s focus on &lt;code&gt;run.sh&lt;/code&gt;, &lt;code&gt;utils.sh&lt;/code&gt;, and &lt;code&gt;packages.conf&lt;/code&gt;. You’ll learn how I made the setup modular, safe to re-run, and easy to extend.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Big Picture
&lt;/h3&gt;

&lt;p&gt;In &lt;a href="https://dev.to/lazydoomslayer/runsh-diaries-1-automating-my-ubuntu-dev-environment-1gpi"&gt;Part 1&lt;/a&gt;, I talked about why I built this and now let’s explore &lt;strong&gt;how it works&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At the heart of it is a simple philosophy:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Don’t repeat yourself.&lt;/p&gt;

&lt;p&gt;Don’t install what’s already there.&lt;/p&gt;

&lt;p&gt;Keep everything scriptable and modular.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The setup revolves around three key components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;run.sh&lt;/code&gt; – the orchestrator&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;utils.sh&lt;/code&gt; – helper functions&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;packages.conf&lt;/code&gt; – what to install&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s break each down.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. &lt;code&gt;run.sh&lt;/code&gt; – The Orchestrator
&lt;/h2&gt;

&lt;p&gt;This script is the &lt;strong&gt;entry point&lt;/strong&gt; of the whole bootstrap.&lt;/p&gt;

&lt;h3&gt;
  
  
  Responsibilities:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Define the list of setup scripts to run&lt;/li&gt;
&lt;li&gt;Execute them one by one&lt;/li&gt;
&lt;li&gt;Log their progress&lt;/li&gt;
&lt;li&gt;Fail fast if something breaks&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Structure:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;readonly &lt;/span&gt;&lt;span class="nv"&gt;SCRIPTS&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
  &lt;span class="s2"&gt;"node-setup.sh"&lt;/span&gt;
  &lt;span class="s2"&gt;"rust-setup.sh"&lt;/span&gt;
  &lt;span class="s2"&gt;"docker-setup.sh"&lt;/span&gt;
  &lt;span class="s2"&gt;"lazygit-setup.sh"&lt;/span&gt;
  &lt;span class="s2"&gt;"python-setup.sh"&lt;/span&gt;
  &lt;span class="s2"&gt;"zoxide-setup.sh"&lt;/span&gt;
  &lt;span class="s2"&gt;"tmux-config-setup.sh"&lt;/span&gt;
  &lt;span class="s2"&gt;"tpm-setup.sh"&lt;/span&gt;
  &lt;span class="s2"&gt;"nvim-setup.sh"&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each script is passed into the &lt;code&gt;run_script()&lt;/code&gt; function from &lt;code&gt;utils.sh&lt;/code&gt;, which:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checks if the file exists&lt;/li&gt;
&lt;li&gt;Makes it executable&lt;/li&gt;
&lt;li&gt;Runs it and logs its completion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. &lt;code&gt;utils.sh&lt;/code&gt; – The Bash Toolkit
&lt;/h2&gt;

&lt;p&gt;This file contains reusable helpers that keep the logic clean and DRY.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Functions:
&lt;/h3&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;is_installed_apt&lt;/code&gt; / &lt;code&gt;is_installed_snap&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Check if a package is already installed before trying to install it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  dpkg-query &lt;span class="nt"&gt;-W&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'${Status}'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"install ok installed"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;install_apt_packages&lt;/code&gt; / &lt;code&gt;install_snap_packages&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Take a list of packages, filter out the ones already installed, and install only what’s needed.&lt;/p&gt;

&lt;p&gt;Includes logic to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;apt-get update&lt;/code&gt; only if required&lt;/li&gt;
&lt;li&gt;Handle Snap not being installed&lt;/li&gt;
&lt;li&gt;Try both normal and &lt;code&gt;-classic&lt;/code&gt; Snap installs&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;die&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;A simple exit-on-error function to fail cleanly and loudly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;die&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: &lt;/span&gt;&lt;span class="nv"&gt;$*&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;In the future, I plan to add features like color-coded logging, verbosity levels, timestamps, and WSL detection. But for now, the current setup does exactly what I need: simple, readable, and effective.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  3. &lt;code&gt;packages.conf&lt;/code&gt; – Centralized Package List
&lt;/h2&gt;

&lt;p&gt;This is where all core APT and Snap packages are defined and grouped by category for clarity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;SYSTEM_UTILS_APT&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
  wget curl fzf build-essential fd-find ripgrep
&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;DEV_TOOLS_SNAP&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
  nvim
&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each package list is passed into the installer functions in &lt;code&gt;utils.sh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This structure makes it easy to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add/remove tools cleanly&lt;/li&gt;
&lt;li&gt;See what’s included at a glance&lt;/li&gt;
&lt;li&gt;Extend with new categories later&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why This Structure Works
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Modularity:&lt;/strong&gt; each setup script does one job&lt;/li&gt;
&lt;li&gt;Reusability: helpers prevent duplication&lt;/li&gt;
&lt;li&gt;Clarity: it’s easy to understand what gets installed and how&lt;/li&gt;
&lt;li&gt;Safety: checks prevent reinstalling or breaking existing tools&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Want to Try It?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️Security Reminder&lt;/p&gt;

&lt;p&gt;Never run scripts from the internet — including mine — without reading and understanding them first.&lt;br&gt;
Even if you trust the source, it's good practice to inspect any Bash script before executing it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I care about security too and my WSL bootstrap project is open source, transparent, and written to be as readable and modular as possible. Fork it, inspect it, tweak it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/LazyDoomSlayer/os-bootstraps
&lt;span class="nb"&gt;cd &lt;/span&gt;os-bootstraps/ubuntu
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x run.sh
./run.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Install time:&lt;/strong&gt; ~5 minutes on a VM with 8GB RAM, 8-core CPU, and 50Mbps connection.&lt;/p&gt;

&lt;p&gt;First-time runs may take longer if your system hasn’t been updated with &lt;code&gt;apt-get update &amp;amp;&amp;amp; upgrade&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Everything else happens automatically. Grab a coffee. ☕&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next: The CLI Stack
&lt;/h2&gt;

&lt;p&gt;In Part 3, I’ll show you the terminal tools I bootstrap like &lt;code&gt;tmux&lt;/code&gt;, &lt;code&gt;zoxide&lt;/code&gt;, &lt;code&gt;lazygit&lt;/code&gt;, and &lt;code&gt;neovim&lt;/code&gt; and how they fit into my daily workflow.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Want to go full terminal-first? This next part is for you.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>linux</category>
    </item>
  </channel>
</rss>
