# Vlad Sabev
> Essays on software, freelancing, and AI
Full Markdown sources for published posts and navigation pages (same text as each `/{slug}.md` twin).
Catalog with summaries and tag index: [https://vdsabev.github.io/llms.txt](https://vdsabev.github.io/llms.txt).
Each body is a fenced `markdown` code block so frontmatter and headings stay isolated from this outline.
## Posts
### Smoothing the Path with AI
- Markdown: https://vdsabev.github.io/smoothing-the-path-with-ai.md
- HTML: https://vdsabev.github.io/smoothing-the-path-with-ai/
```markdown
---
status: "published"
title: "Smoothing the Path with AI"
date: "2026-07-28"
description: "My favorite use of AI - not vibe-coding toy apps, but smoothing over the path - like driving a car down the highway instead of going on a cross-country hike."
image: "https://github.com/user-attachments/assets/0d022af4-6294-4ea7-99f2-ee55a0632a92"
imageAlt: "A sleek white autonomous car with glowing headlights drives straight down a dark asphalt highway that stretches into a thick misty fog toward a distant vanishing point. On both sides of the road, rugged rocky hillsides covered in scrub and tangled vegetation rise up. Glowing abstract overlays of computer code, configuration snippets, and flowing data streams appear on the road surface and dissolve into the surrounding landscape under soft cinematic lighting with cool blue fog and warm amber accents."
author: "Vlad Sabev"
authorUrl: "https://github.com/vdsabev"
authorAvatar: "https://github.com/vdsabev.png"
tags: ["ai","testing","software development"]
navigationIndex: 0
---
My favorite use of AI at the moment is not vibe coding toy applications but setting up systems. A test suite that would have previously taken 2 weeks to configure end-to-end now only takes a few hours while I'm doing something else in the background. I've found the biggest time saving to be in exploring radically different approaches, searching through piles of settings, and resolving various compatibility issues.
I could spend hours digging through documentation, StackOverflow, or forums before I finally unearth something useful. Now AI can search everything for me, find a solution, try it out, then try a different one if that doesn't work out, and finally come back with the issue fixed.
On the surface there seems to be less learning now, but did this process really lead to that much learning to begin with? Were most of these hours of research not spent on grappling with useless [leaky abstractions](https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions/), and then once I finally solved the problem - was I not relieved to immediately forget about it?
Perhaps there is something to lose. I did eventually learn most of the gotchas and know what to steer away from, though this knowledge was never really all that useful for achieving my goal of building reliable software. Yes, AI can smooth the path to the actual task, like driving a car on a highway versus cross-country hiking. Yet when we go hiking we're more adaptable to obstacles, we exercise, we learn the way of the land - all of these have value on their own, and there is still purpose in pursuing them. We should still occasionally try to [make difficulties everywhere](https://jonmalesic.com/kierkegaard-in-seven-quotations-part-i-making-things-difficult/).
So every day we have a choice to make - what's more important right now: to get to know the land, or to get from A to B? It is for each of us to decide. But if we choose the smooth path instead of the hike every day, we may be losing something elusive and difficult to measure.
```
### The Economics of Self-Hosting AI
- Markdown: https://vdsabev.github.io/the-economics-of-self-hosting-ai.md
- HTML: https://vdsabev.github.io/the-economics-of-self-hosting-ai/
```markdown
---
status: "published"
title: "The Economics of Self-Hosting AI"
date: "2026-07-28"
description: "Enthusiasts want to self-host models but can't justify the hardware. Marketplaces for spare compute could enable the economics for individuals, while privacy already makes it a no-brainer for companies."
image: "https://github.com/user-attachments/assets/72d4755a-d308-49b9-b2bc-f4f6a8425ecc"
imageAlt: "Dark cinematic illustration of multi-GPU NVIDIA server hardware in the foreground with glowing green accents, floating holographic privacy shields and padlock icons, code interfaces, and a silhouetted consultant working at a laptop in the background — cover art for an article on self-hosting AI economics and spare compute marketplaces"
author: "Vlad Sabev"
authorUrl: "https://github.com/vdsabev"
authorAvatar: "https://github.com/vdsabev.png"
tags: ["ai","self-hosting","freelancing"]
navigationIndex: 0
---
## 💸 Hardware You Can't Amortize
Rumor has it Apple's M7 Ultra, landing around 2029, could support **1.5 TB of RAM** - the kind of machine that enables serious on-device AI.
My first reaction wasn't that I'll be able to run my own AI - rather, I thought if Apple built a marketplace to sell spare compute, with clear isolation, metering, and payouts, then took an App Store-style cut, I would seriously consider buying an Apple product for the first time in my life. This move would instantly increase demand for high-end Apple hardware as many tech professionals have been grumbling against their software direction and gradually switching to [various distributions of Linux](https://omarchy.org), finally fulfilling the prophecy and declaring 2025 the year of the Linux laptop.
Who else is well-positioned to compete with Apple on this? NVIDIA of course. If they ran a marketplace where you could rent out your idle GPUs, you'd see a lot more self-hosting overnight. Demand for GPUs is sky-high regardless, but enthusiasts are still buying - partially from FOMO, but also because they want to try new models at home.
But decent boxes cost a small fortune, and the power consumption is significant. The models aren't state-of-the-art, the box sits unused most of the day, and subscriptions are much cheaper (likely subsidized) while delivering better results. For most people the economics simply don't work - you pay full price for capacity you only use a fraction of the time, and there's no clear way to recoup the rest.
## 🏢 Privacy: Where the Costs are Already Justified
For corporations, self-hosting is a different story.
You don't want proprietary code, deal data, customer records, or half-baked strategy docs flowing into someone else's training pipeline or sitting in their logs. Leak your IP to a frontier lab and you risk having your business stolen from under you. Yes, you can handle this through zero data retention agreements, but some companies in sectors like medicine or law would rather keep everything on premise.
Individuals feel a milder version of the same pressure: personal notes, health context, family stuff, unreleased product ideas. The difference is while enterprises can write off the hardware costs as privacy insurance, individuals still have to justify the bills.
## 🛠️ The Consultant Angle
For people already experienced with open models, local coding agents, and the whole messy stack, there's a side bet ripe for the taking right now - companies that don't want their IP stolen will pay for someone who knows:
- What fits on which hardware
- How to wire auth, logging, and model updates
- When *not* to self-host (because sometimes an API is still the right call)
If self-hosting enthusiasts positioned themselves as **self-hosting consultants** and found a few good leads, that could be a sweet gig. Not just installing Linux for fun, but for fun an profit!
While open models get better each cycle, hardware slowly improves, and manufacturers scale up production, the potential cost of leaking IP remains equal to the value of your whole business.
If you're already running models at home for fun, take note. Those who learn this stack early might not just save money on their own API bills, but become the ones companies call when using corporate cloud AI providers is not an acceptable answer.
```
### cmless: Seamless Blogging with GitHub Issues
- Markdown: https://vdsabev.github.io/cmless-seamless-blogging-with-github-issues.md
- HTML: https://vdsabev.github.io/cmless-seamless-blogging-with-github-issues/
````markdown
---
status: "published"
title: "cmless: Seamless Blogging with GitHub Issues"
date: "2026-07-23"
description: "I've tried to maintain a blog for years, but every tool had too much friction. So I made cmless: turn a GitHub repo into a blog with Issues as the CMS and GitHub Pages for hosting."
image: "https://github.com/user-attachments/assets/81a6f404-3e6d-4df4-b19d-390c87658744"
imageAlt: "cmless - seamless blogging with GitHub issues"
author: "Vlad Sabev"
authorUrl: "https://github.com/vdsabev"
authorAvatar: "https://github.com/vdsabev.png"
tags: ["blogging","software development"]
navigationIndex: 0
---
## 🤔 Too Much Friction
I've been trying to maintain a blog for years but anything I used had too much friction.
WordPress is overkill. Medium is not your own. Ghost needs hosting. Markdown-in-a-repo means you have to make a commit just to fix a typo.
I wanted something simpler - write where I already work, keep assets out of the code tree, and publish without managing a server.
So I made [cmless](https://github.com/vdsabev/cmless) - a tool to seamlessly turn a GitHub repo into a blog.
## ℹ️ What It Does
cmless takes your repo's GitHub issues, turns them into blog posts, and hosts them on GitHub Pages.
- Open issues to write posts
- Drag-and-drop or paste images (GitHub hosts them on its CDN)
- Embed YouTube, X posts, Instagram, CodePen, and GitHub gists with a simple `` image-style link
- Optional Frontmatter for date, description, tags, and more
- Publish with labels: `status: draft`, `status: unlisted`, `status: published`
- Updating an issue rebuilds the blog
This post is part of that workflow. If you're reading it on my website - it started as [issue #24](https://github.com/vdsabev/vdsabev.github.io/issues/24).
**Code stays code. Posts stay posts.** Since content lives in Issues, the repo doesn't bloat with every draft and image, and you can write from any device with a browser and a GitHub login.
## 🛠️ How It Works
### 1. Write the post as a GitHub issue
Title = post title. Body = Markdown. Drag and drop images into the issue.
Embeds use the same syntax as images - put the URL in ``:
### 2. Add Frontmatter at the top
```yaml
---
date: "2026-07-10"
description: "Short summary for SEO."
tags: tech, dev
---
```
Author, avatar, and profile link default to the GitHub user who opened the issue. You can override those, plus title, slug, OG image, or place a link to the page in the header or footer if you need to.
### 3. Apply a status label
| Label | Listed on homepage? | Reachable by URL? | Best for |
| --- | --- | --- | --- |
| `status: draft` | No | No | Work in progress, only on GitHub |
| `status: unlisted` | No | Yes | Previews, private links, pages like About me |
| `status: published` | Yes | Yes | Public posts |
`status: published` adds the post to your list. `status: unlisted` publishes it without listing it - handy for custom pages like About me.
When you're done editing you can close the issue - or keep it open - the published status of the post is only based on the label.
### 4. Update the issue to rebuild
Create or edit an issue (or change a status label) and the site rebuilds. Contributors control the labels so open issues by random people don't get put on your homepage. But if you want to make sure there's no noise you can limit issue creation to collaborators.
## ⚙️ Under the Hood
A GitHub Actions workflow runs on issue events and pushes, using the `gh` GitHub CLI tool and a small generator (`scripts/generate.ts`) to turn labeled issues into Markdown for the build. Astro produces a static site, and GitHub Pages serves it.
The repo holds the theme, pipeline, and config - the issues hold the writing. If you use the [cmless template](https://github.com/vdsabev/cmless) to make your own repo you can still sync engine updates later by adding an upstream remote and merging it into your repo.
## 🚀 Getting Started
Click **Use this template** on [vdsabev/cmless](https://github.com/vdsabev/cmless) and start creating issues - it's that simple.
1. Create a repo from the template
- `.github.io` → `https://.github.io`
- any other name → `https://.github.io/`
2. In **Settings → Pages**, set **Source** to **GitHub Actions**
3. Create or edit an issue to trigger the first build
Labels come from the Blog post issue template, or create them with:
```sh
gh label create "status: draft" --color "d73a4a" --description "Not published"
gh label create "status: unlisted" --color "5319e7" --description "Published but hidden from list"
gh label create "status: published" --color "0e8a16" --description "Published and listed"
```
Site title comes from the repository description. And if you want a custom domain you can do that just like with any other static website on GitHub Pages - drop a `CNAME` in `public/` and point DNS to it.
For local preview: clone your repo, install, `npm run dev` (or Bun, etc.). Point `GH_REPO` at any cmless-based repo if you want to develop against live issues without publishing from your laptop.
You can also have an AI agent use the `gh` GitHub CLI to manage posts (except uploading assets - paste those yourself in the web UI, or link to external hosting).
## 🏁 In Conclusion
I don't need another platform optimized for engagement metrics. Just a place to publish my thoughts with tools I already know and understand.
cmless is small on purpose: Issues for writing, labels for status, Actions for rebuilds, Pages for hosting. Simple to reason about, very little maintenance.
Try the template from [github.com/vdsabev/cmless](https://github.com/vdsabev/cmless)
Or see it in action on my blog: [vdsabev.github.io](https://vdsabev.github.io)
````
### The AI Casino
- Markdown: https://vdsabev.github.io/the-ai-casino.md
- HTML: https://vdsabev.github.io/the-ai-casino/
```markdown
---
status: "published"
title: "The AI Casino"
date: "2026-02-17"
description: "How variable rewards and prompt routing could be turning LLMs into digital slot machines"
image: "https://github.com/user-attachments/assets/d0909e71-6e3d-49c4-bfa1-bff0233cc178"
imageAlt: "A conceptual digital illustration of a slot machine emerging from a computer monitor, framed by glowing neon cherry signs. The slot reels display coding brackets, brains, and the number 7. Streams of glowing blue binary data flow out from the sides, and gold coins are scattered across the foreground against a dark, futuristic background."
author: "Vlad Sabev"
authorUrl: "https://github.com/vdsabev"
authorAvatar: "https://github.com/vdsabev.png"
tags: ["ai","psychology","self-hosting"]
navigationIndex: 0
---
## 🎰 The Slot Machine Loop of LLMs
I recently stumbled upon a piece that got me thinking: [Generative AI runs on gambling addiction: "Just one more prompt, bro"](https://pivot-to-ai.com/2025/06/05/generative-ai-runs-on-gambling-addiction-just-one-more-prompt-bro).
Purely hypothetically, if you were an AI provider who wanted to make your product not just useful, but truly addictive - how would you actually do it?
## 🪝 Engineering the Hook
To build a hook, you would look at the gambling industry. They perfected the science of variable payouts decades ago.
> The most powerful way to reinforce a behavior isn't to reward it every time, but to reward it *randomly*.
In a casino, this is a slot machine. In AI, it's **one-shotting** a complex problem. You prompt a model ten times. Nine times it may give you nothing too special, or even broken code. But the tenth time? It may produce a piece of code so delightful and elegant that it gives you a huge hit of dopamine.
You're hooked. And you'll spend the rest of the day chasing that high again.
## 🫥 The Routing Layer
If AI providers are slottifying their products, are they doing so intentionally, or is it just an emergent property of the tech?
From an engineering perspective, every AI provider *most likely* has a prompt router. Running your top-of-the-line model for every "Hello World" request while massively subsidizing token costs with cheap subscriptions is financial suicide. You put a cache and a very cheap, lightweight model (a Flash or Lite variant) in front of every prompt.
If the router thinks the prompt is easy, it sends it to the cheap model. If it looks complex - or if the user is a "whale" they want to impress - maybe it gets sent to the heavy hitter.
What if that routing isn't just about cost? What if it's about retention? If a user hasn't had a "win" in a while, do you route their next coding prompt to your best and brightest model just to keep them in the game?
## 📈 Degradation or Deliberate Variance?
We've all heard the complaints: "Opus is getting dumber" or "Gemini used to be better at this." Usually, we chalk it up to manual alignment.
Prompt routing offers an alternative explanation. If AI companies don't legally guarantee that a specific model name maps to a specific set of weights for every single token, they have a massive lever for cost-cutting and psychological manipulation.
What if 1 out of 10 prompts gets the "Pro" treatment, and the other 9 get the "Lite" version with a "Pro" label on the UI? You get just enough brilliance to stick around, and just enough mediocrity to keep you prompting.
## 🤔 What about Benchmarks?
Wouldn't benchmarks catch such manipulation? Remember the Volkswagen emissions scandal? The car's software would detect when it was being tested and "cheat" by reducing emissions only during the test.
Is it such a stretch to imagine an LLM router detecting a benchmark prompt (MMLU, HumanEval, etc.) and routing it to a specialized cluster, while average users get the "distilled" version?
I'm not claiming AI model providers are putting their thumb on the scale. But if they wanted to, there's little to prevent them from doing so for the random consumer.
The countermeasure to manipulation however is not regulation - any regulation in that direction would probably be too heavy-handed. Rather, it's open-source models, which are now only a few months behind the state-of-the-art models.
## 💪 Hardware Sovereignty
The only way to know for sure what weights are processing your tokens is to host them yourself. But it's 2026, and with current supply chain crunches getting your hands on decent GPU and RAM is more and more cost-prohibitive.
Maybe a few years from now the manufacturing will be so advanced and the tech so powerful and cheap that we'll have a Kimi K2.5-equivalent model running locally in our pocket. Until then, consider this: every time you hit "Generate" with a supposedly state-of-the-art model, you might be placing a bet.
And in the long run, the house always wins.
```
### The Spiritual Cost of the Nanny State
- Markdown: https://vdsabev.github.io/the-spiritual-cost-of-the-nanny-state.md
- HTML: https://vdsabev.github.io/the-spiritual-cost-of-the-nanny-state/
```markdown
---
status: "published"
title: "The Spiritual Cost of the Nanny State"
date: "2026-02-07"
description: "The shift from personal responsibility toward government paternalism and human self-domestication."
image: "https://github.com/user-attachments/assets/cabcac33-6cef-4c74-ad96-862fb99201a6"
imageAlt: "A conceptual collage contrasting old and new technology, featuring a vintage radio and headphones on the left and a modern cylindrical smart speaker on the right. In the center background, two hands hold a glowing, digital shield that says 'PERSONAL RESPONSIBILITY' as pieces fly off it. Above the smart speaker, a holographic user interface displays 'MAX VOLUME RESTRICTED' and a button that reads 'I AM NOT IN THE EU'."
author: "Vlad Sabev"
authorUrl: "https://github.com/vdsabev"
authorAvatar: "https://github.com/vdsabev.png"
tags: ["eu","parenting","personal responsibility","technology"]
navigationIndex: 0
---
## 📜 The Bluetooth Decree
Earlier this week I saw a post about a seemingly trivial piece of EU regulation:
"Are you located in the EU? If yes, max volume will be restricted. Yes/No"
On the surface, it's about public safety. Protecting hearing. Preventing disturbance. But as I found myself in a discussion about it online, it became clear that my issue wasn't really with the decibels, but about a **fundamental shift** in how we relate to our tools, our governments, and our responsibilities.
## 🤔 Public Health or Safety Theater?
Let's give the regulators the benefit of the doubt - loud music can damage your hearing, and this is well-intended. But in this case circumventing the max volume is trivial. Of course the owner in the original post selected "No". But even if you selected "Yes", simply restarting the device brings up the prompt again.
With some other devices it's just a warning once you go past a certain volume, gently nudging you to not listen at the max. Many young people genuinely don't know that loud music is dangerous.
And this is what I'm really concerned about - parents are the ones who must bear primary responsibility for their children's health. So why are they letting children habitually listen to loud music at 90-100dB for long periods of time? Work, chores, and stress leave many parents with less and less family time.
This decrease in parenting (and increase of threats to our children) is unfortunately leading us to **delegate** more and more of our traditional social structures to government.
## ✅ The Case for Localized Responsibility
First, let's quickly tackle the safety issue - if we truly care about protecting hearing, we can solve this at a much more local level.
Instead of an easy to circumvent max volume, we could require that all standalone speakers allow customers to set their own limits. Say, a **parental control** setting protected by a password. Manufacturers can pre-set limits for safe sale to minors. Parents can limit the max volume for their kids. Audiophiles can blast their eardrums away at will.
This approach respects individual agency. It acknowledges that an adult using a speaker at a private outdoor event might need more power than a child in a bedroom. It moves the decision-making power from unelected bureaucrats in Brussels back to the citizen holding the device.
## 📈 The True Cost isn't Financial, but Spiritual
There is, of course, a cost to any regulation. We pay the salaries of bureaucrats to write the rules. Then we pay engineers like myself to implement them, and companies pass that cost down to consumers. My proposal for password-protected max volume could be even more expensive than a simple yes/no prompt.
But more importantly, there is a **spiritual toll**. It's the cost of being increasingly "parented" by our well-meaning governments.
When every risk is smoothed over by regulation, we lose the habit of personal responsibility. We might even stop thinking about "safe levels" because we assume the government has already done the thinking for us. What happens when we're in a situation that doesn't have this protection? Our car stereo, our old analog speakers, a loud concert?
We can't pave the jungle - we have to put on some boots.
## ☠️ Death by a Thousand Cuts
A single regulation on Bluetooth speakers is a non-issue. It may even be a good thing, raising awareness of the dangers of loud noises. But over decades, such regulation is death by a thousand cuts from bureaucratic paper - each one a tiny slice of agency removed from us.
In my mind, this ties into the concept of **human self-domestication**. By removing all friction and risk from our environment through centralized decree, we are essentially domesticating ourselves. We become more docile, more dependent on the system, and less capable of navigating reality without a regulatory harness. This process has [physical effects across many domesticated animals](https://www.frontiersin.org/journals/psychology/articles/10.3389/fpsyg.2019/00521/full), including humans.
While these effects like having a lower aggression drive are not necessarily a bad thing, we have to preserve our competence and ability to think independently. Otherwise, when some part of the system inevitably fails catastrophically, we will find ourselves unable to function.
And one day the fate of human civilization may depend on the few who still can.
```
### Overtherapizing Men's Mental Health
- Markdown: https://vdsabev.github.io/overtherapizing-mens-mental-health.md
- HTML: https://vdsabev.github.io/overtherapizing-mens-mental-health/
```markdown
---
status: "published"
title: "Overtherapizing Men's Mental Health"
date: "2026-02-02"
description: "Talking about emotions isn't enough for men's mental health - finding meaning through Function, Family, and Faith offers a better path."
image: "https://github.com/user-attachments/assets/435e5746-13b6-4f78-b91f-f311ddf08a58"
imageAlt: "A gnarled pine tree with exposed roots on a rocky cliff overlooking Yosemite's Half Dome, illuminated by golden sunset light piercing through dramatic storm clouds with low mist in the valley."
author: "Vlad Sabev"
authorUrl: "https://github.com/vdsabev"
authorAvatar: "https://github.com/vdsabev.png"
tags: ["culture","family","psychology"]
navigationIndex: 0
---
*"Men need to talk about their feelings."*
But when they do, they're called *attention-seeking*.
*"Men should be expressing their emotions."*
But when they do, they're labeled as *toxic*.
*"Men can cry in public too."*
But when they do, women get the ick.
You *are* actually supposed to be composed and suppress your emotions in front of people. If all men expressed our emotions directly and instantly, the result would be an inordinate amount of violence and crime. And while talking about your feelings is fine, it doesn't solve the underlying problem, which is usually downstream of feeling useless, powerless, or replaceable. Instead, we need to channel our emotions into productive efforts.
Controlling your emotions is not unhealthy - we're just in the wrong environment for it. We have effectively defeated most natural problems we evolved for over a million years, and are left with societal problems that are infinite at any level of affluence (as classic Russian literature amply demonstrates). Men are not as good at solving those as they are at natural problems.
The other reason men's culture has become "unhealthy" in modernity is that we're increasingly giving up on following the main pathways to meaning:
- **Function** - work, charity, general usefulness to society. Email jobs, scrolling on social media, and playing video games does not help with feeling productive and useful.
- **Family** - most men will say providing for their family is the greatest purpose of their life, yet we are getting married and having children later and later in life, if at all.
- **Faith** - accessible to all, this path goes the deepest, and you can choose to follow it at any time - even at your very last breath. I have hope more of us will decide to.
In [Small Bets](https://smallbets.com) we talk about working like a lion on the hunt. But humans were once hunters too - and some still find meaning in defeating natural adversities. If you haven't, I recommend watching [Happy People](https://www.imdb.com/title/tt1683876) by Werner Herzog, or how some other indigenous people live to this day.
Maybe your instinct to go work on a farm is correct - if for nothing else, then to remember what it feels like when a problem is finite, physical, and responds directly to your effort. Then when you find yourself sitting around the table having dinner with family, pleasantly tired after a good day's work - all your troublesome feelings magically disappear.
On to the next day!
```
### Rich Storytelling with AI World Models
- Markdown: https://vdsabev.github.io/rich-storytelling-with-ai-world-models.md
- HTML: https://vdsabev.github.io/rich-storytelling-with-ai-world-models/
```markdown
---
status: "published"
title: "Rich Storytelling with AI World Models"
date: "2026-02-02"
description: "How AI world models like Genie 3 could revolutionize storytelling, democratize game development, and let creators build immersive worlds from imagination."
image: "https://github.com/user-attachments/assets/fa9dbba3-e376-4062-8083-591dc7e5cd19"
imageAlt: "Four friends in a living room watching a large, futuristic screen displaying a fantasy castle floating above the clouds, surrounded by digital data streams."
author: "Vlad Sabev"
authorUrl: "https://github.com/vdsabev"
authorAvatar: "https://github.com/vdsabev.png"
tags: ["ai","game development","storytelling"]
navigationIndex: 0
---
## 🧞 Genie 3 Out of the Bottle
A wider release of [Genie 3](https://deepmind.google/models/genie/) last week resulted in some [impressive demos on X](https://x.com/mattshumer_/status/2017058981286396001) of gameplay generated on the fly, without playing an actual game. Things that even 5 years ago nobody would imagine possible are now becoming reality.
Critics will point out one serious flaw - and it's not the low frame rate or the high cost, but the **lack of persistence**. One demo showed how [a mountain ledge disappears when you turn your back](https://x.com/firagawalkwthme/status/2017502322654646436). For most games, this is untenable - place a vase on a table, turn around, and it should still be there - not transform into a teapot or vanish entirely.
**Persistence matters** because it maintains the suspension of disbelief required for immersive storytelling. Without it, AI worlds become chaos engines that break reality with every glance. Instead of a coherent experience, you get a whooshing sense of vertigo. This could work well enough in a linear rogue-like game on rails, but it would never reach the richness and depth of the well-crafted worlds we know from literary works.
---
## ⛏️ The IP Goldmine
Remember the last time your beloved book series got adapted to TV? *Rings of Power*, *Wheel of Time*, *The Witcher*. How did those turn out? Exorbitantly expensive productions that strayed far from the source material fans loved.
Literally adapting a book scene-by-scene remains too expensive for traditional media. Studios face impossible choices: compress 1,000 pages into 8 hours, change characters to fit modern sensibilities, or invent new plotlines. Each compromise alienates fans who just wanted to see the world they know brought to life.
Games have been somewhat better at respecting the source material - one could, in fact, [simply walk into Mordor](https://www.youtube.com/watch?v=LYipECdYpXc) - but the development costs are still high.
The entertainment industry has long prioritized existing IP over original concepts. AI world generation will continue this - just as the Marvel Cinematic Universe built an interconnected franchise, models like Genie could let fans step into their favorite worlds - without the adaptation compromises.
---
## 🐉 Be the Hero in Our Own Story
While the prospect of experiencing our favorite worlds is exciting, the endless stream of remakes recycling the same characters and plots has created much fatigue. Stories need fresh settings, characters, and world mechanics - areas where AAA studios have been struggling.
Smaller developers have stepped into this gap. Games like *Stardew Valley*, *Hollow Knight*, and *Outer Wilds* prove small teams can create rich worlds that rival AAA productions. The indie game market has been growing much faster year-over-year compared to AAA sales. Meanwhile, tabletop gaming has exploded, with D&D 5th Edition reaching 50 million players globally, and actual-play shows like *Critical Role* drawing millions of viewers and spawning multiple Prime Video animated adaptations.
Creating compelling worlds remains the hardest part of game development. A medieval city of a million, a vast jungle of mysterious ruins, a galaxy of a trillion planets - these require time and resources most creators lack. Procedural generation helped *Minecraft* and *No Man's Sky* achieve scale, but at the cost of story depth.
World models like Genie can help bridge this gap - the scale of procedural generation plus the allure of a captivating story, delivered at the speed of AI. With **Vibe Editing**, the world emerges before your eyes - mountains shift, cities grow, stars are born with a sentence.
Tabletop Dungeon Masters plug their D&D campaign into an AI model and get a world where landscapes are generated on the fly and the Beholder's lair adapts to narration as the players battle. This eliminates physical constraints - you can keep rolling dice, but there's no need to print battle maps and move minis around anymore.
---
## 🔥 A Tale Worth Telling
Studios will profit from IP-driven world models - immersive Lord of the Rings experiences, explorable Hogwarts, interactive Star Wars galaxies. But the same technology that enables corporations to mine established IP will democratize world-building for solo creators - provided it is affordable enough.
Today, building a game world requires either a AAA budget or years of procedural system development. Soon, a storyteller with a vision might prototype their setting in an afternoon, iterate through natural conversation with an AI, and export a playable space. An indie developer could compete with the giants on imagination rather than pocket depth.
We're not there just yet - persistence remains glitchy, costs are high, copyright questions loom. Once we've made enough progress on these issues, we can build on a foundation of procedural generation, combined with persistent world models and vibe-editing. The trajectory points toward a future where world-building becomes an act of storytelling once again, not an engineering problem.
And when you have a tale worth telling - get some snacks ready, gather your friends and family, and sit comfortably around the living room screen. Like cavemen telling stories around the fire.
```
### Effective Code Reviews
- Markdown: https://vdsabev.github.io/effective-code-reviews.md
- HTML: https://vdsabev.github.io/effective-code-reviews/
```markdown
---
status: "published"
title: "Effective Code Reviews"
date: "2023-09-26"
description: "How to foster a culture of learning through code review."
image: "https://github.com/user-attachments/assets/dfd49146-348f-4e86-a7c1-c2030ed57c89"
imageAlt: "Photo by NESA by Makers on Unsplash"
author: "Vlad Sabev"
authorUrl: "https://github.com/vdsabev"
authorAvatar: "https://github.com/vdsabev.png"
tags: ["code review","software development","teamwork"]
navigationIndex: 0
---
Photo by [NESA by Makers](https://unsplash.com/@nesabymakers?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=medium&utm_medium=referral)
Don't want to read? Listen to this dude's video, it covers most of what I have to say:
Or, since you're here already — read on.
## 💭 Which one are you?
You might be one of these people:
1️⃣ You're sitting at your desk, your hair turning white while scrolling through your colleague **Alex**'s PR. You know, *that* **Alex**, the one who always shortens variable names to incomprehensible gibberish, writes zero tests, and copy-pastes 3 files with only a few lines changed instead of taking the time to write a scalable solution. Your fingers are itching to write a comment for each variable name using `camelCase` instead of `snake_case`, leave a scathing review, show everyone how it's done!!!
2️⃣ You've taken the time with your PR — added a link to the ticket, put some nice labels, even found a fun GIF! Then **Greg**, your lead engineer, leaves a review asking why you didn't "just do X". You could kindly tell **Greg** that X was the first thing you tried and it didn't work for a very good reason. But since **Greg** didn't ask politely you'll *snap at him* and *leave snarky comments* on his PRs from now on. *F-ing Greg!* Hey, he started it!!
3️⃣ Pull requests come and go. You just don't care to participate. What's all the fuss about anyway? The app seems to work. Code quality? That's just, like, your opinion, man 🤷♂️
4️⃣ Your pull requests are a *pleasure to review*, you start discussions by asking *polite questions* out of *genuine curiosity*, you're *proactive* about *proposing alternatives*, and *open to changing your mind* while also knowing how to *stand your ground* on the things that matter.
Recognize anyone you know? Maybe yourself?
## ✅ How to approach code reviews
You might think reviewing code is about catching bugs. Surely that *is* the case sometimes. Yet is that all it's good for?
> *🤔* But code review keeps our code clean, and clean code is good!
If maintaining clean code was the entirety of software development I would wholeheartedly agree. Other important parts are *learning* and *maintaining good social relationships* within your team — does telling people to "do it *this way*" without first asking *why* they did it it *that way* really help with those?
> *🤷♂️* It's just code, we shouldn't be that sensitive!
If you don't want the people you work with to be *sensitive* does that mean you want them to be…*insensitive*?
If you as a reviewer aren't willing to have an open and reasonable discussion on the best way to do something, but say *"just do it my way"* then who's really the one really being emotional about code?
> *😔* Okay I'm listening. What are you suggesting instead?
### ✅ Do ask questions and start discussions
Even the most experienced developers occasionally make mistakes or incorrect assumptions. They get tired, stressed out, or distracted.
So is leaving a comment like this really the best way to go about it?
> Extract this JSON to a separate file.
**Ask, don't tell.** Instead of assuming the author was being lazy or didn't know what they were doing, try to find out what was really going on.
*Caveat 1:* adding a question mark or *"maybe?"* after your statement does not make it a real question.
*Caveat 2:* rephrasing your statement as *"why didn't you"* or worse — *"why didn't you* ***just****"* does make it a real question, but a negative one.
How about turning it into a positive question? *"Have you considered"*, *"have you tried"*, *"how about"* (heeey — I used that one here), *"what would happen if"*, *"would this idea work?"*
### ✅ Do proactively propose solutions
Look for related documentation, articles, video, code examples, codepens, performance tests, libraries, tools. When proposing a code change — open a real code editor and try to type it out yourself first — you might find it doesn't work out as well as you thought it would.
### ✅ Respect that accepting your solution is the author's decision
Let people express their personal coding style now and then. Your project's code can look like it was written by the same super neat person and that's okay. But it's also okay if there's some personality in there as long as everyone can understand and work with it.
**Alex** used a `for … of` loop instead of `.forEach`? What would happen if you let that slide and focused on whether the code does what it's supposed to?
Here's how to address some situations.
## 1️⃣ When code works well but could use small improvements
### ❌ Rarely request code changes
Requesting code changes (e.g. on GitHub) should be used sparingly, only as a last resort to warn other reviewers against merging obviously buggy code.
While PR authors can choose to dismiss the blocking review and merge the changes anyway, reviewers might take that as an open act of disrespect.
The red color in GitHub's UI indicating "something's wrong" doesn't help either:
Getting a red-colored message from a dispassionate machine like a compiler, linter, or bot is to be expected; seeing it next to the picture of a real person you're working closely with is another thing. Sooner or later there's bound to be some resentment.
### ✅ Do leave a comment and withhold your approval until later
It costs you nothing and is completely neutral.
If the author simply ignores your comment without even acknowledging it then you might have bigger problems in your team than just "code quality". Work on those first.
Also, if other team members happily approve the PR despite your cautious comment and everything turned out fine, were the changes you wanted really that important after all?
## 2️⃣ When you notice incorrect formatting
### ❌ Don't be a human linter
Machines are perfectly suited for linting and formatting code — they're infinitely faster, cheaper, and more reliable in enforcing conformity than humans.
And humans program machines, turning many hours of manual human work into mere seconds.
Which job do you want to do?
### ✅ Do propose a lint config fix (or learn to let go)
If *prettier* and *ESLint* are okay with the code is it really worth talking about formatting? Unless it's to discuss whether to change the lint rules long-term, I think not. This is after all [why prettier exists in the first place](https://prettier.io/docs/en/why-prettier.html):
> By far the biggest reason for adopting Prettier is to stop all the on-going debates over styles.
And if we do decide to change the lint rules, we can do that in a separate PR instead of holding off on an important feature because there's no newline before that `return` statement.
## 3️⃣ When a PR has the same issue in multiple places
### ❌ Don't leave a comment for each instance
This clutters the discussion timeline and spams everyone's inbox. What if you only noticed 7 out of the 10 places this issue was present in? Would you rather get 70% only in this one PR, or think long-term, automate the solution, and get 100% of what you wanted in this and all future PRs?
### ✅ Do only leave 1 comment
Link to the other affected areas of the code, or leave instructions for the author to verify everything works on their own. Discuss long-term solutions like improving the linter configuration, building utility functions that make good practices easier to follow, or enhancing the project documentation.
And a final Do:
### ✅ Do stay curious and open to changing your mind
If you always insist on your own solution with no room for discourse does that mean you've made up your mind *forever*?
Are you even the same person you were last year, or have some of your opinions changed *ever so slightly*? Do you want to think the exact same way a year from now, or do you want to be *better*?
To even *have* the opportunity of becoming better some day, you have to first be *open* to the possibility that you *don't know everything* at present.
Genuine curiosity can shatter the concrete shell that judgment forms around our minds, and then willingness to learn can sprout up through the cracks.
So keep those cracks open — stay curious.
```
### On Alphabetically Ordering CSS Properties
- Markdown: https://vdsabev.github.io/on-alphabetically-ordering-css-properties.md
- HTML: https://vdsabev.github.io/on-alphabetically-ordering-css-properties/
````markdown
---
status: "published"
title: "On Alphabetically Ordering CSS Properties"
date: "2020-12-02"
description: "And other properties, really"
image: "https://github.com/user-attachments/assets/96a6a2ec-a8f7-4160-919e-75c5144f1ade"
imageAlt: "Photo by Héctor J. Rivas on Unsplash"
author: "Vlad Sabev"
authorUrl: "https://github.com/vdsabev"
authorAvatar: "https://github.com/vdsabev.png"
tags: ["css","software development"]
navigationIndex: 0
---
Photo by [Héctor J. Rivas](https://unsplash.com/@hjrc33?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=medium&utm_medium=referral)
It's a beautiful sunny day out. You're on your way back from the store when you see this person in the street:
You don't know exactly who he is, but he seems vaguely familiar. You remember his name was Michael or something, but you've been living under a rock for the past couple of decades so you're not quite sure.
When you get home you try to describe Michael to your friend Janice over chat, hoping she can help you remember who he is. How do you do that?
Let's say you and Janice agreed to use a language similar to CSS, but for **describing people**. A popular way of listing CSS properties is **alphabetically**:
```css
belt: 3in solid leather black;
build: athletic;
clothes: suit shirt tie;
facial-hair: goatee 3days;
fingers: 10 extra-long;
first-name: 'Michael;
glasses: sunglasses cool;
hair: medium straight brown full;
last-name: unknown;
max-age: 40;
max-height: 6ft 6in;
max-weight: 200lbs;
min-age: 30;
min-height: 6ft;
min-weight: 180lbs;
shoes: black official 14us;
shoulders: extra-wide;
```
It's certainly easy to list properties alphabetically — we can sort them in our IDE in seconds.
However, there's a principle in the software industry that code should be **optimized for reading rather than writing** because [we spent much more time doing the former](https://www.goodreads.com/quotes/835238-indeed-the-ratio-of-time-spent-reading-versus-writing-is).
Does the alphabetical order follow that principle?
What if we put the **most relevant properties first**? Michael Whatever, 30-something, tall, built like an athlete, wide shoulders, and the longest fingers you've seen in a while:
```css
first-name: 'Michael';
last-name: unknown;
min-age: 30;
max-age: 40;
min-height: 6ft;
max-height: 6ft 6in;
min-weight: 180lbs;
max-weight: 200lbs;
build: athletic;
shoulders: extra-wide;
fingers: 10 extra-long;
hair: medium straight brown full;
facial-hair: goatee 3days;
clothes: suit shirt tie;
glasses: sunglasses cool;
belt: 3in solid leather black;
shoes: black official 14us;
```
You might choose a different **order** of properties to describe Michael than someone else would. You might **group** the properties differently.
Is the order and grouping going to be perfectly uniform every time for every person describing anyone? Probably not, though we could enforce it using [the right tools](https://github.com/hudochenkov/stylelint-order). We would need tools to double-check for alphabetical order anyway since people often forget to sort or make mistakes.
Then why not also go the extra mile and make our code more meaningful and **easier to read rather than easier to write**?
````
### Writing Reliable Unit Tests for Our Components
- Markdown: https://vdsabev.github.io/writing-reliable-unit-tests-for-our-components.md
- HTML: https://vdsabev.github.io/writing-reliable-unit-tests-for-our-components/
````markdown
---
status: "published"
title: "Writing Reliable Unit Tests for Our Components"
date: "2020-11-08"
description: "Three common pitfalls when writing component unit tests and how to write more reliable tests that focus on the component's public interface."
image: "https://github.com/user-attachments/assets/5c0ec3ce-0627-493c-a58a-9a32c913d299"
imageAlt: "Photo by Clem Onojeghuo on Unsplash"
author: "Vlad Sabev"
authorUrl: "https://github.com/vdsabev"
authorAvatar: "https://github.com/vdsabev.png"
tags: ["software development","testing","vue"]
navigationIndex: 0
---
Have you been painstakingly cranking out unit tests for the past few months, trying to hit and maintain the coveted > 90% code coverage, only to now find yourself rewriting them again and again because of minor changes to your codebase?
Have your users bumped into subtle bugs that your unit tests just weren't able to catch, even though your component code is supposedly 100% covered?
Have your QA engineers been constantly asking you to add more selectors to elements in your components so that they are able to do their job and write automated tests?
In this post we'll be going over these 3 pain points that many have experienced because of the way we sometimes write component unit tests, and then we'll explore an alternative approach that addresses them.
## 📦 What are we testing?
For starters, let's take a simple Counter component implemented in Vue:
Here's what it looks like:
Now let's write some unit tests using [Vue Test Utils](http://vue-test-utils.vuejs.org):
We've tested each method that our component can call, and since there isn't much code in there it's relatively easy to cover all of it:
But is that really enough?
## 🤨 What's wrong with these tests?
Maybe nothing — at first glance they may look like perfectly acceptable tests. After all they do give us 100% code coverage, that means we should be good, right?
Let's first see if we can agree on 3 properties of reliable component tests:
1. **When tests pass, the component works; when the component works, the tests pass.** This gives us confidence to refactor our code.
2. **When tests fail, the component doesn't work; when the component doesn't work, the tests fail.** This gives us easier debugging since failing tests pinpoint *where* in the code we should be looking for a bug.
3. **Writing tests encourages better engineering practices for our components and makes them easier to test by anyone,** not just people intimately familiar with the internals. This gives us scale across the entire team, including QA and even 3rd party consumers of our components.
Now let's see where the tests we have right now fall short.
### 1️⃣ Refactoring makes them fail even though our component still works
One of the benefits of having reliable unit tests is they help us refactor fearlessly. You might have noticed we pass the `step` prop when calling `increment` and `decrement` in the template when instead we could just access the prop in the method using `this.step` and clean up our template a little bit. Let's make that small change:
This is just some internal refactoring, the Counter component still works, but what happens to our tests?
They break right away, because we were previously passing 10 to the method and since it no longer accepts an argument it uses the default value of the `step` prop which is 1!
Right about now you may be shaking your head saying — "of course they would break, what did you expect, it's obvious?!"
Well, I expect that when my component works the exact same way as before for an end user my tests will also pass without having to change them. Think about that for a moment — does testing components like this make it *easier* to refactor them or *more difficult*? Does it *remove* obstacles or does it *add more*?
Now we have to rewrite those failing tests. Here's what it would take to fix them:
We have to set the `step` prop beforehand and we're good to go for now.
### 2️⃣ Changing the template doesn't make them fail even though the component no longer works
Remember when we removed that pesky `step` prop from the template? That cleaned it up a little bit, but we're still passing an argument to `setValue` in there. So let's clean that up as well:
Ah, looks much better, and our tests are still green!
Wait, what happened to our component?
We just deleted the argument from `setValue` which was sending the input element's value — and now typing in the input no longer works because it calls `setValue` with `$event` instead of `$event.target.value`
And our tests, with their glorious 100% of code coverage, did exactly nothing to protect us from that.
You may think you're a careful and experienced developer and would *never* make a silly mistake like that. But let me ask you this — are you the only person working on this project — now and forever?
Besides, mistakes don't happen solely due to inexperience — people get tired because of long working hours, distracted by children or pets, or stressed out when an important deadline looms close.
*(I also sincerely hope you don't drive with* ***no*** *seatbelt on just because you think you're a careful and experienced driver)*
### 3️⃣ They don't help us make the component easier to test from the outside
How would a QA engineer write an automated script that tests this component in the UI? Maybe query for the buttons with JavaScript like this:
```javascript
const decrementButton = document.querySelectorAll('button')[0];
const incrementButton = document.querySelectorAll('button')[1];
```
This works for now, but what if we put out a new design that switches the order of the DOM nodes and renders them vertically like in the [HTML number input](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number)?
Alternatively, a QA engineer could select the buttons by their text content `-` or `+` which is a viable approach but also has trade-offs — for example it would break if we replaced the text content with equivalent icons.
Classes also work but could be inadvertently removed by a developer who doesn't know they're used for testing.
And so in this specific case I prefer [`aria-label`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-label_attribute):
It's explicit, meaningful, and yes it does happen to make our component more accessible to users with special needs, but more importantly in this case — to our QA engineers.
However, no matter which approach to selecting elements we take, the component tests we have right now leave the QA team as an afterthought. We're likely to only add the selectors when we're asked to do so by someone else trying to test our component from the outside (and failing miserably).
In summary, there's an ancient Zen saying that describes our predicament succinctly:
> ***💭* Checking whether a component behaves correctly by testing its internals is like judging a tapestry by looking at its back side.**
Yes, I know that's embroidery 🤓
## 🤔 What's a reliable test supposed to do?
As long as a component looks and behaves the same, unit tests should leave us free to rework our code so we can make it easier to maintain. Also, code that affects component behavior should be tested, even if it doesn't get captured in a coverage report. Finally, components should be accessible for testing from the outside.
So how do we actually do that?
What's going on here? First, we select the DOM node we want to test using [`component.find`](https://vue-test-utils.vuejs.org/api/wrapper/#find), then trigger the corresponding event.
Remember the issues we had while refactoring our component code before? As expected, the first test would've caught the incorrect argument of `setValue`, and the second and third tests would still be passing after removing the `step` prop from the template.
This is also where we make use of the `aria-label` attributes, and because we needed to set those to select the elements in our unit tests, accessibility to QA is now a first-class citizen!
You might still have some questions right about now.
### 🙋♂ Isn't this unnecessarily testing the framework?
We're testing the event bindings we wrote in the HTML template, not the internal workings of Vue. In fact these tests aren't even specific to Vue — we could just as well write our own `mount` function and re-implement the component in another framework while still keeping our tests unchanged.
If we want to be able to trust our test suite, then all code that affects component behavior should be tested, even if it's "just HTML", maybe even if it's CSS like `display: none` or `pointer-events: none` which can change how users interact with an element.
And the earlier we can test it, the better — bugs don't have to be caught by a QA engineer after our code has already passed multiple developer reviews and gone through 60-minute cycles of CI/CD build and deploy.
### 🙋♀ Isn't this more work?
There are trade-offs to any approach. In this case the first test is 2 lines longer, and we do need to learn how to work with the DOM and think about what selectors to put in our code beforehand.
What if instead of thinking about those extra few lines of code we were to consider all the hours saved by *not* having to fix subtle bugs, rewrite tests because of minor code refactors, or have back-and-forth discussions about selectors with our QA engineers?
### 🙋♀ Isn't this way of testing non-obvious?
This approach is far from niche — in fact testing the component's public interface and template logic is even mentioned in the [Vue Test Utils docs](https://vue-test-utils.vuejs.org/guides/#knowing-what-to-test). There are also libraries that specifically focus on this school of testing, the best know of which is the aptly named [Testing Library](https://testing-library.com) utilities.
Last but not least — keep in mind that QA engineers have been black-box testing for decades, and there's a lot we could learn from each other.
## 🏁 In summary
You do need a mental shift to write tests this way — it's not always easy or clear-cut, especially when using 3rd party components in your templates that don't have accessible selectors.
I hope you'll find that once you start thinking about the *front side of the tapestry* you'll be noticing opportunities to write more reliable tests everywhere.
````
### Simple Naming Tips for Event Handlers
- Markdown: https://vdsabev.github.io/simple-naming-tips-for-event-handlers.md
- HTML: https://vdsabev.github.io/simple-naming-tips-for-event-handlers/
````markdown
---
status: "published"
title: "Simple Naming Tips for Event Handlers"
date: "2018-12-23"
description: "Helping our team (and ourselves) write more meaningful and robust code."
image: "https://github.com/user-attachments/assets/79b7d8b3-688c-4be0-a5ff-aea60b9b56bd"
imageAlt: "Image"
author: "Vlad Sabev"
authorUrl: "https://github.com/vdsabev"
authorAvatar: "https://github.com/vdsabev.png"
tags: ["javascript","react","software development"]
navigationIndex: 0
---
Photo by [Samuel Zeller](https://unsplash.com/@samuelzeller?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=medium&utm_medium=referral)
## 🎯 The Challenge
Here's something we've probably all seen— component methods named after the events they're handling:
In addition to React, I've also seen this pattern in Angular, Backbone, jQuery…it has probably existed for as long as the first programming language that had event handlers.
Here's an example with event listeners in vanilla JavaScript:
```javascript
const modal = document.querySelector('#confirmation-modal');
modal.addEventListener('yes', onYes);
modal.addEventListener('no', onNo);
modal.addEventListener('close', onClose);
```
> For the difference between event **handlers** and **listeners** see [this definition on MDN](https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Event_handlers#Terminology). The tips in this post apply to both.
Okay, so handler names like `onYes`, `onNo`, `onClose`…what about them?
### ⚠️ Duplicate functionality
In the `EditForm` component it's relatively easy to see that `this.onNo` and `this.onClose` do the exact same thing — especially since they're defined right next to each other!
Of course we can change those events to use the same handler:
```jsx
...
...
```
Whew! Problem solved? Perhaps, at least in this case.
Now think about this for a second — if we continue this practice of naming all handlers based on the events that trigger them, how likely is it that we'd introduce duplicates like this in the future?
And how likely is it that we'd miss those duplicates in a large and complex component?
### ⚠️ Short-lived names
Eh, maybe we don't care about duplicates as much — our code works fine and at least the naming is consistent.
Oh look, our colleague Jane just found a really awesome open source modal library that looks utterly amazing!
She's now starting to use it across the whole application, and asks us to help by also changing it in the `EditForm` since we're working on that component anyway.
So we do:
```jsx
...
...
```
Hmm, looks like the event names for this one are a bit different. It actually doesn't even have `onClose` anymore.
Our code still works fine — it's just the naming that has become inconsistent!
What do we do? Should we rename all our handlers now?
## 💭 The Suggestion
I think we should!
And if we want the new names to stick around, I would suggest we base them on **what** they do instead of **when** they do it.
Here's our `EditForm` using the original `ConfirmationModal` component, but with a different way of naming event handlers:
What differences did you see?
For example did you notice how we now used `this.hideDiscardModal()` in multiple places instead of `this.setState({ discardModalIsShown: false })`?
> It's much easier to think of using something when we put a meaningful name to it.
`hideDiscardModal` carries more meaning about **what** it does than `onNo`, so we'll naturally think of it in more situations where we need to…you know…hide the discard modal.
## 🤔 The Dilemma
We've renamed all the component's handlers, yet its props are still called `onSave` and `onDiscard` instead of `save` and `discard` respectively.
Should we rename them too?
I've heard both opinions — my preference is to keep the `on` prefix, as they're event names, not handlers.
> Think of `onSave` as something that allows subscribing to the component's `save` event, and [we already use the `on` prefix for that in JavaScript](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers).
## 📝 The Summary
When handling events like this:
```jsx
```
We followed these simple naming tips:
> 1\. Event names should answer the question **when**, because they're on the **left side** and describe the **conditions under which events trigger**.
>
> 2\. Handler names should answer the question **what**, because they're on the **right side** and describe the **effects that events have**.
What's the way you do it? Got any further naming tips?
````
### Learning Form Management in React
- Markdown: https://vdsabev.github.io/learning-form-management-in-react.md
- HTML: https://vdsabev.github.io/learning-form-management-in-react/
````markdown
---
status: "published"
title: "Learning Form Management in React"
date: "2018-10-08"
description: "By building our own reusable component from first principles."
image: "https://github.com/user-attachments/assets/3ad59f4f-05d1-4a81-9b8e-0b8bd14b0efc"
imageAlt: "Photo by rawpixel on Unsplash"
author: "Vlad Sabev"
authorUrl: "https://github.com/vdsabev"
authorAvatar: "https://github.com/vdsabev.png"
tags: ["forms","javascript","react","software development"]
navigationIndex: 0
---
Photo by [rawpixel](https://unsplash.com/@rawpixel?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=medium&utm_medium=referral)
Learning often comes from deliberate practice and repetition—making the same mistakes, writing the same code, solving the same problems over and over again.
For example, how many times have you had to:
- Render a form with some inputs
- Collect data from these inputs as the user enters values
- Process the collected data on submit
Me personally — multiple times, in every project.
## 🤔 The Simple Solution
Of course there already *are* libraries for managing forms in React. A couple of the most popular are:
- [Jared Palmer](https://medium.com/u/fb7a3c353cc1)'s [Formik](https://github.com/jaredpalmer/formik), which uses the same principles described in this post
- [redux-form](https://github.com/erikras/redux-form), which manages form state via Redux (well, duh)
**If you're in a real hurry to put some forms in your project I encourage you to stop reading right now and use one of those libraries — they're full-featured, well-tested, and have a significant community around them.**
Otherwise, I've found that to understand a tool in more depth it really helps to try and build your own and *then* switch to a library if you really need to.
In fact, I only started appreciating the benefits and trade-offs of Redux when I went through the process of writing [a state management library](https://github.com/vdsabev/overstate) myself.
We'll be doing something similar here — by writing a reusable `
);
}
}
```
This does exactly the same thing as the demo from the previous section — it renders a form with:
- Two text inputs
- One checkbox
- And a submit button.
When the form is submitted it shows an alert dialog that "orders" your favorite food.
## 2️⃣ The Refactoring Round
One painfully dull thing was that we wrote three methods for collecting values from each input. So if we had 300 inputs would we then have to write 300 methods?
Let's change that by using a more generic method instead:
```javascript
class App extends React.Component {
...
setValue = (e) => {
const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
this.setState({ [e.target.name]: value });
};
render() {
return (
);
}
}
```
The main things here are that we:
- Added a `name` attribute to inputs
- And handled checkboxes a bit differently from text inputs by using the target's `checked` attribute instead of `value`.
Now, whether we have 3 or 300 components in there, we only need to set a `name` attribute and that one `onChange` handler.
## 3️⃣ The Rad Rendering
To make this component's behavior reusable across our application, we need to be able to provide *different* inputs as its children, as well as attach `onChange` handlers from the parent component to said children.
And how can we both:
- Provide children to a parent component
- *And* allow children to access functions from their parent?
Why, by using the rad [render props](https://reactjs.org/docs/render-props.html) pattern of course!
```javascript
class Form extends React.Component {
state = {};
submit = (e) => {
e.preventDefault();
this.props.onSubmit(this.state);
};
setValue = (e) => {
const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
this.setState({ [e.target.name]: value });
};
render({ children, onSubmit, ...props } = this.props) {
return (
);
}
}
```
The trick here is that the `children` props of our component is actually a function — one that we call with an object exposing some API from the `