Full Width [alt+shift+f] FOCUS MODE Shortcuts [alt+shift+k]
Sign Up [alt+shift+s] Log In [alt+shift+l]
20
AltTab solves a simple problem really well: it brings Windows-style Alt + Tab window switching to Mac OS. That’s it. That’s all there is to it. I think Windows way of Alt - tabbing windows is better than Mac’s. It’s a must for someone like me, who switches between using Windows and Mac OS. That small difference drove me mad when using Mac so super thankful someone wrote an app that fixes it. It’s free and open source so no excuse to not to use it.
a month ago

Comments

Improve your reading experience

Logged in users get linked directly to articles resulting in a better reading experience. Please login for free, it takes less than 1 minute.

More from Krzysztof Kowalczyk blog

Ideas for faster web dev cycle

I strongly believe that fast iteration cycle is important for productivity. In programming fast iteration means: how quickly can I go from finishing writing the code to testing it. That’s why I don’t like languages that compile slowly: slow compilation puts a hard limit on your iteration cycle. That’s a general principle. How does it translate to actions? I’m glad you asked. Simulated errors I’m writing a web app and some code paths might fail e.g. server returns an error response. When that happens I want to display an error message to the user. I want to test that but server doesn’t typically return errors. I can modify the server to return error and restart it but in a compiled language like Go it’s a whole thing. Instead I can force error condition in the code. Because web dev typically offers hot reload of code, I can modify the code to pretend the request failed, save the code, reload the app and I’m testing the error handling. To make it less ad-hoc another strategy is to have debug flags on window object e.g.: window.debug.simulateError = false Then the code will have: try { if (window.debug.simulateError) { throw Error("fetch failed"); } let rsp = await fetch(...); } That way I can toggle window.debug.simulateError in dev tools console, without changing the code. I have to repeat this code for every fetch(). More principled approach is: async function myFetch(uri, opts) { if (window.debug.simulateError) { throw new Error("featch failed"); } return await fetch(uri, opts); } To go even further, we could simulate different kinds of network errors: Response.ok is false response is 404 or 500 failed to reach the server We can change simulateError from bool to a number and have: async function myFetch(uri, opts) { let se = window.debug.simulateError; if (se === 1) { // simulate Response.ok is false return ...; } if (se === 2) { // simulate 404 response return; } if (se === 3) { // simulate network offline } return await fetch(uri, opts); } Start by show dialog Let’s say I’m working on a new dialog e.g. rename dialog. To get to that dialog I have to perform some UI action e.g. use context menu and click the Rename note menu item. Not a big deal but I’m still working on the dialog so it’s a bit annoying to repeat that UI action every time I move the button to the right, to see how it’ll look. In Svelte we do: let showingRenameDialog = $state(false); function showRenameDialog() { showingRenameDialog = true; } {#if showingRenameDialog} <RenameDialog ...></RenameDialog> {'if} To speed up dev cycle: let showingRenameDialog = $state(true); // show while we're working ont While I’m still designing and writing code for the dialog, show it by default. That way app reloads due to changing code won’t require you to manually redo UI actions to trigger the dialog. Ad-hoc test code Let’s say you’re writing a non-trivial code server code to process a request. Let’s say the request is a POST with body containing zip file with images which the server needs to unzip, resize the files, save them to a file system. You want to test it as you implement the logic but iteration cycle is slow: you write the code recompile and restart the server go through UI actions to send the request Most of the code (unpacking zip file, resizing images) can be tested without doing the request. So you isolate the function: func resizeImagesInZip(zipData []byte) error { // the code you're writing on } Now you have to trigger it easily. The simplest way is ad-hoc test code: func main() { if true { zipData := loadTestZipFileMust() resizeImagesInZip(zipData); return; } } While you’re working on the code, the server just runs the test code. When you’re done, you switch it off: func main() { if false { // leave the code so that you can re-enable it // easily in the future zipData := loadTestZipFileMust() resizeImagesInZip(zipData); return; } } Those are few tactical tips to increase dev cycles. You can come up with more such ideas by asking yourself: how can I speed iteration cycle?

a month ago 21 votes
Stage manager in Mac OS

Stage Manager in Mac OS is not a secret, but I’ve only learned about it recently. It’s off by default so you have to enable it in system settings: It’s hard to describe in words, so you’ll have to try it. And experiment a bit because I didn’t get in the first hour. It’s a certain way to manage windows for less clutter. Imagine you have a browser, an editor and a terminal i.e. 3 windows. Might be annoying to have them all shown on screen. When Stage Manager is enabled, thumbnails of windows are on the left edge of the screen and you can switch to the window by clicking the thumbnail. By default Stage Manager shows each window by itself on screen. You can group them by dragging thumbnail onto screen. For example, when I’m developing, I’m using text editor and terminal so I will group them so they are both visible on screen at the same time, but not the other apps. So far I’m enjoying using Stage Manager.

a month ago 21 votes
Zed debug setup for go server / Svelte web app

Today I figured out how to setup Zed to debug, at the same time, my go server and Svelte web app. My dev setup for working on my web app is: go server is run with -run-dev arg go server provides backend apis and proxies requests it doesn’t handle to a vite dev server that does the serving of JavaScript etc. files from my Svelte code go server in -run-dev mode automatically launches vite dev go server runs on port 9339 It’s possible to setup Zed to debug both server and frontend JavaScript code. In retrospect it’s simple, but took me a moment to figure out. I needed to create the following .zed/debug.json: // Project-local debug tasks // // For more documentation on how to configure debug tasks, // see: https://zed.dev/docs/debugger [ { "adapter": "Delve", "label": "run go server", "request": "launch", "mode": "debug", "program": ".", "cwd": "${ZED_WORKTREE_ROOT}", "args": ["-run-dev", "-no-open"], "buildFlags": [], "env": {} }, { "adapter": "JavaScript", "label": "Debug in Chrome", "type": "chrome", "request": "launch", "url": "http://localhost:9339/", "webRoot": "$ZED_WORKTREE_ROOT/src", "console": "integratedTerminal", "skipFiles": ["<node_internals>/**"] } ] It’s mostly self-exploratory. First entry tells Zed to build go program with go build . and run the resulting executable under the debugger with -run-dev -no-open args. Second entry tells to launch Chrome in debug mode with http://localhost:9339/ and that files seen by Chrome come from src/ directory i.e. if browser loads /foo.js the source file is src/foo.js. This is necessary to be able to set breakpoints in Zed and have them propagate to Chrome. This eliminates the need for terminal so I can edit and debug with just Zed and Chrome. This is a great setup. I’m impressed with Zed.

a month ago 29 votes
lazy import of JavaScript modules

When working on big JavaScript web apps, you can split the bundle in multiple chunks and import selected chunks lazily, only when needed. That makes the main bundle smaller, faster to load and parse. How to lazy import a module? let hljs = await import("highlight.js").default; is equivalent of: import hljs from "highlight.js"; Now:   let libZip = await import("@zip.js/zip.js");   let blobReader = new libZip.BlobReader(blob); Is equivalent to: import { BlobReader } from "@zip.js/zip.js"; It’s simple if we call it from async function but sometimes we want to lazy load from non-async function so things might get more complicated: let isLazyImportng = false; let hljs; let markdownIt; let markdownItAnchor; async function lazyImports() { if (isLazyImportng) return; isLazyImportng = true; let promises = await Promise.all([ import("highlight.js"), import("markdown-it"), import("markdown-it-anchor"), ]); hljs = promises[0].default; markdownIt = promises[1].default; markdownItAnchor = promises[2].default; } We can run it from non-async function: function doit() { lazyImports().then( () => { if (hljs) { // use hljs to do something } }) } I’ve included protection against kicking of lazy import more than once. That means on second and n-th call we might not yet have the module loaded so hljs will be still undefined.

a month ago 22 votes
Using await in Svelte 5 components

Svelte 5 just added a way to use async function in components. This is from Rich Harris talk The simplest component <script> async function multiply(x, y) { let uri = `/multiply?x=${x}&y=${y}` let rsp = await fetch(uri) let resp = await rsp.text(); return parseInt(resp); } let n = $state(2) </script> <div>{n} * 2 = {await multiply(n, 2)}</div> Previously you couldn’t do {await multiply(n, 2) because Svelte didn’t understand promises. Now you can. Aborting outdated requests Imagine getting search results from a server based on what you type in an input box. If you type foo, we first send request for f, then for fo then for foo at which point we don’t care about the results for f and fo. Svelte 5 can handle aborting outdated requests: <script> import { getAbortSignal } from "svelte"; let search = $state("") const API = "https://dummyjson.com/product/search"; const response = $derived(await fetch(`${API}?q=${search}`), { signal: getAbortSignal() }) </script> <input bind:value={search}> <svelte:boundary> <ul> {#each (await response.json().products as product)} <li>{product.title}</li> {/each} </ul>

a month ago 23 votes

More in programming

Announcing the 2025 TokyoDev Developers Survey

The 2025 edition of the TokyoDev Developer Survey is now live! If you’re a software developer living in Japan, please take a few minutes to participate. All questions are optional, and it should take less than 10 minutes to complete. The survey will remain open until September 30th. Last year, we received over 800 responses. Highlights included: Median compensation remained stable. The pay gap between international and Japanese companies narrowed to 47%. Fewer respondents had the option to work fully remotely. For 2025, we’ve added several new questions, including a dedicated section on one of the most talked-about topics in development today: AI. The survey is completely anonymous, and only aggregated results will be shared—never personally identifiable information. The more responses we get, the deeper and more meaningful our insights will be. Please help by taking the survey and sharing it with your peers!

12 hours ago 4 votes
The Angels and Demons of Nondeterminism

Greetings everyone! You might have noticed that it's September and I don't have the next version of Logic for Programmers ready. As penance, here's ten free copies of the book. So a few months ago I wrote a newsletter about how we use nondeterminism in formal methods. The overarching idea: Nondeterminism is when multiple paths are possible from a starting state. A system preserves a property if it holds on all possible paths. If even one path violates the property, then we have a bug. An intuitive model of this is that for this is that when faced with a nondeterministic choice, the system always makes the worst possible choice. This is sometimes called demonic nondeterminism and is favored in formal methods because we are paranoid to a fault. The opposite would be angelic nondeterminism, where the system always makes the best possible choice. A property then holds if any possible path satisfies that property.1 This is not as common in FM, but it still has its uses! "Players can access the secret level" or "We can always shut down the computer" are reachability properties, that something is possible even if not actually done. In broader computer science research, I'd say that angelic nondeterminism is more popular, due to its widespread use in complexity analysis and programming languages. Complexity Analysis P is the set of all "decision problems" (basically, boolean functions) can be solved in polynomial time: there's an algorithm that's worst-case in O(n), O(n²), O(n³), etc.2 NP is the set of all problems that can be solved in polynomial time by an algorithm with angelic nondeterminism.3 For example, the question "does list l contain x" can be solved in O(1) time by a nondeterministic algorithm: fun is_member(l: List[T], x: T): bool { if l == [] {return false}; guess i in 0..<(len(l)-1); return l[i] == x; } Say call is_member([a, b, c, d], c). The best possible choice would be to guess i = 2, which would correctly return true. Now call is_member([a, b], d). No matter what we guess, the algorithm correctly returns false. and just return false. Ergo, O(1). NP stands for "Nondeterministic Polynomial". (And I just now realized something pretty cool: you can say that P is the set of all problems solvable in polynomial time under demonic nondeterminism, which is a nice parallel between the two classes.) Computer scientists have proven that angelic nondeterminism doesn't give us any more "power": there are no problems solvable with AN that aren't also solvable deterministically. The big question is whether AN is more efficient: it is widely believed, but not proven, that there are problems in NP but not in P. Most famously, "Is there any variable assignment that makes this boolean formula true?" A polynomial AN algorithm is again easy: fun SAT(f(x1, x2, …: bool): bool): bool { N = num_params(f) for i in 1..=num_params(f) { guess x_i in {true, false} } return f(x_1, x_2, …) } The best deterministic algorithms we have to solve the same problem are worst-case exponential with the number of boolean parameters. This a real frustrating problem because real computers don't have angelic nondeterminism, so problems like SAT remain hard. We can solve most "well-behaved" instances of the problem in reasonable time, but the worst-case instances get intractable real fast. Means of Abstraction We can directly turn an AN algorithm into a (possibly much slower) deterministic algorithm, such as by backtracking. This makes AN a pretty good abstraction over what an algorithm is doing. Does the regex (a+b)\1+ match "abaabaabaab"? Yes, if the regex engine nondeterministically guesses that it needs to start at the third letter and make the group aab. How does my PL's regex implementation find that match? I dunno, backtracking or NFA construction or something, I don't need to know the deterministic specifics in order to use the nondeterministic abstraction. Neel Krishnaswami has a great definition of 'declarative language': "any language with a semantics has some nontrivial existential quantifiers in it". I'm not sure if this is identical to saying "a language with an angelic nondeterministic abstraction", but they must be pretty close, and all of his examples match: SQL's selects and joins Parsing DSLs Logic programming's unification Constraint solving On top of that I'd add CSS selectors and planner's actions; all nondeterministic abstractions over a deterministic implementation. He also says that the things programmers hate most in declarative languages are features that "that expose the operational model": constraint solver search strategies, Prolog cuts, regex backreferences, etc. Which again matches my experiences with angelic nondeterminism: I dread features that force me to understand the deterministic implementation. But they're necessary, since P probably != NP and so we need to worry about operational optimizations. Eldritch Nondeterminism If you need to know the ratio of good/bad paths, the number of good paths, or probability, or anything more than "there is a good path" or "there is a bad path", you are beyond the reach of heaven or hell. Angelic and demonic nondeterminism are duals: angelic returns "yes" if some choice: correct and demonic returns "no" if !all choice: correct, which is the same as some choice: !correct. ↩ Pet peeve about Big-O notation: O(n²) is the set of all algorithms that, for sufficiently large problem sizes, grow no faster that quadratically. "Bubblesort has O(n²) complexity" should be written Bubblesort in O(n²), not Bubblesort = O(n²). ↩ To be precise, solvable in polynomial time by a Nondeterministic Turing Machine, a very particular model of computation. We can broadly talk about P and NP without framing everything in terms of Turing machines, but some details of complexity classes (like the existence "weak NP-hardness") kinda need Turing machines to make sense. ↩

4 hours ago 2 votes
Incredible Vitest Defaults (article)

Learn how to use Vitest’s defaults to eliminate extra configuration and prevent flaky results, letting you write reliable tests with less effort.

2 days ago 8 votes
you are a good person

In my previous post, I advocate turning against the unproductive. Whenever you decide to turn against a group, it’s very important to prevent purity spirals. There needs to be a bright line that doesn’t move. Here is that line. You should be, on net, producing more than you are consuming. You shouldn’t feel bad if you are producing less than you could be. But at the end of your life, total it all up. You should have produced more than you consumed. We used to make shit in this country, build shit. It needs to stop. I have to believe that the average person is net positive, because if they aren’t, we’re already too far gone, and any prospect of a democracy is over. But if we aren’t too far gone, we have to stop the hemorrhaging. The unproductive rich are in cahoots with the unproductive poor to take from you. And it’s really the unproductive rich that are the problem. They loudly frame helping the unproductive as a moral issue for helping the poor because they know deep down they are unproductive losers. But they aren’t beyond saving. They just need to make different choices. This cultural change starts with you. Private equity, market manipulators, real estate, lawyers, lobbyists. This is no longer okay. You know the type of person I’m talking about. Let’s elevate farmers, engineers, manufacturing, miners, construction, food prep, delivery, operations. Jobs that produce value that you can point to. There’s a role for everyone in society. From productive billionaires to the fry cook at McDonalds. They are both good people. But negative sum jobs need to no longer be socially okay. The days of living off the work of everyone else are over. We live in a society. You have to produce more than you consume.

2 days ago 4 votes
Bear is now source-available

Updates to the Bear license

3 days ago 11 votes