<?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: Adam Świderski</title>
    <description>The latest articles on DEV Community by Adam Świderski (@asvid).</description>
    <link>https://dev.to/asvid</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F594857%2Fb0277b91-576b-4ad6-b3e8-560ab55b3a05.jpeg</url>
      <title>DEV Community: Adam Świderski</title>
      <link>https://dev.to/asvid</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/asvid"/>
    <language>en</language>
    <item>
      <title>AI continuous development - My Toilet-to-Prod Pipeline</title>
      <dc:creator>Adam Świderski</dc:creator>
      <pubDate>Wed, 25 Mar 2026 21:42:36 +0000</pubDate>
      <link>https://dev.to/asvid/ai-continuous-development-my-toilet-to-prod-pipeline-11ek</link>
      <guid>https://dev.to/asvid/ai-continuous-development-my-toilet-to-prod-pipeline-11ek</guid>
      <description>&lt;h2&gt;
  
  
  The Itch
&lt;/h2&gt;

&lt;p&gt;I've always liked playing with scripts, CI, and automating things. But this time, with LLMs, I think I outdid myself. &lt;/p&gt;

&lt;p&gt;I built a workflow that starts as a Telegram chat, which helps me shape the specs for a feature that launches as a GitHub issue. From there, I use a Docker image for planning changes, implementing code, running reviews, and merging code when quality gates and the CI build pass. And then CI releases a new version, but this is the boring part.&lt;/p&gt;

&lt;p&gt;I had an unhealthy amount of fun connecting all those things together, and I still am tweaking it to my liking. I know there are out-of-the-box agent systems that just pick your prompt and plan work on a kanban board, use worktrees for parallel development, and generate specs using one of the frameworks (what a bold name for a markdown file). I tried some and didn’t like it. It was burning tokens to provide a generic experience, with the same approach to a TypeScript backend app, React frontend, native mobile, and GoLang.&lt;/p&gt;

&lt;p&gt;So I made my own system, and I want to showcase my process.&lt;/p&gt;

&lt;p&gt;Like every good automation, it begins with the itch. Some repetitive boring task, something I don't want to do, but I like when it's done, and I hate remembering about it. Like washing dishes. Or bumping the version number before app release so it's not failing after CI builds the Android app, runs tests and lint, pushes to store, and is rejected on the last step of deploy without notifying me. That kind of itch.&lt;br&gt;
I am one of those maniacs who would spend half a day writing a script, saving me 3 minutes of work, or better, the need to remember some step in the process. And now I don't have to spend that time scripting; I have LLMs for it. And they even add cute Unicode icons in bash messages.&lt;/p&gt;

&lt;p&gt;This post shows an AI-augmented development maturation process, an important journey. You may just jump to the last chapters, but you will miss the point.&lt;/p&gt;




&lt;h2&gt;
  
  
  Stage 1: Like An Animal
&lt;/h2&gt;

&lt;p&gt;It started with chatting with… chat to generate my code, fix a function I pasted into the text input, or generate SQL based on the DB schema I had to provide. Like an animal, coping from IDE into the browser and back.&lt;br&gt;
But it showed me the power of this tool. Now I didn’t have to spend days debugging something silly, drowning in tutorial hell, and stack overflow posts just to move forward with my task.&lt;/p&gt;

&lt;p&gt;The project I am currently working on with LLMs is my pet Android app with a Golang backend. I made the core of the app the good old-fashioned way, manually typing. Craft code produced from craft beer and craft pizza. &lt;/p&gt;

&lt;p&gt;Then they started plugging LLMs. I had a blueprint of how I wanted things to be, so ChatGPT could generate me something similar. I hate writing the same boilerplate over and over - this is why I tend to overengineer :)&lt;br&gt;
This was before Claude Code; there was no real way for LLMs to interact with code. The IDE LLMs were useless for anything other than inline code generation.&lt;/p&gt;

&lt;p&gt;It proved to be… okish at best, and the experience of coping and pasting was bad. It showed value for product discussions. What features go to MVP, what is nice to have, and what is a distinguishing factor for the app? You know, the type of stuff product managers are supposed to do.&lt;/p&gt;




&lt;h2&gt;
  
  
  Stage 2: LLM in the IDE
&lt;/h2&gt;

&lt;p&gt;The next stage is to use LLMs directly in an IDE. I started with Cursor, then tried Windsurf, but went back to Cursor. The LLM chat is now in my work environment, in a dedicated tab. I could link files or code snippets into the conversation, instead of copying and pasting them. &lt;/p&gt;

&lt;p&gt;I built an &lt;a href="https://apps.apple.com/pl/app/candle-studio/id6752621480" rel="noopener noreferrer"&gt;iOS app&lt;/a&gt; then, without knowing Swift or anything about iOS development. I had to have  Xcode on the side to run code and fix compilation errors because Cursor couldn’t do it. The model was also far from what we have now (early 2026), but I learned how to use it. Make very precise, concrete instructions, set boundaries. &lt;a href="https://dev.to/2022-10-12-candle-studio-story"&gt;Read more&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also learned what rulesets are, and that they don’t guarantee anything. They are a kind suggestion and a friendly reminder to the LLM, not an absolute law. But it was a breakthrough: I could unify rules across conversations, I didn’t have to tell the whole story to the LLM each time, like when calling the bank with a minor issue and going through 10 consultants.&lt;/p&gt;

&lt;p&gt;I repurposed an iPad as a 3rd screen, just to have a window for LLM interaction below my code and browser, or notes on the other 2 screens.&lt;/p&gt;

&lt;p&gt;I was moving way faster. Since I had the boilerplate, I could just ask it to generate another UseCase, ViewModel, bind them together, and move to the next feature.&lt;br&gt;
But switching between Cursor and Xcode, or Android Studio, was still annoying. I used Cursor to generate code, and another IDE to run it in an emulator and debug it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Stage 3: Claude Code
&lt;/h2&gt;

&lt;p&gt;Then came our lord and savior, Claude Code from Anthropic. A terminal tool. In 2026. Not sure if it's the ongoing nostalgia-vintage trend or people realizing Terminal and Vim are actually cool, but it was spot-on for what I needed.&lt;/p&gt;

&lt;p&gt;It's counterintuitive, but having a dedicated terminal tool instead of relying on an IDE is way more comfortable. I could use my favourite IDE for the project: Android Studio or Xcode for mobile apps, IntelliJ IDEA for anything else, or, hell, even Neovim when I feel adventurous. And still have the same AI helper, with the same setup, right next to it. I could even use a terminal tab in an IDE and have Claude Code there, without any plugins.&lt;/p&gt;

&lt;p&gt;I noticed I keep asking the LLM to do the same things over and over, like updating docs, building an app, running a linter, etc. People started sharing their prompts like they were the new libraries. Tools for tools. We can persist reusable prompts in the Claude config, globally or per project. We got MCPs that communicate with the outside world, like pulling language or library documentation. Specification generation frameworks started to pop up.&lt;/p&gt;




&lt;h2&gt;
  
  
  Stage 4: Skills and Context Hygiene
&lt;/h2&gt;

&lt;p&gt;All those Claude tools, plugins, and MCPs are awesome. But swallow context and tokens like my dog does a half-rotten sandwich he finds in the bushes.&lt;/p&gt;

&lt;p&gt;The context window available for a single interaction was often way too small. No chance of generating a whole SaaS in one go, after a single sentence of description. Work had to be divided into scoped chunks, the output of each serving as input to the next phase. Like human-centipede or UNIX philosophy tools.&lt;/p&gt;

&lt;p&gt;The bigger shift, for my workflow at least, was Skills and Commands. I could have a CLI or a script that I used before LLMs, and wrap it in an .md file that Claude could understand and use. Make small tools that work together, but this time semi-automated.&lt;/p&gt;

&lt;p&gt;I had to learn how to maintain context hygiene. It was pointless to ask Claude to develop a full feature in one go. My project was already too big and complex. I had to create specs.md (with a Skill checking project docs), split it into tasks.md file (also using Skill, but looking into the actual code). Then, in another interaction, Claude completed the tasks. This allowed me to reset the interaction when needed and pick up the work where it had been left off.&lt;br&gt;
But it still took a lot of manual work. The itch is back. I can do better.&lt;/p&gt;

&lt;p&gt;I created skills for running tests, because I was too lazy to run commands in the terminal. Same for bumping app version and committing a new release tag, so the CI runs the release workflow and pushes the new app to the store. I used to have bash/python scripts for that; now I have a markdown file with a list of CLI example calls, and the LLM figures out how to use them.&lt;/p&gt;

&lt;p&gt;But it doesn’t end there; I built a code review tool based on patterns and rules extracted from the project, with some generic good practices I reviewed. It runs locally before pushing code; it's good to find nitpicks and minor issues, so human reviewers can focus on what is most important - arguing about variable names.&lt;/p&gt;

&lt;p&gt;Later, I added more tools and skills, and the workflow required running tests and compilation before marking the task as done and moving to the next one.&lt;/p&gt;

&lt;p&gt;I discovered cheaper models like GLM for 6usd gave me 3x more usage than Claude code for 18usd - but it was dumber. Opencode, which was not causing epileptic seizures and had some nice features, such as displaying context usage or a list of tasks the LLM was performing.&lt;/p&gt;

&lt;p&gt;I was using Claude for planning features, generating tasks, and reviewing its output manually. Then, using OpenCode with GLM to implement those tasks, following a strict workflow.&lt;/p&gt;

&lt;p&gt;Why am I orchestrating this?&lt;/p&gt;




&lt;h2&gt;
  
  
  Stage 5: Agents and the Workflow
&lt;/h2&gt;

&lt;p&gt;Often, a single interaction was still too small to finish all the tasks on the list. The specification and task files are verbose; the coding interaction involved reading code and other project docs, running tests, and compiling code while reading terminal output. I initially was doing all those steps in a single interaction. Creating spec.md and tasks.md was a simple way to share important context between LLM interactions. But it still required some manual labor, and I’m not a fan of it. &lt;/p&gt;

&lt;p&gt;Here come the agents. Agent is another way to make the .md file the center of attention. But this time, it works in its own LLM interaction, with its own dedicated context window, not as part of the main interaction, like using skills or commands would.&lt;/p&gt;

&lt;p&gt;Using my skills as a blueprint, I created some agents and organized them into a workflow. Agents themselves are not that different from skills; they just run in their own context window. But having a workflow (well, it's a Skill technically…) that calls those agents in order and orchestrates them means I can fire and forget. I can focus on writing the initial specification, then polishing it with the first agent. After it's approved, the whole machine starts, tasks are created, code is written, QA is performed, documentation is updated, a PR is created, and merged after successful CI validations.&lt;/p&gt;

&lt;p&gt;I don’t use .md files to pass specs or tasks anymore. I switched to GitHub issues, which are easily accessible via the CLI (and a Skill to handle them), so agents can read and update issue descriptions, labels, and status.&lt;/p&gt;

&lt;p&gt;I made an analyst-techlead Skill that checked that I was on the main branch, gathered my spec input, asked clarifying questions, checked the code for the implementation plan, and then created a GitHub issue with gh-cli. Later, I called another Skill to pick up the issue and start implementing.&lt;/p&gt;

&lt;p&gt;Why do I need to do this manual labor in the middle? If only I could mark the issue as “ready to implement” and have some bot pick it up…&lt;/p&gt;

&lt;p&gt;Hold my beer.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Factory
&lt;/h2&gt;

&lt;p&gt;I wanted to be able to literally sit on a toilet and work on new feature specs instead of doomscrolling. It will be useful soon, as I am expecting my first kid, so I won't have time to work after work.&lt;/p&gt;

&lt;p&gt;Here's the part where a Telegram message becomes a merged PR without me touching a keyboard.&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%2Ffkn7lkzcuyfvmdrj6zsa.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%2Ffkn7lkzcuyfvmdrj6zsa.png" alt="n8n Telegram bot setup overview" width="800" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I used a Telegram bot that pushes messages to a standalone n8n instance, running on my sandbox VPS Docker. It picks a message and routes it to one of several workflows, including the specs creation workflow. I have an LLM integration with GLM, Redis for local storage, and a GitHub connection for project documentation. The LLM is not working as a chat but as a set of one-off messages; this is why I need to manually keep memory in Redis, and it resets after an issue is created or manually, with a bot command. The LLM is prompted with a set of instructions, uses my project docs and conversation history to clarify the task, and, in the end, calls GitHub to create an issue with a comprehensive set of requirements.&lt;/p&gt;

&lt;p&gt;I approve these specs by adding a label “ready-to-plan.” Another Docker container running on my home QNAP NAS (the VPS was not powerful enough) will pick them up, use the same prompt I was using locally to take the specs and generate tasks, then create subissues for the main task. It follows my work style and the workflow I established for this project. It almost works :) I am still tweaking here and there. I call it a techlead agent.&lt;/p&gt;

&lt;p&gt;There is also a worker agent. It will pick up sub-issues raised by the tech lead that are relatively small and simple to implement. The Techlead is supposed to create issues that junior to mid-level developers can work on independently. The agent's independence is key here. I don’t want to pay too much, as I am a proud citizen of Poznań City. I still use just a 6usd GLM legacy plan subscription.&lt;/p&gt;

&lt;p&gt;The worker agent is more complex, but it works on a simple task, so it's OK. After implementation is complete, a QA subagent runs linters, tests, checks code style, etc. If issues are found, it goes back to the coder to fix it - like in real life :) If I have to suffer, why shouldn't LLMs?&lt;br&gt;
After QA is happy with the code, documentation is updated if needed, and the Maintainer creates a PR with a clear title and description that follows the rules. Then it periodically checks whether CI passes; my old-fashioned automation is the last line of defense, and it merges the PR.&lt;/p&gt;

&lt;p&gt;My last interaction with this workflow is accepting the specification, the one I wrote while contemplating life in a solitary place.&lt;/p&gt;

&lt;p&gt;The synchronization point is GitHub issues, specifically issue labels. When an agent picks a task, it changes its labels to reflect this. I even had a few instances where those containers were running multiple tasks simultaneously. The implementation there is ridiculously simple: it's a Linux image running an infinite-loop script that checks every minute for issues with a specific label. If there are any, it picks the first and fires the master agent, which then uses sub-agents in opencode with bypassed permissions. I don't use API calls, just my subscription, like I was using on my local machine :) I use the same, or very similar, agents, skills, tools, etc. &lt;/p&gt;

&lt;p&gt;Everything was slowly evolving, rather than me jumping from tool to tool. And the workflow is the one I made for myself, one I know and understand, and it's crafted for this particular project.&lt;/p&gt;

&lt;p&gt;Yes, I know &lt;a href="https://stripe.dev/blog/minions-stripes-one-shot-end-to-end-coding-agents" rel="noopener noreferrer"&gt;Stripe has a similar setup with Slack and Minions&lt;/a&gt;, but I haven’t read the post yet. I was busy building this:&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%2F6ri89zv2g9dotkg9rvc1.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%2F6ri89zv2g9dotkg9rvc1.png" alt=" " width="800" height="2073"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons From the Factory Floor
&lt;/h2&gt;

&lt;p&gt;I wonder if I landed on the scrum team layout with automated agents because it is the best solution, or the only one I know?&lt;/p&gt;

&lt;p&gt;I just merged a 10k lines of code pull request made with my agents, and it was way too much for my taste :) It works, and most of the code was standard HTTP client boilerplate with DTOs, api spec, some documentation, and 3 simple screens.&lt;/p&gt;

&lt;p&gt;On the other hand, Techlead can create tasks that are way too granular. Once, the whole issue was about adding a single DTO, and a QA agent requested tests for it. So the Coder agent wrote a test for a bloody data transfer object.&lt;/p&gt;

&lt;p&gt;The agents often get stuck on something, and since they run in Docker containers, I don’t have any visibility into what they are doing. Their only reporting place is GitHub issues, comments, and labels. &lt;/p&gt;

&lt;p&gt;I constantly iterate on minor changes to the workflow. Last update was better handling for blocked and already started issues, or picking the lowest issue number first - I had a situation where implementation started from updating docs, before any implementation was done :)&lt;/p&gt;

&lt;p&gt;I enjoy the independence from vendors or tools. I can switch models as I want, use one for the Techlead and a different one for coding or QA. Add or remove steps, change workflow to fit my needs. And I use hardware and subscriptions I already had. I just put them into the OpenSpace cubicles and said - You are a team now.&lt;/p&gt;

&lt;p&gt;I had an unhealthy amount of fun working on this automation, and it works well for me. Right now, I am on a train writing this post, while my workers are implementing small tasks created by the Techlead agent, based on the spec I wrote while pretending to listen to my wife :)&lt;/p&gt;

&lt;p&gt;It would seem that, as in traditional software development, the more work done before coding, the better the long-term result. Who would have thought?&lt;/p&gt;

&lt;p&gt;I wouldn’t trust code generated by an LLM if I didn't understand the whole process, and it would be the result of a single-line feature description. But I worked my way up to this point, and I know that after accepting specs, even a dumb model will not surprise me, because the task it gets is small and simple.&lt;br&gt;
Splitting the project into multiple steps, tweaking each one independently, and creating quality gates makes me sleep well while my coding workers push the buttons, in the factory I build.&lt;/p&gt;

&lt;p&gt;PS. Waking up to 20 messages on the Telegram bot about new issues and pull request updates, with agents working overnight, is a bit creepy.&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%2F1c6oqey31f8pb5cfr4of.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%2F1c6oqey31f8pb5cfr4of.png" alt="Telegram bot notifying me about repo changes" width="800" height="1734"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>llm</category>
      <category>automation</category>
    </item>
    <item>
      <title>Do You Want to Learn About Our Lord and Savior SOLID?</title>
      <dc:creator>Adam Świderski</dc:creator>
      <pubDate>Thu, 19 Feb 2026 12:37:28 +0000</pubDate>
      <link>https://dev.to/asvid/do-you-want-to-learn-about-our-lord-and-savior-solid-15ka</link>
      <guid>https://dev.to/asvid/do-you-want-to-learn-about-our-lord-and-savior-solid-15ka</guid>
      <description>&lt;p&gt;I woke up with the flu, felt adventurous, and, for a while, wanted to disrupt things. So this one is going to be about not praising the SOLID principles.&lt;/p&gt;

&lt;p&gt;There are no rocks that you could hide from this acronym. Doesn't matter what technology, languages, or frameworks you use. But maybe it should?&lt;/p&gt;

&lt;p&gt;Quick reminder, the acronym stands for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;S&lt;/strong&gt;ingle Responsibility Principle (SRP)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;O&lt;/strong&gt;pen/Closed Principle (OCP)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L&lt;/strong&gt;iskov Substitution Principle (LSP)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I&lt;/strong&gt;nterface Segregation Principle (ISP)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;D&lt;/strong&gt;ependency Inversion Principle (DIP)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You would be right to connect it to Uncle Bob, but he just wrapped it into an acronym; those rules have been around since 70. Again, &lt;strong&gt;there is nothing new in software engineering since 70, change my mind.&lt;/strong&gt; I will wait here with hot cocoa while sneezing.&lt;/p&gt;

&lt;p&gt;This whole post was triggered by a fantastic conversation between Kevlin Henney and Daniel Terhorst-North exploring whether clean code is a myth or timeless truth&lt;sup id="fnref1"&gt;1&lt;/sup&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Although the phrase "clean code" itself does not appear in the 1974 book The Elements of Programming Style, Kernighan and Plauger emphasized the importance of writing programs for readability and maintainability, principles that later became central to the concept of clean code&lt;sup id="fnref2"&gt;2&lt;/sup&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Robert C. Martin did take the principles he had used for years and imposed on himself the role of Moses with tablets of rules, writing "Clean Code" in 2008&lt;sup id="fnref3"&gt;3&lt;/sup&gt;. Which is a book you get suggested as a junior developer from the mid-senior one, that you praise like a gospel at first, get annoyed with it later, like with that overly spirited art teacher, and find it's detached from reality and unfitting for actual projects.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don't get me wrong - I own Clean Code, I've read it, and I learned from it. Uncle Bob did valuable work collecting these principles. The issue isn't the book itself, but the religious adherence it inspires. It's a field guide, not scripture.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Clean code contains rules like the maximum function length, which originated from the size of displays at the time - but don't quote me on that. &lt;/p&gt;

&lt;p&gt;Or, strictly speaking, passing booleans into functions is forbidden: if you pass a boolean, you need 2 functions and call the right one according to the boolean value. &lt;/p&gt;

&lt;p&gt;Or that, ideally, you would have a function that reads like a story, with few calls here and there, all well-named methods, clean with no side effects, going like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;suchFantasticFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aSingleArgument&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
 &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;whatADayToBeAlive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aSingleArgument&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="nf"&gt;pleaseLogItOut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aSingleArgument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="nf"&gt;myJobHereIsDone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="nf"&gt;letsWrapItUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I was that junior trying to write like this. And it's amazing at first, super annoying after a while, and not effective after 2 months of having millions of 3-line functions calling other functions that call other 5-line functions just to see… each one also calls 3 short functions. Those loose connections are then moved to files, modules, etc., and get spread out, even when used together, because some methods feel like 'util' and can be used later in a different context. So let's make everything nice and generic. Watering down the whole purpose for which it was created in the first place. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Have you ever opened up a project and tried to trace the call stack through a maze of tiny, abstracted functions, only to forget where you started?&lt;/strong&gt; Did you ever find yourself burrowing through folders and files thinking, "Surely there's one place where these all come together,"—but there isn't? If you've wrestled with this kind of code jungle, you know the pain I'm talking about.&lt;/p&gt;

&lt;p&gt;And after having millions of those nice, short, generic, reusable, testable methods, a new guy (or gal) comes in and starts writing their own, because they didn't spend 3 months learning all the existing 'utils'. You, the one developer who never wrote a util function just to be reminded on codereview that one already exists, you can throw your stone now.&lt;/p&gt;

&lt;p&gt;Clean Code and SOLID were created with object-oriented programming in mind, so are they useless for scripts and functional programming? Do we have a dedicated list of rules for those? Or is it more about adjusting which principles matter most, rather than inventing everything from scratch? &lt;/p&gt;

&lt;p&gt;Personally, I think most of the core ideas can still apply, but often need a shift in emphasis rather than a total replacement. Common sense and context-aware choices might be enough, and sometimes, thanks to their constraints, functional or scripted codebases almost self-organize anyway.&lt;/p&gt;

&lt;p&gt;But you may wonder: are there alternatives?&lt;/p&gt;

&lt;h2&gt;
  
  
  CUPID
&lt;/h2&gt;

&lt;p&gt;While SOLID is a lumberjack-level manly man demanding you follow safety code or you get hurt and die under a falling timber, CUPID is all nice and fluffy, a chubby little guy that wants everyone to be in love.&lt;/p&gt;

&lt;p&gt;And it's a bit of a joke on SOLID, actually.&lt;/p&gt;

&lt;p&gt;Again, it's nothing new, just wrapped into a nice acronym. Backronym to be fair. The 'CUPID' was first, and then Daniel Terhorst-North started to think what words it could actually describe&lt;sup id="fnref4"&gt;4&lt;/sup&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt;omposable (plays well with others)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;U&lt;/strong&gt;nix philosophy (does one thing well)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;P&lt;/strong&gt;redictable (does what you expect)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I&lt;/strong&gt;diomatic (feels natural)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;D&lt;/strong&gt;omain-based (models the problem domain)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Isn't it nice?&lt;/p&gt;

&lt;p&gt;But is it any different, actually?&lt;/p&gt;

&lt;p&gt;They both want us to make code that is maintainable. Some say &lt;strong&gt;SOLID is like pregnancy: you can't be 50% SOLID; it's all or nothing.&lt;/strong&gt; This is when you see people creating millions of interfaces with a single method, and composing their abstract classes with them, within a complex inheritance tree, because you know - SOLID. Or creating interfaces everywhere, when all of them have a single implementation, because you know - good practices.&lt;/p&gt;

&lt;p&gt;More boilerplate than actual code.&lt;/p&gt;

&lt;p&gt;What does 'predictable' mean in CUPID? How to measure it? How can I make it a KPI so my annual bonus depends on it?&lt;/p&gt;

&lt;p&gt;Well, if you name your method &lt;code&gt;generatePdf()&lt;/code&gt;, maybe it shouldn't modify the database. It feels fishy; something is off.&lt;/p&gt;

&lt;p&gt;Imagine this mess:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;generatePdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invoiceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;invoice&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetchInvoiceFromDb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invoiceId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;pdf&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pureMagic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;userConfig&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getUserConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;sendEmail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;checkEmailPermissions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="nf"&gt;sendPdfByEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;markAsProcessed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;updateInvoiceInDb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks innocent, but suddenly, asking for a PDF accidentally updates your database, checks user preferences, and sends an email. And not even return the pdf. Predictable? Not so much.&lt;/p&gt;

&lt;p&gt;Now compare that to something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;generatePdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Pdf&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;pdf&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pureMagic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, you give the function everything it needs. No hidden fetches, no side effects. Calling it does what it says on the tin. Predictable.&lt;/p&gt;

&lt;p&gt;Could it read from the DB, with an injected connection pool? I'd say no, let's just give it all the static data to generate the PDF. &lt;/p&gt;

&lt;p&gt;Should it have 150 arguments passed in when generating the PDF? Nope, let's have an object with those arguments organized, using domain-language naming conventions. &lt;/p&gt;

&lt;p&gt;Should it be tested through and through using 1547 edge cases? Depends: if this PDF is your money maker and it's used billions of times a day, you want it to work in all cases. If it's used by 3 people twice a year, internally, and sometimes one column gets border cut by the page margin - well, I could live with that.&lt;/p&gt;

&lt;p&gt;And this is how to use CUPID. Composable function, doing one thing well, predictable outcome, and no surprises with side effects inside, feels natural to use - you pass stuff that gets onto pdf, and is using domain-based names and models.&lt;/p&gt;

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

&lt;p&gt;CUPID wasn't the first way of having fluffy principles, describing the overall vibe of the code rather than giving a SOLID prescription (pun intended). Richard Gabriel coined the term 'habitable code,' describing how it's comfortable for developers&lt;sup id="fnref5"&gt;5&lt;/sup&gt;. It focuses on experience and emphasizes human understanding in software design. It is very vague by design. &lt;/p&gt;

&lt;p&gt;With some experience, you know which parts of your project are 'less habitable' than others. The parts that are scary to change, the parts you need to spend 2 days reminding yourself what is going on. We don't want to be there. &lt;strong&gt;It's easier to spot the unhabitable code than to pinpoint the actual issues. The issues are rarely 'not having enough interfaces' and abstraction layers.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sometimes, the term 'cognitive load' is used in this topic. For me, it often comes down to this: &lt;strong&gt;how many places do I have to keep in my head when working with a particular part of the code?&lt;/strong&gt; If it takes twenty tabs to grok a fix, that's a red flag. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I'd rather wrangle a single monstrous 1000-line method with no side effects than chase through 15 short methods scattered all around&lt;/strong&gt;, some buried in generic utils, some in domain-specific folders, all intricately tangled. Here's a quick rule-of-thumb you can try: count how many files or functions you actually touch or jump between to implement a single task, or put breakpoints to debug. &lt;strong&gt;"Jumps per task" or "files touched per fix" makes a concrete yardstick for cognitive load&lt;/strong&gt; the next time you check your codebase. Try tracking this next time you dig into unfamiliar code and see how much mental energy it actually costs.&lt;/p&gt;

&lt;p&gt;In my first job, we had a function we called 'godly 300' - it was more than 300 lines of code, and was doing way more than a single thing. It was used frequently across multiple contexts. Changes were scary; we had no tests - it's hard to test a multi-headed monster that does a lot of things, has side effects, and calls a lot of smaller creatures.&lt;/p&gt;

&lt;p&gt;After over a decade in the industry (man, I sound like an experienced pornstar), I know &lt;strong&gt;300 LoC is rookie numbers&lt;/strong&gt;. Check how big the methods and classes are in the Android framework. Google devs having no idea how to write good code? Not really. Methods may be huge, but still do mostly one thing; but the thing is complex. And I like it better kept in this one place than scattered around in an unholy set of generic, short functions used once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Write Everything at Least Twice
&lt;/h2&gt;

&lt;p&gt;Another principle we like to follow blindly is not to repeat ourselves. At first, as a junior dev, you copy-paste code, change or add a few arguments, and call it a day.&lt;/p&gt;

&lt;p&gt;Then you have to change the same line of code in 20 places, and you get the idea that extracting that code to a separate place and reusing it would actually be an improvement.&lt;/p&gt;

&lt;p&gt;Yes, it is a good idea to have a single place to change that is then used in multiple places in code. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No, it's not a good idea to create this single place right from the start. You don't know what you want until you have it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This sentence is always blocking me from using a pure TDD approach. I do not know what to test or how, because I have no idea what the solution will look like, what methods, objects, and structure I will end up with. How can I write a test at the beginning? Well, you can, and it's a fun experience I do sometimes, but it's another topic. Testable code is generally nicer to work with; you can train yourself to write it using the TDD approach and some kata programming problems, but I rarely, if ever, use it in prod code.&lt;/p&gt;

&lt;p&gt;Anyway, you get a better understanding of what to extract, which abstraction to create, after you've written the same thing twice. &lt;/p&gt;

&lt;p&gt;Not exactly the same thing, static code analysers would point it out if you actually copy-paste code, but close logically enough that you go 'huh…' when looking at both parts. And then, my friend, you can build an abstraction. Can, but don't have to. You can leave it until you write it a third time, because then it's guaranteed that you are repeating yourself.&lt;/p&gt;

&lt;p&gt;Of course, with experience, you will immediately see places that can be reused from the start. But you will also make the mistake of overgeneralizing such places. &lt;/p&gt;

&lt;p&gt;So just don't. &lt;/p&gt;

&lt;p&gt;Write duplicates and live with them. Often, code has accidental similarities and will develop in completely different ways over time. It won't be an issue if you duplicated code at the beginning and, after a year, it's no longer similar. If you overabstracted because some book said so, now you have to squish the cube into a circular hole, knowing you carved both things to be unfitting. Bad feeling, I was there and will be again for sure.&lt;/p&gt;

&lt;p&gt;Another acronym here 'AHA' - avoid hasty abstractions&lt;sup id="fnref6"&gt;6&lt;/sup&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Die Dogmats
&lt;/h2&gt;

&lt;p&gt;What you may notice already from all those acronyms and rules is that they are intentionally vague. But we like precision. Numbers, metrics, graphs, hell, I dare to say Scrum was invented to gamify work.&lt;/p&gt;

&lt;p&gt;From this perspective, Clean Code, with its rather precise rules, looks appealing. And it is. Until it's not. As Donald Knuth famously put it, &lt;strong&gt;"Premature optimization is the root of all evil."&lt;/strong&gt; In the same spirit, premature devotion to rules can lead to its own mess.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Following rules without understanding why they exist, and the context and time when they were created, sounds awfully religious.&lt;/strong&gt; Does it mean those rules are wrong? No. Does not eating pork on Friday make you a better person? Also no.&lt;/p&gt;

&lt;p&gt;Rules in software engineering are not a result of epiphany, some mysterious force giving us wisdom from higher realms. They were noticed, described, and tested on the battlefield by people like you and me.&lt;/p&gt;

&lt;p&gt;But since our screens can show more than 20 lines of code, and modern IDEs, or even VIM, can let you jump between lines with a click, maybe we should shift the focus from rules to more mentally-oriented ones.&lt;/p&gt;

&lt;p&gt;Ask yourself these questions whenever you're working on code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How much do I need to remember to work on a particular part?&lt;/li&gt;
&lt;li&gt;How far do I have to jump from one place to another?&lt;/li&gt;
&lt;li&gt;How often do I need to update 15 files when I was sure it would be a one-liner?&lt;/li&gt;
&lt;li&gt;How quickly can I add a new feature, or completely change the behavior?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Turning these thoughts into a checklist gives you a way to pause and reflect on actual maintainability rather than just abstract principles.&lt;/p&gt;

&lt;p&gt;I believe it's better to know about SOLID, Clean Code, and related principles, but keep them as suggestions, not commandments. &lt;/p&gt;

&lt;p&gt;Context is everything: there are moments when these principles save you from future headaches, and there are situations where following them to the letter is overkill. You will learn the hard way which ones would help you, unfortunately, postfactum. &lt;strong&gt;You need to burn yourself to learn the rules.&lt;/strong&gt; By burn, I mean mental breakdowns after spending countless hours on something you know would be much easier if you just followed the principles.&lt;/p&gt;

&lt;p&gt;Not all code is written equally. Not all require the same level of attention, robustness, or ease of change. And SOLID rules are great for parts that are constantly changed. But applying them blindly because 'good principles blah blah' will be counterproductive. Guess how I know.&lt;/p&gt;

&lt;p&gt;Can you imagine writing a bash script for a single back-office-related job, splitting it into multiple small files with a single 20-line function, no more than 3 input parameters, and then composing the actual tool with that?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bad abstraction is worse than no abstraction, and you need to make a lot of bad ones to learn what is good.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This whole topic reminds me a bit of learning to play an instrument. I play guitar, it was a bundle with long hair, I guess. I can learn all the music theory, all the licks and riffs, but it means nothing without hours of practising and playing wrong notes, training your brain to connect theory with practise to actually play something meaningful. The same muscle memory is used when coding.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Twist
&lt;/h2&gt;

&lt;p&gt;Obviously, I had to put AI into a blog post; it's 2026.&lt;/p&gt;

&lt;p&gt;If you are one of those perverts whipping Claude Code at 3am to vibecode one more feature to your pet project, like me, you may have noticed how often it creates a new method or class almost identical to already existing ones. It was well trained on books, blog posts, and source code written based on all those rules I mentioned above, so why is it doing so?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Well, apparently, AI is smart enough to know how dumb it is&lt;/strong&gt;, and it avoids creating abstractions or fiddling with existing code when building something new. It will try to have as few touchpoints with existing code as possible to not break it. Later, during a refactor, when you point out what can be merged or reused, it will switch context to those small refactors. Instead of blowing the whole codebase with code that doesn't compile, it will copy-paste new code, make it work, then fix duplicates or figure out an abstraction layer. That is, if you tell it to.&lt;/p&gt;

&lt;p&gt;You, starting from abstractions, are not 'smarter than AI'; your ego just won't let you admit you have no idea what you are doing. You feed it with those arbitrary rules, feeling morally superior over simple folks who first make it work in whatever way, then make it good. Or not even make it good, because it's used 3x a year by the internal team, so nobody cares.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We've covered SOLID, CUPID, DRY, AHA, Habitable Code, Tidy First… notice a pattern? They're all trying to say the same thing: make code maintainable.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pick the wisdom, skip the worship.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Just Be Lazy (But Smart)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;I don't write clean code for Uncle Bob's approval. I write it because Future Adam (3 months later, half-asleep, running on coffee, dealing with a bug at 3am) will either thank me or curse me.&lt;/strong&gt; I used to feel anxiety about other developers' negative reactions to my code, but that happens regardless of whether I follow all the rules and deliver peak performance code, so I just stopped caring. :)&lt;/p&gt;

&lt;p&gt;I find myself with a fresh golden hammer in hand every now and then, only to realize a screw would've been better. &lt;strong&gt;Following principles blindly doesn't automatically render 'better code'—there are always tradeoffs, and managing them is a sign of seniority and actual craftsmanship.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Both extremes hurt: not knowing the principles, and religiously implementing them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Collect the wisdom of principles, don't follow commandments. Write code that Future You can live in comfortably.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the way.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Additional Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Beck, K. (2022). &lt;em&gt;Tidy First?: A Personal Exercise in Empirical Software Design&lt;/em&gt;. O'Reilly Media. Promotes continuous, small, incremental improvements rather than major refactoring.&lt;/li&gt;
&lt;li&gt;Martin, R. C. &lt;em&gt;The SOLID Principles&lt;/em&gt;. Various publications and talks throughout the 2000s.&lt;/li&gt;
&lt;li&gt;Unix Philosophy: The principle that programs should "do one thing and do it well," originating from the Unix operating system design in the 1970s.&lt;/li&gt;
&lt;/ul&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Henney, K., &amp;amp; Terhorst-North, D. (2026). &lt;em&gt;Is Clean Code a Myth or Timeless Truth?&lt;/em&gt; YOW! Conference. Retrieved from: &lt;a href="https://www.youtube.com/watch?v=OjW_0ZRdN5E" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=OjW_0ZRdN5E&lt;/a&gt; - A discussion exploring the evolution and interpretations of clean code concepts, challenging whether rigid rules help developers write better code. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;Kernighan, B. W., &amp;amp; Plauger, P. J. (1974). &lt;em&gt;The Elements of Programming Style&lt;/em&gt;. McGraw-Hill. This early work discussed code clarity and style, predating the modern "clean code" movement. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;Martin, R. C. (2008). &lt;em&gt;Clean Code: A Handbook of Agile Software Craftsmanship&lt;/em&gt;. Prentice Hall. The book that popularized specific coding practices and the SOLID principles, though many concepts existed earlier. Available at: &lt;a href="https://www.oreilly.com/library/view/clean-code-a/9780136083238/" rel="noopener noreferrer"&gt;https://www.oreilly.com/library/view/clean-code-a/9780136083238/&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;Terhorst-North, D. (2022). &lt;em&gt;CUPID—for joyful coding&lt;/em&gt;. Personal blog. Retrieved from: &lt;a href="https://dannorth.net/2022/02/10/cupid-for-joyful-coding/" rel="noopener noreferrer"&gt;https://dannorth.net/2022/02/10/cupid-for-joyful-coding/&lt;/a&gt; - A backronym created to emphasize properties that make code "joyful to use." ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;Gabriel, R. P. (1996). &lt;em&gt;Patterns of Software: Tales from the Software Community&lt;/em&gt;. Oxford University Press. Contains the concept of "habitable code" emphasizing developer comfort and human understanding. PDF available at: &lt;a href="https://www.dreamsongs.com/Files/PatternsOfSoftware.pdf" rel="noopener noreferrer"&gt;https://www.dreamsongs.com/Files/PatternsOfSoftware.pdf&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn6"&gt;
&lt;p&gt;Dodds, K. C. &lt;em&gt;AHA Programming&lt;/em&gt;. Personal blog. Retrieved from: &lt;a href="https://kentcdodds.com/blog/aha-programming" rel="noopener noreferrer"&gt;https://kentcdodds.com/blog/aha-programming&lt;/a&gt; - Coined "Avoid Hasty Abstractions" as a counter to premature DRY. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>softwareengineering</category>
      <category>programming</category>
      <category>solidprinciples</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>Bomb the Silo From Inside: Why Mission Teams Work</title>
      <dc:creator>Adam Świderski</dc:creator>
      <pubDate>Wed, 28 Jan 2026 22:20:58 +0000</pubDate>
      <link>https://dev.to/asvid/bomb-the-silo-from-inside-why-mission-teams-work-o1p</link>
      <guid>https://dev.to/asvid/bomb-the-silo-from-inside-why-mission-teams-work-o1p</guid>
      <description>&lt;p&gt;Most software teams are split up by technical layers—backend, frontend, and mobile. This seems logical, but it often creates bottlenecks, delays, and frustration. &lt;strong&gt;Mission teams flip the script: cross-functional developers own features end to end, from idea to release.&lt;/strong&gt; This approach speeds up delivery, builds real ownership, and cuts down on endless handoffs. Too perfect to be true? It demands more upfront effort in communication and shared knowledge, but the payoff is worth it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I heard &lt;code&gt;mission team&lt;/code&gt; term first from Moritz Wilfer at Payworks&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The bottleneck problem
&lt;/h2&gt;

&lt;p&gt;Early in my career, I was the lone frontend developer supporting a squad of four backend engineers. Unsurprisingly, I became the bottleneck. To keep the wheels turning, I taught them how to run the frontend app and make simple tweaks. Features kept moving forward, and I could polish things up later. It was a scrappy solution that kept us afloat until reinforcements arrived.&lt;/p&gt;

&lt;p&gt;Even though we used PHP for the backend and JS for the frontend, cross-functional development still worked. Some backend developers liked seeing visual feedback and got a better sense of how their API design affected the frontend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mission team pattern
&lt;/h2&gt;

&lt;p&gt;Much later, I joined a team as an Android developer that was called 'the mission team.' &lt;strong&gt;We had backend developers, iOS, and me for Android. We were responsible for adding new features and making sure we met new market requirements on our own.&lt;/strong&gt; Each of us had our own area of expertise, like mobile SDKs or the core backend service. But to deliver what was needed, whether for legal reasons or other needs, we often had to work across the whole system, including services owned by other teams.&lt;/p&gt;

&lt;p&gt;The goal was for us to work independently, without taking too much time from other teams. They had their own work and priorities, and might not be able to help when we needed it. Timing was usually critical.&lt;/p&gt;

&lt;p&gt;And it worked, thanks to careful planning. We had a rough plan for each quarter, clear sprint goals, and reviews every two weeks. &lt;strong&gt;This setup takes effort, but it delivers faster results, clearer accountability, and more flexibility than layered teams.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example, we knew we needed to add an enum and a short method to one service in the next sprint. This let us finish a whole feature in two sprints and hit our quarterly goal. It required a good understanding of how our whole system worked, and the learning curve was steep, especially in fintech. But with that knowledge, we delivered many complex features on time. Often, these features were just a few lines of code in different places, like adding a new card transaction type or generating configs for terminals, all while keeping things secure and backwards compatible.&lt;/p&gt;

&lt;p&gt;When I say 'we had our own field of expertise,' I don't mean we owned it completely. We just knew those parts best, unlike some of the satellite services, which we only understood in a general way. Since we used Java and Spring everywhere, all the services looked similar, so it was easy to ignore the framework code and focus on the domain.&lt;/p&gt;

&lt;p&gt;Many teams contributed to the core service and SDK. In fact, I think every developer in the company had committed something to the core service. The SDK was just for mobile developers, but since we had several mobile teams, there were still many contributors. Each team owned a part of the project, and an architect helped guide its direction. We held guild meetings to sync on ideas, refactoring, and needed changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The silo consequence
&lt;/h2&gt;

&lt;p&gt;Being part of an effective mission team was a much better experience than my earlier jobs with the classic layered approach, where managers had to sync projects, align tasks, and coordinate features across teams.&lt;/p&gt;

&lt;p&gt;But that approach never really worked.&lt;/p&gt;

&lt;p&gt;I once worked where backend, frontend, and mobile devs sat close, juggling ongoing projects and demos that depended on backend support. Coordination across teams was weak. As a result, crucial discussions often stayed isolated, and mobile frequently got stuck with APIs designed for the web, missing backward compatibility—critical for users updating at their own pace.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two mobile developers once found out during a coffee chat that they were building the same thing twice for different projects because of poor communication, even though managers were sitting nearby.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Smoke breaks unexpectedly became a way to share information across teams, since there weren't good official communication channels.&lt;/p&gt;

&lt;p&gt;There were many reasons for this situation: poor communication skills, bad team organization, lack of transparency, and no single mobile architect to connect the teams.&lt;/p&gt;

&lt;h2&gt;
  
  
  The hero manager trap
&lt;/h2&gt;

&lt;p&gt;At another job, my manager handled all the sync work, meetings, and task prep. Developers had it easy—we just picked a task with a clear description, and any issues with other teams were already sorted out. As convenient as this was, I felt disconnected from the project. I was more of a code monkey than someone with real ownership.&lt;/p&gt;

&lt;p&gt;One manager can only do so much; it doesn't scale, and working 14 hours a day isn't healthy. If some meetings and sync work were shared, the risk of project delays would go down. You know the bus factor rule.&lt;/p&gt;

&lt;p&gt;A previous manager encouraged us to take the lead on features or epics. Everyone became a specialist and owner of part of the work, but no one person was a single point of failure. I grew to like this approach, even though leading isn't really my style. Later, I missed it when all I had to do was pick a task. &lt;strong&gt;This way, the manager could lead several teams by focusing on unblocking us instead of micromanaging.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The tradeoffs
&lt;/h2&gt;

&lt;p&gt;Empowered feature teams don't come cheap. There are challenges, like scheduling sync meetings, setting boundaries, team collaboration, and engineering maturity.&lt;/p&gt;

&lt;p&gt;From my experience, mostly Android SDK work, merge conflicts were rare because we had our own areas to work in and kept PRs as small as possible, using feature flags extensively. We just couldn't afford 10k lines of changes after 3 weeks of work in the cave. Nobody would even read this - rightfully so. After a few days without rebase, it would be based on an out-of-date codebase anyway, so good luck solving conflicts.&lt;/p&gt;

&lt;p&gt;Releasing the SDK wasn't as dramatic as releasing a new backend version. Our CI built a jar, and we published it on our page. Sometimes, teams introduced flaky tests or bugs that delayed releases for others. At some point, a release train procedure was being introduced to avoid these issues.&lt;/p&gt;

&lt;p&gt;With the backend and multiple services, we had to juggle things. Ideally, all services would be independent and could be released separately, but that's not how it works in reality. We had to deploy them in order or at least in sync, and then update flags.&lt;/p&gt;

&lt;p&gt;The way we organized our work and cooperated with other teams was impressive, but it took time to reach that point.&lt;/p&gt;

&lt;p&gt;We had to understand the whole system layout, not in great detail, but more or less who to talk to, to get things done in some area. Without using a black market approach(&lt;a href="https://www.goodreads.com/book/show/49828197-the-software-architect-elevator" rel="noopener noreferrer"&gt;as described in this book&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;We had to plan ahead and know what we needed to do. That way, we could give other teams a heads-up: 'Hey, we need to add this and that next sprint, and we'd appreciate some support and a code review.' Usually, it only took a few minutes of their time to review code, plus maybe a quick ad hoc huddle or pair programming. Nothing dramatic, and no managers needed.&lt;/p&gt;

&lt;p&gt;Here's what we managed to avoid:&lt;/p&gt;

&lt;p&gt;You'd have to create a ticket on another team's board. Then, managers would talk to each other to prioritize it. There would be pushback because everyone is busy and already has work planned far ahead, so squeezing in a new enum or a quick fix is tough. Even if the other team started the work, they wouldn't have context, know the impact, or have much incentive to do it quickly or well, since they have their own priorities.&lt;/p&gt;

&lt;p&gt;And we would wait, and wait, and ping them, and wait, and then ping some more, and then threaten to kill their cat… wait, that was before AI. No threats, HR was looking. We were nice to each other.&lt;/p&gt;

&lt;p&gt;We'd get blocked by another team that didn't understand what needed to be done or why. Then we'd have calls to explain, everyone would get frustrated, and the feature would be delayed.&lt;/p&gt;

&lt;p&gt;Then, a bug would show up because someone guessed the requirements when creating a ticket on another team's board. Both teams would have to debug their parts, probably asynchronously at first, then together, and maybe play the blame game. &lt;strong&gt;After that, you might just want to Google how to become a beekeeper and never look at the code again.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's a waste of time and people's potential.&lt;/p&gt;

&lt;p&gt;It was much faster to get some space and do the work ourselves, even if we had to learn about extra services first. We were in control and made the decisions. Cooperation was still important, but it was up to us to handle it, and we didn't fully depend on other teams. We needed their support and help, but not their actual work. This approach worked well for small changes, but if we had to rebuild a large part of someone else's service, it wouldn't go as smoothly—and we shouldn't be doing that. For us, those services were building blocks we might tweak or add to, but the main structure stayed the same. The owner team was responsible for major refactoring and guiding the service's direction. We could add or change things within the existing framework, but we were guests and needed to respect the owners.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resist the urge to be a good Boy Scout by refactoring an unfamiliar codebase when you're only supposed to make minor changes.&lt;/strong&gt; There are stories of people who tried that and &lt;a href="https://medium.com/lets-code-future/refactoring-15-year-old-code-everything-i-broke-in-3-weeks-675570209c51" rel="noopener noreferrer"&gt;ended up wasting a month because they got lost in refactoring an unknown project&lt;/a&gt;. It's better to leave that to the maintainers.&lt;/p&gt;

&lt;p&gt;Of course, my own changes sometimes blocked or conflicted with other teams' work. That's why we have guilds. &lt;strong&gt;It's not just encouraged—it's required to be proactive about planned changes that might affect others and to let people know well in advance.&lt;/strong&gt; Use open documents, tech proposals, or Request For Comments. Share updates on guild Slack, email lists, or even Teams. Remind developers at the next sync meeting. Add reviewers from other teams to the pull request and make the needed changes.&lt;/p&gt;

&lt;p&gt;And sometimes, you'll still hear complaints that they didn't get the memo. Some people just can't be helped.&lt;/p&gt;

&lt;p&gt;I was part of the mobile developers guild, but I sometimes worked on backend features too. Did that mean I had to join the backend guild? No, because we had backend developers on the team who could guide me on my tasks while focusing on their own work. That way, they weren't a bottleneck when most of our work needed backend changes. Sometimes, roles switched, and they worked on the mobile part with my help, just like in my first example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it's worth it
&lt;/h2&gt;

&lt;p&gt;Building a silo and protecting it from outside interference slows down development and creates unnecessary tension between stakeholders. Still, it can feel like the natural way to work. &lt;strong&gt;Changing mindsets and culture is much harder in organizations than making technical changes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This kind of setup brings new challenges. Who handles end-to-end QA? Who controls or blocks releases? There's no single answer—it depends on the organization's structure and specific issues. You'll make mistakes and wrong decisions when changing team cooperation policies, but that's part of progress.&lt;/p&gt;

&lt;p&gt;I'm biased by my own experience, but I saw this approach work well in a complex, regulated, and time-sensitive environment. I believe it can work anywhere. My mission team was cross-functional but it doesn't have to be, not all features need UI or client apps, but may stretch across multiple backend services.&lt;/p&gt;

&lt;p&gt;If you work on a client-side app, you probably know the backend service API well. How hard would it be to add a new field? &lt;strong&gt;Be curious, ask a colleague from another team, and get it done before the manager even replies to your email.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A platform guild helps teams coordinate planned maintenance and refactoring, so important or urgent work by other teams isn't blocked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Empower people to do their jobs instead of waiting for someone in the 'ivory tower' to prepare detailed task descriptions, requirements, screens, and flows, while managing complex features at every level.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not all teams are mission teams. For mission teams to succeed, you need platform teams to support them and hybrid teams to deliver business features, while also making room for fully feature-oriented teams.&lt;/p&gt;

&lt;p&gt;Then you can benefit from true cooperation, without a whole manager-orchestration-ticket-golem being involved. Allowing other teams to step in and change stuff under the main repo owner's supervision reduces a lot of planning and work, syncing with the repo owner team, and allows features to be brought in faster.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;Hohpe, G. (2020). &lt;em&gt;The Software Architect Elevator: Redefining the Architect's Role in the Digital Enterprise&lt;/em&gt;. O'Reilly Media. Chapters 30-32 (Part IV: Organizations). Available at: &lt;a href="https://www.goodreads.com/book/show/49828197-the-software-architect-elevator" rel="noopener noreferrer"&gt;https://www.goodreads.com/book/show/49828197-the-software-architect-elevator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ozcay, D. (2026). &lt;em&gt;Refactoring 15-Year-Old Code: Everything I Broke in 3 Weeks&lt;/em&gt;. Medium. &lt;a href="https://medium.com/lets-code-future/refactoring-15-year-old-code-everything-i-broke-in-3-weeks-675570209c51" rel="noopener noreferrer"&gt;https://medium.com/lets-code-future/refactoring-15-year-old-code-everything-i-broke-in-3-weeks-675570209c51&lt;/a&gt;&lt;/p&gt;

</description>
      <category>agile</category>
      <category>teamwork</category>
      <category>softwareengineering</category>
      <category>programming</category>
    </item>
    <item>
      <title>Look at Me, I'm the iOS Developer Now</title>
      <dc:creator>Adam Świderski</dc:creator>
      <pubDate>Sun, 19 Oct 2025 19:42:05 +0000</pubDate>
      <link>https://dev.to/asvid/look-at-me-im-the-ios-developer-now-icj</link>
      <guid>https://dev.to/asvid/look-at-me-im-the-ios-developer-now-icj</guid>
      <description>&lt;h1&gt;
  
  
  Look at Me, I'm the iOS Developer Now
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Timeline:&lt;/strong&gt; September 14 - October 3, 2025 (19 days)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lines of code I wrote:&lt;/strong&gt; 0 (Claude did it all)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apple Store rejections:&lt;/strong&gt; 3&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hours debugging CI/CD:&lt;/strong&gt; 5&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; Live app with paid subscriptions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://apps.apple.com/pl/app/candle-studio/id6752621480" rel="noopener noreferrer"&gt;Check the app!&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;I'm the iOS developer now. I mean, kinda, I just released my first purely native iOS app to the App Store. It took just a week of filling out forms and being rejected because some rule, 9.5.1, was broken. It feels like dealing with insurance companies...&lt;/p&gt;

&lt;p&gt;I didn't write a single line of code, though. &lt;strong&gt;Even if Swift looks nice, like Kotlin with untreated ADHD&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It was all written by Claude Code :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Building What She Asked For
&lt;/h2&gt;

&lt;p&gt;I did the opposite of what I (and most developers) usually do. I didn't set up CI/CD pipelines, I didn't design architecture, and I didn't go to tutorial hell to learn new technology. I just started building what my wife asked me to.&lt;/p&gt;

&lt;p&gt;My wife has recently taken up candle making, and she was a bit annoyed with the manual calculation of fragrance. There are some rules to follow, but it's a trial-and-error process. She was making some notes, but it's just inconvenient to follow, especially after a week or two, when you test the candle burning and the compiled scent. She asked if I could make an app to help with calculating fragrance load in the candle. Of course I can't. We are an Apple household, and I'm only familiar with Android development. But Claude can do anything, and I have a general idea of what I want to build. I have over a decade of software development experience, and I've gained gray hair as a result.&lt;/p&gt;

&lt;p&gt;I built it locally on my machine. I heard about not using MVVM, Viper, or any other framework, and instead using SwiftUI as it was designed to be used, and asked Claude to follow this approach. And I don't even know how to create "Hello World" in Swift. (This is a lie, I wrote it once ~8 years ago, as soon as it was possible to compile and run Swift on Ubuntu)&lt;/p&gt;

&lt;p&gt;The app began as a simple calculator for wax-to-fragrance ratios. However, it now features multiple panels for calculating candle-related information, a recipe management system with a photo gallery, notes, and rates, a label designer, and a timer. And a few other things are not finished or working well yet that are hidden for now :)&lt;/p&gt;




&lt;h2&gt;
  
  
  Xcode Culture Shock
&lt;/h2&gt;

&lt;p&gt;My biggest pain so far is the overall developer experience, compared to Android development. Xcode is a very different IDE compared to JetBrains' Android Studio. The app configuration is in menus, not in files. So Claude couldn't edit those, just wrote me step-by-step instructions on where to click. In 2025, with the help of an LLM, I followed a generated tutorial on which button to click and what to type to make my app compliant with Apple App Store regulations. I know that Java XML files are not the nicest thing to deal with. Android used to have its own Groovy configurations, but LLM could edit those without me even knowing. Why, for iOS, do I have to manually find options hidden in multi-level settings?&lt;/p&gt;

&lt;p&gt;I didn't use any task management system, not even a to-do list. If I had some ideas or feedback from my wife, the tester, or the product owner, I would write them down on paper or start working on them right away. No deadlines, no expectations, just building. It's pretty therapeutic after working a 9-to-5 job. I could focus on features, UI, and creating something valuable, rather than dealing with Jira bureaucracy. With no CI and tests, I was not slowed down by this one flaky test that needed to be rerun, or the few that I had to change every time. I knew everything would be changing a lot, so why bother with unit tests when the unit lifespan is only two days? Since I wasn't writing code, I didn't feel any anxiety before a PR or commit. I was conducting a code review, and since I'm not familiar with writing idiomatic Swift, I focused on higher-level aspects rather than nitpicking. So even Claude could chill and bring me more features.&lt;/p&gt;

&lt;p&gt;The first commit was on September 14; the app was released in the store on October 3. I worked on it in my spare time, on weekends, and occasionally in the evenings. Not hustling. Not grinding.&lt;/p&gt;




&lt;h2&gt;
  
  
  Apple's Bureaucracy
&lt;/h2&gt;

&lt;p&gt;Applying to the app store is from hell. Oh, you want to have an app available in France? Extra form. What taxes are you going to pay in Micronesia? Do you have your Kazakhstan tax ID? It's been a while since I released an app to Google, and I recall a storm of forms surrounding content and child safety, but man... This is on another level.&lt;/p&gt;

&lt;p&gt;The biggest challenge was adding subscriptions to the app. I wanted to check how it's done and have some premium features. Of course, it has to go through Apple; they take 15% for in-app purchases. The app must use product IDs set in the website form; there is no direct link between Xcode and App Store Connect. It's your job to put the products together and then use them in the app. The products, like subscriptions, are also reviewed. Separately from the app, unless it's the first release, then together - even Claude wasn't sure. The warnings about missing data are useless: "metadata missing" tells me nothing about what and where I should put. "ready to submission" status doesn't help me to submit it for review. There is no button to click to submit. I have no idea why Apple, a company that has always valued UX, is doing so much to make developer life harder. I couldn't condone such bad practices, even if I were paid to do so.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://apps.apple.com/pl/app/candle-studio/id6752621480" rel="noopener noreferrer"&gt;The app is finally approved&lt;/a&gt;&lt;/strong&gt;. Take it, enjoy your candles. Pay me for premium :)&lt;/p&gt;

&lt;h2&gt;
  
  
  CI/CD and Certificate Hell
&lt;/h2&gt;

&lt;p&gt;Now the next episode is coming: marketing and making the app discoverable and appealing to users. This is just barely working as an MVP. I have a backlog of features and ideas; there are no tests, no CI, and no Fastlane configured. I should add those, but on the other hand... if my wife is happy with what it is now and nobody else uses it... Why bother?&lt;/p&gt;

&lt;p&gt;I forgot how cool it is to use Fastlane. Even though it was designed to streamline iOS deployments, since they are arguably ~more complex~ more annoying than Android ones, I was using it with Android apps, back in a day when we used Jenkins running on a PC next to my desk as a CI agent. It has now expanded in terms of utilities and tools, but the ease of use and configuration remain. It's reassuring when tools last for many years.&lt;/p&gt;

&lt;p&gt;I used the free Canva tier to draw a logo and some promo screenshots. The premium version offers some lovely templates and a wide range of backgrounds, but... I plan to do it once within 5 minutes; there's no need for a monthly subscription.&lt;/p&gt;

&lt;p&gt;After releasing the app and being approved by the App Store gods, I decided that manually building and uploading it was beneath my standards. How hard can it be to have a GitHub action build, sign, and push the new app to the App Store? On Android, it was relatively easy, as long as you didn't lose that one precious certificate file; however, it was eventually changed to Google Play, which handles signing. But it was a simple few-step process. Apple can't make this hard, right?&lt;/p&gt;

&lt;p&gt;Five hours later, I can say with complete confidence that Apple can.&lt;/p&gt;

&lt;p&gt;It's not generating a cert. It's generating a cert, to create a profile, to generate a cert, to bundle it with a key, to get the base64 of it while adding a password, to then set 7 GitHub secrets, to then debug some more, because the first cert was for development, not distribution, and the profile is wrong. &lt;strong&gt;And I'm a single dev in a shed, hammering my cute little app&lt;/strong&gt;. I can barely imagine this process in larger companies. IT has to love it. Or maybe the process is so complex because it's designed for larger companies with multiple teams, where not everyone should have access to everything? I choose to believe that.&lt;/p&gt;

&lt;p&gt;I have 3 pages full of failed builds in the GitHub actions tab. Some errors surprised even Claude Code, and it responded with &lt;code&gt;this is a weird error, it shouldn't be there&lt;/code&gt;. My list of errors:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Certificate installation error&lt;/li&gt;
&lt;li&gt;Profile UUID mismatch&lt;/li&gt;
&lt;li&gt;Another certificate error (after regenerating)&lt;/li&gt;
&lt;li&gt;Build succeeded, but TestFlight upload failed (password auth)&lt;/li&gt;
&lt;li&gt;Bundle version conflict (build 2 vs existing 22)&lt;/li&gt;
&lt;li&gt;Invalid Pre-Release Train (version 1.0 closed)&lt;/li&gt;
&lt;li&gt;Altool error with bundle version 22&lt;/li&gt;
&lt;li&gt;Train version 1.0 closed error&lt;/li&gt;
&lt;li&gt;CFBundleShortVersionString still 1.0&lt;/li&gt;
&lt;li&gt;Still version 1.0 after increment_version_number&lt;/li&gt;
&lt;li&gt;agvtool can't find Xcode project files&lt;/li&gt;
&lt;li&gt;agvtool "Cannot find project.xcodeproj/../YES" error&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After all that 5h grind, Claude said it's a good time anyway, because it's normal to spend days debugging Apple app signing issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Writing code was not the central part of developing this app&lt;/strong&gt;. The road from 'it works on my machine' to 'you can get it on your phone' was pretty long. I want to believe that each next project would be easier to set up, but I cannot remember all those steps, even if my life depended on it. They are also not written in stone; they are written in ever-changing policies, rules, and workflows in digital form. Adding and removing steps is part of the game.&lt;/p&gt;




&lt;h2&gt;
  
  
  App update
&lt;/h2&gt;

&lt;p&gt;A few days after the successful release, I updated the graphics in the store and fixed some minor UI bugs. To update graphics in store I had to release new build - OK, your store your rules. And pass another review... waited over a day for the review to even start. I really hoped each following review would be quicker... It went smooth this time, but if I had a critical bug in prod, waiting a day for the review to even start is scary.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Actually Learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;LLM-assisted development works&lt;/strong&gt; - I built a functional iOS app without knowing Swift. However, I can't imagine this happening without my previous experience of over a decade as a mobile developer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer experience matters&lt;/strong&gt; - Xcode feels like time travel to 2010. Multi-level menus with tabs that I had to manually tune to pass App Store requirements. LLMs can't work with that; they can with text-file-based settings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apple's bureaucracy is real&lt;/strong&gt; - Forms, reviews, certificates, profiles, more forms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shipping beats perfecting&lt;/strong&gt; - No tests, no CI initially, but it works and my wife is happy. Fortunately, she is not a professional product owner, so she wasn't changing her mind five times for each feature and was very open-minded about how I implemented things.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Building without JIRA is therapeutic&lt;/strong&gt; - Paper notes and just doing it felt liberating. I recommend that every developer on the edge of burning out have such a pet project, one without deadlines and with a simple enough goal to complete in two weeks. Use the initial motivational spark to bring something to life.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fastlane is still cool&lt;/strong&gt; - I wonder if it's still used on Android, or if we have even better tools?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;iOS apps look good with minimum effort&lt;/strong&gt; - I didn't design any UI, just used most native elements I could find, and it looks great. On the brand-new iOS 26 and on older iOS 18, the design language is totally different. On Android, it would require a shitload of support libraries with juggling their versions to fit other dependencies. My iOS app has no external dependencies.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;The app works. My wife is happy. That's technically a success.&lt;/p&gt;

&lt;p&gt;But now comes the hard part: making it discoverable, appealing to actual users who aren't married to me, adding tests, proper CI, and all the things "real" apps should have.&lt;/p&gt;

&lt;p&gt;Or it's already good enough. If the only user is satisfied, why add complexity?&lt;/p&gt;

&lt;p&gt;(And yes, I'm tempted to build the Android version just to compare the pain. Stay tuned.)&lt;/p&gt;

</description>
      <category>ios</category>
      <category>mobile</category>
      <category>claude</category>
      <category>ai</category>
    </item>
    <item>
      <title>The Genie's Curse: My Month with AI-Augmented Coding</title>
      <dc:creator>Adam Świderski</dc:creator>
      <pubDate>Fri, 25 Jul 2025 16:08:03 +0000</pubDate>
      <link>https://dev.to/asvid/the-genies-curse-my-month-with-ai-augmented-coding-3a7i</link>
      <guid>https://dev.to/asvid/the-genies-curse-my-month-with-ai-augmented-coding-3a7i</guid>
      <description>&lt;p&gt;You know that meme where a &lt;a href="https://youtu.be/lM0teS7PFMo?si=BIrCjOsNYvkztGpB&amp;amp;t=38" rel="noopener noreferrer"&gt;guy asks a genie to grant his wish&lt;/a&gt;, and the genie's sole purpose is to twist that wish to make him miserable? Like he wishes for $1 million and gets it, but inflation is 300,000,000% so he can't even afford bubble gum with it.&lt;/p&gt;

&lt;p&gt;After a month of experience with vibe/AI-augmented coding, I have a similar feeling.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Project
&lt;/h2&gt;

&lt;p&gt;I used Cursor to build a native Android app for Todo/Pomodoro. Most basic stuff ever, but &lt;strong&gt;I got carried away a bit&lt;/strong&gt;. What started simply ended up with task statistics, progress notifications, haptic feedback on setting changes, background noises, focus mode control, and even a watch companion app. Not so basic anymore - about &lt;strong&gt;30k lines of code&lt;/strong&gt;. Some of which are dead, because LLMs tend to excel at adding code, but struggle with cleaning up after it's no longer used.&lt;/p&gt;

&lt;p&gt;I have pretty strict and comprehensive rule files that explain exactly, with code examples, how I want Views to be structured, what ViewModels should and should not be injected with, and what return types from UseCases should look like—all based on my preferred approach to building apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rules are for people
&lt;/h2&gt;

&lt;p&gt;It worked well most of the time, especially for the phone app. However, for the watch, there are significantly fewer sources, blog posts, and code examples available on &lt;code&gt;GitHub&lt;/code&gt;. Especially when trying to use the latest &lt;code&gt;Jetpack Compose&lt;/code&gt; with Material 3 features.&lt;/p&gt;

&lt;p&gt;But Cursor was continuously doing some annoying things. Instead of using built-in view components like &lt;code&gt;HorizontalPageIndicator&lt;/code&gt;, it created a new one from simple UI elements and added the entire logic to it. Not making a UI element to be reused, but instead hammering the code into the concrete &lt;code&gt;View&lt;/code&gt; until it worked.&lt;/p&gt;

&lt;p&gt;When told that Views should not use Services directly, it simply removed the calls, without changing to call a &lt;code&gt;ViewModel&lt;/code&gt; to perform the action. I didn't notice that at first, and asked to add &lt;code&gt;Services&lt;/code&gt; to &lt;code&gt;DI&lt;/code&gt;, create their interfaces, and use those types rather than concrete implementations, which removed the services entirely. Therefore, none of the existing services were used directly, as &lt;strong&gt;they no longer existed&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Cursor rules are similar to speed limits in Poland - &lt;em&gt;just a mere suggestion&lt;/em&gt;. Even though I had rules about keeping Views small, trying to extract reusable parts from them, and having each part use its own view model to maintain state, &lt;strong&gt;Cursor had no issues generating massive views with 1000+ lines&lt;/strong&gt;, where a single View Model was doing way too much. A lot of logic was embedded into the View, rather than being moved out to &lt;code&gt;UseCases&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After implementing some features, I wanted to split them into packages, so I wouldn't have 20 unrelated files in one generic directory. Oh, it failed miserably. Moving some files, deleting others, and not updating imports...It made such a mess I just reverted to last commit. I did it manually, and it was way faster.&lt;/p&gt;

&lt;p&gt;I want to extract as much shared code as possible and try using it on iOS with Kotlin Multiplatform, but I'm reasonably sure there is no point in asking Cursor to do that for me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I ruled out generating tests entirely&lt;/strong&gt;. The LLM checks what code appears to do and then generates tests to verify it, rather than determining whether it produces something that actually makes sense. The tests were generating zero value when run alongside code generation, and they were causing compilation errors because &lt;strong&gt;Cursor was very stubborn about using Mockito instead of Mockk&lt;/strong&gt;, even when the rules specifically stated what the entire tech stack was.&lt;br&gt;
I think it was on &lt;a href="https://www.youtube.com/@ModernSoftwareEngineeringYT/featured" rel="noopener noreferrer"&gt;Modern Software Engineering YT channe&lt;/a&gt; where I heard the idea of writing tests manually (do we really have to add an adjective now to precisely state code was NOT AI-generated?), but then using LLM to generate code that would pass the tests. Remember the promise that POs will write Cucumber BDD tests? You are the PO now. Is this the way?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Annoying Dance
&lt;/h2&gt;

&lt;p&gt;It also has an annoying tendency to add &lt;strong&gt;imports to nonexistent dependencies&lt;/strong&gt;, but with names that make sense. For example, when I asked to add a button, the LLM did so, and also added import to a Material Design dependency that was not in the project. It fails on compilation, so then I have to ask politely to fix it. A lot of ping pong.&lt;/p&gt;

&lt;p&gt;Usually, it works well when I point to other places in code that already have the desired structure or elements. But this is not quite vibe coding, right?&lt;/p&gt;

&lt;p&gt;I noticed after a while that I have a lot of &lt;strong&gt;dead code&lt;/strong&gt;. Cursor tends to add new functions, replace the call, but leave the old one hanging around. Removing it by hand is faster and safer than asking LLM for it. In my experience, it has trouble identifying unused code and can leave some parts out or remove used code instead. Little prankster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I was using two IDEs at all times&lt;/strong&gt;: Cursor (a glorified version of VSCode) and Android Studio. I'd rather have the Cursor plugin in my primary IDE, which has all the tools I need for Android development. I started using my iPad as my third screen, to have a big prompt window there, as my 34-inch ultrawide was occupied with Android Studio running two emulators, highlighting code issues that VSCode missed, and managing file order. Oh, and my laptop screen to search online for &lt;strong&gt;Material Design UI elements that Cursor was intentionally not using&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There were &lt;a href="https://arxiv.org/pdf/2507.09089" rel="noopener noreferrer"&gt;studies&lt;/a&gt; and &lt;a href="https://youtu.be/DWkHN0VaFJA?si=yZF32v5XNG3Tnb0U" rel="noopener noreferrer"&gt;opinionated videos&lt;/a&gt; about how &lt;strong&gt;AI is making programmers slower (and dumber)&lt;/strong&gt;. There is something to it. I don't know my project codebase by heart, unlike the ones I wrote myself. Whatever I would learn by grunting through compilation errors, and UI issues, LLM just handled me for &lt;del&gt;free&lt;/del&gt; 20$ a month.&lt;br&gt;
LLMs have a limited memory (context), so it's not like a junior developer who learns things on the way. You will need to repeat the same steps multiple times, pointing to the same code examples and possibly tweaking the rule files. As the codebase grows, the results are getting worse and slower.&lt;/p&gt;

&lt;p&gt;If I had precise and small enough tasks to implement, I'd probably be faster, learning the project as I go, and having a full context in my mind. &lt;strong&gt;However, this wasn't the case for this project&lt;/strong&gt;. And rarely in my working experience, when working on multiple tasks simultaneously, without precise requirements or UI design, looming deadlines, and nobody knows what they are doing or who to ask for help.&lt;/p&gt;

&lt;p&gt;I would probably still be drawing diagrams, creating UI, and writing tasks, assigning them to epics and phases, but instead, &lt;strong&gt;I have a working app&lt;/strong&gt;.&lt;br&gt;
It wouldn't work if I were part of a team, and everyone would just vibecode whatever they wanted whenever they are pleased. But for solo devs...&lt;/p&gt;

&lt;h2&gt;
  
  
  The Sweet Spot
&lt;/h2&gt;

&lt;p&gt;It is an excellent tool for prototyping, though. I could test and &lt;strong&gt;try a lot of ideas and scrap them&lt;/strong&gt; without feeling like I wasted 3 days building a screen that doesn't make sense. It took me five minutes of prompting to verify that it wasn't what I actually wanted, and then I moved to another idea.&lt;/p&gt;

&lt;p&gt;In this project, I took a different approach than I usually do, or that professional developers typically take at work. I didn't plan anything or design anything - &lt;strong&gt;I just started building the next thing I thought was the most important&lt;/strong&gt;. It's refreshing and liberating. I could do this because I have no budget, no deadline, and nothing but the urge to build something I'll use. And AI is great for that - to test ideas, because what I like may not be the thing I actually need. AI tools like Cursor are great for prototyping and exploration, allowing me to test and discard ideas without investing significant time and effort.&lt;/p&gt;

&lt;p&gt;The nice thing was that after some update, or using a new model (I dunno, I keep it on auto, I'm vibing), it started to compile the code and react to build errors, fixing hallucinated imports and imaginary method calls. On its own, without me changing the rules or asking it explicitly.&lt;/p&gt;

&lt;p&gt;Around the same time, I noticed that Cursor improved at searching the codebase and finding connections. Rather than manually testing changes, I asked: What will happen when &lt;em&gt;{{insert edge case}}&lt;/em&gt;? It could generally answer and suggest solutions when issues were found. This seems like a nice tool to &lt;strong&gt;learn a new codebase&lt;/strong&gt;, where you just point to a file and ask, 'What's going on here? Who hurt you?'&lt;/p&gt;

&lt;h2&gt;
  
  
  UI Generation
&lt;/h2&gt;

&lt;p&gt;While it's useful when generating UI, especially since I don't want to PhD in Jetpack Compose that &lt;strong&gt;STILL DOESN'T HAVE NATIVE SCROLL INDICATORS&lt;/strong&gt; and keeps changing or forcing me to use experimental APIs, the LLM doesn't see or understand what it's generating.&lt;/p&gt;

&lt;p&gt;Each time I ask to add a button somewhere to perform an action via &lt;code&gt;ViewModel&lt;/code&gt;, &lt;code&gt;UseCase&lt;/code&gt;, &lt;code&gt;Service&lt;/code&gt;, and &lt;code&gt;Repository&lt;/code&gt;, it does just that. Then I have to run the code or check Preview (God I love those), &lt;strong&gt;capture part of the screen, add it to the conversation&lt;/strong&gt;, with note: "This button is in the wrong spot, I want it elsewhere and smaller, and use &lt;code&gt;IconButton&lt;/code&gt; as I asked you 15 minutes ago."&lt;/p&gt;

&lt;p&gt;At some point, I learned it's actually better to paste an image of a wireframed design and ask the LLM to do "this" without providing too much information, rather than trying to specify &lt;strong&gt;exactly&lt;/strong&gt; what I need the AI to do. Then do manual fixes. A single image is worth more than 2^10 words. Typically, there are no concerns about font sizes or paddings, as the LLM will use standard, statistically correct options.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LLM tends to use the simplest UI components at all times&lt;/strong&gt;, rather than more sophisticated yet standard ones. If I hadn't told it to use &lt;code&gt;CardView&lt;/code&gt;, it would simply draw a box container and place text and buttons within it. The same applies to dialogs - it will add one, but not following Material Design principles, unless I explicitly describe them. Something that's a standard thing in Android.&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking Back Control
&lt;/h2&gt;

&lt;p&gt;As I reach the stage of taking back control, I feel a sense of relief. There is a lot, and I mean &lt;strong&gt;A LOT&lt;/strong&gt;, of refactoring before I can release the app. It works, but it has to be &lt;strong&gt;human-rewritten&lt;/strong&gt;. While happy paths are covered, &lt;strong&gt;LLMs often fail to anticipate and handle error cases&lt;/strong&gt;, or they can be less creative in breaking things than a typical user.&lt;/p&gt;

&lt;p&gt;But I have something to work with. It's easier to fix a broken car than build a new one from scratch.&lt;/p&gt;

&lt;p&gt;I will still use Cursor or other AI tools to perform tedious tasks, such as adding new languages, extracting logic to separate files, verifying that I'm using correct logging everywhere, or simply questioning features and architecture in the context of real code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=vagyIcmIGOQ" rel="noopener noreferrer"&gt;DHH, in a podcast with Lex Fridman&lt;/a&gt;, at some point in a 6-hour conversation (worth it), &lt;strong&gt;compared writing code to playing on an instrument&lt;/strong&gt;. While you can listen to a recorded song, and it's going to be perfect each time, a lot of folks still spend hours to learn how to play an instrument, just to play a poor version of the song. Because playing is joy. For DHH, coding is joy, and he doesn't want it to be taken away by LLMs, even if they can do it well.&lt;br&gt;
I enjoy coding, but I prefer building things more; &lt;strong&gt;coding is just a means to achieve that&lt;/strong&gt;. If I can delegate tedious tasks to an LLM and focus on the bigger picture or actually challenging problems, while overseeing the code generation, I'm OK with that.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Unexpected Gem
&lt;/h2&gt;

&lt;p&gt;What I was positively surprised by using Cursor as the discussion partner about the project. For example, what features are missing? Asking it to generate a README based on used libraries and Use Cases, what else should I add to make it MVP-worthy, etc? It's like that one colleague you want to get an opinion from, but don't want to bother too much asking those questions - but the LLM doesn't care, it has all the time in the world. You bump global warming a bit with each question, that's all.&lt;/p&gt;

&lt;p&gt;The LLM asks pretty decent clarifying questions. When I requested the implementation of a new feature with a detailed description of how it should work and look, it still had five clarifying questions with possible answers. And it made me think about how I actually want it, and sometimes even change my mind from my original protein-based idea.&lt;/p&gt;

&lt;p&gt;With that, LLMs shine more than just as code-generating tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Rules of Thumb for LLM Coding
&lt;/h2&gt;

&lt;p&gt;After 30k lines of AI-generated code and countless hours of ping-pong conversations, here are the rules I've learned the hard way:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Point to existing code instead of describing&lt;/strong&gt; - LLMs work best when you can reference other parts of your codebase that already have the desired structure. "Make it like the UserService but for Tasks" works better than explaining dependency injection from scratch. "Do as I say, not as I do" will have exactly the same poor result as when raising a toddler.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use wireframes over verbal descriptions&lt;/strong&gt; - Paste an image of what you want rather than trying to describe UI in words. I wasted hours saying "move the button to the right and make it smaller" when a simple mockup would have done it instantly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start small, then expand&lt;/strong&gt; - LLMs work better on smaller codebases where they can understand the full context. Once you reach a certain complexity threshold, their reasoning begins to break down. Having a well-organized structure in a project leads to a smaller context, even when working within a large codebase. This works well for protein-based developers too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write comprehensive rules files&lt;/strong&gt; - Treat the LLM like a new team member who needs a detailed project handbook. My rules files became as important as the documentation I'd write for human developers. I used to write docs for "my future self" and my goldfish memory, never thought I would need that skill for AI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use AI for prototyping, not production&lt;/strong&gt; - Great for testing ideas quickly and seeing if they make sense, but be prepared for major refactoring. Scrapping AI-generated code leaves no emotional damage, since you didn't spend days writing it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Leverage AI as a discussion partner&lt;/strong&gt; - Ask it about missing features, architecture decisions, and MVP requirements. You may already have a solid idea, but you never know what else you don't know. Some ideas may be off the mark, but it doesn't hurt to ask. It's like having that colleague who's always available for brainstorming and never gets annoyed by your questions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Avoid AI-generated tests&lt;/strong&gt; - They test what the code looks like it should do, not what it actually should do. The tests were bringing zero value and causing more compilation errors than the actual code. LLM can help you write tests, but you should control it, rather than relying on automagically created unit tests and achieving code coverage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use GIT&lt;/strong&gt; - Cursor has some idea of reverting changes, but I learned that making a commit as soon as something remotely close to what I wanted can save me a lot of time. With continuous prompts aimed at ironing out this one minor issue, LLM can completely destroy something that was already OK.&lt;/p&gt;

&lt;p&gt;It seems that &lt;strong&gt;I wouldn't progress&lt;/strong&gt; more than 20% in the project &lt;strong&gt;without a complete understanding of everything the LLM generates&lt;/strong&gt;. Having experience not only in coding in general but also in the specific technology being used is essential. AI is only as good as the programmer who uses it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Verdict
&lt;/h2&gt;

&lt;p&gt;Maybe for web apps it works better? For mobile, it's so-so; for the watch, it's even worse. But &lt;strong&gt;it allowed me to move fast and change my mind often&lt;/strong&gt; - I don't need much more at this stage of the project.&lt;/p&gt;

&lt;p&gt;So, if you're building another CRUD-like SaaS using &lt;code&gt;Next.js&lt;/code&gt; and &lt;code&gt;Tailwind&lt;/code&gt;, hosting it on &lt;code&gt;Vercel&lt;/code&gt;, and utilizing &lt;code&gt;Supabase&lt;/code&gt;, LLM can replace a team of developers.&lt;/p&gt;

&lt;p&gt;It won't, for now, replace &lt;strong&gt;seasoned, grumpy CS grads&lt;/strong&gt; who have seen and made all the possible mistakes a software engineer can commit. But remember coding &lt;strong&gt;bootcamps&lt;/strong&gt;? The place where they explain some frontend JS framework for 6 weeks to career-switching enthusiasts? Yeah, they are fucked.&lt;/p&gt;

&lt;p&gt;For anything more &lt;strong&gt;sophisticated, niche, or actually interesting&lt;/strong&gt;, you still have to understand what the LLM is doing and be able to write everything yourself. However, it makes my work faster, at least when used in a narrow context with clear directions.&lt;/p&gt;

&lt;p&gt;While writing rules for Cursor, including code examples, it reminded me of documenting our mobile project a few years ago. To avoid any misunderstanding about how it works, how it's built, what the structure is, and how it's implemented. I did it to end useless discussions during code reviews, where each developer had slightly different opinions about project architecture. However, &lt;strong&gt;documentation is also useful when new people join the team, as it provides a project handbook. LLM is that new guy, but he never actually learns.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But he is excellent for generating &lt;code&gt;AWK&lt;/code&gt; commands that I have to use once a decade.&lt;/p&gt;

</description>
      <category>cursor</category>
      <category>ai</category>
      <category>programming</category>
      <category>android</category>
    </item>
    <item>
      <title>I built my own split keyboard</title>
      <dc:creator>Adam Świderski</dc:creator>
      <pubDate>Wed, 19 Mar 2025 15:41:45 +0000</pubDate>
      <link>https://dev.to/asvid/i-built-my-own-split-keyboard-3ebe</link>
      <guid>https://dev.to/asvid/i-built-my-own-split-keyboard-3ebe</guid>
      <description>&lt;p&gt;Back in the day, when I was more actively playing the electric guitar, there was an interesting topic on my favorite guitar forum. Guitarists often add some attachment to the guitar but not to the amp or effects. And you need all of that to make your sound on electric guitar. One guy suggested that it's because we touch the instrument with our hands all the time, &lt;strong&gt;its physical connection creating a mental bond&lt;/strong&gt;. I agree. I've bought and sold amps, effects, cables, and accessories, but my guitars are here to stay. I gave them names.&lt;/p&gt;

&lt;p&gt;Do we, software engineers also develop a connection to things we use at work? Personally, not really. As long as it works and gets the job done, we don't touch our code with our hands. &lt;strong&gt;We do touch keyboards&lt;/strong&gt;.&lt;br&gt;
They are the most diversified and opinionated accessories we use. Chassis shapes, switches, keycaps, low—or high-profile... But can they be even more personalized?&lt;/p&gt;

&lt;p&gt;Paraphrasing the first Polish encyclopedia, &lt;strong&gt;everyone knows how the keyboard looks&lt;/strong&gt;. If you read this, you probably write fluently on a QWERTY row staggered keyboard without looking. We all grow up with those, writing school essays, typing The Sims cheat codes, and making angry comments online.&lt;/p&gt;

&lt;p&gt;But then I saw this:&lt;br&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%2Fioglyx1j6n9z7xwjx492.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%2Fioglyx1j6n9z7xwjx492.png" alt="Split keyboard" width="800" height="449"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=evCmP4hH7ZU" rel="noopener noreferrer"&gt;in the YT video&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And I had 2 thoughts: incredible shoot with the terminal in the middle… the middle of what??? &lt;strong&gt;What is this typing device?&lt;/strong&gt; Because it's not a regular keyboard?&lt;/p&gt;

&lt;p&gt;It appears to be &lt;a href="https://dygma.com/products/dygma-defy?variant=53880747983172" rel="noopener noreferrer"&gt;Dygma Defy&lt;/a&gt;. Interesting; I've never heard of those before. But &lt;strong&gt;369$&lt;/strong&gt; for the base model is successfully discouraging me. Maybe there are cheaper options? Not really... and there are works of art like &lt;a href="https://naya.tech/" rel="noopener noreferrer"&gt;Naya&lt;/a&gt; that can go around &lt;strong&gt;1000$&lt;/strong&gt;, absolute units like &lt;a href="https://kinesis-ergo.com/keyboards/advantage360/" rel="noopener noreferrer"&gt;Kinesis&lt;/a&gt; or more common &lt;a href="https://ergodox-ez.com/" rel="noopener noreferrer"&gt;ErgoDox EZ&lt;/a&gt;, which is still over 300$. There is none aftermarket where I live for those, so I can't even try them out. And I don't want to spend that much on something I might not like.&lt;/p&gt;

&lt;p&gt;I have to admit, I like the idea of a different key layout; using column stagger rather than row stagger seems more natural.&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%2Fw2jx6er3o7564v20lp11.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%2Fw2jx6er3o7564v20lp11.png" alt="keyboard layouts" width="800" height="1104"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is also healthier for the hand. The idea is to make fingers move up and down, not diagonally. Staggered columns reflect differences in finger length. Ideally, there would be a single row above and below the home row, so fingers do not have to stretch and hands move.&lt;/p&gt;

&lt;p&gt;But it means having fewer keys overall—no function keys, number keys, or arrows. How can it work for a programmer with wrist-bending IDE shortcuts?&lt;/p&gt;

&lt;h2&gt;
  
  
  Down the rabbit hole
&lt;/h2&gt;

&lt;p&gt;I already own a slightly modded Keychron K2 (tape mod, sound damping, lubed switches, new keycaps) and a K11 low profile with Alice layout. I don't need a new keyboard with 2 nice ones and working, and I don't want to spend so much on that.&lt;br&gt;
Wait. &lt;strong&gt;I'm an engineer&lt;/strong&gt;. I have a 3D printer and a soldering iron. &lt;strong&gt;How hard can it be?&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Macropad prototype
&lt;/h3&gt;

&lt;p&gt;It's doable with what I know and have at home, or can easily get online. That is all I needed to start researching some Open Source projects. And boy, I've found so much more. A whole community of keyboard builders that, for free, share their 3d designs, board projects, software, firmware, tips, and tricks. &lt;a href="https://kbd.news/" rel="noopener noreferrer"&gt;There is a website that gathers intel&lt;/a&gt; about all that.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://golem.hu/sound/" rel="noopener noreferrer"&gt;The catalog of keyboard switch sounds&lt;/a&gt;, popularity, and &lt;a href="https://kbd.news/switch/" rel="noopener noreferrer"&gt;the whole DB of switches data&lt;/a&gt;. Do you want a lubed tactile cherry switch? There is a search with filters. You can then pick what fits your desired pressure force.&lt;/p&gt;

&lt;p&gt;Designs range from pretty standard keyboards, through ortho-linear and split, to ergonomically shaped, or some monsters with the trackballs on the side or in the middle. Why not? There is no limit as long as it works for you. &lt;a href="https://golem.hu/boards/" rel="noopener noreferrer"&gt;Check yourself!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here are some of the most known designs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/foostan/crkbd" rel="noopener noreferrer"&gt;Corne&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/GEIGEIGEIST/TOTEM" rel="noopener noreferrer"&gt;Totem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/adereth/dactyl-keyboard" rel="noopener noreferrer"&gt;Dactyl&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/adereth/dactyl-keyboard" rel="noopener noreferrer"&gt;Dactyl ManuForm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Bastardkb/Skeletyl" rel="noopener noreferrer"&gt;Skeletyl&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;I did some reading and gathered what I wanted to make and what I was comfortable doing. The number of options and places to fail makes it a bit more complex than a lego set, or even predefined keyboard kit. Let's do iterations rather than jump into deep water to sink. The MVP is a macropad, a small keyboard with a few keys, to get a feel for the switches, keycaps, and the whole process.&lt;/p&gt;

&lt;p&gt;I found &lt;a href="https://github.com/victorlucachi/void16" rel="noopener noreferrer"&gt;this hand-wired macropad&lt;/a&gt;, which seems just about what I need.&lt;/p&gt;

&lt;p&gt;It requires just one cheap Arduino board, 15 switches, and an encoder. Chassis is a simple 3d print, so I don't have to get PhD before starting.&lt;/p&gt;

&lt;h3&gt;
  
  
  3d printing Learnings
&lt;/h3&gt;

&lt;p&gt;Starting from 3d printing. I'm not experienced yet; I press print, and the printer goes brrr brrr brrr. And I faced some issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;my print was sticking to the board; I couldn't take it off&lt;/li&gt;
&lt;li&gt;prints often had a few layers broken, like the filament clogged the extruder&lt;/li&gt;
&lt;li&gt;embedding the screw threads into the print is not easy with a soldering iron without temperature control&lt;/li&gt;
&lt;li&gt;cheapest Aliexpress screws are shit&lt;/li&gt;
&lt;li&gt;model dimensions are not the same after printing; some post-processing is required to fit screws into their spots&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The problem with clogging had a surprising solution. I use the Creality K1C, a fully enclosed 3D printer. &lt;strong&gt;The top cover should be opened when printing PLA&lt;/strong&gt;, the most common filament. Otherwise, the inside temperature is too high, and the filament can melt too early in the extruder, expanding and clogging it. No, I didn't read the instructions. I said I'm an engineer.&lt;/p&gt;

&lt;p&gt;I &lt;strong&gt;let the print cool down&lt;/strong&gt; to solve the problem of sticking to the printer bed. I was too eager to use the model and tried to detach it immediately. Now, I just put it next to the window, where it's colder, and let it cool down for a few minutes. It works like a charm.&lt;/p&gt;

&lt;p&gt;I got some post-processing tools, such as sanding paper, a special rotating knife, a hand driller, etc., like for a plastic plane or ship models. They make everything much more manageable.&lt;/p&gt;

&lt;p&gt;And better, lighter soldering iron with temperature control. With some extra endings for precision soldering.&lt;/p&gt;

&lt;h3&gt;
  
  
  OK, now what?
&lt;/h3&gt;

&lt;p&gt;I have the chassis printed and screwed down. Well, because I didn't put threads at a 90-degree angle, &lt;strong&gt;it doesn't fit perfectly, but it's good enough&lt;/strong&gt;.&lt;br&gt;
I got some pre-lubed linear switches from Aliexpress. I didn't want to get anything expensive because there is a huge chance I will screw it up. I put them in their places, printed encoder holder, and cap; all good for now.&lt;/p&gt;

&lt;p&gt;And &lt;strong&gt;understand the pin assignment and electrical schema&lt;/strong&gt;. The first surprise is that individual keys are not connected to separate pins on the board. It's a &lt;strong&gt;matrix of rows and columns&lt;/strong&gt;. That way, a single board can handle many more keys. &lt;strong&gt;Genius&lt;/strong&gt;. When key is pressed the board gets 2 signals, on row and column and can use it to &lt;strong&gt;map it to the key&lt;/strong&gt;. Because of that, each key has to have a diode soldered in, otherwise the unintended electrical paths could be made when pressing multiple keys.&lt;/p&gt;

&lt;p&gt;Switches have 2 pins, and the encoder has 5. The encoder has more functions: button press (handled by 2 pins) and left/right twist (managed by the other 3 pins). I had to check the technical diagram to figure out what goes where. University PTSD is kicking in.&lt;/p&gt;

&lt;p&gt;I've watched a few YouTube videos on hand-wiring keyboards (like &lt;a href="https://www.youtube.com/watch?v=hjml-K-pV4E&amp;amp;t=760s" rel="noopener noreferrer"&gt;this one&lt;/a&gt; and have somewhat of an idea how it should look like. I'll use diode wires to connect the rows. I've got copper wire, some heat-shrink bands for columns, and some silicon wire (awesome stuff) to connect it all to the board.&lt;/p&gt;

&lt;p&gt;I had issues flashing my ProMicro board or putting it into a flash boot state. Eventually, it worked, but not like the official instructions promised. Shorting the RST and GND pins didn't work, but &lt;strong&gt;brute force pushing firmware&lt;/strong&gt; exactly 1s after connecting the USB-C cable to the board did.&lt;/p&gt;

&lt;p&gt;The project README wanted me to wire one column to pin E6 and another to pin C6 on the board, but &lt;strong&gt;my board has only one pin with number 6&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.amazonaws.com%2Fuploads%2Farticles%2Fycioa0b7jhmpud3njab2.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%2Fycioa0b7jhmpud3njab2.png" alt="arduino pin layout" width="800" height="872"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Official Arduino inputs are different from clones. In the diagram above, it can be described as one of three names. Why make stuff easy, right? &lt;strong&gt;It took me about an hour to learn it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I've put everything together, and &lt;strong&gt;it worked!!!&lt;/strong&gt; I pressed the button, and a digit was sent to my computer. My brother laughed a bit at my soldering skills, but it works, and that's all that matters.&lt;/p&gt;

&lt;h3&gt;
  
  
  The split
&lt;/h3&gt;

&lt;p&gt;I can make something of a keyboard work. I can print the case, solder it, and flash the board. &lt;strong&gt;The next logical step is to create a regular split keyboard.&lt;/strong&gt;&lt;br&gt;
The number of options is scary. Switch type, case shape, number of keys, wired or wireless, PCB or hand-wired, hot-swap or fixed... So, I decided to not make any decisions and simply build&lt;br&gt;
&lt;a href="https://github.com/victorlucachi/void_ergo" rel="noopener noreferrer"&gt;macropad author designed one&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It was more-less the same, with key matrix and encoders, with additional &lt;a href="https://en.wikipedia.org/wiki/Phone_connector_(audio)#TRRS_standards" rel="noopener noreferrer"&gt;TRRS (Tip-Ring-Ring-Sleeve)&lt;/a&gt; sockets and cable to connect 2 boards.&lt;/p&gt;

&lt;p&gt;I couldn't really understand how 2 microcontrollers communicate with each other, send power, and use the same exact software. At the same time, one is the main board connected to a computer, and the other only captures the key presses. And I didn't have to. QMK works like magic here. TRRS wire uses standard jack-jack cable like headphones with a microphone to send 5V, ground, and serial data, leaving 1 wire unused. Both boards use the same pins for that, so plugging the left MAIN board was powering the right one, and the key presses were flowing. &lt;/p&gt;

&lt;p&gt;It took me a while to figure out which pin of TRRS goes where. The GitHub repo for the project expects the reader to have some idea of what they are doing.&lt;/p&gt;

&lt;p&gt;Checking diagrams again:&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%2F3ql153v44um3un2lleq6.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%2F3ql153v44um3un2lleq6.png" alt="TRRS" width="800" height="552"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pin 5 (GND) → GND on Pro Micro&lt;/li&gt;
&lt;li&gt;Pin 4 (Tip) → D1 (Serial) on Pro Micro&lt;/li&gt;
&lt;li&gt;Pin 3 (Ring 1) → Not used&lt;/li&gt;
&lt;li&gt;Pin 2 (Ring 2) → VCC/RAW on Pro Micro&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Trust me, I'm an engineer. I can do this. I do not know where is pin 1 and why pin 3 is used for ring 1 but &lt;strong&gt;at this point I'm too afraid to ask&lt;/strong&gt;. I chose to believe there is logic there.&lt;/p&gt;

&lt;p&gt;I didn't have issues printing and putting the case together this time, thanks to my previous experience with the macropad project. Soldering went better; everything fit inside. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But I used SuperGlue to make TRRS sockets stay in place&lt;/strong&gt;, which made one of them not work, and I couldn't get it out of the case... so I had to print a new one. No more SuperGlue. Ever. &lt;/p&gt;

&lt;p&gt;I also had a problem finding a short enough TRRS cable with both jack connectors having 4 sections and not importing it from Australia. So I bought 3 cheap ones when I saw some, just in case.&lt;/p&gt;

&lt;p&gt;Putting everything together took a few evenings. I mostly spent time figuring out missing pieces and soldering 44 individual switches and 2 encoders.&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%2Fg1rs1dvjc2n4k1u6vvqo.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%2Fg1rs1dvjc2n4k1u6vvqo.png" alt="Soldered board" width="768" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And again, it worked.&lt;/strong&gt; This whole text was clumsily written on &lt;strong&gt;my very own keyboard&lt;/strong&gt;.&lt;br&gt;
I started spending time with &lt;a href="https://www.keybr.com/" rel="noopener noreferrer"&gt;https://www.keybr.com/&lt;/a&gt; to practice writing in the new alien key setup. It's painful—imagine learning to write with your non-dominant hand—but it's also enjoyable. It feels weird and uncomfortable, but there is progress every day, and that's a pleasant feeling. My fingers are actually barely moving, my hands are spread and angled as I feel comfortable. &lt;strong&gt;And I can put a coffee mug in the middle&lt;/strong&gt;. Can your magic keyboard do that?&lt;/p&gt;

&lt;h3&gt;
  
  
  Layers
&lt;/h3&gt;

&lt;p&gt;I've mentioned soldering 44 keys, while standard keyboard has 104. My old Keychrons are 75% and 60% keyboards, but now I have just 40%. And layers. 4 layers containing all I need and still some empty space. You may not be familiar what layers are in keyboard but you are using them anyway, with a &lt;code&gt;shift&lt;/code&gt; key you switch to the upper layer with capital letters and symbols instead of numbers, etc. I just happen to have more layers like that :)&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%2Fayiundbnwx95d66w56ai.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%2Fayiundbnwx95d66w56ai.png" alt="keyboard layers" width="800" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I use 2 extra thumb keys to switch between layers. I have a layer with numbers, symbols, and function keys, a layer with mouse movement, and a layer with macros. I can switch between them with a single key press.&lt;/p&gt;

&lt;p&gt;The whole idea of the thumb cluster was very interesting for me. In standard keyboard thumb is used just for spacebar, which is waste of its potential. After all, evolving an opposable thumb allowed us to make tools and climb on top of the food chain. I can use it for 4 keys, and it's much more comfortable than stretching fingers to the top row. &lt;/p&gt;

&lt;p&gt;I added there also &lt;code&gt;home row mods&lt;/code&gt; which means if I longpress &lt;code&gt;a&lt;/code&gt; I get &lt;code&gt;cmd&lt;/code&gt;, and longpress on &lt;code&gt;d&lt;/code&gt; gives me &lt;code&gt;shift&lt;/code&gt;, etc. This setup is mirrored on both sides, so I don't have to move my hands to use modifier keys.&lt;/p&gt;

&lt;h3&gt;
  
  
  Compiling firmware
&lt;/h3&gt;

&lt;p&gt;The QMK fork for the Void keyboard linked in the repo works fine. There is a Vial browser editor or standalone app to change keybindings, and it's powerful. From normal keys, to mouse movement and macros. However, &lt;strong&gt;I wanted to add some additional options&lt;/strong&gt; that were turned off by default for some reason. How hard can it be to compile some keyboard firmware, right?&lt;/p&gt;

&lt;p&gt;Well...&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%2Fmwh6fo2ifvniaii3jbp6.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%2Fmwh6fo2ifvniaii3jbp6.png" alt="Compilation error" width="768" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The board I used, a cheap basic ProMicro 32U4, &lt;strong&gt;has VERY limited memory&lt;/strong&gt;, so the original QMK firmware barely fits there. No more space for fireworks like &lt;a href="https://thomasbaart.nl/2018/12/13/qmk-basics-tap-dance/" rel="noopener noreferrer"&gt;tap dance&lt;/a&gt;. It reminded me of the conversation I had with a colleague at the smart home system company, where he worked as embedded software engineer. He was jealous of us mobile engineers discussing best practises, code readability etc. while they had to write hacks on hacks to make compiled code fit on the energy efficient chips. Now I get your struggle a bit better man.&lt;/p&gt;

&lt;p&gt;But I was able to add &lt;strong&gt;home row mods&lt;/strong&gt; and change some timeouts to make them feel better. After fighting with config files written in C, installing QMK with homebrew, learning that &lt;strong&gt;it's broken&lt;/strong&gt;, fixing some dependencies manually because I use &lt;strong&gt;Apple Silicone&lt;/strong&gt;, and talking with Claude to guide me through compilation, I made it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I had my own firmware.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The wrong, pure QMK one, without Vial GUI. Vial QMK is a separate repo with a different configuration and setup.&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%2Fzajuze4hbhwodjnvfphx.gif" 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%2Fzajuze4hbhwodjnvfphx.gif" alt="Image description" width="640" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I fixed that eventually, works as expected.&lt;/p&gt;

&lt;p&gt;I got new keycaps. I wanted an &lt;strong&gt;XDA profile&lt;/strong&gt;, primarily white. I had to put symbols with a Sharpie from other layers on them. The XDA profile means all keys are the same shape, no matter the row, which is useful for this build, since I may not put letters but symbols representing values from other layers.&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%2Fshv106cttof3r4na917q.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%2Fshv106cttof3r4na917q.png" alt="keycap profiles" width="639" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is just another variable to think about when building a keyboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Was it worth it?
&lt;/h2&gt;

&lt;p&gt;Given how much time, materials, and profanity language I used while working on the keyboard, I'd be better off buying a basic assembled Corne keyboard.&lt;/p&gt;

&lt;p&gt;But I had a lot of fun, learned a ton, and discovered a whole new world of my most commonly used computer peripheral.&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%2F2q9l3oqvrk4q6cfzqe51.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%2F2q9l3oqvrk4q6cfzqe51.png" alt="Final effect" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And I want to play more with it. I want to make a concave shape keyboard like Dactyl, use hot swappable sockets, make a low profile wireless keyboard running on ZMK firmware, check tinting options for individual keys, or even 3d print a set of custom keycaps. I want to use Choc switches and PCB instead of hand-wiring and learn to design my own board from scratch.&lt;br&gt;
The community around custom keyboards will be a fantastic inspiration for all that.&lt;/p&gt;

&lt;p&gt;As I type this, a few packages from Aliexpress are heading to me and are full of parts I need for future keyboard projects. And they are wife-approved :)&lt;/p&gt;

</description>
      <category>keyboard</category>
      <category>3dprinting</category>
      <category>programming</category>
      <category>diy</category>
    </item>
    <item>
      <title>Country administration is a bad implementation of programming good practices</title>
      <dc:creator>Adam Świderski</dc:creator>
      <pubDate>Wed, 04 Dec 2024 15:09:48 +0000</pubDate>
      <link>https://dev.to/asvid/country-administration-is-a-bad-implementation-of-programming-good-practices-ge7</link>
      <guid>https://dev.to/asvid/country-administration-is-a-bad-implementation-of-programming-good-practices-ge7</guid>
      <description>&lt;p&gt;🚿 I just had a random shower thought: country administration is actually a bad implementation of programming good practices. 🤔 Now hear me out:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SOLID:&lt;/strong&gt; 🏗️&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Each unit takes care of a single area of life.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;And granularly distributes the load on floors, rooms, and windows, like some bureaucratic microservices gone wrong.&lt;/li&gt;
&lt;li&gt;It's up to you to figure out which office is the right one for your case. The API is not well documented and changes often.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Open to adding new procedures while keeping old ones invincible. 🧟‍♂️&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You will never know anything about them, they are package private.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Every office of the same kind offers the same services and interface.🤡&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;But they often interpret the law in various ways - implementation details.&lt;/li&gt;
&lt;li&gt;Error handling ranges from "overly detailed 20 pages of legal gibberish" to "We know what's wrong, but we won't tell you. Now fix your tax report in 7 days or else."&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;The stable interface contract is provided by using forms.📝&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;And boy, do they love their interfaces...&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;The government creates high-level policies, and the implementation follows them.⚙️&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Created a system so independent it would run fine without users (dream architecture?)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;DRY:&lt;/strong&gt;🔄&lt;br&gt;
My mother's maiden name has achieved a quantum state - simultaneously crucial enough to ask for it 15 times and irrelevant enough to ignore when I show my verified ID.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SLA:&lt;/strong&gt;⏰&lt;br&gt;
Your passport will be ready in 30 days. Ish. Exceptions may occur. Go sue us if you want. The administration will pay fines from your taxes anyway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Documentation:&lt;/strong&gt;📚&lt;br&gt;
Got a handy 17-page guide on how to fill one form. Delegated to an accountant because sometimes technical debt is worth the outsourcing cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best part?&lt;/strong&gt; 🎭 After each election cycle, this enterprise startup called the government does a pivot and a full architecture redesign and promotes new tech leads based on their networking skills rather than technical knowledge.&lt;/p&gt;

&lt;p&gt;Are we learning from them, or are they learning from us? Because someone definitely copied someone's homework here...&lt;/p&gt;

&lt;p&gt;PS. Should I take half of my usual vitamins? 💊&lt;/p&gt;

</description>
      <category>programming</category>
      <category>bureaucracy</category>
      <category>jokes</category>
      <category>discuss</category>
    </item>
    <item>
      <title>The Stoic Engineer's Guide to Technical Debt</title>
      <dc:creator>Adam Świderski</dc:creator>
      <pubDate>Tue, 12 Nov 2024 21:24:31 +0000</pubDate>
      <link>https://dev.to/asvid/the-stoic-engineers-guide-to-technical-debt-2gk</link>
      <guid>https://dev.to/asvid/the-stoic-engineers-guide-to-technical-debt-2gk</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;TL;DR: Technical debt isn't just a coding problem—it's a communication challenge. Through better dialogue between engineers and management, we can transform it from a source of conflict into a strategic tool. This post explores practical ways to discuss, document, and decide which technical challenges are worth addressing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Encounter
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;While waiting for my flight at the airport, I overheard a conversation that made me rethink how we handle technical debt.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Behind me were 3-4 middle-aged guys talking about work, and what else. From the context, I understood that they are product/project managers in some IT companies. They stated some triggering stuff like "&lt;strong&gt;there is no difference when managing digital or physical product, it's just managing timelines and resources&lt;/strong&gt;", etc. Fine. Have your opinions. But then they went into a "tech dept" discussion. Blaming software engineers for its existence. Complaining that they reserve a portion of time each week to fix things that should work already, and are not even bugs. They need to rewrite everything constantly.&lt;/p&gt;

&lt;p&gt;Little did they know, as a software engineer sitting right behind their backs, that &lt;strong&gt;I've created, solved, and sometimes intentionally ignored vast amounts of technical debt&lt;/strong&gt;. I probably have a better understanding of it than managers. Most of us engineers do.&lt;/p&gt;

&lt;p&gt;So why are they so ignorant about it? Why can't they put a little effort into trying to understand the origins, cost, and benefits of technical debt?&lt;/p&gt;

&lt;p&gt;And then it struck me.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is our fault.
&lt;/h2&gt;

&lt;p&gt;Maybe I got this thought inspired by the "Daily Stoic" podcast I've been listening to, or quotes from Mark Aurelius' "Meditations":&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Begin the morning by saying to yourself, I shall meet with the busy-body, the ungrateful, arrogant, deceitful, envious, unsocial. All these things happen to them because of their ignorance of what good and evil are. (Book II, 1)&lt;/p&gt;

&lt;p&gt;Consider whether thou shouldst not rather blame thyself, because thou didst not expect such a man to err in such a way. For thou hadst means given thee by thy reason to suppose that it was likely that he would commit this error, and yet thou hast forgotten and art amazed that he has erred. (Book IX, 9)&lt;/p&gt;

&lt;p&gt;Fifth, consider that thou dost not even understand whether men are doing wrong or not, for many things are done with a certain reference to circumstances. In short, a man must learn a great deal to enable him to pass a correct judgment on another man’s acts. (Book XI, 18)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I shouldn't expect any manager to have the same level of technical knowledge as I have. They don't care about details but about the results. They manage budgets, and timelines, play courtyard games, company politics, etc. They (should) have a bigger picture image in their head.&lt;/p&gt;

&lt;p&gt;Technical details are our thing to manage. And we do it well; we just communicate it poorly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Nature of the Beast
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Technical debt is like a Balrog woken up by Dwarfs. It was already there, just greedy diggers forced it to come out. And kill everyone.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The "debt" part is screaming "problem". While it's not always that big of a deal.&lt;br&gt;
&lt;strong&gt;There are different types of debt.&lt;/strong&gt; You can use a credit card to get a LV bag for the girl you like - this is bad. You can take a mortgage to buy a flat - depending on circumstances it's good, or at least better than the previous example. And you can &lt;strong&gt;take a loan to invest&lt;/strong&gt; in technology, machines, whatever that will make your business thrive in a long shot - perfect.&lt;/p&gt;

&lt;p&gt;Technical debt also has multiple faces. Taking shortcuts to deliver features and take over the market, knowing that it will need to be redone to handle scaling is not a problem. But the cost of moving faster now needs to be clearly communicated and planned.&lt;br&gt;
&lt;strong&gt;Quickly delivered feature may be a one-time use, and won't be relevant anymore after some "Black Friday"&lt;/strong&gt; or other "Superbowl" kinda thing. No need to do it according to the same standard that software lasting decades is.&lt;br&gt;
Computer Games eg. are often written... quickly, because of deadlines, or new experimental mechanics that may work or be trashed after internal testing. They are often not maintained too much after the release, with few patches and bug fixes, and thats it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Having a technical debt doesn't mean it has to be paid off&lt;/strong&gt;. As long as it's not limiting or slowing down too much other development. When the suboptimal function is running once a year, does it really make a difference if you rewrite it up to perfectionist standards, shaving 0.5s of its runtime? Making it more readable and maintainable, even though the last change was made there 3 years ago and nobody plans to touch it in the future?&lt;/p&gt;

&lt;p&gt;Be smart-lazy about technical debt management. Not all debt needs immediate attention, and some can just be left alone there because the interest rate is so low.&lt;/p&gt;

&lt;p&gt;On the opposite side, &lt;strong&gt;if your system does not have any technical debt, it was probably over-engineered in the first place&lt;/strong&gt;. Too much time and resources were used to prepare for the volume of users that never came. You've built an F1 bolid when you needed a city car.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Origins
&lt;/h2&gt;

&lt;p&gt;The managers I've unwillingly listened to at the airport probably don't realize how often they are the source of issues. Changing plans, priorities, shifting deadlines - it's not helping us engineers. In a way, managing software and physical products may be similar. No point in focusing on the roof shape and color when the house model is not decided yet. If you build a roof first and then want to build foundations you will suffer some engineering challenges. Just like in software... It's easy to imagine how to build a building, everyone saw some construction site. The problem with software products is that they are abstract, it is hard to see the order of things or the magnitude of issues caused by decisions. If you are a non-technical manager.&lt;/p&gt;

&lt;p&gt;We, engineers are guilty of picking the mythical industry standard solution, without questioning it. Does it fit our needs? Is it an overkill? Do we know how to use it? What are the tradeoffs?&lt;br&gt;
Or try the latest hyped one from a talk at the conference, promising to solve everything.&lt;br&gt;
Or simply allow feature-driven development, where there is no time to step back and look at what could be redesigned and refactored. When there is no true planning, architecture is random or a mix of a few randomly picked, loosely glued together, but somehow it works. It's not (entirely) non-technical managers' fault for making wrong calls when engineers could stop this madness. But it depends heavily on the organization's culture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Communication debt
&lt;/h2&gt;

&lt;p&gt;The ignorance shown by managers at the airport is triggering. But possibly nobody from our guild has taken the time to explain it to them. We assume they are not interested in reasons, they will just complain a bit, and then we can come back to our cave and just fix stuff.&lt;/p&gt;

&lt;p&gt;I'm a bit spoiled with having technical-aware managers and/or tech leads who were shielding us from higher-ups' ignorance. But I faced it from time to time. So I wonder, &lt;strong&gt;how can we refactor communication?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The most important thing that we as engineers can do is &lt;strong&gt;be aware of the consequences of our decisions&lt;/strong&gt;. The more senior I get, the more long-cost decisions I see. Unfortunately, you mostly learn that from your own errors. Reading books, blogposts, etc. can give you some ideas but nothing leaves a permanent mark as making bad decisions and fixing their consequences.&lt;/p&gt;

&lt;p&gt;There are tools to facilitate the decision process, like tech proposals (or other RFC documents). Ask the manager for insights, and understand their motivations. When a decision is made, put it in the Decision Log (ADR). It is for your own sanity, so in a few months, you can recall why you picked some framework, some library, or vendor and not the other.&lt;/p&gt;

&lt;p&gt;Always assume the decision is made with maximum knowledge available for the time of making. You will know later if it was good or bad when new information comes. But it won't help the project to postpone everything until all risks and possibilities are known.&lt;/p&gt;

&lt;p&gt;When you have an educated decision, communicate it to the manager if it may cause technical debt that will need to be paid off. Mention it daily, put a ticket in Jira to discuss during refinement, and don't let it die just in your head. You are building here the "ass shield" :) Manage your managers a bit.&lt;br&gt;
There is no better feeling than pointing someone to the document or ticket that they approved when being blamed for it.&lt;/p&gt;

&lt;p&gt;You can use ChatGPT to explain concepts or reasoning "like to 5yo" or "to people with MBA".&lt;/p&gt;

&lt;p&gt;Plan implementation for the priority shifts. Even if project goals change, or there are pivots, etc. try to plan work so at any point there is some value generated. Even if it's paused halfway through. Or in the worst case, parts of the project can be scrapped without harming others. Use feature toggles, configs, and design patterns. That is a sign of good engineering when lots of code can be removed and users don't notice.&lt;/p&gt;

&lt;p&gt;Nobody said it's gonna be easy.&lt;/p&gt;

&lt;h2&gt;
  
  
  In the end
&lt;/h2&gt;

&lt;p&gt;It doesn't even matter how well you communicate and plan. There will always be some misunderstanding between engineers and managers, and it's OK. &lt;strong&gt;It's expected&lt;/strong&gt;, don't be surprised by it. It takes some extra effort to document, plan, and communicate, but I don't think there is another way around it.&lt;/p&gt;

&lt;p&gt;Seniority in engineering manifests itself not by slaying every Balrog of technical debt you encounter, but by knowing which ones are worth fighting and which should be left dormant in their ancient caves. Sometimes, the best strategy isn't charging in with a "YOU SHALL NOT PASS" attitude, but rather carefully mapping the tunnels, marking the dangers, and making sure everyone knows where not to dig too deep and too greedily.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your manager will react differently on mentioning technical debt if you manage it well&lt;/strong&gt;. Working only on places that are worth fixing, are slowing you down, or are vulnerable. Demanding the time for refactors just because the code isn't to your liking won't make you friends, or make you look professional.&lt;/p&gt;

&lt;p&gt;After all, technical debt, like the Balrog, isn't inherently evil - it's a force of nature in software development. Our job isn't to eliminate it completely but to understand it, manage it wisely, and know when to stand our ground.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>techdebt</category>
      <category>programming</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>I'm done with multiplatform software development</title>
      <dc:creator>Adam Świderski</dc:creator>
      <pubDate>Fri, 01 Nov 2024 14:39:13 +0000</pubDate>
      <link>https://dev.to/asvid/im-done-with-multiplatform-software-development-278d</link>
      <guid>https://dev.to/asvid/im-done-with-multiplatform-software-development-278d</guid>
      <description>&lt;h2&gt;
  
  
  How I got here
&lt;/h2&gt;

&lt;p&gt;I had several chances to try a few ways of multiplatform software development. I used &lt;code&gt;Ionic/Phonegap&lt;/code&gt; in the early days of mobile development. I enjoyed &lt;code&gt;Flutter&lt;/code&gt; and bounced away from &lt;code&gt;ReactNative&lt;/code&gt;. However, I was curious about the Kotlin Compose Multiplatform way. So I could use the technology I already know to build Android, desktop, and iOS apps, sharing internal logic, and UI elements.&lt;/p&gt;

&lt;p&gt;It's supposed to be stable on the desktop. Stable, as a dead man's heartbeat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reality check
&lt;/h2&gt;

&lt;p&gt;I started with the Android app, which is the thing I know best. I could deliver basic features quickly. Then I wanted to do a desktop app, using the same services, domain models, etc. Maybe even UI elements.&lt;br&gt;
How naive I was.&lt;/p&gt;

&lt;p&gt;My Android App was using the &lt;code&gt;Material3&lt;/code&gt; library for basic UI elements, it's fresh and modern, nice and snappy.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;It's not supported on Desktop.&lt;/strong&gt;&lt;br&gt;
I can't directly reuse UI elements. That is unless I decide to downgrade the Android app.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I used &lt;code&gt;Hilt&lt;/code&gt; as a state-of-the-art and recommended DI library. BTW it's not that much nicer than &lt;code&gt;Dagger&lt;/code&gt; in terms of how much boilerplate code it requires... when it works it works, but when it crashes, it's bad.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The &lt;code&gt;Hilt&lt;/code&gt; is not supported on a multiplatform setup.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well ok, I don't have that much code done, I can reimplement everything using &lt;code&gt;Kodein&lt;/code&gt; - suppose the only DI (or rather service locator?) lib that claims to support multiplatform.&lt;br&gt;
I don't know yet if it works. I decided to remove all DI to make at least something work. &lt;strong&gt;Passing stuff through the constructor is also DI&lt;/strong&gt;, you third-party library perverts.&lt;/p&gt;

&lt;p&gt;After a &lt;strong&gt;few hours of countless fixes&lt;/strong&gt;, combining versions of multiple libraries, and plugins, and using various AI assistants to figure shit out (chatGPT, built-in Android Studio Gemini, Claude Sonnet)...&lt;/p&gt;

&lt;p&gt;I finally compiled and ran a desktop app, using some shared components.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;But not ViewModels because it's a separate thing for Android/Mobile and Desktop&lt;/strong&gt;. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That made me sad because it handles UI state and proxies commands further the line... but well. I can rewrite the same thing twice just using 2 dedicated libraries. At least I can share the domain code, I guess.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The desktop app is crashing after I press the button.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Final Straw
&lt;/h2&gt;

&lt;p&gt;After another hour of digging, I learned that the Material button implementation may not work correctly on the desktop. Or rather just the animation part. &lt;strong&gt;So it's better to implement it separately&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;No problem.&lt;br&gt;
That's why I want to go multiplatform, to reimplement the simplest, most common UI elements on my own. To not use the same DI abstraction/abomination. To rewrite stuff that should work because it's not platform-specific.&lt;/p&gt;

&lt;p&gt;And I didn't even touch things like using Bluetooth, playing sounds or notifications, using file systems, etc. There was no 3rd party auths or even HTTP client implemented. I'm already tired.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It will literally take me less time to perfect what I'm building on a single platform and then use AI to rewrite it natively to another one.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons learned
&lt;/h2&gt;

&lt;p&gt;This is not the promise I was given "&lt;strong&gt;Write once, run everywhere&lt;/strong&gt;" if I have to rewrite most things 3-4 times. I'd rather use native at this point. &lt;strong&gt;Fewer limitations, worries, and dependencies.&lt;/strong&gt; Or I can write once, but it won't be code I would write normally. I would bend and twist it just to work in a multiplatform setup rather than focus on delivering features. And then I would have to maintain it...&lt;/p&gt;

&lt;p&gt;If I'm lucky, I could get everything working together, in a single codebase, reusing themes, UI components, and everything else. I'm 213,7% sure that an update of some dependency will break it.&lt;/p&gt;

&lt;p&gt;To keep socially acceptable levels of sanity I've decided to &lt;strong&gt;drop multiplatform dream&lt;/strong&gt;. I will write acceptable MVP Android app, add backend service and see how it goes. Adding desktop, iOS or web clients with already battle tested features and API seems like a better idea than experimenting on 5 fronts.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;The "write once, run everywhere" promise comes with an asterisk the size of Mount Everest&lt;/li&gt;
&lt;li&gt;Shared code is great for platform-agnostic domain logic, but UI components are a different beast entirely&lt;/li&gt;
&lt;li&gt;Fighting with compatibility issues takes more time than writing platform-specific code&lt;/li&gt;
&lt;li&gt;The maintenance burden of a multiplatform setup might outweigh its benefits&lt;/li&gt;
&lt;li&gt;Current state of tooling feels more like an experiment than a production-ready solution&lt;/li&gt;
&lt;li&gt;True "write once" happens at the backend level - a proper API service serves all platforms equally well&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kotlin</category>
      <category>multiplatform</category>
      <category>softwareengineering</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Geo Viewer in IntelliJ Idea is cool</title>
      <dc:creator>Adam Świderski</dc:creator>
      <pubDate>Tue, 22 Oct 2024 10:07:39 +0000</pubDate>
      <link>https://dev.to/asvid/geo-viewer-in-intellij-idea-is-cool-2d22</link>
      <guid>https://dev.to/asvid/geo-viewer-in-intellij-idea-is-cool-2d22</guid>
      <description>&lt;h2&gt;
  
  
  Hello old friend
&lt;/h2&gt;

&lt;p&gt;Thanks to &lt;strong&gt;&lt;a href="https://www.linkedin.com/company/jug-poznan/posts/?feedView=all" rel="noopener noreferrer"&gt;Poznań Java User Group&lt;/a&gt;&lt;/strong&gt; randomly selecting me during a meetup to get a &lt;strong&gt;JetBrains IntelliJ Idea Ultimate&lt;/strong&gt; license, I started using it daily. It's not entirely new software for me. I've been using Android Studio for almost a decade now, occasionally working on side projects in the Community Edition of IntelliJ. Recently at work, I've been using &lt;strong&gt;VS Code&lt;/strong&gt; and &lt;strong&gt;NeoVim&lt;/strong&gt;. Quite a different IDE philosophy with the latter.&lt;/p&gt;

&lt;p&gt;I happen to work on the backend currently, and IntelliJ is an absolute beast with built-in tools for everything you can imagine. &lt;/p&gt;

&lt;p&gt;Next to the usual language support for TypeScript, SQL, and build configs, there are nice database tools. Data display is similar to an Excel spreadsheet, allows filtering, can generate DDL code, draw diagrams, etc. Nothing too special; other tools make this happen. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But this is just inside the IDE, so it is easy to reach without changing the context.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Geo Viewer
&lt;/h2&gt;

&lt;p&gt;I noticed the Geo Viewer tool by accident. &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%2Fdmf59vhxfqznjirti21a.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%2Fdmf59vhxfqznjirti21a.png" alt="Image description" width="516" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm dealing with some geographic data, like areas and points. Geo Viewer works out of the box. No plugins or configs are required. At least for Postgres with Postgis setup.&lt;/p&gt;

&lt;p&gt;It may seem like unnecessary visuals, but it's actually useful for debugging. Think about having a query that returns points from one table, inside the area defined in another one. How great it is to see the result on the actual map.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's put it to work
&lt;/h2&gt;

&lt;p&gt;I used ChatGPT to generate data for Poland's voivodships. It's not entirely bad... They are kinda in the right places, just way too small.&lt;/p&gt;

&lt;p&gt;Data for city locations is OK.&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%2F5prexq4iytwp92gj2yfq.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%2F5prexq4iytwp92gj2yfq.png" alt="Image description" width="800" height="582"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To visualize cities and areas on a single Geo View, I used a simple SQL view. I haven't touched SQL since university, so this is also a fun experience :D&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Enable PostGIS extension if not already enabled  &lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;EXTENSION&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;postgis&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  

&lt;span class="c1"&gt;-- Create vovoidships table  &lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;vovoidships&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;  
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="n"&gt;bounds&lt;/span&gt; &lt;span class="n"&gt;GEOMETRY&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;  
&lt;span class="p"&gt;);&lt;/span&gt;  

&lt;span class="c1"&gt;-- Create cities table  &lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;cities&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;  
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="n"&gt;coordinates&lt;/span&gt; &lt;span class="n"&gt;GEOMETRY&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;  
&lt;span class="p"&gt;);&lt;/span&gt;  

&lt;span class="c1"&gt;-- Insert Polish voivodeship capitals into cities table  &lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;cities&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt;  
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Warsaw'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_MakePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0122&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;52&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2297&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;  &lt;span class="c1"&gt;-- Mazowieckie  &lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Kraków'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_MakePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;9449&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0647&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;  &lt;span class="c1"&gt;-- Małopolskie  &lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Łódź'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_MakePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;456&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;51&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;7592&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;     &lt;span class="c1"&gt;-- Łódzkie  &lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Wrocław'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_MakePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0385&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;51&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1079&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="c1"&gt;-- Dolnośląskie  &lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Poznań'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_MakePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;9286&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;52&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4064&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;  &lt;span class="c1"&gt;-- Wielkopolskie  &lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Gdańsk'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_MakePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;646&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;352&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;    &lt;span class="c1"&gt;-- Pomorskie  &lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Szczecin'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_MakePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5528&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;53&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4289&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;&lt;span class="c1"&gt;-- Zachodniopomorskie  &lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Bydgoszcz'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_MakePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0076&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;53&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1235&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="c1"&gt;-- Kujawsko-Pomorskie  &lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Lublin'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_MakePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5686&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;51&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2465&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;   &lt;span class="c1"&gt;-- Lubusz  &lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Białystok'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_MakePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1641&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;53&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1325&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="c1"&gt;-- Podlaskie  &lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Katowice'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_MakePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;039&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2583&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="c1"&gt;-- Śląskie  &lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Opole'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_MakePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;9213&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;6644&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;    &lt;span class="c1"&gt;-- Opolskie  &lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Rzeszów'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_MakePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;9981&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0415&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;  &lt;span class="c1"&gt;-- Podkarpackie  &lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Gorzów Wlkp.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_MakePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2299&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;52&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;7387&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="c1"&gt;-- Lubusz  &lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Zielona Góra'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_MakePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5061&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;51&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;9353&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;-- Lubusz  &lt;/span&gt;

&lt;span class="c1"&gt;-- Insert Polish voivodeships into vovoidships table with corrected boundaries  &lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;vovoidships&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt;  
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Mazowieckie'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_GeomFromText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'POLYGON((20.5937 52.4304, 20.7031 52.2398, 21.0994 52.1985, 21.4855 52.2738, 21.7426 52.5456, 21.4822 52.6935, 20.8778 52.6281, 20.5937 52.4304))'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;  
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Małopolskie'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_GeomFromText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'POLYGON((19.0013 49.6121, 19.3004 49.2235, 19.8534 49.1386, 20.1253 49.2158, 20.3469 49.7248, 20.1154 49.9501, 19.0013 49.6121))'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;  
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Łódzkie'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_GeomFromText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'POLYGON((18.9224 51.6847, 19.5032 51.5472, 19.7415 51.7594, 19.6886 52.0549, 19.1579 52.0201, 18.9224 51.6847))'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;  
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Dolnośląskie'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_GeomFromText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'POLYGON((16.2795 50.1585, 16.6575 49.9253, 17.1573 49.8861, 17.3046 50.3278, 17.1566 50.4869, 16.6676 50.5302, 16.2795 50.1585))'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;  
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Wielkopolskie'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_GeomFromText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'POLYGON((16.4570 52.0254, 16.9745 51.8472, 17.4446 51.8598, 17.8387 52.0295, 17.5519 52.3232, 16.4570 52.0254))'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;  
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Pomorskie'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_GeomFromText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'POLYGON((17.9927 54.0531, 18.7247 54.0065, 18.7840 53.8160, 18.5911 53.7163, 17.9927 54.0531))'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;  
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Zachodniopomorskie'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_GeomFromText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'POLYGON((14.2102 53.4019, 14.8960 53.3481, 15.0853 53.3305, 15.0006 53.0747, 14.2102 53.4019))'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;  
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Kujawsko-Pomorskie'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_GeomFromText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'POLYGON((17.8260 53.0401, 18.2550 52.9635, 19.1827 52.9581, 19.1902 53.1355, 18.0730 53.1274, 17.8260 53.0401))'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;  
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Lubuskie'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_GeomFromText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'POLYGON((14.3215 52.2755, 14.7083 52.2985, 15.0293 52.4335, 15.0641 52.5437, 14.3215 52.2755))'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;  
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Podlaskie'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_GeomFromText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'POLYGON((22.7210 53.6851, 22.9785 53.4699, 23.4987 53.4057, 23.7810 53.6431, 22.7210 53.6851))'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;  
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Śląskie'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_GeomFromText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'POLYGON((18.6704 50.1671, 19.0423 50.1492, 19.3875 50.2675, 19.5927 50.2046, 19.1676 50.0395, 18.6704 50.1671))'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;  
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Opolskie'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_GeomFromText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'POLYGON((17.2070 50.5458, 17.4982 50.3454, 17.7513 50.2998, 17.8897 50.5008, 17.2070 50.5458))'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;  
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Podkarpackie'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_GeomFromText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'POLYGON((21.1791 49.8919, 21.4867 49.8395, 21.9074 49.7579, 22.0595 49.8491, 21.1791 49.8919))'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;  

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt; &lt;span class="n"&gt;CombinedVoivodeshipsCitiesView&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt;  
&lt;span class="k"&gt;SELECT&lt;/span&gt;  
    &lt;span class="s1"&gt;'Voivodeship'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bounds&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;geometry&lt;/span&gt;  
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;vovoidships&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;  

&lt;span class="k"&gt;UNION&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt;  

&lt;span class="k"&gt;SELECT&lt;/span&gt;  
    &lt;span class="s1"&gt;'City'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="n"&gt;ST_Y&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="n"&gt;ST_X&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coordinates&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;geometry&lt;/span&gt;  
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;cities&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Influenced by &lt;strong&gt;&lt;a href="https://www.linkedin.com/in/kacper-koza-352a5913b/" rel="noopener noreferrer"&gt;Kacper Koza&lt;/a&gt;&lt;/strong&gt; presentation on last &lt;strong&gt;JUGtoberfest&lt;/strong&gt; I started using more IntelliJ shortcuts. And turned off tabs. So far so good, my mouse is getting some rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's your latest discovery in the tools you use?
&lt;/h2&gt;

</description>
      <category>softwaredevelopment</category>
      <category>java</category>
      <category>database</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Using Design Patterns as Communication Skill</title>
      <dc:creator>Adam Świderski</dc:creator>
      <pubDate>Sun, 13 Oct 2024 16:00:05 +0000</pubDate>
      <link>https://dev.to/asvid/using-design-patterns-as-communication-skill-2266</link>
      <guid>https://dev.to/asvid/using-design-patterns-as-communication-skill-2266</guid>
      <description>&lt;h2&gt;
  
  
  Hermits
&lt;/h2&gt;

&lt;p&gt;The man, the legend once said "I did not study computer science to work with people", when asked to be present at a meeting. It's a popular slogan present on coffee mugs and T-shirts of software engineers. &lt;a href="https://koszulkowy.pl/1708-nie-po-to-studiowalem-informatyke-zeby-teraz-pracowac-z-ludzmi-meska-koszulka-z-nadrukiem.html?search_query=informatyke&amp;amp;results=3" rel="noopener noreferrer"&gt;At least in my country :) &lt;/a&gt;&lt;br&gt;
Are meetings and calls the only way we, engineers, communicate? If we limit those to the bare minimum, work like hermits in our basements, do not answer Slack messages or emails, and do not write any documentation, there is one way we do talk to each other, that we won't get rid of. &lt;strong&gt;It's code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Line by line we lay down our intentions and ideas. We usually work in teams, so we must be clear to others (and our future selves) what we mean to do. Programming languages are fairly structured and limited to only those words that are required for performing commands. But often we need more than that to express more complex ideas. We need patterns. Code structures that are built with programming grammar but expressed in a higher abstraction layer than bare code.&lt;/p&gt;
&lt;h3&gt;
  
  
  What are Design Patterns?
&lt;/h3&gt;

&lt;p&gt;When my wife asks "What's for dinner?", she doesn't need 10 minutes of recitation of ingredients and how I will make it eatable. My cooking intention would be clear to her after just saying the dish's name.&lt;br&gt;
Design Pattern name or shape should tell other developers enough intention details. The point of the Design Pattern is to provide a common vocabulary and structure for common problems. It's called a pattern because it is repeated, in multiple projects and places. So when I see a Builder, I know how to use it. When I see a Factory, I know what it does. So let me cook.&lt;/p&gt;

&lt;p&gt;All patterns have names, but do you need to know the name and encyclopedia-like definition to effectively use it? Are all patterns equally valuable in all technologies? Is the &lt;a href="https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612" rel="noopener noreferrer"&gt;Gang Of Four design patterns book&lt;/a&gt; from 30 years ago everything you need to know or is it obsolete? Can you overuse patterns?&lt;/p&gt;
&lt;h2&gt;
  
  
  My take
&lt;/h2&gt;

&lt;p&gt;I personally enjoy learning about patterns. I did a &lt;a href="https://swiderski.tech/series/kotlin-design-patterns/" rel="noopener noreferrer"&gt;series of articles&lt;/a&gt; implementing classical Gang of Four patterns in Kotlin. Triggered, by a conversation I had in a workplace, where a colleague claimed there is no need to use the &lt;code&gt;Builder Pattern&lt;/code&gt; in Objective-C, while in Java it's everywhere.&lt;/p&gt;

&lt;p&gt;How do other patterns hold today? It seems that after 30 years our modern languages are solving multiple issues that used to be solved by using patterns. Or we learned that using some patterns makes code less readable, more verbose, hiding logic in the wrong places, and it's better to not use them. Finally, we learned how to divide problems into smaller chunks, modules, deliverables, and microservices. So even a messy codebase in one place has a smaller influence on the whole system.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Builder's case
&lt;/h3&gt;

&lt;p&gt;Let's zoom in on the Builder pattern. It's used to always create a valid object instance, with potentially multiple fields that have to be set. Some fields may depend on other fields. We don't like big constructors. We don't want to pass the same values every time if we need eg. 2 objects with a single field difference. Doing so is error-prone, and hard to detect during PR with huge blocks of constructor code.&lt;br&gt;
Builder solves it, by providing structure related to the created object, setting fields to default values, and exposing methods to set all settable fields with optional values. Cool.&lt;/p&gt;

&lt;p&gt;Well, Kotlin has named arguments with default values, so... I don't find the need for Builder. The same result is achieved by using the constructor.&lt;/p&gt;

&lt;p&gt;Objective-C is using initializer methods (or something, IDK).&lt;/p&gt;

&lt;p&gt;GoLang has an interesting take on this problem, its called &lt;code&gt;functional options&lt;/code&gt;, where functions setting fields are optionally passed to a factory method. I saw this pattern in &lt;a href="https://www.youtube.com/watch?v=MDy7JQN5MN4" rel="noopener noreferrer"&gt;this video&lt;/a&gt; where the author admits, that he doesn't know the name of it. Someone in the comments helped.&lt;/p&gt;

&lt;p&gt;And it got me thinking...&lt;/p&gt;
&lt;h3&gt;
  
  
  Do we need to learn patterns from books?
&lt;/h3&gt;

&lt;p&gt;Or can we use them, instinctively? Monkey see monkey do, as I write my Typescript code :) I often notice some structure in the code written by other developers, can understand why it exists and what problem it solves, and start using it to solve similar problems. I do a little research on it, but that's post-factum. Patterns often communicate intentions so well, that can be used without studying too much.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But there is a catch.&lt;/strong&gt; It does require experience with using various patterns, causing and solving multiple code issues, actually reading books and articles, and having some opinions on your own to do it that way. Otherwise, there is a danger of mindless coping code here and there and praying for PR to be approved, and not causing more issues than solving.&lt;/p&gt;

&lt;p&gt;It is worth knowing a few commonly used patterns from the GoF book, but not all of them. The useful ones in any technology would be &lt;strong&gt;Strategy, Factory, Command, and Facade.&lt;/strong&gt;&lt;br&gt;
Others are optional, or currently useless, or plain dangerous and disliked.&lt;/p&gt;

&lt;p&gt;Long IF or SWITCH statements are eye-sore but less confusing than solving simple problems with convoluted object hierarchy. Especially if it is rarely revisited, changed, or updated.&lt;/p&gt;

&lt;p&gt;I've never created in a production code Abstract Factory, Interpreter, or Visitor. You may say, that I didn't work with complex enough problems. And that would be true. I was usually doing some variant of CRUD with presentation logic. And I'm not special. &lt;strong&gt;You are not special&lt;/strong&gt;. Most projects are not special. So I like to think, not that my projects were simple, but that &lt;strong&gt;I didn't add additional complexity&lt;/strong&gt;. It is a common flex of software engineers. And trust me, I have a tendency to overengineer :) Or maybe it's just Post-Fintech-Stress-Disorder.&lt;/p&gt;
&lt;h3&gt;
  
  
  Enterprise patterns
&lt;/h3&gt;

&lt;p&gt;So far I've talked only about "small" patterns in code, single or few classes or functions creating some structure. Software Engineering is a broad field, where you can work solo or in a team, in a small company or global megacorp. And there are patterns for each level of communication challenges. Solving issues between services or entire systems. Described in &lt;a href="https://www.amazon.com/o/asin/0321200683/ref=nosim/enterpriseint-20" rel="noopener noreferrer"&gt;books like this&lt;/a&gt; that I have not read, and maybe will never need to.&lt;br&gt;
Another examples are Martin Fowler's &lt;a href="https://www.amazon.pl/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420/ref=zg-te-pba_d_sccl_2_2/258-7091817-8921600?pd_rd_w=WHKsM&amp;amp;content-id=amzn1.sym.51352be5-b545-4b4a-9087-316ea981b81f&amp;amp;pf_rd_p=51352be5-b545-4b4a-9087-316ea981b81f&amp;amp;pf_rd_r=2ZG69WVW2GQ7T0VRC4C3&amp;amp;pd_rd_wg=4enUw&amp;amp;pd_rd_r=9c530356-4275-4f9e-90ac-24f15c51932a&amp;amp;pd_rd_i=0321127420&amp;amp;psc=1" rel="noopener noreferrer"&gt;Patterns of Enterprise Application Architecture&lt;/a&gt; and &lt;a href="https://www.amazon.pl/Analysis-Patterns-Reusable-Object-Models/dp/0134186052#customerReviews" rel="noopener noreferrer"&gt;Analysis Patterns: Reusable Object Models&lt;/a&gt;. The &lt;a href="https://www.amazon.pl/Enterprise-Patterns-MDA-Building-Archetype/dp/032111230X" rel="noopener noreferrer"&gt;Enterprise Patterns and MDA: Building Better Software with Archetype Patterns and UML&lt;/a&gt; is also being suggested in that field.&lt;/p&gt;

&lt;p&gt;With that caliber of patterns, I don't think there is a way around without reading books and articles about them. They are typically way too complex to just copy-paste without deeply understanding the problem and consequences. And making the wrong choice at this scale will be expensive.&lt;/p&gt;
&lt;h3&gt;
  
  
  Archetypes
&lt;/h3&gt;

&lt;p&gt;We engineers like to think we need to invent the wheel every few months. This is why we have so many JS frameworks. But the reality is &lt;strong&gt;we are using those wheels to create similar vehicles&lt;/strong&gt; each time.&lt;/p&gt;

&lt;p&gt;When building a scheduling system, someone probably did that already. Hotel reservations or some other availability problem sounds like known to humanity and solved multiple times until now. There are books with models called Archetypes, that contain generic data structures that exactly fit those kinds of problems. With some adjusting, bending, and twisting you don't have to reinvent the solution. There is ongoing work to implement some archetypes &lt;a href="https://softwarearchetypes.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Code quality
&lt;/h2&gt;

&lt;p&gt;There are some studies around this topic, and I did briefly read a few. The usual conclusion is, that there is some sort of correlation between using Design Patterns in the project and a smaller number of code smells. And that it's hard to measure this topic reliably. Code smells are not yet errors or bugs. They show potential to be problematic and could be implemented better. What does it mean "better"? Whatever is agreed as a good practice in a particular situation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I think that fewer code problems, when patterns are used come from better communication, or better coding skills and experience of the people who know patterns, compared to those who don't. Correlation, not causation.&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media.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%2Fnmn3jgi1xaxqx4uphlzq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fnmn3jgi1xaxqx4uphlzq.png" alt="Weird correlation chart" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Consistent Pattern Set
&lt;/h2&gt;

&lt;p&gt;Having a consistent set of patterns shared inside the codebase makes reading and writing code faster. If you start mixing programming paradigms (object, functional, reactive), and adding new style patterns to address issues that are already solved another way, you will make reading code slower, and you will surprise the reader. That is making communicating the intentions worse, introducing gray areas for bugs to pop.&lt;/p&gt;

&lt;p&gt;Each generation, each subculture will use their own slang, and it's still all within the same official language boundaries. If you put GenZ Rizzer, 80' punk, elder old money snob, and soccer mom in 1 room, they may have communication issues, even if they all speak English. Same with putting patterns from different worlds in the same project, without thinking too much "Is it the right solution for the problem, &lt;strong&gt;in the context of the existing codebase&lt;/strong&gt;".&lt;/p&gt;
&lt;h2&gt;
  
  
  Patterns Antipatterns?
&lt;/h2&gt;

&lt;p&gt;Obviously, patterns can be misused. Like whatever Spring did &lt;a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/aop/framework/AbstractSingletonProxyFactoryBean.html" rel="noopener noreferrer"&gt;here&lt;/a&gt; with &lt;code&gt;Class AbstractSingletonProxyFactoryBean&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Hold your horses
&lt;/h3&gt;

&lt;p&gt;When I learned about the command pattern, I started seeing the use of it everywhere. I mean EVERYWHERE. &lt;strong&gt;I had a golden hammer, and every screw was suddenly a nail.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I used it in some pet Android projects, worked well. It was way before the RX era. I had to figure out how to refresh the data on display after the command was done. I went through a few concepts, did some research, etc. As a result, I used some sort of CQRS before I knew what it was. Valuable experience, but it doesn't change the initial bias I had.&lt;/p&gt;

&lt;p&gt;We often fall into this trap with every new shiny piece of tech we learn about. It's cool to experiment, but the older I get the more conservative I am, and I like to understand the consequences of it before I invest my time into it.&lt;/p&gt;
&lt;h3&gt;
  
  
  Don't reinvent the wheel
&lt;/h3&gt;

&lt;p&gt;Everyone knows what a Builder pattern is. &lt;a href="https://www.youtube.com/watch?v=s35n-eaqwTE" rel="noopener noreferrer"&gt;Just like everyone knows what a horse is&lt;/a&gt;. Don't do something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;creator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Creator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;creator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Double&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;creator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;

    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Creator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// fancy builder&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Double&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;

        &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;apply&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;setPrice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Double&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;apply&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Usage:&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;product&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Creator&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Coffee"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setPrice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2.99&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Implementing a well-known pattern but naming it differently just for the sake of it, or because you didn't do your research. There are a lot of patterns, I know. Sometimes, like I mentioned, you don't even have to know their name, but just the structure. But don't reinvent them, rename basic parts and confuse people reading them later.&lt;br&gt;
Other examples could be &lt;code&gt;BobTheBuilder&lt;/code&gt; or &lt;code&gt;FactoryGirl&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Abusing patterns
&lt;/h3&gt;

&lt;p&gt;Everything can be a poison, it's the dose that kills.&lt;br&gt;
&lt;a href="https://media.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%2F5pxpln9lqyf6q0j7srkw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F5pxpln9lqyf6q0j7srkw.png" alt="OOP makes everything unnecessarily convoluted" width="700" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The hardest thing is not to know all the patterns, it's to &lt;strong&gt;know when to use them, and when not&lt;/strong&gt;. Popular patterns make code easier to extend in the future, giving you building blocks so you don't have to think too much about it, just focus on the value you are adding.&lt;/p&gt;

&lt;p&gt;But if you work solo, and won't need to extend the code later or maintain it for 20 years... &lt;a href="https://www.reddit.com/r/programminghorror/comments/1exqik2/undertale_dialog_system_is_one_giant_switch/" rel="noopener noreferrer"&gt;Undertale game dialog system is a single switch statement with some nested IFs&lt;/a&gt; And it made $45.1m on Steam with 96.7%&lt;br&gt;
positive reviews. It wouldn't be 100% with clever use of design patterns. It could be never released, if the author focused more on technical excellence than on the fun the game should bring.&lt;/p&gt;

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

&lt;p&gt;As a software engineer, you may want to be a hermit and limit verbal communication with people. Sounds mentally unhealthy, but you do you. You will communicate with others using code anyway. &lt;/p&gt;

&lt;p&gt;So make it nice, readable, and understandable. Use common patterns, and understand their traits and consequences. Try not to abrupt the existing codebase ecosystem with clever ideas that just don't fit. Be strategic in planning implementation and making abstractions. &lt;/p&gt;

&lt;p&gt;Ultra-generic flexible solutions often end up handling one thing in a corner.&lt;/p&gt;

</description>
      <category>designpatterns</category>
      <category>softwareengineering</category>
      <category>programming</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Breaking Free from Analysis Paralysis</title>
      <dc:creator>Adam Świderski</dc:creator>
      <pubDate>Mon, 10 Jun 2024 18:56:41 +0000</pubDate>
      <link>https://dev.to/asvid/breaking-free-from-analysis-paralysis-2g5p</link>
      <guid>https://dev.to/asvid/breaking-free-from-analysis-paralysis-2g5p</guid>
      <description>&lt;h2&gt;
  
  
  Pet project
&lt;/h2&gt;

&lt;p&gt;Ever found yourself paralyzed by endless choices when starting a new project? I recently did, and it all began with binge-watching way too much &lt;a href="https://www.youtube.com/@ThePrimeTimeagen/featured" rel="noopener noreferrer"&gt;ThePrimeagen&lt;/a&gt;. Inspired, I decided to learn GoLang, thinking it would be the perfect next step for my half-baked Vim skills. I had this pet project idea simmering for months, and GoLang seemed like a solid choice for the backend. But as I soon discovered, the journey from idea to implementation is riddled with obstacles, especially when you’re not a seasoned backend developer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Paralysis
&lt;/h2&gt;

&lt;p&gt;There are a lot of questions when working on the backend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where to host it?&lt;/li&gt;
&lt;li&gt;What DB to use?&lt;/li&gt;
&lt;li&gt;How will I use Kafka with it, and where to put the AI engine and blockchain?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I started by creating a new project in Todoist (no way I’m using Jira for a pet project) and started adding tasks: set repo, write hello world app, add REST API endpoint, set CI, deploy... to where?&lt;br&gt;
I do have a VPS that I sometimes use for pet projects, but it’s not an all-in-one solution, more like a remote Linux I can run things on. I need to host the DB somewhere, have monitoring and alerting, a notification service, a messaging queue… I don’t want to do it all. I don’t know how to do it all. What’s the point of even starting a project when I don’t know the basics?&lt;/p&gt;

&lt;p&gt;Let me start googling for…&lt;/p&gt;

&lt;h2&gt;
  
  
  Service Vendors
&lt;/h2&gt;

&lt;p&gt;There are three standard choices: AWS, Google, and Microsoft, which provide all possible services out of the box. I used AWS, and I don't like it, no particular reason, just the look and feel. Google, as for an Android dev, looks familiar to me, but Google can kill the whole platform next week, I'll pass. Microsoft seems nice, and we use it in our current work project. Is it a good choice? I don’t want to be vendor-locked from the very beginning of the project. Or regret the choice, migrate to something else…&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Do you pine for the nice days of minix-1.1, when men were men and wrote their own device drivers?" - Linus Torvalds&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I won’t be writing my own DB, and I don’t even want to host and administer it. I don’t want to write my own OAuth or keep users' data. I need 3rd party vendors: Firebase for notifications, an OAuth provider with SDKs for mobile and web, and a DB provider.&lt;/p&gt;

&lt;p&gt;Which DB to use? There is about a gazillion of those, in various paradigms. Later I may need an event store or message queue. Even more, decisions to make.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.tenor.com%2Fm%2FmZZoOtDcouoAAAAC%2Fstop-it-get-some-help.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.tenor.com%2Fm%2FmZZoOtDcouoAAAAC%2Fstop-it-get-some-help.gif" alt="Stop it, get some help"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Stop it, get some help
&lt;/h2&gt;

&lt;p&gt;What am I doing here, I simply want to build an app.&lt;/p&gt;

&lt;p&gt;For now, I decided to screw all this and build it locally as much as possible. When I'm satisfied with the results, I’ll happily pay someone to figure out how to put it online. I can go pretty far with the Docker Compose setup I made in 3 minutes. &lt;strong&gt;And it works on my machine&lt;/strong&gt;. Good enough for the next few months :)&lt;/p&gt;

&lt;p&gt;I’ve spent way too much time trying to find the best hosting/DB. And even more when trying to make it work. Comparing free/paid plans, calculating how much horsepower and space I will need. Going through tutorials, YT videos, and setup guides.&lt;/p&gt;

&lt;p&gt;I don’t even have a concrete plan of what and how I want to implement it yet. But I’m burning time finding a nice place to put it online. There’s like a 95% chance it won’t even leave my computer. And it’s still gonna be worth it if I learn a new language or other tech.&lt;/p&gt;

&lt;p&gt;But I don’t. I want to build an app, not fiddle with infra. &lt;strong&gt;Infra is my kryptonite.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s easy to fall into flashy vendor websites after attending meetups and conferences where their logos are omnipresent. It’s not a bad thing, it’s business.&lt;br&gt;
But it obfuscates the true need I have. I want to build a working software solution and have fun learning new tech along the way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Procrastination
&lt;/h3&gt;

&lt;p&gt;Since I don’t know the language well or have a concrete app design in my head (or documented like an adult), it’s easy to escape into googling all possible tools I may or may not need for this project. Doing endless research rather than working on the project itself. The project is doomed to fail that way. I’m losing my motivation momentum on comparing &lt;code&gt;Azure&lt;/code&gt; to &lt;code&gt;Digital Ocean&lt;/code&gt;. My app should have a working API, data models, and DB collections by now. Or at least a directory structure. Having a high-level design wouldn’t hurt.&lt;/p&gt;

&lt;p&gt;But no, let’s compare OAuth providers when I don’t even have endpoints.&lt;/p&gt;

&lt;p&gt;How is it that when I write code, I divide the problem into smaller pieces and conquer them one by one, but when I’m about to start a whole new greenfield project, I fall into this trap? I try everything new at once, losing sight of the actual problem I want to solve. Is it that when working in a team, other devs are subconsciously holding my horses, before I go too deep into details? Or I feel more responsible in my daily job, while a pet project may never be finished, and it’s not there to earn any money (so it should cost little to build).&lt;/p&gt;

&lt;p&gt;Maybe I should change my approach and treat a pet project seriously. As an investment. Plan it better, and see potential future income. But will it be fun and a learning experience or just another job?&lt;/p&gt;

&lt;p&gt;It’s hard to merge those two faces of software development. One where I’m being paid for doing my job, which may not always be fun and joyful. And the other, where I don’t have to be paid, but I need to have fun. I will enjoy it more when the thing I’m building works and even has users. But ultimately, it’s the journey that matters in pet projects.&lt;/p&gt;

&lt;p&gt;Starting the journey with options paralysis is often its end.&lt;/p&gt;

&lt;h3&gt;
  
  
  Oops I did it again
&lt;/h3&gt;

&lt;p&gt;While writing this post, I decided to test a few writing apps. I mainly used &lt;a href="https://ia.net/writer" rel="noopener noreferrer"&gt;iA Writer&lt;/a&gt;, but I also downloaded &lt;a href="https://papereditor.app/" rel="noopener noreferrer"&gt;Paper&lt;/a&gt;, &lt;a href="https://www.ulysses.app/" rel="noopener noreferrer"&gt;Ulysses&lt;/a&gt;, and &lt;a href="https://www.literatureandlatte.com/scrivener/overview" rel="noopener noreferrer"&gt;Scrivener&lt;/a&gt;. Distraction-free writing is amazing. I can configure my Vim and Obsidian to look and feel similar. Wait… What am I doing?&lt;/p&gt;

&lt;p&gt;Instead of writing this post, I was doing tutorials and watching comparisons between writing software.&lt;/p&gt;

&lt;p&gt;I did it again. The same thing I did when starting the backend project.&lt;/p&gt;

&lt;p&gt;Is it procrastination that drives me more into simple, non-measurable tasks, like “doing research” and “checking out tools” rather than doing real work? It reminds me of when I had exams and suddenly house chores became exciting.&lt;/p&gt;

&lt;p&gt;How to escape that?&lt;/p&gt;

&lt;h2&gt;
  
  
  Tips
&lt;/h2&gt;

&lt;p&gt;I came up with a few tips to help me stay focused:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Start Small&lt;/strong&gt;: Begin with a minimal viable product (MVP) to avoid getting overwhelmed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set Clear Goals&lt;/strong&gt;: Define specific, achievable milestones for your project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Local Development&lt;/strong&gt;: Focus on building and testing locally before worrying about deployment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leverage Simple Tools&lt;/strong&gt;: Use straightforward tools and services to avoid decision paralysis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time-Box Research&lt;/strong&gt;: Allocate a fixed amount of time for research to prevent endless comparisons.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Iterate Quickly&lt;/strong&gt;: Regularly review and refine your project to maintain momentum.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This should help me keep motivated and make progress. With just a slight scent of pet project being another job :)&lt;/p&gt;

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

&lt;p&gt;I think I’ve made the same mistake multiple times in my life. It starts with a pet project idea, but instead of working on the unique part of the solution, I dive into checking tools for the most commonly solved problems. I want to pick the best one while having zero experience with the topic, and I’m scared of making a wrong decision. Selecting the wrong tool would feed my Impostor Syndrome, and destroy the joy of creating something cool.&lt;br&gt;
Later I started to think that picking ANY tool would be just fine, and I wasted hours. I’m losing the creative urge to build something; the momentum was wasted on useless work, and I’ve built nothing. And I’m already tired.&lt;/p&gt;

&lt;p&gt;I hope that noticing my tendencies and acting on them fast, using the tips I put above will save me from doing it again.&lt;/p&gt;

&lt;p&gt;Have you been there?&lt;/p&gt;

</description>
      <category>procrastination</category>
      <category>programming</category>
      <category>go</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
