<?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: Andrés Villarreal</title>
    <description>The latest articles on DEV Community by Andrés Villarreal (@kaeruct).</description>
    <link>https://dev.to/kaeruct</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%2F386854%2Fb257dac5-29dd-4876-9247-ffe1579df236.jpeg</url>
      <title>DEV Community: Andrés Villarreal</title>
      <link>https://dev.to/kaeruct</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kaeruct"/>
    <language>en</language>
    <item>
      <title>Conquering Entropy: Reducing Risk</title>
      <dc:creator>Andrés Villarreal</dc:creator>
      <pubDate>Sat, 19 Sep 2026 08:50:52 +0000</pubDate>
      <link>https://dev.to/kaeruct/conquering-entropy-reducing-risk-j93</link>
      <guid>https://dev.to/kaeruct/conquering-entropy-reducing-risk-j93</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Part of “&lt;a href="https://dev.to/kaeruct/conquering-entropy-4pco"&gt;Conquering Entropy&lt;/a&gt;”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;First, I have to admit some of my recommendations on this topic are based on my own experience and anecdata, and not so much backed by numbers, please take them with a grain of salt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Risk Management
&lt;/h2&gt;

&lt;p&gt;Shipping code is risky. Any change could break the system. Over the years, I have learned to get comfortable with managing risk. For each change that I ship, I do a little risk assessment beforehand. It takes deep understanding of the system, as well as some intuition (which I fear pure vibe coders cannot reach). This is why I find it imperative to still have a clear mental model of the system I work with, even if most of the code is AI-generated. If I don’t understand the system, I cannot make an accurate risk assessment.&lt;/p&gt;

&lt;p&gt;Accurate risk assessments are useful because they can guide you to allocate just the right amount of resources for a change (mental capacity, review effort, follow-up tasks). If a change is low-risk enough, I usually just do it and ship it, no follow-up needed. If it’s high-risk, I find a way to reduce the risk. Below are some strategies you can use for that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scope Changes Appropriately
&lt;/h2&gt;

&lt;p&gt;The way big changes are built and delivered is tied to the development process of each team. Often, it’s recommended to split the tasks into smaller, deliverable chunks. This approach goes well when async work is preferred, but might make it harder to have a coherent high level understanding of the system. A proper review requires the reviewer to understand the work being done, the exact purpose of the change, and what impact it could have on the existing system. All of this must be obvious or clearly stated in the pull request (PR) description.&lt;/p&gt;

&lt;p&gt;I’m not entirely convinced there’s a change too big to be broken down into smaller, individually deliverable and reviewable chunks. However, I understand why it would be preferable to quickly get a big chunk of code into the codebase rather than splitting it and risking merge conflicts, especially if the codebase sees a lot of traffic (common in monorepos).&lt;/p&gt;

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

&lt;p&gt;For big, risky changes I recommend pair or mob programming. Two or more people sit down, discuss and plan the change, scope it out, and implement it (feel free to include your coding agents here). Taking your sweet time during the planning phase can save a lot of time later on. It’s worth it.&lt;/p&gt;

&lt;p&gt;A popular way to do this is to use &lt;a href="https://www.aihero.dev/skills-grill-me" rel="noopener noreferrer"&gt;Matt Pocock’s grill-me skill&lt;/a&gt;. I highly recommend it. The process encourages a shared understanding of the implementation. The team can then skip the review phase because they developed the solution together and are familiar with the code. Of course, some &lt;a href="https://dev.to/kaeruct/conquering-entropy-cultivating-trust-4pn6"&gt;trust&lt;/a&gt; is also required here.&lt;/p&gt;

&lt;p&gt;The biggest downside of this method is that it requires multiple engineers’ uninterrupted time, which might be tricky in some situations. But the benefit often outweighs the cost. The requirements are clear. There will be fewer bugs shipped and less rework, because of all the time spent planning and scoping. In my opinion, this is the most efficient way to get big, urgent changes in.&lt;/p&gt;

&lt;p&gt;Because this method favors front-loading all the planning and scoping before writing code, it can be considered the opposite of the “ship small changes fast” approach. Therefore, it’s essential that the team deliberately picks which approach they will use for each task.&lt;/p&gt;

&lt;p&gt;Another benefit of pair programming is psychological. It’s good for team building (I swear I’m not a manager!). I feel that with the use of coding agents and related tools, we engineers have become a bit more isolated. There’s a stronger focus on delivering a lot of tasks. Most touchpoints with teammates are about getting a review approved, or pushing back against their agent’s generated artifacts (tickets / code / documentation).&lt;/p&gt;

&lt;h2&gt;
  
  
  Observability is Essential
&lt;/h2&gt;

&lt;p&gt;In my first programming job, we only knew if the system had a defect because an end-user reported a bug. When I think about how far we have come, it really feels like that was the stone age. You need to know what’s going on in your system! Ensure there’s some sort of observability set up: Logs, metrics, traces, user telemetry, etc. Use whatever vendor you want, ideally something that’s already in place. LLMs are great at consuming raw observability data and helping you debug issues, so take advantage of them!&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose the Right Deployment Strategy
&lt;/h2&gt;

&lt;p&gt;Reducing risk is another approach which works well for both big and small changes. There are several approaches to reduce risk when releasing changes. For me, it’s important to use the tools you have available already in your system. You don’t have to start all fancy with Kubernetes blue/green, canary, or shadow deployments (although these can be very useful for specific situations).&lt;/p&gt;

&lt;p&gt;Make sure your system supports a way to quickly roll back a specific change, or to turn off a feature at runtime (for example by using &lt;a href="https://martinfowler.com/articles/feature-toggles.html" rel="noopener noreferrer"&gt;feature flags&lt;/a&gt;). If paired with proper observability, this is often good enough for most use cases.&lt;/p&gt;

&lt;p&gt;There are some techniques currently forming around a related concept: using agents to automate releases and observability, then “babysitting” the rollouts and determining whether the system is degraded. If so, the change is automatically rolled back and a human is involved. I think this has great potential, particularly for full-on “software factory” systems. When I find the opportunity to apply this technique, I will do a follow-up post with my findings.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Conquering Entropy: Cultivating Trust</title>
      <dc:creator>Andrés Villarreal</dc:creator>
      <pubDate>Sun, 07 Jun 2026 07:53:43 +0000</pubDate>
      <link>https://dev.to/kaeruct/conquering-entropy-cultivating-trust-4pn6</link>
      <guid>https://dev.to/kaeruct/conquering-entropy-cultivating-trust-4pn6</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Part of “&lt;a href="https://dev.to/kaeruct/conquering-entropy-4pco"&gt;Conquering Entropy&lt;/a&gt;”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most of the issues I have with AI-generated code are related to trust. Do I trust the person who wrote this ticket? Do I trust that the engineer who opened this PR understood the ticket and guided the coding agent to implement it properly? Do I trust the coding agent’s implementation? Do I trust our test suite to catch regressions before they hit production? Do I trust our CI/CD to properly build, test, and deploy our change? Do I trust our observability setup to alert us when the ai-generated code breaks production? Do I trust the AI SRE (Site Reliability Engineer) to properly diagnose the issue and help us mitigate it? Do I trust GitHub &lt;a href="https://mrshu.github.io/github-statuses/" rel="noopener noreferrer"&gt;not to have an incident&lt;/a&gt; when we need it the most?&lt;/p&gt;

&lt;p&gt;Trust is hard to earn and easy to lose. So I think it’s crucial to foster a culture of trust within the engineering team. You must trust engineers to do the right thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Encouraging Accountability
&lt;/h2&gt;

&lt;p&gt;I find it helpful to clearly communicate something like this: “You are accountable for what you ship. If this breaks production and you authored the PR, you should be there to fix it.”&lt;/p&gt;

&lt;p&gt;If engineers are made accountable for the code they ship, they should be given the agency to produce it with their preferred methods. Even if you’re not AI-pilled, you have to admit that AI agents do generate a lot of code very fast. Code still needs to be generated, and the expectation is that now it’s cheap to generate a lot of it. To cope with this, engineers must be allowed to put some measures in place to ensure the quality of the codebase does not degrade.&lt;/p&gt;

&lt;p&gt;In a healthy organization, the engineers should trust each other to only push code of reasonable quality. I say reasonable because it’s not pragmatic to obsess over quality and try to ship always 100% perfect code. Even before coding agents most code was already a buggy mess! So it’s understandable when a “good enough” solution is delivered. Often, we trade speed of delivery for quality, and incur some technical debt.&lt;/p&gt;

&lt;p&gt;Here are some practices I have found useful to facilitate engineering accountability in this new era of coding:&lt;/p&gt;

&lt;h3&gt;
  
  
  Coding Guidelines
&lt;/h3&gt;

&lt;p&gt;Have a clear technology strategy: take some time to decide what matters for your codebase and invest in clear guidelines. Both for humans and for the coding agents. Even if the humans don’t read the guidelines, their coding agents will, and will follow them (mostly).&lt;/p&gt;

&lt;h3&gt;
  
  
  Deterministic Tooling
&lt;/h3&gt;

&lt;p&gt;Make heavy use of deterministic tooling to ensure code quality. Typed languages, linters, dead code detection, security scans, CI/CD, and so on. All of these tools existed before coding agents and help us in the fight against slop. (Stay tuned for a follow-up post with specific recommendations!)&lt;/p&gt;

&lt;h3&gt;
  
  
  Enforce Small PRs
&lt;/h3&gt;

&lt;p&gt;Empower the engineers to reject unreviewable PRs. If possible, codify this criteria so any unreviewable PRs are immediately rejected. Of course, make sure to leave room for exceptions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Own the Tests
&lt;/h3&gt;

&lt;p&gt;Write test cases by hand. This is similar to the job of a business analyst. Deeply think about the feature and define proper test scenarios. Discuss this within the team. Coding agents can implement the tests, but they should be defined by humans.&lt;/p&gt;

&lt;h3&gt;
  
  
  Taste in Product
&lt;/h3&gt;

&lt;p&gt;Work in tandem with product to have a coherent product vision. It’s very easy to go crazy with AI and implement whatever feature comes to mind. Make sure you only implement useful features that actually bring value to users!&lt;/p&gt;

&lt;h3&gt;
  
  
  Prototype
&lt;/h3&gt;

&lt;p&gt;Throwaway code. Since code is very easy to generate now, it’s a good opportunity to try different approaches. Don’t just let the coding agent generate one solution. For example, try three radically different approaches and pick the one that best fits the problem and existing system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Focus on the Outcome
&lt;/h3&gt;

&lt;p&gt;Be pragmatic about the result. Sometimes the code is not the result. The result is a report, or a tool that helps you accomplish something else. For cases like this the quality of the code is not that relevant as long as the result is useful.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Conquering Entropy</title>
      <dc:creator>Andrés Villarreal</dc:creator>
      <pubDate>Sat, 06 Jun 2026 07:44:19 +0000</pubDate>
      <link>https://dev.to/kaeruct/conquering-entropy-4pco</link>
      <guid>https://dev.to/kaeruct/conquering-entropy-4pco</guid>
      <description>&lt;p&gt;A collection of ideas on how to manage entropy in codebases inundated with AI-generated code and survive in the process.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Been Changing
&lt;/h2&gt;

&lt;p&gt;Ever since coding agents became popular, my company’s monorepo line count has exploded. The number of pull requests (PR) has roughly doubled (conservatively), and so has the review burden on all engineers. People think the biggest problem now is that there’s too much code to review. But there’s way worse stuff!&lt;/p&gt;

&lt;p&gt;Other negative consequences I have noticed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Degraded shared understanding of the system, since most code is not written by hand anymore. We are missing this step, which helped us solidify our comprehension of the system and strengthen our mental models.&lt;/li&gt;
&lt;li&gt;Artifacts which are difficult to understand. For example, tickets heavily written by AI, system design documents, code comments and documentation. This is due to the way LLMs write. People are calling this new language “Claudish” and I hate it because it muddles communication.&lt;/li&gt;
&lt;li&gt;Jumping to conclusions too fast. As difficult as the LLM output can be to understand, sometimes it can be very convincing and people let their guard down and easily trust it. I think we need to “trust but verify” even harder nowadays.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I see a lot of anxiety and worry over what the profession is becoming. This one’s tricky and I’m afraid I don’t have any useful actions to help with this. Personally I have chosen to take advantage of the current tools and focus on maintaining a system that I can be proud of. But in a few years we might not even need humans for this.&lt;/p&gt;

&lt;p&gt;More now than ever, it’s essential to be able to &lt;a href="https://dev.to/kaeruct/conquering-entropy-cultivating-trust-4pn6"&gt;trust&lt;/a&gt; each other and the code we deliver.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Contributions Are Staying
&lt;/h2&gt;

&lt;p&gt;In some open source projects (like Zig, NetBSD, GIMP or QEMU), &lt;a href="https://arxiv.org/abs/2603.26487" rel="noopener noreferrer"&gt;AI contributions have been banned&lt;/a&gt;. Reasons vary, but I see a common thread: lack of trust. Slop, spam, untrusted code, no accountability, licensing.&lt;/p&gt;

&lt;p&gt;Within a company, it’s not often feasible to ban AI contributions, as the business also expects that the engineers deliver at an accelerated pace. And the incentive within a company is usually to deliver more, show higher performance, get your promotion, salary increase, etc. So as long as there’s a tool that improves productivity (or looks like it does), people will use it.&lt;/p&gt;

&lt;p&gt;I used to think that if AI usage kept incurring higher and higher costs, it would collapse and everyone would go back to writing code by hand. But I don’t think this will happen anymore. There’s many “good enough” open models that can be self-hosted right now, and they’re only going to get better. &lt;a href="//content/posts/2026/09/05/conquering-entropy-intro.md"&gt;Some companies are already experimenting with this&lt;/a&gt; to reduce their dependency on the big AI providers.&lt;/p&gt;

&lt;p&gt;So it looks like AI usage will not stop. I like it (societal and economical consequences aside). It’s allowed me to have a greater impact that I could have had before by doing a lot of the grunt work that is necessary but I never had the time for. Also allowed me to get so much more done on my side projects in which code quality is not the focus. It’s also fun to tinker with. I have tried many different setups and enjoy discussing these with my colleagues.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>discuss</category>
    </item>
    <item>
      <title>LocalStack License Changed, CI First to Know</title>
      <dc:creator>Andrés Villarreal</dc:creator>
      <pubDate>Sat, 28 Mar 2026 15:09:45 +0000</pubDate>
      <link>https://dev.to/kaeruct/localstack-license-changed-ci-first-to-know-4bkd</link>
      <guid>https://dev.to/kaeruct/localstack-license-changed-ci-first-to-know-4bkd</guid>
      <description>&lt;p&gt;&lt;a href="https://www.localstack.cloud/" rel="noopener noreferrer"&gt;LocalStack&lt;/a&gt; is a great product. It’s basically an emulator for the AWS cloud that you can run locally. We rely on it for development and automated testing. However, nothing comes for free. Understandably, the company behind this amazing piece of software has some bills to pay. They changed the license to require a subscription to run their provided Docker images. If the license is not provided as an environment variable, the image simply fails to start. Normally, this would not be a problem if we pinned our versions properly. But surprise, we don’t!&lt;/p&gt;

&lt;p&gt;CI started “randomly” failing in the middle of the day and annoying all the engineers. I consider myself the self-appointed CI shepherd, so it was my responsibility to find out what was going on. Initially I thought this was just a couple of flaky tests, but after it started failing constantly, it became obvious we had a real problem. We use &lt;a href="https://testcontainers.com/" rel="noopener noreferrer"&gt;testcontainers&lt;/a&gt; with the S3 LocalStack image, and all tests which used this setup were consistently failing.&lt;/p&gt;

&lt;p&gt;The nice thing is that I was able to reproduce the problem locally, so then I just bumped the log levels to the max and looked for funny stuff. After some log spelunking, I noticed the failing image was &lt;code&gt;localstack/localstack:s3-latest&lt;/code&gt;. From there it was easy to find out &lt;a href="https://blog.localstack.cloud/localstack-for-aws-release-2026-03-0/#setting-your-auth-token" rel="noopener noreferrer"&gt;LocalStack’s latest blog post&lt;/a&gt; explaining their licensing changes.&lt;/p&gt;

&lt;p&gt;The immediate fix was to pin the image to version &lt;code&gt;4.4.0&lt;/code&gt;. For example: change &lt;code&gt;localstack/localstack:s3-latest&lt;/code&gt; to &lt;code&gt;localstack/localstack:4.4.0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I would say the main takeaway from all of this is to pin your versions. Then you always know what you get. Package manager dependencies, docker images, Github actions, anything! Always specify a deterministic version. Never use &lt;code&gt;latest&lt;/code&gt; or similar. Oh, how I wish &lt;a href="https://reproducible-builds.org/" rel="noopener noreferrer"&gt;reproducible builds&lt;/a&gt; were more widespread in the software industry!&lt;/p&gt;

&lt;p&gt;Back to the LocalStack topic, now I am actively on the lookout for a proper replacement. I only need a local S3-compatible emulator. I have tried MinIO but for some reason it doesn’t seem to be a drop-in replacement and also has some funny business going on with the licensing. &lt;a href="https://github.com/hectorvent/floci" rel="noopener noreferrer"&gt;floci&lt;/a&gt; also seems promising although it’s quite new. Please reach out if you have any recommendations!&lt;/p&gt;

</description>
      <category>testing</category>
      <category>localstack</category>
      <category>ci</category>
    </item>
    <item>
      <title>Our CI Doesn't Do Weekends</title>
      <dc:creator>Andrés Villarreal</dc:creator>
      <pubDate>Sat, 24 Jan 2026 09:34:06 +0000</pubDate>
      <link>https://dev.to/kaeruct/our-ci-doesnt-do-weekends-4og1</link>
      <guid>https://dev.to/kaeruct/our-ci-doesnt-do-weekends-4og1</guid>
      <description>&lt;p&gt;I think a codebase can tell you a lot about the people that work on it. From this specific example in particular, I felt very reassured that the team takes work-life balance seriously and never works on weekends.&lt;/p&gt;

&lt;p&gt;For context, the software interfaces with old-school banking systems that famously do not run certain processes on holidays or weekends.&lt;/p&gt;

&lt;p&gt;We had some logic to to an early return to avoid running a process on bank holidays, imagine something like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (isBankHoliday(new Date())) {
  logger.info("Skipping process on bank holiday");
  return;
}
// ... continue business logic below
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This logic was not covered by any tests! Not to say that there were no tests, but they were more focused on what would happen on work days and not the fact that the code should not run on bank holidays.&lt;/p&gt;

&lt;p&gt;This worked quite well, until somebody &lt;strong&gt;pushed changes on a weekend&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Weekends are considered bank holidays, so the check to avoid running on bank holidays was triggered and the tests failed. Fortunately, the changes being pushed on the weekend were not urgent and nobody was blocked by this.&lt;/p&gt;

&lt;p&gt;The lesson we learned from this is that you should &lt;strong&gt;not work on weekends!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Although in all seriousness, the tests should have faked the date from the start to avoid this. Even if people are not working on weekends, it could be that they work in a different timezone, or the team needs to push an urgent hotfix during the weekend.&lt;/p&gt;

&lt;p&gt;The way to do this using &lt;code&gt;vitest&lt;/code&gt; is very simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Pick a date that is definitely not a bank holiday!
vi.useFakeTimers({
  now: new Date("2025-03-03"),
  toFake: ["Date"]
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By the way, &lt;a href="https://vitest.dev/" rel="noopener noreferrer"&gt;vitest&lt;/a&gt; is very fast and &lt;a href="https://vitest.dev/guide/migration.html#jest" rel="noopener noreferrer"&gt;very easy to migrate to from jest&lt;/a&gt;! Make the switch if you haven’t already!&lt;/p&gt;

</description>
      <category>testing</category>
      <category>node</category>
      <category>vitest</category>
    </item>
    <item>
      <title>fzf + SSH Config Hosts</title>
      <dc:creator>Andrés Villarreal</dc:creator>
      <pubDate>Thu, 10 Oct 2024 18:42:48 +0000</pubDate>
      <link>https://dev.to/kaeruct/fzf-ssh-config-hosts-23dj</link>
      <guid>https://dev.to/kaeruct/fzf-ssh-config-hosts-23dj</guid>
      <description>&lt;p&gt;SSH has a nice feature in which you can store aliases for frequently accessed hosts.&lt;/p&gt;

&lt;p&gt;Combining this with &lt;a href="https://github.com/junegunn/fzf" rel="noopener noreferrer"&gt;&lt;code&gt;fzf&lt;/code&gt;&lt;/a&gt;, you can have a nice quick shortcut to quickly pick a server to connect to into.&lt;/p&gt;

&lt;p&gt;This comes in very handy if you need to ssh into different servers and forget their IP or hostname often.&lt;/p&gt;

&lt;p&gt;Here’s a sample ssh config file (normally located at &lt;code&gt;~/.ssh/config&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# see https://man.openbsd.org/ssh_config.5 for all the available configuration settings
Host runner-staging
    HostName 10.0.0.8
    User alpha

Host runner-production
    HostName 10.0.0.9
    User beta

Host mainframe
    HostName mainframe.computer.world
    User hackerman

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s a small shell function which calls fzf with the hostnames configured and allows you to pick one to connect to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;s () {
  local server
  server=$(grep -E '^Host ' ~/.ssh/config | awk '{print $2}' | fzf)
  if [[ -n $server ]]; then
    ssh $server
  fi
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add this function to your &lt;code&gt;.bashrc&lt;/code&gt; (or &lt;code&gt;.zshrc&lt;/code&gt;, or whichever config file for your shell) and reload the configuration.&lt;/p&gt;

&lt;p&gt;Now, you can quickly ssh into &lt;code&gt;mainframe&lt;/code&gt; by typing &lt;code&gt;s&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ s

# fzf will allow to quickly search and pick your server
&amp;gt; runner-staging
  runner-production
  mainframe
  3/3 ──────────

# press enter and you will be connected!
[hackerman@mainframe.computer.world ~]$ 

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>linux</category>
      <category>ssh</category>
      <category>tips</category>
    </item>
    <item>
      <title>Using AsyncLocalStorage for Better Traceability in NodeJS Applications</title>
      <dc:creator>Andrés Villarreal</dc:creator>
      <pubDate>Sun, 29 Sep 2024 08:45:54 +0000</pubDate>
      <link>https://dev.to/kaeruct/using-asynclocalstorage-for-better-traceability-in-nodejs-applications-322o</link>
      <guid>https://dev.to/kaeruct/using-asynclocalstorage-for-better-traceability-in-nodejs-applications-322o</guid>
      <description>&lt;p&gt;NodeJS has a neat API called &lt;code&gt;AsyncLocalStorage&lt;/code&gt;. It’s used to share information across callbacks and promise chains.&lt;/p&gt;

&lt;p&gt;For example, it is useful to share information for all the code executed when serving a web request. I also found it very useful to keep trace information to easily keep track of which item was being processed during a batch process.&lt;/p&gt;

&lt;p&gt;The only caveat in my opinion, is that you need to wrap your code in yet another callback.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use &lt;code&gt;AsyncLocalStorage&lt;/code&gt;:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;First, create an instance of &lt;code&gt;AsyncLocalStorage&lt;/code&gt;:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Then, use the &lt;code&gt;run&lt;/code&gt; method to wrap your code:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, use the &lt;code&gt;getStore&lt;/code&gt; method to retrieve the shared state:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Real-world example
&lt;/h2&gt;

&lt;p&gt;The previous example is very simple because it only shows how to use it.&lt;/p&gt;

&lt;p&gt;Here’s a more elaborate example that will hopefully show why this feature is so useful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { AsyncLocalStorage } = from "node:async_hooks";

async function process() {
  try {
    await Promise.all([foo(), bar()]);
  } catch (err) {
    handleError(err);
  }
}

async function foo() {
  const { index } = asyncLocalStorage.getStore();
  console.log(`processing foo ${index}`);
}

async function bar() {
  const { index } = asyncLocalStorage.getStore();
  console.log(`processing bar ${index}`);

  if (Math.random() &amp;lt; 0.1) {
    // simulate a random failure
    throw new Error("A random failure happened");
  }
}

async function handleError(err) {
  const { index } = asyncLocalStorage.getStore();
  console.log(`Handling error when processing ${index}: ${err}`);
}

const asyncLocalStorage = new AsyncLocalStorage();

// process 20 things, asynchronously
const tasks = [];
for (let i = 0; i &amp;lt; 20; i++) {
  tasks.push(
    asyncLocalStorage.run({ index: i }, process)
  );
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you execute this code, it will clearly log for which items the failures occured, because the required information is available in the shared state exposed by &lt;code&gt;AsyncLocalStorage&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Example of the output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;processing foo 0
processing bar 0
processing foo 1
processing bar 1
...
Handling error when processing 3: Error: A random failure happened
Handling error when processing 8: Error: A random failure happened
Handling error when processing 14: Error: A random failure happened

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>node</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Starfield visualization in JavaScript</title>
      <dc:creator>Andrés Villarreal</dc:creator>
      <pubDate>Sat, 31 Aug 2024 18:18:07 +0000</pubDate>
      <link>https://dev.to/kaeruct/starfield-visualization-in-javascript-3cnm</link>
      <guid>https://dev.to/kaeruct/starfield-visualization-in-javascript-3cnm</guid>
      <description>&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%2Fgj4iktlpunre4ptkanno.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%2Fgj4iktlpunre4ptkanno.png" alt="Starfield Visualization" width="800" height="650"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a simple, straightforward implementation of a visualization reminiscent of the classic Windows 95 starfield screensaver.&lt;/p&gt;

&lt;p&gt;It is also interactive: you can touch the screen or use the accelerometer to influence the direction of the movement.&lt;/p&gt;

&lt;p&gt;This is how it works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a bunch of particles (100), each in a random position.&lt;/li&gt;
&lt;li&gt;Every frame, move each particle further away from &lt;em&gt;the center&lt;/em&gt;*. The further the particle is from the center, the more visible it will become. This gives the illusion that the particles are moving closer to the viewer, or that the viewer is going further into space.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;The center&lt;/em&gt; is not really the center of the screen, but a variable point which can be influenced by the user by moving their cursor or tilting their device.&lt;/li&gt;
&lt;li&gt;When the particles go outside of the view, put them near the center again, this keeps the visualization going on in perpetuity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this blog post, I want to share the heavily-commented source code to demonstrate how simple it is to create visually appealing animations with a few lines of code and basic math knowledge.&lt;/p&gt;

&lt;p&gt;Please &lt;a href="https://kaeruct.github.io/projects/starfield/" rel="noopener noreferrer"&gt;click here&lt;/a&gt; to see the visualization in action!&lt;/p&gt;

&lt;p&gt;The code is available in this Gist:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


</description>
      <category>tutorial</category>
      <category>javascript</category>
      <category>html</category>
      <category>canvas</category>
    </item>
    <item>
      <title>Migrating From DokuWiki to Obsidian</title>
      <dc:creator>Andrés Villarreal</dc:creator>
      <pubDate>Sun, 18 Aug 2024 19:37:56 +0000</pubDate>
      <link>https://dev.to/kaeruct/migrating-from-dokuwiki-to-obsidian-1n35</link>
      <guid>https://dev.to/kaeruct/migrating-from-dokuwiki-to-obsidian-1n35</guid>
      <description>&lt;p&gt;About a year ago, I decided to move all my personal notes from &lt;a href="https://www.dokuwiki.org" rel="noopener noreferrer"&gt;DokuWiki&lt;/a&gt; to &lt;a href="https://obsidian.md/" rel="noopener noreferrer"&gt;Obsidian&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;DokuWiki is great software and has served me well, but I never utilized it to its full capacity. It’s biggest strength is that it’s a… wiki, and I was the sole user.&lt;/p&gt;

&lt;p&gt;It made little sense for me to keep a service running when I could just have some Markdown files locally. And Obsidian provides a really nice UI on top of those Markdown files.&lt;/p&gt;

&lt;p&gt;One reason I chose DokuWiki in the first place, is that it doesn’t need a database. All the content is kept in plaintext files. So if I ever wanted to move my notes elsewhere, it would hopefully be a painless process. In the end, I think it was a good choice, because the migration was quite simple.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Migration Process
&lt;/h2&gt;

&lt;p&gt;These are the steps I followed:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Copying the data folder locally
&lt;/h3&gt;

&lt;p&gt;First, I needed to get the source text and image files from DokuWiki to my local machine. All of it is located at &lt;code&gt;/var/www/dokuwiki/data/&lt;/code&gt; for a common installation. This directory contains all the text files and images that comprise the wiki pages. &lt;a href="https://www.dokuwiki.org/config:savedir" rel="noopener noreferrer"&gt;This directory is configurable&lt;/a&gt;, so check your configuration if you’re unsure.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Convert all text files from DokuWiki Syntax to proper Markdown
&lt;/h3&gt;

&lt;p&gt;The next step was to locate all the &lt;code&gt;.txt&lt;/code&gt; files in &lt;code&gt;data/pages&lt;/code&gt;. These files needed to be renamed to &lt;code&gt;.md&lt;/code&gt;, and the content converted to proper Markdown.&lt;/p&gt;

&lt;p&gt;DokuWiki uses its own syntax, which is not really used anywhere else.&lt;/p&gt;

&lt;p&gt;I ended up writing a quick script to handle this, with some help from ChatGPT to speed up the process.&lt;/p&gt;

&lt;p&gt;Main points the script covers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Changing DokuWiki’s heading syntax to Markdown format.&lt;/li&gt;
&lt;li&gt;Adjusting link and image paths to work with Obsidian.&lt;/li&gt;
&lt;li&gt;Handling any quirks like list formatting.&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  3. Moving Media Files
&lt;/h3&gt;

&lt;p&gt;After the text files were converted, I moved all the media files from &lt;code&gt;data/media&lt;/code&gt; to &lt;code&gt;data/pages&lt;/code&gt;. Obsidian can handle images and other media in the same directory as the Markdown files, which DokuWiki did not.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Final Touches
&lt;/h3&gt;

&lt;p&gt;The last step was to verify everything in Obsidian. I went through my notes to check formatting and that there were no broken links or images. A few minor tweaks were necessary, but the script got me 95% of the way there!&lt;/p&gt;

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

&lt;p&gt;It’s been about a year since I did the migration, and using Obsidian is much smoother. I just need to make sure the directory with my notes is properly backed up / synchronized between my devices. Obsidian offers this as a paid feature, but I didn’t want to depend on yet another 3rd party service. I simply use a Git repository to keep my notes.&lt;/p&gt;

&lt;p&gt;For anyone looking to do the same or similar, I hope this post helps you out.&lt;/p&gt;

&lt;p&gt;Here is a small peek of my notes in Obsidian’s beautiful graph view:&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%2Fqibg91z17xs1zf5zhmqr.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%2Fqibg91z17xs1zf5zhmqr.png" alt="obsidian graph view" width="800" height="782"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dokuwiki</category>
      <category>obsidian</category>
    </item>
    <item>
      <title>How to use Let's Encrypt certificates with Keycloak</title>
      <dc:creator>Andrés Villarreal</dc:creator>
      <pubDate>Wed, 04 Jan 2023 16:28:41 +0000</pubDate>
      <link>https://dev.to/kaeruct/how-to-use-lets-encrypt-certificates-with-keycloak-5h98</link>
      <guid>https://dev.to/kaeruct/how-to-use-lets-encrypt-certificates-with-keycloak-5h98</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fkaeruct.github.io%2Fletsencrypt_keycloak.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/http%3A%2F%2Fkaeruct.github.io%2Fletsencrypt_keycloak.png" alt="Let's Encrypt + Keycloak" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Keycloak provides user federation, strong authentication, user management, fine-grained authorization, and more.&lt;/p&gt;

&lt;p&gt;Here is a guide to enable HTTPS access to your Keycloak server using a free Let's Encrypt SSL certificate. The beauty of Let's Encrypt is its ease of use and the fact that it's free!&lt;/p&gt;

&lt;p&gt;This guide assumes you have already installed Keycloak at &lt;code&gt;/opt/keycloak/&lt;/code&gt; using the &lt;a href="https://www.keycloak.org/getting-started/getting-started-zip" rel="noopener noreferrer"&gt;official guide for bare metal installs&lt;/a&gt;, and now you want to enable HTTPS access. You need to have version &lt;strong&gt;20&lt;/strong&gt; or higher.&lt;/p&gt;

&lt;p&gt;In all the instructions below, be sure to replace &lt;code&gt;&amp;lt;DOMAIN&amp;gt;&lt;/code&gt; with the actual domain you will be using. All commands in this guide must be run as root.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Install certbot
&lt;/h3&gt;

&lt;p&gt;First you will need to install &lt;code&gt;certbot&lt;/code&gt;. This depends on the Linux distro you are using.&lt;/p&gt;

&lt;p&gt;For example, for apt-based distros such as Debian or Ubuntu, you can just run the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apt install certbot

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Create HTTPS certificates
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Set up certificates:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ certbot certonly --standalone --preferred-challenges http -d &amp;lt;DOMAIN&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Configure Keycloak to use Let's Encrypt certificates
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Change configuration:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vi /opt/keycloak/conf/keycloak.conf

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Add or update the following lines in that file:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https-certificate-file=/etc/letsencrypt/live/&amp;lt;DOMAIN&amp;gt;/cert.pem
https-certificate-key-file=/etc/letsencrypt/live/&amp;lt;DOMAIN&amp;gt;/privkey.pem
hostname=&amp;lt;DOMAIN&amp;gt;

https-port=443
http-port=80

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Update keycloak config:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ /opt/keycloak/bin/kc.sh build

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;You're done! Now you can run the following command and Keycloak should now be accessible from the browser via HTTPS:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ /opt/keycloak/bin/kc.sh start

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Optional: Set up Keycloak as a systemd service
&lt;/h3&gt;

&lt;p&gt;The official guide purposely leaves this open-ended because there are many ways in which you might want to handle the lifecycle of the Keycloak server. However, I think a good approach is to just use &lt;a href="https://systemd.io/" rel="noopener noreferrer"&gt;systemd&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  4.1. Set up systemd service
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Create user and group:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ useradd -g keycloak keycloak

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Give access to certificates:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ chmod 0755 /etc/letsencrypt/{live,archive}
$ chgrp keycloak /etc/letsencrypt/{live,archive}
$ chgrp -h keycloak /etc/letsencrypt/live/&amp;lt;DOMAIN&amp;gt;/privkey.pem

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Give access to Keycloak directory:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ chown -R keycloak:keycloak /opt/keycloak/

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create systemd service:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ vi /etc/systemd/system/keycloak.service

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Write the following contents into the file. You might need to add your chosen database service to the &lt;code&gt;After&lt;/code&gt; line, for example &lt;code&gt;postgresql.service&lt;/code&gt; or &lt;code&gt;mysql.service&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Unit]
Description=Keycloak Application Server
After=syslog.target network.target

[Service]
Type=idle
User=keycloak
Group=keycloak
LimitNOFILE=102642
ExecStart=/opt/keycloak/bin/kc.sh start --optimized
StandardOutput=append:/var/log/keycloak.log
StandardError=inherit
RestartSec=2s
Restart=always
AmbientCapabilities=CAP\_NET\_BIND\_SERVICE

[Install]
WantedBy=multi-user.target

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Reload systemd config and start service:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ systemctl daemon-reload
$ systemctl start keycloak.service

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4.2. Automatic Keycloak server restart when certificates are updated
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Make certbot restart Keycloak after updating certificates:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cd /etc/letsencrypt/renewal-hooks/deploy
$ vi restart-keycloak.sh

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Write these contents:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/sh
systemctl restart keycloak

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Make the script executable:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ chmod +x restart-keycloak.sh

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You're done! Now Keycloak is set up as a systemd service and will start when your machine boots up.&lt;/p&gt;

</description>
      <category>keycloak</category>
      <category>letsencrypt</category>
      <category>linux</category>
    </item>
    <item>
      <title>Try Andy's Desk</title>
      <dc:creator>Andrés Villarreal</dc:creator>
      <pubDate>Sat, 15 Jan 2022 10:35:29 +0000</pubDate>
      <link>https://dev.to/kaeruct/try-andys-desk-5d17</link>
      <guid>https://dev.to/kaeruct/try-andys-desk-5d17</guid>
      <description>&lt;p&gt;LINK: &lt;a href="https://desk.glitchy.website/" rel="noopener noreferrer"&gt;https://desk.glitchy.website/&lt;/a&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.amazonaws.com%2Fuploads%2Farticles%2F4vg8ahjrmlkbthyat6u7.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%2F4vg8ahjrmlkbthyat6u7.png" alt=" " width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a nostalgic (and not very faithful) recreation of the Windows 7 desktop UI.&lt;/p&gt;

&lt;p&gt;The code is intentionally not too polished, as this is a project I work on for fun.&lt;/p&gt;

&lt;p&gt;Things might be buggy but that's part of the fun.&lt;/p&gt;

&lt;p&gt;You WILL find weird hacks and work-arounds and lots of inefficient stuff.&lt;/p&gt;

&lt;p&gt;One of my main goals with this project was to build it as fast as possible. I managed this by integrating as many existing projects/code as I could find. Of course, everything is credited if you open &lt;code&gt;CREDITS.txt&lt;/code&gt;. You can also take a look at the source code for more information!&lt;/p&gt;

&lt;p&gt;You can find the repo here: &lt;a href="https://github.com/KaeruCT/desk.glitchy.website" rel="noopener noreferrer"&gt;https://github.com/KaeruCT/desk.glitchy.website&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>desktop</category>
    </item>
    <item>
      <title>Free Static Web Hosts for Frontend Developers</title>
      <dc:creator>Andrés Villarreal</dc:creator>
      <pubDate>Sat, 15 Jan 2022 09:30:31 +0000</pubDate>
      <link>https://dev.to/kaeruct/free-static-web-hosts-for-frontend-developers-mjk</link>
      <guid>https://dev.to/kaeruct/free-static-web-hosts-for-frontend-developers-mjk</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fkaeruct.github.io%2Fstatic-website-code.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/http%3A%2F%2Fkaeruct.github.io%2Fstatic-website-code.jpg" alt="F-Droid" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nowadays it's very easy to publish on the web for free.&lt;/p&gt;

&lt;p&gt;There are countless blogging platforms and website creators. But these platforms usually end up controlling your content. Sometimes you cannot even export your own data!&lt;/p&gt;

&lt;p&gt;The other extreme is to set up your own server by yourself. Buy a VPS (virtual private server) or a shared hosting somewhere, install a web server, and upload your files. This is a lot of work already! Specially if all you want is to publish a static website.&lt;/p&gt;

&lt;p&gt;You already know HTML and CSS, and maybe a bit of JavaScript. You just want a place where you can drop your files and see it on the web!&lt;/p&gt;

&lt;p&gt;Here is a list of services that allow you to do that, in no specific order:&lt;/p&gt;

&lt;h2&gt;
  
  
  Netlify
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.netlify.com/" rel="noopener noreferrer"&gt;Netlify&lt;/a&gt; allows you to drag &amp;amp; drop a zip with your files. You can also connect it to your project from Github, Gitlab or Bitbucket. I use Netlify for a lot of my projects, such as &lt;a href="https://desk.glitchy.website" rel="noopener noreferrer"&gt;desk.glitchy.website&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vercel
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://vercel.com/new" rel="noopener noreferrer"&gt;Vercel&lt;/a&gt; is very similar to Netlify. They are also the company behing Next.js, so they have many integrations with it. I use Vercel for my &lt;a href="https://andres.villarreal.co.cr" rel="noopener noreferrer"&gt;personal website&lt;/a&gt; and for &lt;a href="https://glitchy.website" rel="noopener noreferrer"&gt;glitchy.website&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Surge
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://surge.sh/" rel="noopener noreferrer"&gt;Surge&lt;/a&gt; allows you to easily publish your static website from the command line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Github Pages
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://pages.github.com/" rel="noopener noreferrer"&gt;Github Pages&lt;/a&gt; allows you to directly from your GitHub repository. Just edit, push, and your changes are live. &lt;a href="https://kaeruct.github.io/" rel="noopener noreferrer"&gt;My blog&lt;/a&gt; is hosted on Github Pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  GitLab Pages
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.gitlab.com/ee/user/project/pages/" rel="noopener noreferrer"&gt;GitLab Pages&lt;/a&gt; allows you to publish static websites directly from a repository in GitLab.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Words
&lt;/h2&gt;

&lt;p&gt;With these services, you can host your own static website for free. If having your own domain is important, then you will have to buy one, but that's the only thing you need, as many of these services allow you to use your own custom domain as well.&lt;/p&gt;

&lt;p&gt;I recommend you familiarize yourself with the options and not to put all your eggs in one basket. The beauty of static website is that something happens to the provider, you can just copy your files to another one, and you're done! Keep it simple.&lt;/p&gt;

</description>
      <category>css</category>
      <category>html</category>
      <category>javascript</category>
      <category>tips</category>
    </item>
  </channel>
</rss>
