<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Timur Brachkow</title><description>Personal blog of Timur Brachkow</description><link>https://www.brachkow.com/</link><item><title>Tests became cheap, so test everything you want</title><link>https://www.brachkow.com/notes/tests-became-cheap/</link><guid isPermaLink="false">hw2s2ygyDgIxXIxwSNBTQC8fE1zAI5FbG</guid><description>Agentic coding made writing and maintaining tests cost zero human-hours. You can use it not only to have good test coverage, but also to build completely custom test tools</description><pubDate>Wed, 12 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;For many years my main headache was &quot;to test, or not to test&quot;.&lt;/p&gt;
&lt;p&gt;Tests are very important to ensure that code operates as intended, doesn&apos;t break later, and as a way to document code, especially business decisions.&lt;/p&gt;
&lt;p&gt;But tests also come with a heavy tax in the form of constant maintenance — especially frontend and E2E tests.&lt;/p&gt;
&lt;p&gt;Balancing usefulness, coverage and cost was a constant effort full of uncertainty.&lt;/p&gt;
&lt;p&gt;My go-to mix as a frontend engineer was documenting business logic in tests, testing weak spots, and covering everything with visual regression tests, as they had the best effort/problem detection ratio.&lt;/p&gt;
&lt;p&gt;Agentic coding made writing and maintaining tests cost zero human-hours. Now you can have your code fully covered with unit tests, Storybook stories, and E2E scenarios at no human cost — you just need to require writing &amp;amp; updating them in your AGENTS.md.&lt;/p&gt;
&lt;h2&gt;Let&apos;s take this idea further&lt;/h2&gt;
&lt;p&gt;Apart from the standard set of tests, like unit, E2E and visual tests, you can now build entirely custom assertions.&lt;/p&gt;
&lt;p&gt;These can be useful to test hard-to-reach spots, or just to automatically provide feedback to the model.&lt;/p&gt;
&lt;p&gt;Examples of such assertions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You can write custom ESLint rules to provide feedback to the model on lint. Like a rule that reminds the model not to spam comments, or to use &lt;a href=&quot;https://better-result.dev/&quot;&gt;&lt;code&gt;better-result&lt;/code&gt;&lt;/a&gt; instead of &lt;code&gt;try/catch&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;You can use more obscure testing tools without worrying about learning their syntax, lifecycle and so on. For example, write &lt;a href=&quot;https://hurl.dev/docs/running-tests.html&quot;&gt;Hurl&lt;/a&gt; tests for chains of requests&lt;/li&gt;
&lt;li&gt;You can code entirely custom tests. For example, on my Cloudflare D1 projects, I have a script that parses SQL migrations and fails if &lt;a href=&quot;https://www.brachkow.com/notes/d1-on-delete-cascade/&quot;&gt;it contains a wrong order of operations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;You can regex and fail if texts contain &quot;Not X, but Y&quot; or &quot;smoking gun&quot; :)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>How to deal with &quot;vibecoding depression&quot; of indie hackers</title><link>https://www.brachkow.com/notes/vibecoding-depression/</link><guid isPermaLink="false">hi2f7ByYKCZjzrLXK0JfEW8BEporjQw0y</guid><description>What we shall do with our app ideas when our precious CRUD-making skill got democratized?</description><pubDate>Mon, 06 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I see a lot of depressed messages all around the software engineer side of the internet.&lt;/p&gt;
&lt;p&gt;Our skill was largely democratized by AI. Anyone with some technical, design or product building experience can build apps, and we can&apos;t gatekeep them anymore.&lt;/p&gt;
&lt;p&gt;Tons of apps are flowing into the internet and app stores. A lot of them work badly, some of them look bad, and few are actually good.&lt;/p&gt;
&lt;p&gt;But they are here, either competing with our work or not busy creating work contracts for us.&lt;/p&gt;
&lt;p&gt;For people like me, whose hobby outside of my day job was working on my own apps, this may seem like even more devastating news. I&apos;ve spent human-years making apps, thinking that someday this will be my career.&lt;/p&gt;
&lt;p&gt;In this post I will explain why I consider democratizing our work a good thing, why I believe a relatively simple app made by real people will continue to thrive, and why I&apos;m not sad about &quot;wasted time&quot;.&lt;/p&gt;
&lt;h2&gt;What we were actually doing all the time&lt;/h2&gt;
&lt;p&gt;Let me start from a distance: what is the goal of &quot;making apps&quot;, &quot;SaaS&quot;, &quot;CRUD&quot; and being a software engineer in general?&lt;/p&gt;
&lt;p&gt;When I studied in university, my course was called &quot;Information Resource Management&quot;. I always considered this name a random buzzword, unrelated to my education that mixed business, economy and shallow dive into computer science.&lt;/p&gt;
&lt;p&gt;With years of experience I found out that the name actually was very on point. The exact moment when I realized this was during an argument with my grandfather, a very experienced bureaucrat, who rejected some of my points saying: &quot;if everyone was sitting in the office in front of a computer, we would have nothing to eat&quot;.&lt;/p&gt;
&lt;p&gt;Here came the realisation: all the multibillion companies, including the most real-world ones — Uber, Airbnb, DoorDash — don&apos;t do much in the physical world. Their engineers are busy doing CRUD on data. No engineer carries a person from A to B, or maintains an apartment.&lt;/p&gt;
&lt;p&gt;What we were doing all the time wasn&apos;t &quot;solving real-world problems&quot;. What we were actually doing is multiplying real-world work via automated CRUDing of data.&lt;/p&gt;
&lt;p&gt;But a trillion times zero is still zero — and the zero-or-not comes from people in the offline world. This offline work is the product.&lt;/p&gt;
&lt;p&gt;This real work can be multiplied by 10 when organised via the most primitive information system as simple as ink &amp;amp; paper, by 100 when it&apos;s done in Excel, the most generalized information system, and by 100500 multiplied by 9000 when it is done in a tailored, computerized information system.&lt;/p&gt;
&lt;p&gt;That&apos;s what business wants. That&apos;s what we do. That&apos;s our work.&lt;/p&gt;
&lt;h2&gt;The world we lived in before was not normal&lt;/h2&gt;
&lt;p&gt;A few years ago, the hottest topic in tech was &lt;a href=&quot;https://x.com/levelsio&quot;&gt;Pieter Levels&lt;/a&gt; – a guy in shorts and no t-shirt, who was making thousands of dollars a month by making ugly and buggy CRUD apps with PHP &amp;amp; jQuery.&lt;/p&gt;
&lt;p&gt;What he did was &lt;a href=&quot;https://levels.io/projects&quot;&gt;brute-force tons of app ideas&lt;/a&gt; via making very barebones apps, and this &lt;a href=&quot;https://swyx.io/create-luck&quot;&gt;enlarged luck surface&lt;/a&gt;, multiplied by an ever-growing subscriber count, resulted in making a few simple but very profitable apps.&lt;/p&gt;
&lt;p&gt;Set aside his huge audience, and it sounded like a plan anyone could copy. &quot;SaaS indiehacking craze&quot; became a lasting hot topic in the engineering world.&lt;/p&gt;
&lt;h3&gt;But please, hear me out&lt;/h3&gt;
&lt;p&gt;All this success was a symptom of an unhealthy situation. Everyone needed apps to multiply their business output. But only a few, The Engineers™, were able to make those apps.&lt;/p&gt;
&lt;p&gt;This imbalance between huge demand for tailored information systems and limited supply of people who can craft them gave birth to a privileged class of &quot;CRUD makers&quot;.&lt;/p&gt;
&lt;p&gt;They could make a minimal, barely working, CRUD app that solved a pain-point™, and businesses hungry for informational systems tailored to their niche began lining up to sign up for the annual tax-deductible subscription.&lt;/p&gt;
&lt;p&gt;This was great for the &quot;rich from making forms&quot; engineer, but it wasn&apos;t healthy. And this was acknowledged long before AI: &quot;no code&quot; soaked up venture capital, n8n and Zapier became tools of the common folk, and Excel has been the people&apos;s information system since the 80s.&lt;/p&gt;
&lt;p&gt;What I&apos;m trying to say in this section is that you should clearly distinguish when you are sad not because of the &quot;death of craft&quot;, but because the era of golden software shovels is gone. And the new era is better for everyone.&lt;/p&gt;
&lt;h2&gt;The secret ingredient of a CRUD app&lt;/h2&gt;
&lt;p&gt;Another discussion that emerged from this topic is TaStE. Everyone decided plain CRUD is no longer enough — now you need &lt;em&gt;tasteful&lt;/em&gt; CRUD to succeed. &lt;del&gt;Also everyone found that they are lucky holders of this unique &quot;taste&quot; and talent, and that their CRUD apps are already tasty. Yum!&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;Taste is a stupid term. But disregarding the surrounding idea and agreeing that barebones CRUD apps are dead may lead you into a depressing thought – that only REAL software is one made by x100000 engineers.&lt;/p&gt;
&lt;p&gt;So Parallels the VM, YouTube, Office suite with legendary Excel, Git and Graphics editors are the only software that deserves to live, and now you, just a good modest engineer, are out of the game, just like these non-tech people were before.&lt;/p&gt;
&lt;p&gt;But I believe that CRUD apps have a bright future. The secret ingredient isn&apos;t taste — it&apos;s ideas: your opinionated vision of what the product should be.&lt;/p&gt;
&lt;p&gt;These ideas may be either right or wrong, visionary or delusional. You either feel it, or you don&apos;t.&lt;/p&gt;
&lt;p&gt;Just like in music. A well-funded pop star, a homegrown musician going viral on TikTok, or a sincere guy missing notes on a talent show that will end up a meme – all of them sing, all of them believe in their ideas to some degree.&lt;/p&gt;
&lt;p&gt;I would like to explain what I think matters.&lt;/p&gt;
&lt;p&gt;There is a lot of peak software that could be labeled &quot;just CRUD&quot;, but they hold a huge value in ideas and product field expertise:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://culturedcode.com/things/&quot;&gt;Things&lt;/a&gt; – is the top GTD app on iOS. This is the CRUDest app possible. &lt;a href=&quot;https://x.com/culturedcode/with_replies&quot;&gt;If you open developer Twitter, all they do is politely reject ideas from their customers&lt;/a&gt;. Some customers are offended by that. Plenty of apps have the features they want. People stay with Things anyway, because developers know what you actually need to be productive, and what you only think will make you productive.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.are.na/&quot;&gt;Are.na&lt;/a&gt; – is a CRUD app with images. There are a lot of moodboarding apps, but via very specific PR, aesthetics and pricing, they were able to attract a unique community of creatives. I had way more meaningful connections via Are.na than LinkedIn and Twitter combined.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.fastmail.com/&quot;&gt;Fastmail&lt;/a&gt; – everyone can make their own mail server, but they do old&apos;n&apos;boring email so well that people are paying them for the features available in Gmail for free.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://flighty.com&quot;&gt;Flighty&lt;/a&gt; – is a CRUD app with aggregated API output. They make tracking flights into a smooth, nerdy, fun, and social experience.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can build &quot;a clone&quot; of any of these apps in a week of prompting, but what you end up with is a visionless &quot;Temu iPhone&quot; of a CRUD app.&lt;/p&gt;
&lt;p&gt;So mind reading is a new moat – your vision and your ideas, either brilliant or delusional, are completely private to you, and anyone who copies you is permanently one step behind, racing on a track you laid.&lt;/p&gt;
&lt;p&gt;Stealing your precious ideas is not a thing you should worry about. It was always possible to steal an idea.&lt;/p&gt;
&lt;p&gt;Your successful, but uninspired SaaS CRUD app could be easily cloned and outcompeted by a guy with a good VC network, who liked an idea for your app that you shared on Twitter.&lt;/p&gt;
&lt;p&gt;But no one can copy your app plans before you make them happen, which means you always have an advantage with your ideas.&lt;/p&gt;
&lt;h2&gt;What motivates me to work on my CRUD app&lt;/h2&gt;
&lt;p&gt;For years, I&apos;ve been &lt;a href=&quot;https://thingstohave.app/&quot;&gt;making a wishlist app&lt;/a&gt;. This is the CRUDest category ever.&lt;/p&gt;
&lt;p&gt;The most generic CRUD app you can think of, competing only with bookmarking apps. It is CRUD to the point where the biggest competitors are Google Sheets and {your-local-online-marketplace-name} Wishlist.&lt;/p&gt;
&lt;p&gt;I started it way before the AI agents craze. It began as an app for my friends, then I decided to expand it to more people, then to a real product. This is the third or fourth iteration of the app since 2022.&lt;/p&gt;
&lt;p&gt;You might think that I&apos;d at least be mad that three years of my work can now be done by anyone who gave $100 to Anthropic.&lt;/p&gt;
&lt;p&gt;But I&apos;m not. Here&apos;s why:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I didn&apos;t like any wishlist app that existed prior. Now I have an app I always wanted. There is an idea behind everything in my app. From simple, raw look and font choice, to drag-and-drop working in places unusual for the web. There is a lot of me in this work – I just believe in and enjoy everything in this app.&lt;/li&gt;
&lt;li&gt;I have a clear, far-reaching vision for the app that gives me hope it can outcompete anyone in this field. I know what people like me may want, and I believe there are enough of them to make the app sustainable in the long term.&lt;/li&gt;
&lt;li&gt;I learned a lot about software development, SEO, security and DevOps while making it. It was also built on a quirky stack that led to a few well-paid contracts on interesting projects, outside my primary scope of work.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I already gained a lot from this app: I&apos;m already a happy user, and all emerging vibecoded apps are way below my app&apos;s quality.&lt;/p&gt;
&lt;p&gt;Maybe the one thing that&apos;s quite sad for me is that marketing got even more frustrating, as tons of low-quality competition breeds a &quot;yet another vibecoded wishlist app&quot; prejudice. And I was already bad at marketing. But it&apos;s a challenge to overcome!&lt;/p&gt;
&lt;h2&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;So, what I tried to say here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The past decade was a SaaS gold rush, caused not by magnificent results of our skill, but by demand for &quot;informational systems&quot;, most of which were expensive to build due to our labour costs.&lt;/li&gt;
&lt;li&gt;Now they are not expensive to build due to AI, and many SaaS can be replaced internally for a cost lower than an annual subscription to such SaaS.&lt;/li&gt;
&lt;li&gt;What can&apos;t be vibecoded is knowledge of the topic, engineering experience and ideas (but ideas can be wrong!). If you cared a lot about your product or craft in general, you already have this advantage!&lt;/li&gt;
&lt;li&gt;You might still be sad about the disappeared &quot;Golden CRUD shovel making&quot; era. To not be sad, you need to build what you care about and use it yourself. This way you will always end up winning. And, if you think about it for a second – maybe it is a change for a better future? Like, when was the last time making B2C apps was viable in comparison to B2B? It&apos;s today!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Go and make something you care about!&lt;/p&gt;
</content:encoded></item><item><title>How to cut most of automated spam in Telegram channel comments</title><link>https://www.brachkow.com/notes/how-to-cut-most-of-automated-spam-in-telegram-channel-comments/</link><guid isPermaLink="false">hq41Zw5kweU4SctkCem17RuTJOYtceq3Y</guid><description>A simple rule that cuts most of the spam from Telegram comments, and its implementation in the form of a bot</description><pubDate>Thu, 18 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Of the many problems that Telegram has, the one which frustrates me the most is a ton of automated spam — as it has absolutely no automated antispam measures.&lt;/p&gt;
&lt;p&gt;It annoys me especially in the “comment chats”. They are linked to public channels and auto-discoverable by spam bots. With a bit of tinkering, anyone on the internet can send any text to a chat with a known number of recipients and a known topic.&lt;/p&gt;
&lt;p&gt;Sometimes I wake up to clean 20+ spam messages for multiple days in a row.&lt;/p&gt;
&lt;p&gt;The only fix that is available via the built-in Telegram features is to set the comment chat to manual approval of new chat members.&lt;/p&gt;
&lt;p&gt;But due to the nature of Telegram comments, which are basically a regular chat taped to a channel (olympiadniki, blyat), this approach has serious downsides:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You need to approve them manually. A person who wanted to write a comment will lose interest if you are late with the approval, ruining the quality of the discussion&lt;/li&gt;
&lt;li&gt;People who commented on the post will now get a notification for every comment under every post. That&apos;s why most users will want to leave after writing a comment&lt;/li&gt;
&lt;li&gt;Once they leave, they need to go through step 1 again&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;How can we automate approvals without auto-approving spammers?&lt;/h2&gt;
&lt;p&gt;I discovered that these automated spammers don&apos;t bother joining the channel — they go to the comments directly, as it seems to be exposed in the API.&lt;/p&gt;
&lt;p&gt;So when your comments chat receives a join request, it must be matched against a subscriber of the channel.&lt;/p&gt;
&lt;p&gt;You still need to auto-approve real people who visited your channel without joining it. But these are rare, and usually they leave low-value negative comments that can wait :)&lt;/p&gt;
&lt;h2&gt;Doorman bot&lt;/h2&gt;
&lt;p&gt;It was very trivial to build a bot that does that. My bot is called &lt;a href=&quot;https://t.me/doorman_da_bot&quot;&gt;@doorman_da_bot&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As the bot doesn&apos;t require any configs and isn&apos;t resource-heavy — you can use it for free.&lt;/p&gt;
&lt;p&gt;You need to add the bot as an admin to both the channel and the comments chat with a minimal set of rights. You only need to give the “Add members” right in the comments chat and the “Add subscribers” right in the channel.&lt;/p&gt;
&lt;p&gt;There is no reason to give it any other rights.&lt;/p&gt;
&lt;p&gt;If you are worried about security — you can give this post to an LLM and have it one-shot this bot for you.&lt;/p&gt;
</content:encoded></item><item><title>Dell UltraSharp U2725QE is a great display for macOS</title><link>https://www.brachkow.com/notes/dell-ultrasharp-u2725qe-is-a-great-display-for-macos/</link><guid isPermaLink="false">h7KQynqlXbS0b0sP1KwxdM1qmy0LSrN95</guid><description>Why I bought the Dell UltraSharp U2725QE instead of Apple&apos;s Studio Display — 120 Hz, a built-in KVM, and a fair price, with no regrets.</description><pubDate>Sat, 13 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In 2025 I was 100% sure that the new Apple Studio monitor would have a 120 Hz refresh rate, and that I would buy it.&lt;/p&gt;
&lt;p&gt;Sadly, my LG UltraFine started to work unreliably half a year before the expected release, so I ended up buying the Dell UltraSharp U2725QE as a temporary solution.&lt;/p&gt;
&lt;p&gt;I enjoyed this display a lot, and since, to my surprise, the 2026 refresh of the Apple Studio Display was another $1500 60 Hz monitor, I cancelled my plans to swap it with no regrets.&lt;/p&gt;
&lt;p&gt;Here&apos;s a short review of the Dell UltraSharp U2725QE:&lt;/p&gt;
&lt;h2&gt;Pros&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Image quality is great, and unlike the LG, it has no stupid features &lt;a href=&quot;https://www.brachkow.com/notes/macos-monitor/&quot;&gt;that ruin the image&lt;/a&gt; enabled by default&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;120 Hz refresh rate — same as on the MacBook Pro&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;140W Power Delivery&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Has a fully featured KVM inside. I suffered without a KVM on the LG UltraFine, and buying one is a research-intensive and expensive process. Having one included inside the monitor is bliss.&lt;/p&gt;
&lt;p&gt;You can check all available ports on the spec sheet, but I would like to highlight the Ethernet. Having cable internet on a clamshell MacBook is a cool experience.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Waking from sleep is way more reliable than on the LG&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Apart from the KVM, it has a stupid-looking but very useful retractable front-facing USB hub with two USB-C and one USB-A 3.0.&lt;/p&gt;
&lt;p&gt;I didn&apos;t know about it when I bought the display, and got a bit of a jumpscare when this spring-loaded thing jumped out :)&lt;/p&gt;
&lt;p&gt;This ended up being one of my most used features of the monitor. I connect cameras, phones, MIDI controllers, and &lt;a href=&quot;https://www.brachkow.com/notes/caveats-of-ipad-mini/#sidecar-aka-using-ipad-as-display-for-mac&quot;&gt;iPad Sidecar&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;$800 — cheap for the specs&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Cons&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;It has no speakers. Monitor speakers are bad, but I prefer having them.&lt;/li&gt;
&lt;li&gt;It is hard to attach devices to the top of the monitor. As it has a steep curve on the back and quite a thin and weak screen border, many devices that clip on top of the monitor will either not sit well or will overpressure the display, which can cause damage. I&apos;m using a Xiaomi display light bar, and I needed to put around 5mm of densely folded paper under the clip to reduce the pressure. It worked, but looks ugly.&lt;/li&gt;
&lt;li&gt;The monitor feels way brighter and diffuses light differently than the LG UltraFine. Sometimes it feels too bright in a dark room. Auto-brightness on the monitor is buggy and unreliable. I use the display light bar to offset this.&lt;/li&gt;
&lt;li&gt;It has a 4K screen, not 5K. I see no problem with it, but there are people who believe that macOS displays must have 5K resolution.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Why you still might consider the Studio Display&lt;/h2&gt;
&lt;p&gt;Apart from the 5K preference, the only reason you might consider buying the $1500 Studio Display is that it has speakers, a webcam, and a microphone.&lt;/p&gt;
&lt;p&gt;Apple does all of these better than most standalone offerings on the market. Getting these separately will result in a clunkier setup for comparable money, and worse results.&lt;/p&gt;
&lt;p&gt;On the other hand, you will end up with a 60 Hz monitor and a limited set of ports.&lt;/p&gt;
</content:encoded></item><item><title>Caveats of iPad Mini</title><link>https://www.brachkow.com/notes/caveats-of-ipad-mini/</link><guid isPermaLink="false">h95KJbVuDlfHYpMZQVxl2zM7pfvmkULdi</guid><description>A year of living with the iPad Mini as a companion device — the quirks, limits, and niche use cases reviews don&apos;t cover.</description><pubDate>Mon, 01 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import ArticleImage from &apos;../../components/ArticleImage.astro&apos;;
import ImageGroup from &apos;../../components/ImageGroup.astro&apos;;
import keyboard from &apos;../../assets/ipad-mini-keyboard.png&apos;;
import keyboardFloat from &apos;../../assets/ipad-mini-keyboard-float.png&apos;;
import keyboardSplit from &apos;../../assets/ipad-mini-keyboard-split.png&apos;;
import sshKeyboard from &apos;../../assets/ipad-ssh-keyboard.jpg&apos;;
import sshNoKeyboard from &apos;../../assets/ipad-ssh-no-keyboard.jpg&apos;;&lt;/p&gt;
&lt;p&gt;More than a year ago I bought an iPad Mini. To be precise, it is the 2024 A17 Pro with Cellular.&lt;/p&gt;
&lt;p&gt;Deciding to buy one was a tough choice. I knew for sure I had no need for an iPad as a standalone device, but at the same time there were a lot of places where using a Mac/iPhone was quite unwieldy, while the iPad Mini &lt;strong&gt;seemed&lt;/strong&gt; like the right tool for the job.&lt;/p&gt;
&lt;p&gt;That &lt;strong&gt;seemed&lt;/strong&gt; is important here. While iPhone and Mac are straightforward products, it is hard to understand what iPad can do and what it can&apos;t do.&lt;/p&gt;
&lt;p&gt;As someone on the internet coined &quot;Mac is a computer where you are allowed to do anything, iPad is a computer where you are allowed to do a specific list of random things&quot;.&lt;/p&gt;
&lt;p&gt;My goals were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;To have a trip computer, where I can edit docs, edit photos, or watch movies&lt;/li&gt;
&lt;li&gt;Be a companion to my clamshell MacBook via Continuity features (webcam and mic, external display, etc)&lt;/li&gt;
&lt;li&gt;Be able to do minimal programming in case of emergency at work. This goal was later extended to agentic coding&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Reviews I saw were catered towards people who use iPad as their main computer, so it was hard to know whether the iPad would come in handy for these use cases.&lt;/p&gt;
&lt;p&gt;Now that I know the answer, I wrote this post – it is a list of iPad Mini and iPadOS quirks found in niche use cases. Feel free to use this table of contents to read what you are interested in:&lt;/p&gt;
&lt;p&gt;[toc]&lt;/p&gt;
&lt;h2&gt;Hardware&lt;/h2&gt;
&lt;p&gt;Let&apos;s start with hardware. iPad Mini is very strange in terms of hardware:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It has Touch ID instead of Face ID. This once futuristic technology now feels extremely slow. Additional annoyance is that the fingerprint button is located on one side of the device, but you will be using the device from all of its four sides.&lt;/li&gt;
&lt;li&gt;It&apos;s a mostly landscape device with a portrait webcam. Wide angle and clever cam tech can fix this inconvenience.&lt;/li&gt;
&lt;li&gt;The cellular version doesn&apos;t have the ability to make old-fashioned calls. It just has 5G internet. Having 5G is cool for traveling, but your cellular provider may limit roaming to one eSIM which is already in your iPhone.&lt;/li&gt;
&lt;li&gt;The screen is 60fps, and feels slightly dated compared to other Apple devices I own. I observed no notorious &quot;jelly&quot; effect, which was seen in the previous generation.&lt;/li&gt;
&lt;li&gt;Sound is nowhere near Mac, or even the latest iPhone level. But it is good enough.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There were also pleasant surprises:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The device is very small and light. It fits into my very tight daily bag&lt;/li&gt;
&lt;li&gt;The battery holds a day of actual use&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Multitasking went from useless to bad&lt;/h2&gt;
&lt;p&gt;Before iPadOS 26, iPad Mini had two multitasking options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;you could split the screen into two halves&lt;/li&gt;
&lt;li&gt;you could open an iPhone-sized app as a sidebar (this was called &quot;Slide Over&quot;) which appears over any app, including the split&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Bigger iPads also had Stage Manager, but it wasn&apos;t a thing on iPad Mini.&lt;/p&gt;
&lt;p&gt;This wasn&apos;t macOS level multitasking, but it fills UX gaps. You can easily DnD from app to app, you can write in one app while looking at another, etc.&lt;/p&gt;
&lt;h3&gt;But then iPadOS 26 came in...&lt;/h3&gt;
&lt;p&gt;It introduced real windows, enabled Stage Manager for iPad Mini, but removed screen splitting and Slide Over.&lt;/p&gt;
&lt;p&gt;iPad Mini is a small device, and managing windows on it is hard. Previous multitasking options + Stage Manager were limited but perfect for it.&lt;/p&gt;
&lt;p&gt;In iPadOS 26.2 they re-added these features, but now they work only in that almost unusable window mode, while previously they worked in normal mode.&lt;/p&gt;
&lt;h2&gt;Sidecar aka using iPad as display for Mac&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://support.apple.com/en-us/102597&quot;&gt;Sidecar&lt;/a&gt; is a way to use iPad as an external monitor for macOS. It works well both with and without a cable.&lt;/p&gt;
&lt;p&gt;It&apos;s a nice feature – I like to use it as a small YouTube TV, Claude Code window, or Figma reference.&lt;/p&gt;
&lt;p&gt;Also it can increase your screen real estate on trips.&lt;/p&gt;
&lt;p&gt;It can comfortably accommodate one browser window or terminal and feels close to 30fps.&lt;/p&gt;
&lt;p&gt;You can also use it to draw using Apple Pencil or finger on desktop, which is very useful when signing documents, drawing or retouching.&lt;/p&gt;
&lt;p&gt;Even if you are not using Sidecar, you can still put your iPad next to Mac and magically move keyboard and mouse between macOS and iPadOS. This works with cable keyboards too!&lt;/p&gt;
&lt;h2&gt;It&apos;s impossible to use iPad as webcam&lt;/h2&gt;
&lt;p&gt;So you have this small monitor in front of you, and want to &lt;a href=&quot;https://support.apple.com/en-us/102546&quot;&gt;use it as a camera and microphone&lt;/a&gt; for your closed-lid MacBook, like you can do with an iPhone?&lt;/p&gt;
&lt;p&gt;It&apos;s not possible! For god knows what reason, this feature is iOS only!&lt;/p&gt;
&lt;p&gt;You can still use an external app like &lt;a href=&quot;https://camo.com/&quot;&gt;Camo&lt;/a&gt; for that purpose. But my experience with Camo was mostly negative, as it works unreliably and wrecks the camera devices list with its &quot;virtual camera&quot; which stays there if you delete it the wrong way.&lt;/p&gt;
&lt;h2&gt;Apple Home became impossible&lt;/h2&gt;
&lt;p&gt;One of my excuses for buying an iPad Mini was that if I end up not using it a lot, I may use it as an Apple Home center.&lt;/p&gt;
&lt;p&gt;I was very surprised when I realized that &lt;a href=&quot;https://www.macrumors.com/2022/06/22/ipados-16-no-ipad-home-hub-support/&quot;&gt;Apple removed support for iPadOS&lt;/a&gt; back in 2022.&lt;/p&gt;
&lt;p&gt;Other devices that have such capability are HomePods and Apple TVs, all of which I absolutely don&apos;t need.&lt;/p&gt;
&lt;h2&gt;Native apps can be very ugly, but you can use desktop web apps&lt;/h2&gt;
&lt;p&gt;There are quite a lot of professional apps on iPadOS built specifically for these devices.&lt;/p&gt;
&lt;p&gt;There is also a technology that allows iOS apps to be opened on iPadOS. And iPadOS was iOS not so long ago.&lt;/p&gt;
&lt;p&gt;But still a lot of apps will open as an iPhone-sized window with low resolution and a strange bending effect when you try to resize it. Very ugly, annoying and uncomfortable to use.&lt;/p&gt;
&lt;p&gt;I haven&apos;t owned Android tablets for many years, but as far as I can remember any app you had on your Android phone would open on a tablet in tablet dimensions. Why this is not the case on Apple devices is a big question.&lt;/p&gt;
&lt;p&gt;And when an app has an iPad version it may look like a complete afterthought with some bizarre layouts.&lt;/p&gt;
&lt;h3&gt;But you can use web apps!&lt;/h3&gt;
&lt;p&gt;To my surprise Safari on iPadOS works much better than on iOS and has an impressive 1133px x 744px browser viewport, which is not considered &quot;mobile&quot; or &quot;tablet&quot; by many websites.&lt;/p&gt;
&lt;p&gt;If you connect keyboard and mouse, you can use the internet like you do on a desktop computer – maybe do some real work in Figma or other serious web apps.&lt;/p&gt;
&lt;h2&gt;Programming – you actually can do some, but only if you SSH into another computer&lt;/h2&gt;
&lt;p&gt;When I bought this iPad, agentic coding wasn&apos;t as big as it is now. So my plans were modest – I wanted to be able to edit files, commit them and see results. Being able to go on a trip without a real computer while still resolving a disastrous incident at work would buy me peace of mind.&lt;/p&gt;
&lt;p&gt;The only way that proved to be viable is to SSH into a remote machine (preferably not your MacBook, which is hard to keep always on). What I ended up doing is running a remote server, setting up LazyVim there and connecting to it via SSH using Tailscale and Mosh.&lt;/p&gt;
&lt;p&gt;Of all the SSH clients I tested, &lt;a href=&quot;https://webssh.net&quot;&gt;WebSSH&lt;/a&gt; was the best. There is also a Ghostty-based &lt;a href=&quot;https://replay.software/echo&quot;&gt;Echo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This setup works. I wouldn&apos;t do serious work this way, but I edited a few posts on this blog using vim. When Claude Code appeared, I was also able to prompt and oversee some features that ended up in production.&lt;/p&gt;
&lt;p&gt;Keep in mind that with this setup you must learn how to navigate LazyVim and tmux/zellij, and have an external keyboard to have at least some screen real estate:&lt;/p&gt;

  
  


&lt;p&gt;I believe in the near future we will have proper cloud agents, and this device may become much more useful for real work.&lt;/p&gt;
&lt;h2&gt;Typing and keyboards – the biggest caveat&lt;/h2&gt;
&lt;p&gt;The most annoying part of iPad Mini is the typing experience. The device is too big to type efficiently while holding it with one or two hands. It is also kinda small to type using two hands on the onscreen keyboard while the device is resting on your lap.&lt;/p&gt;
&lt;p&gt;There are atrocious-looking keyboard modes to aid you in typing:&lt;/p&gt;

  
  
  


&lt;p&gt;I found the floating variant to be the most effective, as at least I can type at full speed with one hand.&lt;/p&gt;
&lt;p&gt;Unlike other iPads, it has no official physical keyboard.&lt;/p&gt;
&lt;p&gt;The 3rd-party keyboard situation looks like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There are some aliexpress-style case-keyboards. Never tried these as they looked bad even on carefully rendered product images.&lt;/li&gt;
&lt;li&gt;There are folding keyboards. They don&apos;t look good, and I think they are not suitable for typing on your lap.&lt;/li&gt;
&lt;li&gt;There are small standalone keyboards. Keychron non-mechanical keyboards are very bad and still quite big. The smallest Keychron/Nuphy mechanical keyboards are nice to type on but bigger and heavier than the iPad. I also know that Logitech has some small keyboards, but I never tried these.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As a result, you can get a comfortable typing experience only when using a bulky external keyboard, which you can&apos;t bring in the same small bag the iPad Mini fits in, losing all the portability benefits.&lt;/p&gt;
&lt;p&gt;And after all that there is another issue: there is no keyboard repeat setting that exists on macOS and is used by everyone who works with any kind of text.&lt;/p&gt;
&lt;p&gt;This setting allowed you to reduce the delay between keyboard events, which is crucial when you are deleting letters via the backspace button.&lt;/p&gt;
&lt;p&gt;Deleting a single letter on iPad takes a noticeable moment, which means you need to hold the button for a few seconds to fully erase a word. The same applies to navigating with arrow keys. This is annoying and makes editing text unpleasant.&lt;/p&gt;
&lt;h2&gt;USB-C and Hubs&lt;/h2&gt;
&lt;p&gt;Having USB-C is a delight. Surprisingly, considering it is Apple, it is not limited:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Connect cameras&lt;/li&gt;
&lt;li&gt;Read drives and SD cards&lt;/li&gt;
&lt;li&gt;Connect MIDI devices&lt;/li&gt;
&lt;li&gt;Connect external monitors directly via Thunderbolt or via HDMI dongle (will show your screen in iPad resolution)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My only complaint is that I wish external monitor support worked as a proper external monitor or Samsung DeX-like docking.&lt;/p&gt;
&lt;p&gt;This would allow so many use cases: use iPadOS on a big screen, use as a TV stick, etc. Sadly it is not the case – an iPad connected to a 16:9 screen will display 3:2 with a letterbox around it.&lt;/p&gt;
&lt;h2&gt;Gaming&lt;/h2&gt;
&lt;p&gt;To my surprise, having a 5G cellular connection makes it a great portable gaming device.&lt;/p&gt;
&lt;p&gt;The App Store has all kinds of games, even some old AAA games will work on iPad. But I&apos;m not interested in any of these. What interested me is cloud gaming.&lt;/p&gt;
&lt;p&gt;Apple famously does everything to not let any cloud gaming in, but somehow GeForce Now works on iPad (engineers, brace yourselves...) via iOS Safari PWA. And it works well, apart from occasional audio issues.&lt;/p&gt;
&lt;p&gt;With a 5G connection on the cellular model, you can get a decent gaming experience with small enough latency to play a shooter in 60fps (which is the WebKit FPS cap) and a resolution similar to 1080p. For me, that beats taking a huge Steam Deck on a trip.&lt;/p&gt;
&lt;p&gt;To play games on GeForce Now, you will need a gamepad. I had no problem connecting 8BitDo gamepads both by wire and Bluetooth.&lt;/p&gt;
&lt;p&gt;You can&apos;t fit iPad Mini into a Backbone grip controller, but people say that Razer Kishi Ultra can. So you can turn it into a cloud Steam Deck.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;So what do I use iPad Mini for? I actually use it for a lot of things, and I enjoy using it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I use it to extract images from Ricoh GR or edit photos. I also show these pictures on it&lt;/li&gt;
&lt;li&gt;I use it as a typewriter when I want to write something without distractions&lt;/li&gt;
&lt;li&gt;I enjoy using it for productivity apps like Things and Calendar, due to more screen real estate and DnD&lt;/li&gt;
&lt;li&gt;I regularly use it as an external display, primarily for agents or Figma&lt;/li&gt;
&lt;li&gt;I read A4 PDF books&lt;/li&gt;
&lt;li&gt;I play GeForce Now on it&lt;/li&gt;
&lt;li&gt;I use it as my vacation computer&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I also believe it has future potential for programming-related tasks, as soon as cloud agents and agent orchestrators arrive on mobile devices.&lt;/p&gt;
&lt;p&gt;Can I live without it? Unlike Mac or iPhone – of course. But it definitely makes my life better. For me iPad Mini is a missing piece computer. It is very uncomfortable to use as a main device, but it is great at accompanying my other devices, and fits in rare situations where no device is good enough, but iPad Mini is.&lt;/p&gt;
&lt;p&gt;Keep in mind that this post&apos;s goal was to target quite specific things and caveats, you may have a negative sentiment about iPad Mini after reading it. But I want to make it clear that I don&apos;t regret buying it, and I enjoy using it.&lt;/p&gt;
</content:encoded></item><item><title>How Cloudflare&apos;s D1 database makes valid SQLite migrations prone to data loss</title><link>https://www.brachkow.com/notes/d1-on-delete-cascade/</link><guid isPermaLink="false">hzQLzqjNhGDsaKAE2brB3nUMqLq8rzPot</guid><description>Cloudflare D1 can&apos;t disable foreign keys via PRAGMA foreign_keys = OFF, so ON DELETE CASCADE fires during table rebuilds. Unless you structure migrations a very specific way, your data is silently wiped — here&apos;s why, and how to fix it</description><pubDate>Mon, 25 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;D1 is Cloudflare&apos;s managed database service. It provides a serverless database that is compatible with SQLite.&lt;/p&gt;
&lt;p&gt;That said, being compatible doesn&apos;t equal feature parity.&lt;/p&gt;
&lt;p&gt;D1 can&apos;t disable FK via &lt;code&gt;PRAGMA foreign_keys = OFF&lt;/code&gt;, which makes &lt;code&gt;CASCADE&lt;/code&gt; actions unavoidable during migration.&lt;/p&gt;
&lt;p&gt;This means that unless you structure your migrations in a very specific way, your data will be silently wiped.&lt;/p&gt;
&lt;p&gt;In this post I will dig into this problem and how it can be solved.&lt;/p&gt;
&lt;h1&gt;foreign_keys vs defer_foreign_keys&lt;/h1&gt;
&lt;p&gt;If you migrate something with SQL constraints enabled in SQLite, you need to frame your migration in &lt;code&gt;PRAGMA foreign_keys&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This way you disable constraints from firing and re-enable them when the migration is done.&lt;/p&gt;
&lt;p&gt;D1 doesn&apos;t allow &lt;code&gt;PRAGMA foreign_keys = OFF&lt;/code&gt;, but &lt;a href=&quot;https://www.sqlite.org/pragma.html#pragma_defer_foreign_keys&quot;&gt;has a &quot;similar&quot; option also available in SQLite, &lt;code&gt;PRAGMA defer_foreign_keys = ON&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It&apos;s easy to get tricked into thinking that these are the same, with the only difference being that one is a switch and the other is delayed execution. But this is not true.&lt;/p&gt;
&lt;p&gt;While &lt;code&gt;PRAGMA defer_foreign_keys = ON&lt;/code&gt; indeed defers the FK check, constraints like &lt;code&gt;ON DELETE CASCADE&lt;/code&gt; are not disabled and fire immediately.&lt;/p&gt;
&lt;p&gt;So all referenced data will be silently wiped during this migration.&lt;/p&gt;
&lt;h2&gt;Drizzle ORM doesn&apos;t handle this limitation&lt;/h2&gt;
&lt;p&gt;You might think that Drizzle — a popular ORM with first-party D1 support — may solve this issue for you, as it autogenerates migrations.&lt;/p&gt;
&lt;p&gt;But Drizzle actually treats D1 as actual SQLite:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It frames the migration in &lt;code&gt;PRAGMA foreign_keys=OFF&lt;/code&gt;, which doesn&apos;t work in D1. The rest of the migration is based on the fact that we have FK disabled&lt;/li&gt;
&lt;li&gt;Knowing so, Drizzle does alphabetical ordering of migrations instead of relation-based ordering&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As a result, this migration leads to data loss when run against D1, due to &lt;code&gt;ON DELETE CASCADE&lt;/code&gt; firing.&lt;/p&gt;
&lt;h2&gt;Solving this issue&lt;/h2&gt;
&lt;p&gt;In case your migration drops a parent referenced by an FK at any point, you need to edit your autogenerated migrations:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Frame the migration in &lt;code&gt;PRAGMA defer_foreign_keys&lt;/code&gt;. You need that to perform operations on any referenced FK.&lt;/li&gt;
&lt;li&gt;Rewrite migrations in a very specific way that temporarily removes FK constraints from children when doing deletes on parents&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Example with a database that has parent and child tables, where the child has constraints:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;-- Start deferring FK
-- PRAGMA defer_foreign_keys = ON;

-- 1. DETACH: rebuild each cascade child of a table you&apos;re about to drop with the
--    FK temporarily demoted to NO ACTION (deepest children first).
-- 2. REBUILD: rebuild the tables that are actually changing.
-- 3. REATTACH: rebuild those children again with ON DELETE CASCADE restored,
--    now that their parents exist.

-- Do not forget to turn this off at the end of the migration – otherwise you will run into an error
-- PRAGMA defer_foreign_keys = OFF;

PRAGMA defer_foreign_keys = ON;

-- DETACH child → NO ACTION
CREATE TABLE __detach_child (id integer PRIMARY KEY, parent_id integer REFERENCES parent(id));
INSERT INTO __detach_child SELECT id, parent_id FROM child;
DROP TABLE child;
ALTER TABLE __detach_child RENAME TO child;

-- REBUILD parent (child is NO ACTION now, so this DROP cascades nothing)
CREATE TABLE __new_parent (id integer PRIMARY KEY, label text);
INSERT INTO __new_parent (id) SELECT id FROM parent;
DROP TABLE parent;
ALTER TABLE __new_parent RENAME TO parent;

-- REATTACH child → CASCADE
CREATE TABLE __new_child (id integer PRIMARY KEY, parent_id integer REFERENCES parent(id) ON DELETE CASCADE);
INSERT INTO __new_child SELECT id, parent_id FROM child;
DROP TABLE child;
ALTER TABLE __new_child RENAME TO child;

PRAGMA defer_foreign_keys = OFF;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a universal solution that can be automated. Of course, you can craft a more eloquent version tuned for each scenario.&lt;/p&gt;
&lt;h2&gt;Takeaways&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;PRAGMA defer_foreign_keys&lt;/code&gt; doesn&apos;t disable &lt;code&gt;ON DELETE CASCADE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Right now you can&apos;t rely on migrations autogenerated by Drizzle ORM&lt;/li&gt;
&lt;li&gt;If you use FK constraints, you need to rewrite autogenerated migrations in a way that manually removes constraints during migration (Detach -&amp;gt; Rebuild -&amp;gt; Reattach)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I hope that in the future this issue will be resolved, maybe with proper D1 support from Drizzle, the introduction of a way to postpone constraints in D1, or at least a proper notice in the D1 docs.&lt;/p&gt;
&lt;p&gt;Currently I resolve this issue by either relying on programmatic deletes, or by autogenerating proper migrations based on Drizzle data like journals and migrations.&lt;/p&gt;
&lt;p&gt;But I dislike both solutions :(&lt;/p&gt;
</content:encoded></item><item><title>Use light mode when coding in the sun</title><link>https://www.brachkow.com/notes/coding-in-sun/</link><guid isPermaLink="false">hDTISoL5eDgSRxfdT5g3OWsqg0r2CdsyH</guid><description>Light mode can reduce reflections and improve contrast when coding outdoors in bright sunlight</description><pubDate>Sun, 10 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { Image } from &apos;astro:assets&apos;;
import solarizedExample from &apos;../../assets/solarized-example.png&apos;;
import gruvboxExample from &apos;../../assets/gruvbox-example.png&apos;;
import solarizedExampleDark from &apos;../../assets/solarized-example-dark.png&apos;;
import gruvboxExampleDark from &apos;../../assets/gruvbox-example-dark.png&apos;;
import macExampleDark from &apos;../../assets/mac-darkmode-sun.png&apos;;
import macExampleLight from &apos;../../assets/mac-lightmode-sun.png&apos;;&lt;/p&gt;
&lt;p&gt;With the first signs of summer it might be tempting to sit with a laptop on a blanket somewhere outside...&lt;/p&gt;
&lt;p&gt;...just to see a reflection of yourself, a ton of dust and that stain from the oily keyboard!&lt;/p&gt;
&lt;h2&gt;Just use light mode!&lt;/h2&gt;
&lt;p&gt;But there is a fix – use light mode outside:&lt;/p&gt;
&lt;div class=&quot;ImageGroup&quot;&gt;
  &lt;img src=&quot;{macExampleDark}&quot; alt=&quot;VS Code default colors&quot;&gt;
  &lt;img src=&quot;{macExampleLight}&quot; alt=&quot;Gruvbox light&quot;&gt;
&lt;/div&gt;

&lt;p&gt;As you can see with a light theme:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;my reflection is way less contrast&lt;/li&gt;
&lt;li&gt;black-on-white letters aren&apos;t muddled by dust and reflections&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Don&apos;t be scared of light theme!&lt;/h2&gt;
&lt;p&gt;Contrary to the meme perception of light themes as highly contrasty, very white, eye-burning things, the most popular light themes are not white at all.&lt;/p&gt;
&lt;p&gt;I recommend starting with either Solarized or Gruvbox – both are papyrus yellow in light mode. They are widely available for every editor – just search for them!&lt;/p&gt;
&lt;div class=&quot;ImageGroup&quot;&gt;
  &lt;img src=&quot;{gruvboxExample}&quot; alt=&quot;Gruvbox&quot;&gt;
  &lt;img src=&quot;{solarizedExample}&quot; alt=&quot;Solarized&quot;&gt;
&lt;/div&gt;

&lt;p&gt;They also have a matching dark variant to keep highlighting consistent between light and dark mode:&lt;/p&gt;
&lt;div class=&quot;ImageGroup&quot;&gt;
  &lt;img src=&quot;{gruvboxExampleDark}&quot; alt=&quot;Gruvbox dark&quot;&gt;
  &lt;img src=&quot;{solarizedExampleDark}&quot; alt=&quot;Solarized dark&quot;&gt;
&lt;/div&gt;</content:encoded></item><item><title>Sleep, brainfog and managing energy</title><link>https://www.brachkow.com/notes/sleep-brainfog-and-managing-energy/</link><guid isPermaLink="false">hsh2FISQgBGXWCEg68CPwuTRI4psQk77S</guid><description>Observations on sleep, caffeine and melatonin that helped me go from constant brain fog to stable and predictable energy.</description><pubDate>Sun, 15 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;For the past few years, I&apos;ve paid a lot of attention to what affects my energy and productivity, and made a number of observations that improved my life.&lt;/p&gt;
&lt;p&gt;To be clear: I went from constantly fighting brain fog to having very stable and predictable energy. And even when I feel fatigued, it doesn&apos;t frustrate me.&lt;/p&gt;
&lt;p&gt;Everyone is different, but I believe this post will be a good starting point for people who want to stop suffering from constant brain fog, loss of concentration, or fatigue.&lt;/p&gt;
&lt;h2&gt;Sleep&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Learn which periods of the day are most productive or unproductive. Try to follow these periods instead of fighting them&lt;/li&gt;
&lt;li&gt;Learn how much sleep you need to easily get up&lt;/li&gt;
&lt;li&gt;Sunlight is very important for an easy wake-up&lt;/li&gt;
&lt;li&gt;Maintaining a consistent sleep schedule is important&lt;/li&gt;
&lt;li&gt;Keep in mind that &quot;sleep debt&quot; is a real thing. For example, I need 7–9 hours of sleep. If I go to bed significantly late, 12 hours of sleep will be needed in the following days. Until that happens, I will feel fatigued&lt;/li&gt;
&lt;li&gt;Decrease the number of random factors that may affect your sleep. A face mask or good curtains can help with sudden sunlight, earplugs will help with sudden noise, and melatonin will help if you end up going to bed late&lt;/li&gt;
&lt;li&gt;Do not oversleep. Sleeping slightly more is fine, but sleeping a lot more will result in fatigue and ruin your schedule, leading to even more fatigue&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Caffeine&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Drinking Red Bull will give you a productivity boost, but it will also affect the time it takes you to fall asleep. In the long run, it will also lead to gastrointestinal and nervous system problems&lt;/li&gt;
&lt;li&gt;Coffee is milder but should be consumed wisely — don&apos;t drink it too late for a boost of evening productivity, as this will decrease sleep quality and duration, which will affect productivity for the next few days&lt;/li&gt;
&lt;li&gt;Coffee and chocolate can give a significant energy boost in the morning&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Melatonin&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Melatonin is a great way to maintain a consistent sleep schedule&lt;/li&gt;
&lt;li&gt;Find a good dosage of melatonin for yourself. I recommend starting with 1 mg. Then:&lt;ul&gt;
&lt;li&gt;Increase if you still have trouble falling asleep&lt;/li&gt;
&lt;li&gt;Decrease if your dreams feel like a constant TV show&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;When buying melatonin, buy pure melatonin, not a &quot;sleep mix&quot; like melatonin + magnesium. These tend to work more like a placebo&lt;/li&gt;
&lt;li&gt;Take melatonin 30 minutes before going to bed and only do something boring afterwards — chores, brushing teeth/skincare, reading a book, etc.&lt;/li&gt;
&lt;li&gt;Do not watch TikTok or YouTube during that time — you will overstimulate yourself and the melatonin won&apos;t work&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Other factors that affect energy&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Fatigue and brain fog can be caused by hunger&lt;/li&gt;
&lt;li&gt;Vitamins affect energy levels, especially during dark autumn and winter months&lt;/li&gt;
&lt;li&gt;Sometimes brain fog can occur out of nowhere, and it usually correlates with rain or other drastic weather changes. I&apos;m usually suspicious of such &quot;astrology&quot;, &lt;a href=&quot;https://en.wikipedia.org/wiki/Weather_pains&quot;&gt;but Wikipedia says it might be real&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Keep track of activities that drain your energy to plan accordingly. For me, being out for a long time causes brain fog after I return home. So when I have plans to meet someone, I don&apos;t plan anything after&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Brain Fog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;It&apos;s more productive not to work during brain fog&lt;/li&gt;
&lt;li&gt;If you don&apos;t want to waste time during brain fog, do some non-creative things like chores&lt;/li&gt;
&lt;li&gt;Going for a walk may be a good idea too&lt;/li&gt;
&lt;li&gt;It&apos;s very important to realize when you have brain fog and accept it. Fighting it will only make you more tired and frustrated.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;😴&lt;/p&gt;
</content:encoded></item><item><title>Mise solved my tool version management fatigue</title><link>https://www.brachkow.com/notes/mise-solved-my-tool-version-management-fatigue/</link><guid isPermaLink="false">hKHgPFuz6NLx7muWVS5CJ6Ip2bjMKpi9U</guid><description>Why mise is a tool version manager done right — one config for Node, pnpm and everything else, working locally, in CI and in Docker.</description><pubDate>Sat, 14 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Tool version management seemed like an unresolved issue to me.&lt;/p&gt;
&lt;p&gt;My use case is simple — I need to ensure that for each project Node and pnpm versions are always the same on my and other developers&apos; machines.&lt;/p&gt;
&lt;p&gt;There are a lot of tools to do so: &lt;a href=&quot;https://github.com/nvm-sh/nvm&quot;&gt;nvm&lt;/a&gt;, &lt;a href=&quot;https://volta.sh/&quot;&gt;volta&lt;/a&gt;, &lt;a href=&quot;https://asdf-vm.com/&quot;&gt;asdf&lt;/a&gt;. Also, there is corepack, which is both &lt;a href=&quot;https://nodejs.org/dist/latest/docs/api/corepack.html&quot;&gt;deprecated&lt;/a&gt; and &lt;a href=&quot;https://github.com/nodejs/corepack&quot;&gt;not&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;All of them can do the job, but with a lot of asterisks: some use strange formats, some can&apos;t install pnpm, some are just hard to set up.&lt;/p&gt;
&lt;h2&gt;Finally, we have a good one — mise&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://mise.jdx.dev/walkthrough.html&quot;&gt;Mise&lt;/a&gt; is a tool version manager done right:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://mise.jdx.dev/registry.html&quot;&gt;It isn&apos;t tied to one language ecosystem&lt;/a&gt;, e.g., it can handle pnpm, Node and Ruby&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mise.jdx.dev/configuration.html#mise-toml&quot;&gt;It has its own version format&lt;/a&gt;, and &lt;a href=&quot;https://mise.jdx.dev/lang/node.html#nvmrc-and-node-version-support&quot;&gt;supports standard version formats&lt;/a&gt; — chances are you can add it to an existing project, without bothering your colleagues with migrating&lt;/li&gt;
&lt;li&gt;It enables the needed tool version as you navigate the filesystem without doing any bash-gymnastics&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Apart from managing versions on your computer, it&apos;s helpful in a few other use cases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/jdx/mise-action&quot;&gt;You can use it to install tools in GitHub Actions&lt;/a&gt;. It is way simpler than googling a setup action for each tool, and then thinking how to pass version numbers from repo to action&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mise.jdx.dev/mise-cookbook/docker.html&quot;&gt;You can use it to install tools in Docker images&lt;/a&gt;. I don&apos;t think it gives the best build times, but you will have a single source of truth and a simple way to install all build tools you need&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mise-versions.jdx.dev/&quot;&gt;You can use it to download prebuilt binaries that may not be available via your system package managers&lt;/a&gt;. For example, I was able to install an up-to-date Neovim version on Ubuntu 24, which I would otherwise need to build myself.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;P.S. The Mise author also has quite a nice secrets manager — &lt;a href=&quot;https://fnox.jdx.dev/&quot;&gt;fnox&lt;/a&gt;. So you don&apos;t store your API keys as plaintext in &lt;code&gt;$HOME&lt;/code&gt;, you know…&lt;/p&gt;
&lt;p&gt;P.P.S. When writing this post, I found that &lt;code&gt;mise&lt;/code&gt; can also &lt;a href=&quot;https://mise.jdx.dev/environments/&quot;&gt;do environment variable management&lt;/a&gt; and can act as a &lt;a href=&quot;https://mise.jdx.dev/tasks/&quot;&gt;task runner&lt;/a&gt;. Haven&apos;t tried any of these features yet.&lt;/p&gt;
</content:encoded></item><item><title>Use Zod to validate and document your environment variables</title><link>https://www.brachkow.com/notes/use-zod-to-validate-and-document-your-enviroment-variables/</link><guid isPermaLink="false">h1zBwyXXjcSuv8knuNsG1G2ShI8WMH6Z5</guid><description>Validate environment variables with Zod to get inferred types, early crashes on missing variables and documentation that never goes stale.</description><pubDate>Sun, 08 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;One of the most annoying errors in TypeScript is when your app crashes because you asserted the type of an environment variable, but forgot to set it.&lt;/p&gt;
&lt;p&gt;This is an especially frequent issue with various serverless tools, which tend to have a lot of environments. Each of these environments comes with its own secrets that need to be typed manually into the UI, which is easy to forget.&lt;/p&gt;
&lt;p&gt;Tired of such crashes, I decided to always validate environment variables, and found Zod to be a great solution, not only to this, but also to many other validation problems:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Types are inferred from the validation result, not asserted by hand. Forgetting to set a variable will result in an error being thrown.&lt;/li&gt;
&lt;li&gt;This validation serves as documentation for environment variable usage. This type of documentation is much more useful than what&apos;s written in &lt;code&gt;.env.example&lt;/code&gt; or &lt;code&gt;readme.md&lt;/code&gt;. Since you rely on the &lt;code&gt;zod.parse&lt;/code&gt; output, you won&apos;t forget to update it.&lt;/li&gt;
&lt;li&gt;Zod can perform transforms, which is useful because environment variables are always strings. For example, transform &lt;code&gt;&apos;1&apos;&lt;/code&gt; to &lt;code&gt;1&lt;/code&gt;, or &lt;code&gt;one,two&lt;/code&gt; to &lt;code&gt;[&apos;one&apos;,&apos;two&apos;]&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here is an example of how to use Zod to validate environment variables:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import &apos;dotenv/config&apos;;
import { z } from &apos;zod&apos;;

const envSchema = z.object({
  BOT_TOKEN: z.string().min(1, &apos;BOT_TOKEN is not set&apos;),
  OPENROUTER_API_KEY: z.string().min(1, &apos;OPENROUTER_API_KEY is not set&apos;),
  WEBHOOK_URL: z.string().optional(),
});

export type EnvConfig = z.infer&amp;lt;typeof envSchema&amp;gt;;

export const env = envSchema.parse(process.env);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I describe the shape of &lt;code&gt;process.env&lt;/code&gt; as a Zod schema, and then use this Zod schema to parse, and then export valid and typed env values, or throw if something is wrong.&lt;/p&gt;
&lt;p&gt;Run this code as early as possible inside your app, or execute it as part of your CI or build script.&lt;/p&gt;
</content:encoded></item><item><title>Why I use Vue</title><link>https://www.brachkow.com/notes/what-i-like-in-vue/</link><guid isPermaLink="false">h2fvlePozCDFVeSR8e8wKimHLFcQh0Ppt</guid><description>I use Vue as my main framework since 2019. In this post I will explain the reasons behind my choice.</description><pubDate>Wed, 29 Oct 2025 19:03:45 GMT</pubDate><content:encoded>&lt;p&gt;I have used Vue as my main framework since 2019. While Vue is &lt;a href=&quot;https://npmtrends.com/@angular/cli-vs-react-vs-vue&quot;&gt;one of the &quot;big three&quot; by adoption, just alongside React and Angular&lt;/a&gt;, people are still usually surprised when I tell them. In this post I will explain the reasons behind my choice.&lt;/p&gt;
&lt;p&gt;To put it shortly, I like Vue because it has an emphasis on DX, doesn&apos;t overcomplicate things and has everything you need included (and yes, &lt;a href=&quot;https://krausest.github.io/js-framework-benchmark/current.html#eyJmcmFtZXdvcmtzIjpbImtleWVkL2FuZ3VsYXItY2YiLCJrZXllZC9hbmd1bGFyLWNmLW5ldy1ub3pvbmUiLCJrZXllZC9hbmd1bGFyLWNmLW5vem9uZSIsImtleWVkL2FuZ3VsYXItY2Ytc2lnbmFscyIsImtleWVkL2FuZ3VsYXItY2Ytc2lnbmFscy1ub3pvbmUiLCJrZXllZC9hbmd1bGFyLW5nZm9yIiwia2V5ZWQvcmVhY3QtY2xhc3NlcyIsImtleWVkL3JlYWN0LWNvbXBpbGVyLWhvb2tzIiwia2V5ZWQvcmVhY3QtaG9va3MiLCJrZXllZC9yZWFjdC1ob29rcy11c2UtdHJhbnNpdGlvbiIsImtleWVkL3JlYWN0LWtyLW9ic2VydmFibGUiLCJrZXllZC9yZWFjdC1tbHluIiwia2V5ZWQvcmVhY3QtbW9iWCIsImtleWVkL3JlYWN0LW5hdGl2ZS1vbnl4Iiwia2V5ZWQvcmVhY3QtcmVkdXgiLCJrZXllZC9yZWFjdC1yZWR1eC1ob29rcyIsImtleWVkL3JlYWN0LXJlZHV4LWhvb2tzLWltbXV0YWJsZSIsImtleWVkL3JlYWN0LXJlZHV4LXJlbWF0Y2giLCJrZXllZC9yZWFjdC1yeGpzIiwia2V5ZWQvcmVhY3QtdGFnZ2VkLXN0YXRlIiwia2V5ZWQvcmVhY3QtdHJhY2tlZCIsImtleWVkL3JlYWN0LXp1c3RhbmQiLCJrZXllZC9zdmVsdGUiLCJrZXllZC9zdmVsdGUtY2xhc3NpYyIsImtleWVkL3Z1ZSIsImtleWVkL3Z1ZS1qc3giLCJrZXllZC92dWUtanN4LXZhcG9yIiwia2V5ZWQvdnVlLXBpbmlhIiwia2V5ZWQvdnVlLXZhcG9yIiwia2V5ZWQvdnVlcngtanN4Il0sImJlbmNobWFya3MiOlsiMDFfcnVuMWsiLCIwMl9yZXBsYWNlMWsiLCIwM191cGRhdGUxMHRoMWtfeDE2IiwiMDRfc2VsZWN0MWsiLCIwNV9zd2FwMWsiLCIwNl9yZW1vdmUtb25lLTFrIiwiMDdfY3JlYXRlMTBrIiwiMDhfY3JlYXRlMWstYWZ0ZXIxa194MiIsIjA5X2NsZWFyMWtfeDgiLCIyMV9yZWFkeS1tZW1vcnkiLCIyMl9ydW4tbWVtb3J5IiwiMjNfdXBkYXRlNS1tZW1vcnkiLCIyNV9ydW4tY2xlYXItbWVtb3J5IiwiMjZfcnVuLTEway1tZW1vcnkiLCI0MV9zaXplLXVuY29tcHJlc3NlZCIsIjQyX3NpemUtY29tcHJlc3NlZCIsIjQzX2ZpcnN0LXBhaW50Il0sImRpc3BsYXlNb2RlIjoxfQ==&quot;&gt;it is still very fast&lt;/a&gt;)!&lt;/p&gt;
&lt;p&gt;Let&apos;s see some examples:&lt;/p&gt;
&lt;h3&gt;You write js in &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;, css in &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; and html in &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Vue components are written as &lt;a href=&quot;https://vuejs.org/guide/scaling-up/sfc.html&quot;&gt;Single File Components&lt;/a&gt;. You write CSS in &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt;, html with template engine in &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; and javascript in &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;. It feels like writing a simple html file — a cornerstone of the web. Almost anyone in your team will understand how to make edits in such files.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vue&quot;&gt;&amp;lt;script setup&amp;gt;
  import { ref } from &apos;vue&apos;;
  const count = ref(0);
&amp;lt;/script&amp;gt;

&amp;lt;template&amp;gt;
  &amp;lt;button @click=&quot;count++&quot;&amp;gt;Count: {{ count }}&amp;lt;/button&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;style scoped&amp;gt;
  button {
    color: blue;
  }
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Non JSX templates&lt;/h3&gt;
&lt;p&gt;Templates are written as html with attributes like &lt;code&gt;v-for&lt;/code&gt;, &lt;code&gt;v-if&lt;/code&gt;, &lt;code&gt;v-else&lt;/code&gt;. You don&apos;t mix JS with HTML, and as a result get more readable code than JSX (ask non-js people!).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vue&quot;&gt;&amp;lt;template&amp;gt;
  &amp;lt;div v-if=&quot;user&quot;&amp;gt;
    &amp;lt;h1&amp;gt;Hello, {{ user.name }}!&amp;lt;/h1&amp;gt;
    &amp;lt;ul&amp;gt;
      &amp;lt;li v-for=&quot;item in items&quot; :key=&quot;item.id&quot;&amp;gt;
        {{ item.title }}
      &amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;p v-else&amp;gt;Please log in&amp;lt;/p&amp;gt;
&amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;You write CSS as CSS&lt;/h3&gt;
&lt;p&gt;You can write regular CSS and just add &lt;code&gt;scoped&lt;/code&gt; attribute to &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; to encapsulate it. No need for CSS modules, CSS-in-JS and other stuff. Just write CSS and everything works.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vue&quot;&gt;&amp;lt;style scoped&amp;gt;
  .card {
    padding: 1rem;
    border-radius: 8px;
  }
  /* Won&apos;t affect other components */
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Reactivity tools are reasonably named and framework has performance optimizations built-in&lt;/h3&gt;
&lt;p&gt;So you mark values as reactive with &lt;code&gt;ref&lt;/code&gt;, compute values with &lt;code&gt;computed&lt;/code&gt; and watch values with &lt;code&gt;watch&lt;/code&gt;. Usually you don&apos;t need to think about optimizing these.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// `ref` is the way to mark value as reactive, like useState
const count = ref(0);

// `computed` is value that was computed. It won&apos;t be recomputed if all dependent values are the same. Same as useMemo in react
const doubled = computed(() =&amp;gt; count.value * 2);

// `watch` watches value, just like useEffect in react
watch(count, (newValue) =&amp;gt; {
  console.log(`Count changed to ${newValue}`);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Components communicate via events&lt;/h3&gt;
&lt;p&gt;Components communicate with each other via events that go up. This results in much cleaner code without a need for callback prop drilling / overreliance on stores or resolving shadowing issues.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vue&quot;&gt;&amp;lt;!-- Child.vue --&amp;gt;
&amp;lt;button @click=&quot;$emit(&apos;update&apos;, newValue)&quot;&amp;gt;Update&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-vue&quot;&gt;&amp;lt;!-- Parent.vue --&amp;gt;
&amp;lt;Child @update=&quot;handleUpdate&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Vue ecosystem is great&lt;/h3&gt;
&lt;p&gt;For most cases you can find exactly one great solution made by someone close to the Vue or Nuxt teams. A few examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You may know &lt;code&gt;vite&lt;/code&gt; and &lt;code&gt;vitest&lt;/code&gt; that started as part of the Vue ecosystem, and now they are integral (and the least annoying) parts of the frontend toolchain.&lt;/li&gt;
&lt;li&gt;In Vue&apos;s default state manager &lt;a href=&quot;https://pinia.vuejs.org/introduction.html&quot;&gt;pinia&lt;/a&gt; you can update state like &lt;code&gt;store.count++&lt;/code&gt;. In redux you would need to have at least a PhD in computer science to do that.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Attributes passed to component&lt;/h3&gt;
&lt;p&gt;Anything that is not a declared prop is passed to the component as an attribute. The same works for classes, which will be joined in the expected way. Why this is not the default behaviour in other frameworks is a mystery to me.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vue&quot;&gt;&amp;lt;!-- MyButton.vue - only declares &apos;label&apos; prop --&amp;gt;
&amp;lt;template&amp;gt;
  &amp;lt;button class=&quot;btn&quot;&amp;gt;{{ label }}&amp;lt;/button&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;!-- Using the component --&amp;gt;
&amp;lt;MyButton label=&quot;Click&quot; class=&quot;primary large&quot; data-test=&quot;btn&quot; /&amp;gt;
&amp;lt;!-- Renders: &amp;lt;button class=&quot;btn primary large&quot; data-test=&quot;btn&quot;&amp;gt;Click&amp;lt;/button&amp;gt; --&amp;gt;
&amp;lt;!-- Classes are automatically joined! --&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;To sum up&lt;/h2&gt;
&lt;p&gt;I think even with its high adoption Vue is still a very undervalued framework.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It has great DX and it&apos;s accessible for programmers of all backgrounds&lt;/li&gt;
&lt;li&gt;It has great performance, without a need to constantly optimize it&lt;/li&gt;
&lt;li&gt;It&apos;s widely adopted so even quite a big company can find enough people who have hands-on experience with Vue&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, don&apos;t be afraid to try Vue!&lt;/p&gt;
</content:encoded></item><item><title>Guessing user location with Cloudflare Workers</title><link>https://www.brachkow.com/notes/guessing-user-location-with-cloudflare-workers/</link><guid isPermaLink="false">hEhU3aJb9RNWK2XFrAGAEWvA69aJRtxzI</guid><description>How to get a user&apos;s imprecise location from Cloudflare Workers request metadata, without asking for Geolocation API permission.</description><pubDate>Sun, 26 Jan 2025 13:47:51 GMT</pubDate><content:encoded>&lt;p&gt;Recently I was building a landing page that needed to display some information relevant to the user&apos;s location.&lt;/p&gt;
&lt;p&gt;Using the browser&apos;s &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API&quot;&gt;Geolocation API&lt;/a&gt; is an obvious solution, but who wants to give precise geolocation to a random website on the internet. Also, I believe that the mere fact of such a request will make customers uncomfortable.&lt;/p&gt;
&lt;p&gt;Then I found out that Cloudflare has access to the imprecise location of any request going to the Cloudflare worker, and it&apos;s accessible to read.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// this code lives in your cloudflare page worker function
// for example functions/api/location.ts
// which is your example.com/api/location

import type { PagesFunction } from &apos;@cloudflare/workers-types&apos;;

export const onRequest: PagesFunction = async (context) =&amp;gt; {
  const { latitude, longitude, city, country } = context.request.cf || {};

  const coordinates = {
    latitude,
    longitude,
    city,
    country,
  };

  return new Response(JSON.stringify(coordinates), {
    headers: { &apos;Content-Type&apos;: &apos;application/json&apos; },
  });
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are not using Cloudflare Workers, there are services that match IPs with locations.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developers.cloudflare.com/pages/functions/typescript/&quot;&gt;Using typescript in page worker functions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Sharing JavaScript code without headache</title><link>https://www.brachkow.com/notes/sharing-javascript-code-without-headache/</link><guid isPermaLink="false">hvBgvGWaGxdcBa3tRYSVl8AkRuVijlTDJ</guid><description>How to share code without the burden of private npm packages</description><pubDate>Mon, 07 Oct 2024 16:36:20 GMT</pubDate><content:encoded>&lt;p&gt;Sometimes you have multiple apps that share a lot of common code.&lt;/p&gt;
&lt;p&gt;For example, you have an SPA written in Vue and a Blog running on Eleventy, and you want to reuse your Tailwind design system between them.&lt;/p&gt;
&lt;p&gt;The obvious solution is to create a package with common code and publish it as a private NPM package. This usually leads to frustration: publishing packages is not a very straightforward or failure-tolerant process, which is completely reasonable for public packages, but mostly a waste of time for private packages and small-to-medium teams.&lt;/p&gt;
&lt;p&gt;I see this approach as overkill for most projects — there are much simpler alternatives:&lt;/p&gt;
&lt;h2&gt;Use Monorepo&lt;/h2&gt;
&lt;p&gt;The first one is to store code in a monorepo. Using the &lt;a href=&quot;https://pnpm.io/pnpm-workspace_yaml&quot;&gt;workspace&lt;/a&gt; feature, you will be able to install any package inside the monorepo.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.
├── .git
├── apps/
│   ├── spa/
│   │   ├── …
│   │   └── package.json
│   └── blog/
│       ├── …
│       └── package.json
├── packages/
│   └── some-package/
│       ├── …
│       └── package.json
├── pnpm-workspace.yaml
└── package.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then you add a special file called &lt;code&gt;pnpm-workspace.yaml&lt;/code&gt; that allows you to install folders as packages.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;packages:
  - &apos;packages/*&apos;
  - &apos;apps/*&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then you can use them like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;name&quot;: &quot;spa&quot;,
  &quot;dependencies&quot;: {
    &quot;some-package&quot;: &quot;workspace:*&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Due to the fact that code is stored in one repo, you don&apos;t need to think about versioning — code revisions are coupled via commits.&lt;/p&gt;
&lt;h2&gt;Installing Packages from GitHub URLs&lt;/h2&gt;
&lt;p&gt;If you don&apos;t want to use a monorepo, you can install packages via package GitHub repo URLs.&lt;/p&gt;
&lt;p&gt;This way is almost the same as working with standard npm packages, but with one twist — you use a git repo as a package. That means you have a very forgiving publishing flow where you can revert any of your actions.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;pnpm i https://github.com/someorg/some-package#1.2.3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you see — tags can be used to achieve reproducible builds.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;&quot;some-package&quot;: &quot;github:someorg/some-package#1.2.3&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you need to build some files — you can hook up a build script to the &lt;code&gt;postinstall&lt;/code&gt; script in &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;name&quot;: &quot;some-package&quot;,
  &quot;main&quot;: &quot;dist/main.css&quot;,
  &quot;scripts&quot;: {
    &quot;postinstall&quot;: &quot;pnpm i; postcss -i src/main.css -o dist/main.css&quot;
  },
  &quot;dependencies&quot;: {
    &quot;postcss&quot;: &quot;8.4.38&quot; // PostCSS is declared in dependencies, not devDependencies because we need it to be accessible from the postinstall script
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just keep in mind that you must move &lt;strong&gt;build dependencies&lt;/strong&gt; to &lt;code&gt;dependencies&lt;/code&gt; instead of &lt;code&gt;devDependencies&lt;/code&gt;. As this is a private package, this is not an issue, especially when you are using pnpm.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;As your project grows, you might hit the ceiling with this approach and you might need to switch to standard private packages, but your project needs to grow to this point and before that, you can save a lot of time!&lt;/p&gt;
&lt;p&gt;Monorepo, on the other hand, is future-proof. But you need to have a monorepo.&lt;/p&gt;
&lt;p&gt;Usually, I use a monorepo on all code I write by myself. When I fork someone&apos;s code, I install it via URL to keep the same project structure as the original repo.&lt;/p&gt;
</content:encoded></item><item><title>How to preview files in browser</title><link>https://www.brachkow.com/notes/preview-files-in-browser/</link><guid isPermaLink="false">hQZQalK0qW98gUk01JH5r28W70Qh9wwXu</guid><description>Cheatsheet for previewing different file types (images, CSV, PDF, Office docs) directly in the browser, with implementation tips and limitations</description><pubDate>Wed, 10 Apr 2024 16:36:20 GMT</pubDate><content:encoded>&lt;p&gt;Recently I was building an in-browser document preview. It turned out to be a quirky task because browsers usually assume that users handle opening files on their own machine.&lt;/p&gt;
&lt;p&gt;This is what I found out:&lt;/p&gt;
&lt;h2&gt;Images&lt;/h2&gt;
&lt;p&gt;That one is obvious — just use the &lt;code&gt;&amp;lt;img/&amp;gt;&lt;/code&gt; tag&lt;/p&gt;
&lt;h2&gt;CSV&lt;/h2&gt;
&lt;p&gt;It&apos;s trivial to parse CSV in the browser — you just split the file into rows by &lt;code&gt;\n&lt;/code&gt; and then split them into columns by a separator (which can be &lt;code&gt;,&lt;/code&gt;, &lt;code&gt;;&lt;/code&gt;, &lt;code&gt;space&lt;/code&gt;, etc.).&lt;/p&gt;
&lt;p&gt;Since separators introduce some caveats, I recommend using readymade solutions like &lt;a href=&quot;https://www.npmjs.com/package/papaparse&quot;&gt;papaparse&apos;s&lt;/a&gt; &lt;code&gt;Papa.parse(csvAsString)&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;PDF&lt;/h2&gt;
&lt;p&gt;Browsers can preview PDFs with &lt;code&gt;&amp;lt;embed src=&quot;example.pdf&quot;/&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Sadly, mobile Safari can&apos;t, so if this is crucial for you, you might need to use one of the many libraries for that. In my experience, such libraries have tons of glitches — keep that in mind.&lt;/p&gt;
&lt;h2&gt;Office documents&lt;/h2&gt;
&lt;p&gt;For office documents like ppt, pptx, doc, docx, xls, and xlsx, there are two types of solutions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You can use some quirky library made by unknown geniuses (&lt;a href=&quot;https://501351981.github.io/vue-office/examples/dist/#/docx&quot;&gt;example&lt;/a&gt;), but since proprietary formats are complicated, results may vary&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can use an iframe from &quot;format owners&quot; like Microsoft and Google. There is a nice &lt;a href=&quot;https://gist.github.com/tzmartin/1cf85dc3d975f94cfddc04bc0dd399be&quot;&gt;gist about these on GitHub&lt;/a&gt;. In my experience, Google&apos;s iframe is very unreliable, so don&apos;t be tempted to use it because of the huge variety of formats — just go with Microsoft&apos;s one. Of course, if you go with this option, you must keep in mind the privacy implications of sending documents to someone&apos;s API.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Caveats of using macOS with an external monitor</title><link>https://www.brachkow.com/notes/macos-monitor/</link><guid isPermaLink="false">hSoLidchdM2tZAkjipAqhtgUUsbjiHksV</guid><description>Things I learned about using external monitors with macOS: how to fix blurry LG monitors, control your monitor without reaching for buttons, and why your cat can no longer wake up your Mac.</description><pubDate>Thu, 04 Jan 2024 16:36:20 GMT</pubDate><content:encoded>&lt;h2&gt;Fixing LG Monitors&lt;/h2&gt;
&lt;p&gt;LG produces good monitors, but they pack them with &quot;Smart Features&quot; that make picture quality worse.&lt;/p&gt;
&lt;p&gt;Go to the monitor menu and:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Disable &lt;strong&gt;«Super Resolution»&lt;/strong&gt; to prevent everything from becoming blurry.&lt;/li&gt;
&lt;li&gt;Disable &lt;strong&gt;«Smart Energy Saving»&lt;/strong&gt; to prevent the screen from self-managing brightness.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Controlling Monitor from macOS&lt;/h2&gt;
&lt;p&gt;By default, you can&apos;t control the monitor from your Mac; you will need to reach your monitor by hand.
 This is especially annoying with sound. There are apps for controlling monitors from the OS instead of the menu.&lt;/p&gt;
&lt;p&gt;There is a free app called &lt;a href=&quot;https://github.com/MonitorControl/MonitorControl&quot;&gt;MonitorControl&lt;/a&gt; that allows you to control brightness and sound the same way you do it on a MacBook.&lt;/p&gt;
&lt;h2&gt;Waking up Mac&lt;/h2&gt;
&lt;p&gt;If you have ever used Windows you know: if your cat jumps on the computer&apos;s keyboard — it will wake up.&lt;/p&gt;
&lt;p&gt;On macOS — forget about that! To wake a MacBook when using an external monitor you need to swipe on the Magic Trackpad or shake the mouse.&lt;/p&gt;
</content:encoded></item><item><title>How to make Firefox sane and private</title><link>https://www.brachkow.com/notes/firefox/</link><guid isPermaLink="false">h8pCkNXO1BoYYxkJuqFJaxYV9aShZChvO</guid><description>Essential Firefox privacy tweaks, recommended security extensions</description><pubDate>Fri, 06 Oct 2023 16:36:20 GMT</pubDate><content:encoded>&lt;p&gt;Nowadays Firefox is the only good browser.&lt;/p&gt;
&lt;p&gt;Apple ruined Safari by moving extensions into AppStore&apos;s walled garden, the company behind Brave &lt;a href=&quot;https://www.theverge.com/2020/6/8/21283769/brave-browser-affiliate-links-crypto-privacy-ceo-apology&quot;&gt;has a reputation for questionable decisions&lt;/a&gt;, and all other browsers are just Chrome (spyware).&lt;/p&gt;
&lt;p&gt;Being non-chrome and non-apple comes with a price — Firefox is kinda quirky, and you still have some work to do for more privacy.&lt;/p&gt;
&lt;p&gt;Here are my instructions for sane and private Firefox.&lt;/p&gt;
&lt;h2&gt;Flags&lt;/h2&gt;
&lt;p&gt;Enter &lt;code&gt;about:config&lt;/code&gt; in the search bar. Through the search, find the necessary settings and set the desired values. Here&apos;s what needs to be changed and why:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;extensions.pocket.enabled = false&lt;/code&gt; — removes Pocket integration.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dom.private-attribution.submission.enabled&lt;/code&gt; — removes ad activity reports&lt;/li&gt;
&lt;li&gt;&lt;code&gt;network.captive-portal-service.enabled = false&lt;/code&gt; and &lt;code&gt;browser.selfsupport.url = false&lt;/code&gt; — disables &lt;a href=&quot;https://spyware.neocities.org/articles/firefox.html&quot;&gt;browser usage data sending&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;apz.allow_zooming = true&lt;/code&gt; — enables &quot;pinch to zoom&quot; (like in Chrome or Safari).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;general.smoothScroll.mouseWheel.durationMaxMS = 200&lt;/code&gt; — sets scrolling speed as in Chrome.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Extensions for better privacy&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://addons.mozilla.org/ru/firefox/addon/happy-bonobo-disable-webrtc/&quot;&gt;Disable WebRTC&lt;/a&gt; — disables WebRTC by default, allowing it to be enabled when needed (like where audio-video communication is present). WebRTC can reveal your IP address even through VPN.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://addons.mozilla.org/ru/firefox/addon/uaswitcher/&quot;&gt;User-Agent Switcher&lt;/a&gt; — automatically switches the User-Agent. Turn on random mode to prevent fingerprinting.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://addons.mozilla.org/en-US/firefox/addon/clearurls/&quot;&gt;Clear URLs&lt;/a&gt; — clears URLs of trackers.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://addons.mozilla.org/en-US/firefox/addon/ublock-origin/&quot;&gt;uBlock Origin&lt;/a&gt; — an internet-era antivirus.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://addons.mozilla.org/ru/firefox/addon/privacy-badger17/&quot;&gt;Privacy Badger&lt;/a&gt; — cleans up trackers that uBlock missed.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://addons.mozilla.org/ru/firefox/addon/https-everywhere/&quot;&gt;HTTPS Everywhere&lt;/a&gt; — forces websites to use encrypted HTTPS instead of unencrypted HTTP.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://addons.mozilla.org/firefox/addon/decentraleyes/&quot;&gt;Decentraleyes&lt;/a&gt; — protects against tracking via CDNs.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://addons.mozilla.org/ru/firefox/addon/facebook-container/&quot;&gt;Facebook Container&lt;/a&gt; — isolates Meta products from other browser tabs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Separate bookmarks and passwords from the browser&lt;/h2&gt;
&lt;p&gt;Switching from one browser to another is a good time to realize that storing passwords and bookmarks in a browser is a bad idea. Upon logging in, Firefox will try to offer its synchronization service — I recommend refusing it.&lt;/p&gt;
&lt;p&gt;Next, you need to turn off password saving. To do this, go to &lt;code&gt;Preferences → Privacy and Security → Logins and Passwords&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For bookmark synchronization, I recommend &lt;a href=&quot;https://raindrop.io/&quot;&gt;Raindrop.io&lt;/a&gt;, and for passwords, &lt;a href=&quot;https://bitwarden.com/&quot;&gt;Bitwarden&lt;/a&gt;. Both are decent, free of charge, and work on all devices and browsers.&lt;/p&gt;
</content:encoded></item><item><title>Using Sublime Text as IDE</title><link>https://www.brachkow.com/notes/sublime-text/</link><guid isPermaLink="false">h2tx6XW260KzoUykbLt0dfby7scfMd8uw</guid><description>How to turn Sublime Text into a modern IDE — with LSP, linting, Copilot, while retaining the same performance and energy efficiency</description><pubDate>Wed, 06 Sep 2023 16:36:20 GMT</pubDate><content:encoded>&lt;p&gt;import ArticleImage from &apos;../../components/ArticleImage.astro&apos;;
import sublimeTextIde from &apos;../../assets/sublime-text-ide.png&apos;;&lt;/p&gt;


&lt;p&gt;I have been using Sublime Text as my primary IDE for all my development needs for more than 2 years now.&lt;/p&gt;
&lt;p&gt;Why?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;it is sustainable (just like Vim)&lt;/li&gt;
&lt;li&gt;it is very fast (just like Vim)&lt;/li&gt;
&lt;li&gt;it is energy efficient (just like Vim)&lt;/li&gt;
&lt;li&gt;it is easy to use (just like VS Code)&lt;/li&gt;
&lt;li&gt;it is easy to configure (just like VS Code)&lt;/li&gt;
&lt;li&gt;it has an active community so finding plugins is not a problem (but sadly, it&apos;s more like Vim than VS Code)&lt;/li&gt;
&lt;li&gt;it can be used for free if you can&apos;t pay for it (just like WinRAR)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If it is so good why do so many people prefer VS Code / WebStorm?&lt;/p&gt;
&lt;p&gt;Because Sublime Text is a text editor, not an IDE.&lt;/p&gt;
&lt;p&gt;Sublime Text comes from the time when you could build your cool website from any text editor (like NotePad), but times have changed since then. Some modern technologies, like TypeScript, are hard to use without IDE features.&lt;/p&gt;
&lt;p&gt;With time VSCode, which started as a text editor, incorporated all the important IDE features (at the cost of becoming a performance blackhole and battery killer).&lt;/p&gt;
&lt;p&gt;But Sublime Text chose another way — it remained a text editor that users could tailor to their needs. Unlike Vim or Emacs, Sublime Text isn&apos;t a cult, so tweaking settings is not considered cool by anyone. Because of this, most people don&apos;t even think about Sublime Text as an option.&lt;/p&gt;
&lt;p&gt;But Sublime Text is an option.&lt;/p&gt;
&lt;p&gt;In the following guide, I will tell you how to turn Sublime Text into a modern IDE — with LSP, linting, and even Copilot, while retaining the same performance and energy efficiency. And unlike Vim, it won&apos;t take forever.&lt;/p&gt;
&lt;h2&gt;Before we start&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The easiest way to navigate around the editor is the command palette. There are actually two of them: one for files can be summoned via &lt;code&gt;command + P&lt;/code&gt; and one for actions is summoned via &lt;code&gt;command + shift + P&lt;/code&gt;. Use the latter one to find the settings of your editor and plugins.&lt;/li&gt;
&lt;li&gt;You can find editor preferences at &lt;code&gt;Sublime Text / Settings / Settings&lt;/code&gt;, and plugin preferences at &lt;code&gt;Sublime Text / Settings / Package Settings&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;There is no package manager or plugin store preinstalled. To install plugins you need to install &lt;a href=&quot;https://packagecontrol.io/installation&quot;&gt;Package Control&lt;/a&gt;.
Search for plugins at &lt;a href=&quot;https://packagecontrol.io/&quot;&gt;Package Control website&lt;/a&gt; and install them via &lt;code&gt;command + shift + P&lt;/code&gt; then &lt;code&gt;Package Control: Install&lt;/code&gt; then enter the plugin name.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Bringing IDE features with LSP&lt;/h2&gt;
&lt;p&gt;To bring IDE-like features to your text editor you need to use &lt;a href=&quot;https://github.com/sublimelsp/LSP#installation&quot;&gt;LSP&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You will need to add support for languages you need separately. Here is the list of &lt;a href=&quot;https://lsp.sublimetext.io/language_servers/&quot;&gt;languages with installation instructions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Apart from autocomplete and type features, LSP also brings you a lot of other IDE features like code actions, go to definition, find all references, rename symbol, etc. You can find them in the right click menu.&lt;/p&gt;
&lt;p&gt;The annoying window with errors can be disabled by adding &lt;code&gt;&quot;log_server&quot;: false&lt;/code&gt; to LSP settings.&lt;/p&gt;
&lt;h2&gt;Linting&lt;/h2&gt;
&lt;p&gt;All linting works via &lt;a href=&quot;https://packagecontrol.io/packages/SublimeLinter&quot;&gt;SublimeLinter&lt;/a&gt; plugin. By itself it does nothing — you need to add plugins for linters to use. Just search their names in package control like &lt;code&gt;SublimeLinter-eslint&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For prettier that runs on save use &lt;a href=&quot;https://packagecontrol.io/packages/JsPrettier&quot;&gt;jsPrettier&lt;/a&gt;. To enable, add &lt;code&gt;auto_format_on_save: true&lt;/code&gt; to jsPrettier Preferences.&lt;/p&gt;
&lt;h2&gt;Other plugins you will need&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://packagecontrol.io/packages/A%20File%20Icon&quot;&gt;A File Icon&lt;/a&gt; — file icons for everything&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://packagecontrol.io/packages/SideBarEnhancements&quot;&gt;Sidebar Enhancements&lt;/a&gt; — useful actions for file explorer&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://packagecontrol.io/packages/Emmet&quot;&gt;Emmet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Git&lt;/h2&gt;
&lt;p&gt;Sublime Text shows inline diffs but has no integrated Git GUI. I recommend using &lt;a href=&quot;https://www.sublimemerge.com/&quot;&gt;Sublime Merge&lt;/a&gt; for that. You will also be able to access its features via Sublime Text&apos;s right-click menu.&lt;/p&gt;
&lt;h2&gt;Integrated Terminal&lt;/h2&gt;
&lt;p&gt;Sublime Text has no integrated terminal — you should use a dedicated terminal app for that.
If you can&apos;t live without an integrated terminal, you can use &lt;a href=&quot;https://packagecontrol.io/packages/Terminus&quot;&gt;Terminus&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;GitHub Copilot&lt;/h2&gt;
&lt;p&gt;There is an &lt;a href=&quot;https://github.com/TerminalFi/LSP-copilot&quot;&gt;LSP for Copilot&lt;/a&gt; that allows you to use AI autocomplete and code suggestions.&lt;/p&gt;
&lt;h2&gt;Painless Vim&lt;/h2&gt;
&lt;p&gt;To use Vim (or fight Vim FOMO) you can use &lt;a href=&quot;https://www.sublimetext.com/docs/vintage.html&quot;&gt;Vintage Mode&lt;/a&gt; that ships with the editor. It has a lot of Vim stuff included.&lt;/p&gt;
&lt;p&gt;I especially like that with Vintage Mode enabled your initial editing mode is still &lt;code&gt;INSERT MODE&lt;/code&gt;, so if you are not in the mood for Vim you will not be annoyed by pressing &lt;code&gt;i&lt;/code&gt; each time you open the editor.&lt;/p&gt;
&lt;h2&gt;Tips&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;control + shift + P&lt;/code&gt; to view scope name. Useful when configuring plugin scopes or building your own color schemes.&lt;/li&gt;
&lt;li&gt;There is an «Arithmetics» tool in the command palette that allows you to use mathematical expressions on selected text.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;command + K&lt;/code&gt; then &lt;code&gt;command + B&lt;/code&gt; to toggle the sidebar and make more room for the editor. Most of the time you don&apos;t need it as &lt;code&gt;command + P&lt;/code&gt; is a much faster way to open files.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;options + command + S&lt;/code&gt; to save all opened files.&lt;/li&gt;
&lt;li&gt;Click editor tabs or files with &lt;code&gt;shift&lt;/code&gt; to temporarily split the window between them. Very useful to keep reference info in front of your eyes.&lt;/li&gt;
&lt;li&gt;In menu bar go to &lt;code&gt;Selection&lt;/code&gt; and check selection shortcuts.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item></channel></rss>