295 private links
hey there, welcome to my website!
i'm isabelle a.k.a 'cr1ttr' --- i'm a software, tool, and game developer.
i made this website to ramble and show the things i'm working on.
The website looks like a modern terminal. We can clearly see the influence of neovim and Ratatui, but the owner didn't post anything yet. It's a lovely place.
A personal brand website
Even the best have "No idea" for "Safety" at some places. Another one: https://github.com/rust-lang/rust-analyzer/blob/932186d9c2046257c0a1fc38c1cecf7bc19736c3/crates/hir-def/src/lib.rs#L536
There is an "// SAFETY: Come fight me" in the rust uuid crate :D
The client is not a thin view requesting permission to show data. The client is a node in a distributed system with its own database.
It’s overkill for simple CRUD apps with no offline or collaboration needs.
But here’s where it shines: note-taking, document editing, collaborative design tools, project management, field apps with unreliable connectivity, basically anything where data privacy is a selling point, as well as anything with real-time collaboration.
One more thing I wish someone had told me earlier: you don’t have to go all-in. I’ve had the best results using local-first for specific features within otherwise traditional apps. Offline drafts in a blog editor. Real-time collaborative notes inside a project management tool that’s otherwise standard REST.
To do so: SQLite in the browser via WebAssembly; persisted to the Origin Private File System (OPFS). The author describes the method used.
To avoid conflicts: CRDT. Yjs exists. There is also Automerge and the newer Loro.
To grasp the data: replicate rows via database replication.
PowerSync does this well from Postgres to SQLite.
Triplit is a full-stack database with sync built-in.
LiveStore use an event-based approach.
TinyBase for prototyping or small apps.
PGLite (Postgres compiled to WASM) but it has a significant bundle size and memory footprint for mobile browsers.
Often the last-write-wins (LWW) is the best strategy at the field level.
For a document body, CRDT should be used.
To book a meeting, one must verify there is no other meeting booked by someone else. " The approach I’ve landed on (after getting it wrong twice) is: validate on the server during the write-back phase, but flag violations rather than silently rejecting them. When the client pushes mutations to the server during sync, the server runs them through a constraint validation layer before applying them to Postgres". See the example.
The conflict should then be resolved by the user.
For something like inventory management where two people “buy” the last item, that window is unacceptable, and that’s exactly why I said earlier that local-first is wrong for systems requiring strong transactional consistency.
Conflict resolution works well for texts with CRDT.
See such app architecture: https://www.smashingmagazine.com/2026/05/architecture-local-first-web-development/#building-a-real-app-architecture-auth-and-migrations
Example of E2E (local, on device) encryption for https://anytype.io/
One thing to consider is migrations: Design your migrations to be additive. New columns with defaults. New tables. Don’t rename or drop columns unless you absolutely must, because users running old app versions will still be syncing data, and your server needs to handle the mismatch. I learned this the hard way when I dropped a column that an older client was still writing to, which caused silent sync failures for about 200 users over a weekend.
Performance are awesome (< 10ms for read or writes). The initial sync is where the cost occurs.
The architecture can be tested with Playwright and context.setOffline(true).
I’m excited about where this is going. PGlite (full Postgres in the browser) feels like a glimpse of a future where the client/server data layer distinction just dissolves. You write SQL, it runs everywhere, sync is a runtime concern rather than an architectural decision. We’re not there yet, but you can see it from here.
There is also no standard for a sync engine. Migrating away a sync engine is not trivial. I’m also worried about the complexity budget. Local-first adds real architectural complexity: sync engines, conflict resolution, client-side migrations, partial replication, and auth at the sync boundary.
A new model emerges after the cathedral and the bazaar: the kitchen. The author describes it.
Every kitchen evolves around the habits of its cook. Tools sit where they are convenient. Ingredients are substituted freely. Recipes are modified on instinct. Two people may start from the same dish and end up with completely different results.
Kitchen rarely converge into a universal standard.
During my research I identified nine types of comments:
- Function comments: They prevent the reader from reading code in the first place. Instead, after reading the comment, it should be possible to consider some code as a black box that should obey certain rules
- Design comments: they states how and why a given piece of code uses certain algorithms, techniques, tricks, and implementation. [...] With such background, reading the code will be simpler.
- Why comments: explain the reason why the code is doing something, even if what the code is doing is crystal clear.
- Teacher comments: They teach instead the domain (for example math, computer graphics, networking, statistics, complex data structures) in which the code is operating, that may be one outside of the reader skills set, or is simply too full of details to recall all them from memory.
- Checklist comments: sometimes because of language limitations, design issues, or simply because of the natural complexity arising in systems, it is not possible to centralize a given concept or interface in one piece, so there are places in the code that tells you to remember to do things in some other place of the code
- Guide comments: they do a single thing: they babysit the reader, assist him or her while processing what is written in the source code by providing clear division, rhythm, and introducing what you are going to read.
- Trivial comments: a bad one, a guide comment where the cognitive load of reading the comment is the same or higher than just reading the associated code.
- Debt comments: debt comments are technical debts statements hard coded inside the source code itself:
- Backup comments: the developer comments older versions of some code block or even a whole function, because she or he is insecure about the change that was operated in the new one. We have git now.
Comments can be considered analysis tools; and they are often harder to write than code.
- Convert external sources to motivation: a bot that reminds a new subscription for example
- Leave tasks unfinished: I try to leave a task 90% finished at the end of a working session. It feels slightly worse than closing out the work, but it makes starting the next day 10x easier.
- Use the thing myself, as much as possible
- Address the pain, instead of pushing through: The trick, is that you can almost always make these less painful.
- do nothing before work
- update the users (or keep a notebook)
- get a partner
- no zero days, to avoid listless guilt
Google course on error messages
Every attempt to score open source is not accurate.
The most consequential mistake is treating the absence of a signal as a low value of that signal.
Missing FUNDING file
Easy to collect doesn't mean something
Stars on Github (ICU only 3.5k, 2.5k), CVE counts (compare the Linux kernel to
One number, many units
npm "download" is mostly a count of CIcache misses. Dependent counts are different between a string-padding helper on npm and a C compression library that is statically linked and distributed as vendor or a git submodule.
Github as the visible universe
Not everything is on GitHub. Contributors (so the bus factor count too)
Project identity is different on different platform
curl has many names across platforms.
Invisible funding
The most common funding arrangement for critical infrastructure is none of those. It’s a maintainer employed by Red Hat, Google, Intel, Canonical, or a hardware vendor, with the project as some or all of their job, and that arrangement leaves no trace in any file a crawler can fetch. The second most common is consulting and support contracts around the project, which is similarly invisible.
and it compounds because the project doesn't look like an npm package. "The quiet system library with one tired maintainer and no dashboard footprint is exactly what we built all of this tooling to find, and it remains the thing the tooling is structurally worst at seeing."
There are reasons to be negative about the future of the web. The author reminds what is awesome about the web. There are many topics: Accessibility, animations, building stuff, optimizing, semantic markup, styling.
- Your best users are the ones who complain. A user told me at 10pm that my uninstaller just nuked his shell config. My instinct was to get defensive. Instead I traced it — and found it was worse than reported. That one message led to rewriting the entire uninstall logic from scratch. Every angry bug report is a gift.
- Your favorite metric can lie to you. I built a cache that reduced file reads from 2,000 tokens to 13. Great numbers. Then a user told me: "Models waste more tokens working around stale cache than the cache saves." He was right. The fix wasn't removing caching — it was making invalidation smarter. Your dashboard can look great while the experience is terrible.
- Saying no is the hardest part. A new feature would have let me compress all tool output automatically. Massive savings on paper. I designed it, prototyped it, then killed it. Because when compression eats an error message, there's no undo. Protecting quality beats shipping features.
- Community is a relationship, not a channel. When someone reports a bug, my first response matters more than the fix. "Will check" buys time but shows I'm listening. Following up shows respect. Shipping the fix shows they matter. My best testers are people who once filed angry reports.
- Ship the boring stuff first. Nobody cares about your adaptive entropy-based compression algorithm if the installer breaks their dotfiles. Get the fundamentals right — install, uninstall, doctor, setup — before you get clever.
- Focus means killing good ideas. My backlog has 50+ ideas. Each one is good. But spreading across all of them means none become great. Rust helps here — the compiler forces you to finish what you start.
Pour naviguer et décoder des formats binaires
En réalité, cela introduit un coût immédiat.
Chaque abstraction anticipée augmente :
- le temps de développement
- le temps de review
- la difficulté d’onboarding
- la surface de bugs potentiels
- la complexité du debugging
- la quantité de tests nécessaires
Et surtout :- le besoin anticipé peut ne jamais arriver.
Mais même si ce besoin arrive un jour, un autre problème apparaît. La solution que nous avons imaginée n’est peut-être pas la bonne.
C'est ce qu'on fait: tester en tant qu'utilisateur. C'est le minimum, je dirais même avant les tests automatisés.
Les tests valident le code
l’usage valide le produit.
En comparant la réalisation du ticket avec l'essai du produit en tant qu'utilisateur:
Exécuter un ticket est simple :
- lire les requirements
- implémenter la logique
- écrire les tests
- ouvrir une pull request
Tester réellement le produit demande un effort différent.
Il faut :
- suivre le parcours complet
- imaginer les actions d’un utilisateur (se mettre à sa place)
- tester des scénarios inattendus.
C’est plus lent, et demande de l’empathie.
Les pièges a éviter:
- readme cimetière
- theator of documentation: useless comments
- outdate trap: "how" is a wrong answer
- un ROI négatif.(Lectures × Temps gagné) - (Temps d’écriture + Maintenance).
Pour maximiser ce ROI, il faut distinguer quatre types de documentation :
- ROI Infini : La doc fonctionnelle (le problème). C’est la boussole de la feature. Elle ne décrit pas les boutons, mais la valeur.
- Le contenu : À quel problème client répond-on ? Quelle est la vision à 6 mois ? Quelles sont les limites actuelles (ce qu’on a décidé de ne pas faire) ?
- Pourquoi c’est crucial : Sans elle, un dev qui itère sur la feature 1 an plus tard risque de supprimer un comportement “bizarre” qui était en fait une réponse vitale à un besoin client.
- Le public : Elle est le pont entre la Tech, le Produit et le Marketing. C’est la doc qui aligne tout le monde.
- ROI Élevé : La Doc d’Intention (l’histoire).
- Pourquoi ce choix ? Quelles étaient les limitations ? C’est ce qui survit aux refactos (ex: les ADR - Architecture Decision Records). N’importe quel dev peut comprendre votre code, mais personne ne peut deviner une intention métier disparue.
- ROI Opérationnel : La Doc d’Usage (le manuel). Le strict nécessaire pour être autonome. Par exemple le guide du “Day One”. Si je ne peux pas lancer le projet à tous les coups en quelques minutes lors d’un onboarding, le README a échoué.
- ROI Négatif : La Doc d’Implémentation. Les détails techniques changeants. C’est du “Waste”. À supprimer au profit d’un code propre, typé et bien nommé.
La documentation est écrite pour être lue, sinon elle est nulle. Une bonne documentation doit être compréhensible par la majorité de l’équipe (Tech, Produit, voire Marketing).
Le vrai danger lors d’une passation, ce n’est pas la perte du “savoir-faire” technique, c’est la disparition de la compréhension du système.
Customer research is always quantitative. The start the next Google with venture capital. That’s not true. The research is really what allows you to distinguish yourself from your competition so that when someone comes to your site.
No SEO wakes up in the morning and thinks, “Damn, I have a WordPress problem.” They wake up in the morning and say, “Damn, I have a business problem.”
As an example:
You’ve gotten this level of traffic so far with your existing site. You want to double that over the course of the next year. You were saying that the roadblocks that are stopping you are a) you’re on an outdated architecture where you, personally, have to write everything, and b) it’s been impossible to find someone who is a subject matter expert at this who can also make web pages, because that combination of interests just does not exist.
The response:
I totally understand that it is impossible to find someone who is both good enough of a journalist in the space to work for a genuine published magazine and also can edit HTML pages. I have an idea for you. I’m going to make a CMS for your website. That’s just a page that they can go into, like WordPress, and they can do their writing like they do so well. The CMS will ship it over to you, and you figure out which business model that you experimented with will work well with this content.
I pay them pretty well. I have passed on hiring engineers who may be more technically proficient, but they didn’t understand what I wanted. Honestly, guys, as a business owner, do you think I care if you’re using this technology or that? I just don’t give a damn [about technology]. I really don’t.
Nothing motivates people like having their own words repeated right back to them, which is something that you should try to do more often.
Even if you’re building a website for someone, it’s not just a website, right?
There is some particular need that they have for that website, whether it’s for their business purposes or because a lot of business owners are very personally invested in their business.
By the way, guys, for all the engineers in the audience, you sell engineering services to for profit companies because they are the only people who can pay actual professional engineer rates.
The CEO only sees it in the “This is the tool that someone I trust is doing something incredibly valuable with me on. It is cheap at any reasonable price."
Focus on taking your B+ customers/prospects to be A customers. Don’t focus on the D people, trying to turn them into A customers. It will never happen.
Author blog about the business of making and selling software: https://www.kalzumeus.com/blog/
90% of programming jobs are in creating Line of Business software. [...] Software solves business problems. Software often solves business problems despite being soul-crushingly boring and of minimal technical complexity. [...] It does not matter to the company that the reporting form is the world’s simplest CRUD app, it only matters that it either saves the company costs or generates additional revenue.
Engineers are hired to create business value, not to program things: Businesses do things for irrational and political reasons all the time (see below), but in the main they converge on doing things which increase revenue or reduce costs. [...] Add revenue. Reduce costs. Those are your only goals. [...] You really want to be attached to Profit Centers [notion from Peter Drucker]. [...] If you can’t, either a) work elsewhere or b) engineer your transfer after joining the company.
About programming languages:
In the real world, picking up a new language takes a few weeks of effort and after 6 to 12 months nobody will ever notice you haven’t been doing that one for your entire career.
Co-workers and bosses are not usually your friends: You should be a good person to everyone you meet — it is the moral thing to do, and as a sidenote will really help your networking — but do not be under the delusion that everyone is your friend.
“Read ad. Send in resume. Go to job interview. Receive offer.” is the exception, not the typical case, for getting employment: Most jobs are never available publicly, just like most worthwhile candidates are not available publicly (see here).
Networking just means a) meeting people who at some point can do things for you (or vice versa) and b) making a favorable impression on them.
Academia is not the real world. [...] Your major and minor don’t matter.
Be better at negotiation:
a) Remember you’re selling the solution to a business need (raise revenue or decrease costs) rather than programming skill or your beautiful face.
b) Negotiate aggressively with appropriate confidence, like the ethical professional you are. It is what your counterparty is probably doing. You’re aiming for a mutual beneficial offer, not for saying Yes every time they say something.
c) “What is your previous salary?” is employer-speak for “Please give me reasons to pay you less money.” Answer appropriately.
d) Always have a counteroffer. Be comfortable counteroffering around axes you care about other than money. If they can’t go higher on salary then talk about vacation instead.
e) The only time to ever discuss salary is after you have reached agreement in principle that they will hire you if you can strike a mutually beneficial deal.
f) Read a book. Many have been written about negotiation. I like Getting To Yes. It is a little disconcerting that negotiation skills are worth thousands of dollars per year for your entire career but engineers think that directed effort to study them is crazy when that could be applied to trivialities about a technology that briefly caught their fancy.
The author is negative about equity grants.
So would you recommend working at a startup? Working in a startup is a career path but, more than that, it is a lifestyle choice. This is similar to working in investment banking or academia. Those are three very different lifestyles. Many people will attempt to sell you those lifestyles as being in your interests, for their own reasons. If you genuinely would enjoy that lifestyle, go nuts.
Your most important professional skill is communication.
Communication is a skill. Practice it: you will get better. One key sub-skill is being able to quickly, concisely, and confidently explain how you create value to someone who is not an expert in your field and who does not have a priori reasons to love you.Modesty is not a career-enhancing character trait: The right tone to aim for in interviews, interactions with other people, and life is closer to “restrained, confident professionalism.”
All business decisions are ultimately made by one or a handful of multi-cellular organisms closely related to chimpanzees, not by rules or by algorithms: People are people. Social grooming is a really important skill.
At the end of the day, your life happiness will not be dominated by your career. Either talk to older people or trust the social scientists who have: family, faith, hobbies, etc etc generally swamp career achievements and money in terms of things which actually produce happiness. Optimize appropriately. Your career is important, and right now it might seem like the most important thing in your life, but odds are that is not what you’ll believe forever. Work to live, don’t live to work.
Oh shit we are in deep trouble: names are complex to handle.
My rules of thumbs:
- they can't be identifiers
- they are mutable
- the format is so complex that a single "display name" field should be used
- create dynamic models (or SQL tables) if you need details for legal reasons
- UTF-8 encoding is a must. All characters are allowed because I can't verify Japanese, Chinese and all the possible character dataset