<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="pretty-atom-feed.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>Colton Voege</title>
  <subtitle>Just another grumpy dev blog.</subtitle>
  <link href="https://colton.dev/feed/feed.xml" rel="self" />
  <link href="https://colton.dev/" />
  <updated>2025-09-20T00:00:00Z</updated>
  <id>https://colton.dev/</id>
  <author>
    <name>Colton Voege</name>
  </author>
  <entry>
    <title>Listen to Me Yap about AI on Better Offline w/ Ed Zitron!</title>
    <link href="https://colton.dev/blog/listen-to-me-yap-on-better-offline/" />
    <updated>2025-09-20T00:00:00Z</updated>
    <id>https://colton.dev/blog/listen-to-me-yap-on-better-offline/</id>
    <content type="html">&lt;p&gt;My &lt;a href=&quot;https://colton.dev/blog/curing-your-ai-10x-engineer-imposter-syndrome/&quot;&gt;last post&lt;/a&gt; went more viral than I could have expected! I got a lot of emails from people saying how much they appreciated my thoughts on the subject. I hope I made some people feel like they were not falling behind.&lt;/p&gt;
&lt;p&gt;Ed Zitron (host of &lt;a href=&quot;https://www.betteroffline.com/&quot;&gt;Better Offline&lt;/a&gt; and writer for &lt;a href=&quot;https://www.wheresyoured.at/&quot;&gt;Where&#39;s Your Ed At&lt;/a&gt;) invited me onto Better Offline to talk more about what AI is and isn&#39;t in coding. Pop over there to hear me yap more!&lt;/p&gt;
&lt;p&gt;On &lt;a href=&quot;https://open.spotify.com/episode/1jS1cDADdC0bdiubN9sA3B?si=510a68982aff487c&quot;&gt;Spotify&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;On &lt;a href=&quot;https://podcasts.apple.com/us/podcast/ai-isnt-making-engineers-10x-as-productive-with-colton/id1730587238?i=1000727446652&quot;&gt;Apple Podcasts&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>No, AI is not Making Engineers 10x as Productive</title>
    <link href="https://colton.dev/blog/curing-your-ai-10x-engineer-imposter-syndrome/" />
    <updated>2025-08-05T00:00:00Z</updated>
    <id>https://colton.dev/blog/curing-your-ai-10x-engineer-imposter-syndrome/</id>
    <content type="html">&lt;p&gt;A few months ago I went through a bit of a mental slump. I&#39;ve always been confident of my abilities as an engineer, but I couldn&#39;t help but feel like my skills were falling hopelessly behind as I scrolled places like LinkedIn and Twitter. If these sources were to be believed, engineering had moved on from the medieval practice of typing code into an editor. &lt;em&gt;Real&lt;/em&gt; engineers were now 10-100x more productive than I was. I&#39;m writing this hoping to help others who are feeling similar anxieties.&lt;/p&gt;
&lt;p&gt;I&#39;m a skeptical person so I don&#39;t usually fall over myself immediately when I hear a claim like that. I usually roll my eyes in the same way I do when someone tells me a simple herbal remedy cures all disease. But the sheer volume these engineer productivity claims are reaching right now started to hit a nerve. What if I&#39;m &lt;em&gt;wrong&lt;/em&gt;? Will I miss the bus and become unemployable if I don&#39;t learn to use AI right now? After all, there are a lot of fancy words going around that distance the &amp;quot;AI&amp;quot; these people are talking about with the &amp;quot;AI&amp;quot; I was familiar with.&lt;/p&gt;
&lt;p&gt;These people were using &lt;em&gt;✨agentic✨&lt;/em&gt; AI. They were using &lt;em&gt;✨thinking✨&lt;/em&gt; models that surfed the internet, ran tests, and corrected their own mistakes. Sure I popped into a chat window here and there and asked it to write some code, then promptly discarded most of the output once I got the idea that I needed. But these engineers were letting Claude fully take the wheel and had agents ripping 5 PRs for them while they made morning coffee. Was I becoming a dinosaur, an old man yelling at cloud?&lt;/p&gt;
&lt;p&gt;Part of what made me feel so anxious was that it was entirely possible AI changed without me knowing it because I didn&#39;t use AI very much. Because I didn&#39;t &lt;em&gt;like&lt;/em&gt; using AI that much. Reviewing code is vastly less enjoyable process than writing it. Had my stubborn desire to &lt;em&gt;enjoy coding&lt;/em&gt; set me up to be left behind?&lt;/p&gt;
&lt;h2 id=&quot;diving-in&quot;&gt;Diving In&lt;/h2&gt;
&lt;p&gt;Eventually I hit a breaking point and decided I simply had to dive in head first to AI coding. I tried Claude Code, Cursor, Roo Code, and Zed for their agentic coding promises. I started asking AI to write all sorts of code in all sorts of projects. I tried the different models and compared them. I even vibe coded a few things, not editing the code manually once.&lt;/p&gt;
&lt;p&gt;And it was... Fine. Despite claims that AI today is improving at a fever pitch, it felt largely the same as before. It&#39;s good at writing boilerplate, especially in Javascript, and particularly in React. It&#39;s not good at keeping up with the standards and utilities of your codebase. It tends to struggle with languages like Terraform. It still hallucinates libraries leading to significant &lt;a href=&quot;https://en.wikipedia.org/wiki/Slopsquatting&quot;&gt;security vulnerabilities&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;AIs still struggle to absorb the context of a larger codebase, even with a great prompt and &lt;code&gt;CLAUDE.md&lt;/code&gt; file. If you use a library that isn&#39;t StackOverflow&#39;s favorite it will butcher it even after an agentic lookup of the documentation. Agents occasionally do something neat like fix the tests they broke. Often they just waste time and tokens, going back and forth with themselves not seeming to gain any deeper knowledge each time they fail. Thus, AI&#39;s best use case for me remains writing one-off scripts. Especially when I have no interest in learning deeper fundamentals for a single script, like when writing a custom ESLint rule.&lt;/p&gt;
&lt;p&gt;Dark warnings that if I didn&#39;t start using AI now I&#39;d be hopelessly behind proved unfounded. Using AI to code is not hard to learn. Obviously? Well, the AI coding community seems split on whether AI makes coding so easy a caveman can do it or that it requires an advanced, dedicated prompt engineer skillset. There are a few things you need to learn but they come quickly. You learn how to split up tasks into smaller pieces so the AI doesn&#39;t lose its mind late in the context window. Tools like Claude Code can do a bit of this themselves, even, though not always reliably. And you learn to identify when the AI is too far off and it&#39;s time to take the wheel.&lt;/p&gt;
&lt;p&gt;A competent engineer will figure this stuff out in less than a week of moderate AI usage. Further, if AI is about to get 2x, 10x, or 100x better at any minute (as everyone keeps saying it will), then any lessons about how to use it now are moot for the future.&lt;/p&gt;
&lt;p&gt;Every time I encountered AI working &amp;quot;just okay&amp;quot;, it strangely made me more anxious, not less. It meant I couldn&#39;t find the spicy secret sauce that made everyone else so productive. I just didn&#39;t have what it takes: dinosaur, meet asteroid, thy name is AI. Eventually, a few things shook me out of this slump. One of those was &lt;a href=&quot;https://ludic.mataroa.blog/blog/contra-ptaceks-terrible-article-on-ai/&quot;&gt;this article&lt;/a&gt; from Ludicity, directly countering the claims of the AI pumpers. I write this article to share more things that helped me get out of the AI 10x engineer imposter syndrome.&lt;/p&gt;
&lt;h2 id=&quot;the-math&quot;&gt;The Math&lt;/h2&gt;
&lt;p&gt;Let&#39;s start by looking at the simple math of 10-100x productivity. 10x productivity means ten times the outcomes, not ten times the lines of code. This means what you used to ship in a quarter you now ship in a week and a half. These numbers should make even the truest AI believer pause. The amount of product ideation, story point negotiation, bugfixing, code review, waiting for deployments, testing, and QA in that go into what was traditionally 3 months of work is now getting done in 7 work days? For that to happen each and every one of these bottlenecks has to also seen have 10x productivity gains.&lt;/p&gt;
&lt;p&gt;Any software engineer who has worked on actual code in an actual company knows this isn&#39;t possible. You can&#39;t compress the back and forth of 3 months of code review into 1.5 weeks. When you code review you:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Tag your reviewer&lt;/li&gt;
&lt;li&gt;Hope they will get to it sooner rather than later (which will be tough because they are apparently code reviewing 10x as much code as before)&lt;/li&gt;
&lt;li&gt;Context switch to something else while you wait&lt;/li&gt;
&lt;li&gt;See a notification (perhaps immediately, perhaps 2 hours after your reviewer went offline for the day)&lt;/li&gt;
&lt;li&gt;Context switch back to the review&lt;/li&gt;
&lt;li&gt;Read their comments&lt;/li&gt;
&lt;li&gt;Respond accordingly&lt;/li&gt;
&lt;li&gt;Rinse and repeat.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This process can be made fairly efficient at a competent company with good standards and communication practices. But you&#39;re telling me you made this process &lt;strong&gt;10 times&lt;/strong&gt; as efficient to handle 10x the work? This simply can not be done.&lt;/p&gt;
&lt;p&gt;The human processes involved in actual corporate software engineering have not changed significantly. Product managers might use ChatGPT to do &amp;quot;research&amp;quot; but they aren&#39;t suddenly pumping out ten times as many well vetted, well justified, well estimated stories as they did before. They can not do 10 user interviews all at once. The same goes for Designers and QA testers. Hiring 10x the number of PMs to keep up isn&#39;t feasible. Each hire has diminishing returns as network effects and bureaucracy take hold.&lt;/p&gt;
&lt;p&gt;Even if we assume people mean only the actual code writing process is now 10-100x faster, we should still be skeptical of how this maths out. When you write code, how much of your time do you truly spend pushing buttons on the keyboard? It&#39;s probably less than you think. Much of your prime coding time is actually reading and thinking, often while waiting for compiling, a page refresh, or for tests to run. LLMs do not make &lt;code&gt;rustc&lt;/code&gt; go faster.&lt;/p&gt;
&lt;p&gt;What LLMs produce is often broken, hallucinated, or below codebase standards. The frequency of these errors go up with the size of the codebase. When that happens you have to re-prompt, which could instantly fix the problem or could be a huge waste of time. Or you can go in and fix the code yourself. But then you&#39;re back to measly 1x engineer status, perhaps worse if you&#39;ve gotten so used to vibe coding you &lt;a href=&quot;https://nmn.gl/blog/ai-and-learning&quot;&gt;forgot how to code&lt;/a&gt;. If you&#39;re &amp;quot;embracing the vibes&amp;quot; and not even looking at the code produced, you&#39;re simply going to hit a productivity wall once the codebase gets large enough. And once you do you&#39;ll have to reckon with the complete lack of standards and proper abstractions.&lt;/p&gt;
&lt;p&gt;I think sometimes people lose the scale of just how big a 10x improvement is. 10x is the difference between your mini-van and a record setting &lt;a href=&quot;https://en.wikipedia.org/wiki/ThrustSSC&quot;&gt;supersonic land jet&lt;/a&gt;. Imagine trying to drive your 10 minute commute down your city streets in a car that goes 600mph. Will you get to the other side of town in one tenth the time? No, because even a single 60 second stoplight will eat up your entire time budget. F1 cars slow down to mini-van speeds in basic turns. It turns out that most of any activity is not spent going at top speed.&lt;/p&gt;
&lt;p&gt;100x productivity means you now do what used to be one year of work in two days. I shouldn&#39;t even need to touch the ludicrousness of numbers at that scale.&lt;/p&gt;
&lt;h2 id=&quot;do-10x-engineers-exist&quot;&gt;Do 10x Engineers Exist?&lt;/h2&gt;
&lt;p&gt;This debate isn&#39;t something I want to weigh in on but I might have to. My answer is sometimes, kinda. When I have had engineers who were 10x as valuable as others it was primarily due to their ability to &lt;em&gt;prevent unnecessary work&lt;/em&gt;. Talking a PM down from a task that was never feasible. Getting another engineer to not build that unnecessary microservice. Making developer experience investments that save everyone just a bit of time on every task. Documenting your work so that every future engineer can jump in faster. These things can add up over time to one engineer saving 10x the time company wide than what they took to build it.&lt;/p&gt;
&lt;p&gt;Work of this nature is not always available, so great engineers will only find themselves being 10x as productive in certain situations. At a certain point every engineer just needs to build features, which a great engineer might do twice as fast as a junior engineer, but they&#39;ll still hit the same bottlenecks as before. Flawed as story points are, I&#39;ve never seen an engineer actually complete ten times as many as an average engineer consistently.&lt;/p&gt;
&lt;p&gt;Notably, AI coding assistants do very little to prevent unnecessary work. On the contrary, AI often seems to encourage hastiness and over-building. When I ask architectural questions, it often recommends something that I realize is not necessary after a good night&#39;s sleep or a talk with a great engineer. All other things held the same, is a faster coder a better engineer? Yes, but it&#39;s not the 10x difference maker and it&#39;s hard to hold everything else constant. The more you focus on pumping out tasks as fast as possible the easier is to miss the important time savers that reduce total work.&lt;/p&gt;
&lt;h2 id=&quot;so-are-the-ai-posters-lying-or-what&quot;&gt;So are the AI-posters lying or what?&lt;/h2&gt;
&lt;p&gt;I think the AI-posters are a mix of the following, in order of least to most malevolent:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Good-natured folks who are mismeasuring themselves and others&lt;/li&gt;
&lt;li&gt;People heavily invested, personally or financially, in the success of AI (AI startup founders, investors, etc.)&lt;/li&gt;
&lt;li&gt;Bosses outright trying to make their engineers feel precarious so they don&#39;t quit, look for other jobs, or ask for raises&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-good-natured-engineer-with-bad-math-skills&quot;&gt;The good-natured engineer with bad math skills&lt;/h3&gt;
&lt;p&gt;In my experience, AI delivers rare, short bursts of 10-100x productivity. When I have AI write me a custom ESLint rule in a few minutes, which would have taken hours of documentation surfing and tutorials otherwise, that&#39;s a genuine order of magnitude time and effort improvement. Moments like this do happen with AI. Many career non-coders have felt the magic in the first few days after spinning an app up with Lovable.&lt;/p&gt;
&lt;p&gt;The problem is that productivity does not scale. I don&#39;t write more than one ESLint rule per year. This burst of productivity was enabled solely by the fact that I didn&#39;t care about this code and wasn&#39;t going to work to make it readable for the next engineer. If constantly writing ESLint rules became a core job requirement I&#39;d sink the one-time cost to learn how ESLint internals work. After that, there simply wouldn&#39;t be a big difference in the time it takes to vibe code a rule vs. write it myself, especially when you add in the extra time to make my code human readable for when I come back to this file in 6 months.&lt;/p&gt;
&lt;p&gt;Eventually every vibe coder reaches the point where the returns start heavily diminishing. Their &lt;a href=&quot;https://pivot-to-ai.com/2025/03/18/guys-im-under-attack-ai-vibe-coding-in-the-wild/&quot;&gt;site gets hacked&lt;/a&gt; and they need to actually sink the time to learn how security works. The app gets too big for context windows and things start looking and functioning inconsistently. Real frontend engineers who know what they are doing are hired to implement a consistent design system and UX.&lt;/p&gt;
&lt;p&gt;There&#39;s also a lot of simple biases and blind spots that can cause a productivity illusion. If you leave the depths of big corporate for a startup you will genuinely be shocked at how much more productive each engineer is. It&#39;s easy to credit this to AI. Some people really enjoy the technological novelty of AI coding and when you are working in something new you often feel like you&#39;re doing more than you ever did. I know the first time I used Python I felt like I was &amp;quot;sipping rocket fuel&amp;quot;, but, as with all other technologies, it always comes back down to earth.&lt;/p&gt;
&lt;p&gt;I think a lot of the more genuine 10x AI hype is coming from people who are simply in the honeymoon phase or haven&#39;t sat down to actually consider what 10x improvement means mathematically. I wouldn&#39;t be surprised to learn AI helps many engineers do certain tasks 20-50% faster, but the nature of software bottlenecks mean this doesn&#39;t translate to a 20% productivity increase and certainly not a 10x increase.&lt;/p&gt;
&lt;h3 id=&quot;incentives-matter&quot;&gt;Incentives matter&lt;/h3&gt;
&lt;p&gt;Look, I&#39;m not an AI startup hater. If you want to plug OpenAI&#39;s API into your healthcare startup I might raise an eyebrow of concern over the risks, but I&#39;d do the same for any startup desiring to move fast and break things in the medical field. My goal here isn&#39;t to say AI startup founders or investors are evil or even dishonest. My point is to say in the droll voice of your high school Econ 101 professor, &amp;quot;Incentives Matter&amp;quot;.&lt;/p&gt;
&lt;p&gt;If you are running an AI startup and every other AI startup is telling investors they are seeing 10x more productivity thanks to AI, the incentives are plain and simple: you should say the same publicly and privately. If your company is built on the back of AI, you are incentivized to sell AI as a miracle solution in every part of life. If you are an engineer and your boss asks you:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hey, you&#39;re getting 10x the productivity thanks to AI, just like all the other engineers, right?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You are strongly incentivized to say yes. And when every other engineer also says yes for the same reason, that CEO &lt;a href=&quot;https://www.youtube.com/watch?v=vn_PSJsl0LQ&quot;&gt;isn&#39;t lying&lt;/a&gt;, they are just relaying what they heard.&lt;/p&gt;
&lt;p&gt;What I&#39;d like to stress to those feeling anxiety like me is that this is nothing new. CEOs are not unbiased sources. Executives have been claiming that everything from Agile to Meyers-Briggs have unlocked limitless productivity. There will always be a new synergistic buzzword on LinkedIn, don&#39;t let it get you down. In fact, stop scrolling LinkedIn at all. It&#39;s a silly place.&lt;/p&gt;
&lt;h3 id=&quot;outright-malice&quot;&gt;Outright Malice&lt;/h3&gt;
&lt;p&gt;When something is said that makes people feel anxious, at least some of the time you should conclude it&#39;s because that&#39;s what the speaker wanted to happen. Bosses trying to make their engineers feel like their position is precarious is also nothing new. We all remember the narrative that a 3 month coding bootcamp could churn out 4-year-degree quality engineers, so you&#39;d best not get too uppity or you&#39;ll be replaced with a bachelor of arts doing a career pivot. Then a few years went by and people realized that bootcamp grads were usually &lt;a href=&quot;https://www.sandofsky.com/lambda-school/&quot;&gt;woefully underprepared&lt;/a&gt; for actual software engineering since they were not given the proper foundation.&lt;/p&gt;
&lt;p&gt;Bootcamps and AI are just examples in a long series of poorly born out threats to commoditize the highly expensive, highly professionalized field of software engineering. They are rhetorical devices designed to imply precarity. Your boss can&#39;t actually fire you and replace you with AI, but he can make you &lt;em&gt;feel&lt;/em&gt; like he &lt;em&gt;could&lt;/em&gt;, and maybe not ask for that raise.&lt;/p&gt;
&lt;p&gt;Some amount of the 10x AI engineer story is likely being told by people who simply want you to feel bad for this purpose. How much of it, I don&#39;t know. Despite how highly distrustful we&#39;ve become of each other in these times, I still believe most people are fundamentally decent, so I&#39;m not inclined to believe it&#39;s a high percentage.&lt;/p&gt;
&lt;h2 id=&quot;degrees-of-separation&quot;&gt;Degrees of separation&lt;/h2&gt;
&lt;p&gt;One thing I&#39;ve noticed about all these characters in AI coding hype pieces is there is almost always a degree of separation from the writer to the actual productivity benefits. The poster is a founder, or a manager, or an investor, making grandiose claims about someone else&#39;s productivity. There&#39;s nothing wrong with secondary sources but if you can&#39;t find a primary source, you might start questioning the reliability of the information.&lt;/p&gt;
&lt;p&gt;Presentations from actual engineers demonstrating how they achieve more productivity with AI are much more varied and much more muted in their praise. These demos show largely AI as the same technology you and I were familiar with before we got so anxious: a neat text generator that sometimes does magic but often requires you to take the wheel.&lt;/p&gt;
&lt;p&gt;AI usage on open source projects, where the productive process can be publicly witnessed, has famously been a &lt;a href=&quot;https://old.reddit.com/r/ExperiencedDevs/comments/1krttqo/my_new_hobby_watching_ai_slowly_drive_microsoft/&quot;&gt;hilarious failure&lt;/a&gt;. I have learned things about how to use AI better from a few youtube videos. &lt;a href=&quot;https://www.youtube.com/watch?v=sQYXZCUvpIc&quot;&gt;Here&#39;s&lt;/a&gt; a good one referenced in that Ludicity article above. I&#39;ll spoil it for you though, this engineer has not found the fountain of coding productivity.&lt;/p&gt;
&lt;h2 id=&quot;its-okay-to-be-less-productive&quot;&gt;It&#39;s okay to be less productive&lt;/h2&gt;
&lt;p&gt;Even after I got over the idea that there was a secret clade of engineer who was now ten times as productive and strong and tall and sexy as I was, I still felt some anxiety over the fact that I still didn&#39;t enjoy using AI very much. Vibe coding is a complete bore once the magic wears off. Reading LLM generated code sucks. Asking it politely to use a not hallucinated library is painful. But what if I was, despite all that, 20% more productive vibe coding than regular coding? Would it be wrong for me to do &amp;quot;normal&amp;quot; coding if a higher output path is available?&lt;/p&gt;
&lt;p&gt;No. It&#39;s okay to sacrifice some productivity to make work enjoyable. More than okay, it&#39;s &lt;em&gt;essential&lt;/em&gt; in our field. If you force yourself to work in a way you hate, you&#39;re just going to burn out. Only so much of coding is writing code, the rest is solving problems, doing system design, reasoning about abstractions, and interfacing with other humans. You are better at all those things when you feel good. It&#39;s okay to feel pride in your work and appreciate the craft. Over the long term your codebase will benefit from it.&lt;/p&gt;
&lt;p&gt;It doesn&#39;t matter if digital music sounds objectively better than vinyl. It doesn&#39;t matter if flipping the record is less &amp;quot;productive&amp;quot; than letting the streaming service automatically roll over to the next song in 100x less time. If listening to a 70 year old disk makes you happier, just do it. You&#39;ll listen to more music if you do that than you would by forcing yourself to use the more &amp;quot;productive&amp;quot; streaming service. You will spend more time writing code and you&#39;ll write better code if you do it the way you like to.&lt;/p&gt;
&lt;p&gt;Oh, and this exact argument works in reverse. If you feel good doing AI coding, just do it. If you feel so excited that you code more than ever before, that&#39;s awesome. I want everyone to feel that way, regardless of how they get there.&lt;/p&gt;
&lt;h2 id=&quot;how-to-be-a-good-ai-leader&quot;&gt;How to be a good AI leader&lt;/h2&gt;
&lt;p&gt;Making all your engineers feel constantly anxious about their performance is &lt;em&gt;bad for your company&lt;/em&gt;. It will make your engineers not want to work for you. This is a recipe for short term thinking that will encourage engineers to max out bad metrics, like lines of code. Code review will get neglected, tech debt will compound, and in the long term the whole company will be footing the bill of those errors.&lt;/p&gt;
&lt;p&gt;Unrealistic 10x expectations will result in rushed and thus subpar work without fail. Engineers need to have room to breathe. Room to take a little bit more time to do the thing right. Good codebases and good companies are built on a healthy balance of thinking for today and tomorrow. I&#39;m thankful to work at one of these companies right now, but many aren&#39;t so fortunate.&lt;/p&gt;
&lt;p&gt;Do not scold engineers for not using enough tokens. Your engineers are highly educated professionals in an extremely competitive field. Software engineers are already infamous for an over-eager cycle of embracing and abandoning new languages and tools. If you are paying these people this much, you should have the trust in them that if a super amazing productivity boost becomes available, they&#39;ll &lt;em&gt;come to you&lt;/em&gt; asking for the pro plan. If you&#39;re worried about missing out on all the AI coding gains everyone else seems to be getting, sign up for a LLM team plan, host a training session, and see what comes out of it. That&#39;s all you need to do.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;There is no secret herbal medicine that prevents all disease sitting out in the open if you just follow the right Facebook groups. There is no AI coding revolution available if you just start vibing. You are not missing anything. Trust yourself. You are enough.&lt;/p&gt;
&lt;p&gt;Oh, and don&#39;t scroll LinkedIn. Or Twitter. Ever.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Tailwind is the Worst of All Worlds</title>
    <link href="https://colton.dev/blog/tailwind-is-the-worst-of-all-worlds/" />
    <updated>2025-07-21T00:00:00Z</updated>
    <id>https://colton.dev/blog/tailwind-is-the-worst-of-all-worlds/</id>
    <content type="html">&lt;p&gt;Tailwind is the worst of all worlds. It is a regrettable step backwards that takes everything bad about CSS and modern web development and brings it all together in one library.&lt;/p&gt;
&lt;h2 id=&quot;csss-successes-and-failures&quot;&gt;CSS&#39;s Successes and Failures&lt;/h2&gt;
&lt;p&gt;Of all the web technologies that underlie the modern web, the one that has received the fewest fundamental changes is CSS. We&#39;ve gotten amazing things like flexbox, grid, container queries, and more which have supercharged the ability to quickly build responsive styles. While we&#39;re far from the days of using tables for layout and googling &amp;quot;how to vertically center css&amp;quot; once a week, the language semantics themselves hasn&#39;t had to change much. This is because CSS offers two well rounded ways of applying styles that work pretty well: inline styles and stylesheets.&lt;/p&gt;
&lt;p&gt;Inline styles let you quickly add targeted rules to a single element. Much maligned historically due to the fact that many abused them and thus failed to keep their code DRY, there&#39;s really nothing wrong with inline styles. If there &lt;strong&gt;is&lt;/strong&gt; something wrong with them, it&#39;s simply that an HTML attribute is a kind of clunky place to write out styles. They&#39;ll wander off screen on your editor once you have enough rules and it&#39;s harder to get nice formatting or static analysis. But if you&#39;re a good developer you won&#39;t use inline styles this way very often. (This is what we call foreshadowing).&lt;/p&gt;
&lt;p&gt;Stylesheets are the much more powerful feature. They let you assign a set of rules to zero, one, or many elements at once with the minimum amount of code. They also include all the fancy features like media queries, container queries, and pseudo-elements, which are not available inline. Flaws that existed in the early days of stylesheets have largely been remedied, albeit not always in the most desirable way. SCSS variables had broader support (since they simply compiled to basic CSS) and were prettier than CSS variables, but eventually SCSS started losing steam in the face of an improving CSS.&lt;/p&gt;
&lt;p&gt;CSS definitely isn&#39;t perfect. Like JavaScript&#39;s &lt;code&gt;==&lt;/code&gt; operator, some ideas were bad then and are still bad now. CSS chooses which styles overwrite others based on selector specificity, leading some style debugging to be a bit of a rat&#39;s nest where the developer ends up bailing out to &lt;code&gt;!important&lt;/code&gt; tags, which just kick the can down the road. Worse yet, when two rules have the same specificity, the default functionality is to pick whichever is defined last in the &lt;em&gt;stylesheet&lt;/em&gt;. So, in this example, the styles of &lt;code&gt;two&lt;/code&gt; win out over &lt;code&gt;one&lt;/code&gt;, because the &lt;code&gt;.two&lt;/code&gt; rule is declared after the &lt;code&gt;.one&lt;/code&gt; rule, even though the &lt;code&gt;one&lt;/code&gt; class comes second on the div.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style&quot;&gt;&lt;span class=&quot;token language-css&quot;&gt;
&lt;span class=&quot;token selector&quot;&gt;.one&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; red
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.two&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; blue
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;two one&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With modern multi-sheet websites and bundlers, this can get confusing fast as it can be hard to keep track of which sheet was loaded first on every page.&lt;/p&gt;
&lt;p&gt;Additionally, CSS&#39;s status as a separate language from JavaScript is a blessing and a curse. It&#39;s a blessing in that CSS can be downloaded, parsed, and rendered in parallel extremely quickly by modern browsers, sidestepping the single-threadedness of JavaScript. But a curse in that modern apps simply require some amount of JavaScript-CSS interop since CSS is not a general purpose language. Sharing variables to keep styles consistent betweent he two means duplicating code between the two languages.&lt;/p&gt;
&lt;p&gt;Much of these problems are solved with modern tooling, albeit often with their own caveats. Tools like &lt;a href=&quot;https://styled-components.com/&quot;&gt;styled-components&lt;/a&gt; brought JS and CSS together fairly seamlessly, giving you all the power of JavaScript (functions, etc) in your CSS. The caveat is that styles are compiled into Javascript (slow, single threaded) rather than CSS (fast, parallel). Tools like &lt;a href=&quot;https://vanilla-extract.style/&quot;&gt;vanilla-extract&lt;/a&gt; have done an admirable job giving you fully typed CSS-in-JS-in-CSS, compiling JS created styles into CSS files. Lastly, tools like React have made inline styling much better with objects that are linter, type system, and formatter friendly.&lt;/p&gt;
&lt;h2 id=&quot;okay-now-can-we-dunk-on-tailwind&quot;&gt;Okay, now can we dunk on Tailwind?&lt;/h2&gt;
&lt;p&gt;For reasons we&#39;ll get into later, Tailwind decided to burst on to the scene and become the default styling solution for all modern web development despite taking each and every one of these problems, exacerbating them, and applying them to places that didn&#39;t have them before. Lets see how Tailwind replicates CSS&#39;s main paradigms. Remember, inline styles let you control single element styles simply and fairly explicitly: &lt;code&gt;style=&amp;quot;background: red; color: blue;&amp;quot;&lt;/code&gt;. Classes let you control multi-element styles simply, with the slight annoyance that it&#39;s easy to misspell your class name and spend way too long debugging before realizing it: &lt;code&gt;class=&amp;quot;todo-item&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Tailwind exclusively offers inline styles via classes. Classes are single strings separated by spaces, not key-value pairs. So the classes must be something like &lt;code&gt;class=&amp;quot;bg-red txt-blue&amp;quot;&lt;/code&gt;. Key and value must be obscured into a plain string, lowering readability and writability. If you misspell one of these plain strings your editor is not going to tell you. You&#39;ll just have to find out once you get into the browser. Did you notice that &lt;code&gt;txt-blue&lt;/code&gt; is wrong, and it should actually be &lt;code&gt;text-blue&lt;/code&gt;? No? Get used to it, you&#39;ll make that mistake daily because Tailwind is inconsistent in how it names things.&lt;/p&gt;
&lt;h2 id=&quot;rule-sets&quot;&gt;Rule sets&lt;/h2&gt;
&lt;p&gt;What about applying a set of rules to multiple elements, as you do in style sheets with class rule sets, what does Tailwind offer for that?&lt;/p&gt;
&lt;p&gt;Nothing. The answer is nothing. Tailwind&#39;s official recommendation for this is to ignore everything you&#39;ve ever learned about constants and duplicate your class names. Then simply remember to always use multi-cursor editing any time you touch them:&lt;/p&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://colton.dev/blog/tailwind-is-the-worst-of-all-worlds/rDECSIA49o-1550.avif 1550w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://colton.dev/blog/tailwind-is-the-worst-of-all-worlds/rDECSIA49o-1550.webp 1550w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://colton.dev/blog/tailwind-is-the-worst-of-all-worlds/rDECSIA49o-1550.png&quot; alt=&quot;When duplication is localized to a group of elements in a single file, the easiest way to deal with it is to use multi-cursor editing to quickly select and edit the class list for each element at once.&quot; width=&quot;1550&quot; height=&quot;1034&quot;&gt;&lt;/picture&gt;
&lt;p&gt;You can, of course, disobey the Tailwind overlords and create constants or components, but those solutions end up clunky because you are dealing purely with strings instead of objects. An element with a mere 10 CSS rules, will inevitably wrap in your editor and be more difficult to read and extend than a simple object would be:&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; sharedClass &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;mx-auto max-w-md overflow-hidden rounded-xl bg-white shadow-md text-black border border-black border-solid&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token function&quot;&gt;clsx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sharedClass&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string-property property&quot;&gt;&#39;display-none&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;visible&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;vs.&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; sharedInline &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token literal-property property&quot;&gt;marginLeft&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;auto&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token literal-property property&quot;&gt;marginRight&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;auto&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token literal-property property&quot;&gt;maxWidth&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MEDIUM_WIDTH&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token literal-property property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;hidden&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token literal-property property&quot;&gt;borderRadius&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;EXTRA_LARGE_ROUNDED&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token literal-property property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;white&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token literal-property property&quot;&gt;boxShadow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MEDIUM_BOX_SHADOW&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token literal-property property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;black&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token literal-property property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;1px solid black&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;styles&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;sharedInline&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; visible &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;block&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;none&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;csss-problems-are-tailwinds-problems&quot;&gt;CSS&#39;s Problems are Tailwind&#39;s Problems&lt;/h2&gt;
&lt;p&gt;Tailwind offers no solution to the built in problems of CSS. Tailwind classes are generated dynamically outside of your purview by the bundler and thus make it even harder to understand who will win a specificity conflict. For example:&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clsx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;text-red bg-blue&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token string-property property&quot;&gt;&#39;text-blue bg-red&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; currentPage &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; activePage
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This seems straightforward. Red when inactive, blue when active, right? Nope. Which class comes first in the generated stylesheet is not predictable. Tailwind&#39;s recommendation is to, once again, ignore basic coding principles and recommend you duplicate your business logic:&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clsx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;text-sm&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token string-property property&quot;&gt;&#39;text-red bg-blue&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; currentPage &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; activePage
	&lt;span class=&quot;token string-property property&quot;&gt;&#39;text-blue bg-red&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; currentPage &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; activePage
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As a result, tailwind developers fall back on the same trick as the CSS developers of yore: spamming important tags (in the form of &lt;code&gt;!&lt;/code&gt; suffixes on their class names) every time things don&#39;t work out nicely.&lt;/p&gt;
&lt;h2 id=&quot;did-you-really-say-bundler&quot;&gt;Did you really say bundler?&lt;/h2&gt;
&lt;p&gt;Yes. Despite offering no real solutions to CSS&#39;s problems and instead making them worse, Tailwind requires a plugin and a bundler to function. It can not be simply dropped into a plain web page. This bundler is doing code processing so basic it&#39;s almost admirable. It simply searches all your files and looks for things that look like Tailwind classes, and then it makes a CSS class rule set for each one.&lt;/p&gt;
&lt;p&gt;The oft-touted benefit of this system is that Tailwind will minimize your bundle sizes by only including what you need. Unfortunately this benefit does not hold up to basic scrutiny on medium-to-large codebases. By the time your codebase gets big enough to actually worry about how much CSS you have, you will have one class for every individual possible CSS rule you&#39;ll ever use. Still, this isn&#39;t much of a problem since CSS loads in parallel and it loads fast.&lt;/p&gt;
&lt;p&gt;The problem is what it does to your &lt;em&gt;JavaScript bundle sizes&lt;/em&gt;. Tailwind expects you to define styles via several classes on basically every element in your codebase. Meaning every time you use it you are writing out long strings which increase your slow, single-threaded, fully blocking JavaScript bundle size. Consider the following simple example with plain CSS:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.todo-item&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--warning-color&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; blue&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-jsx&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#39;&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#39;&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#39;&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And this one with tailwind:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;// CSS (Generated by Tailwind)
.text-warning&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #ffcc00&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;bg-blue:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; blue&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-jsx&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#39;&lt;/span&gt;text-warning bg-blue&lt;span class=&quot;token punctuation&quot;&gt;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#39;&lt;/span&gt;text-warning bg-blue&lt;span class=&quot;token punctuation&quot;&gt;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#39;&lt;/span&gt;text-warning bg-blue&lt;span class=&quot;token punctuation&quot;&gt;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this simple but common example, tailwind generates a larger CSS bundle &lt;em&gt;and&lt;/em&gt; a larger JS bundle! Lets give Tailwind the benefit of the doubt and say the codebase designed around the first style, with plain CSS classes, is going to end up with more CSS due to the same rules being duplicated across different selectors. That &lt;code&gt;color: var(--warning-color)&lt;/code&gt; rule may be repeated hundreds of times in a mature codebase. This will generate more CSS. But &lt;em&gt;that&#39;s just fine&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;CSS bundle size matters an order of magnitude less than JavaScript bundle size. On the React projects that Tailwind is typical on you will be writing your CSS in &lt;code&gt;className&lt;/code&gt; attributes. That means hundreds of repeats the string &lt;code&gt;txt-warning&lt;/code&gt; strewn about your JSX files. This is increasing your &lt;em&gt;Javascript&lt;/em&gt; bundle size, halting your entire app while that larger file is downloaded, parsed, and run.&lt;/p&gt;
&lt;h2 id=&quot;developer-friendliness&quot;&gt;Developer Friendliness&lt;/h2&gt;
&lt;p&gt;Tailwind does nothing to reduce the mental load on your developers to understand, read, and write in your codebase. In fact it does the opposite. Your developers are not spared the hassle of learning CSS by instead learning tailwind&#39;s abstractions. Developers have to understand not just that &lt;code&gt;background-color&lt;/code&gt; is the CSS key to set a background color, but they also must understand that classes prefixed with &lt;code&gt;bg-&lt;/code&gt; mean background color. Every feature of CSS, such as media and container queries, will need to be learned twice, CSS style and Tailwind style.&lt;/p&gt;
&lt;p&gt;Tailwind offers no type safety and little in the way of developer tooling outside of editor extensions that let you hover over a class to see the actual CSS that it translates to. Writing code with Tailwind means constantly hovering your cursor over class names and waiting a moment to see that your class translates to &lt;code&gt;align-items: center&lt;/code&gt;. And when no hover comes up you know you mistyped (or your VSCode extension crashed) and now you have to Google &amp;quot;tailwind flexbox align items center&amp;quot; to remember it&#39;s not &lt;code&gt;align-items-center&lt;/code&gt; or even &lt;code&gt;align-center&lt;/code&gt;, it&#39;s &lt;code&gt;items-center&lt;/code&gt;. All problems in tailwind must be solved this way.&lt;/p&gt;
&lt;p&gt;When Tailwind does a major version update, it will sometimes rework its class names a bit. Every time this happens you need to use the codemods that Tailwind distributes with each update. These can only do so much to understand your codebase and realize what is a tailwind class and what is a plain string so you have to review every one of the thousands of changes carefully. This is entirely a manufactured problem for Tailwind. When other libraries change APIs they can make the process straightforward through type systems, deprecation warnings, and clear error messages because they did not decide to base their entire library on parsing arbitrary strings.&lt;/p&gt;
&lt;h2 id=&quot;but-what-about-using-media-queries-inline&quot;&gt;But what about using media queries inline?&lt;/h2&gt;
&lt;p&gt;One decent thing about Tailwind is the relative ease with which CSS&#39;s &amp;quot;stylesheet only&amp;quot; features can be accessed inline. For example you can set up a CSS rule that only applies to an element on large screens with the simple matter of prefixing that class with &lt;code&gt;lg:&lt;/code&gt;. This is something that is annoying to do in raw CSS. However, every other CSS-in-JS tool I&#39;ve used also offers a convenient way to do the same. More importantly, those solutions scale better. If you have five rules that should be different on large screens, you must write &lt;code&gt;lg:&lt;/code&gt; five times and hover each one to make sure it&#39;s working. In a tool like &lt;a href=&quot;https://vanilla-extract.style/&quot;&gt;vanilla-extract&lt;/a&gt; you would define one type checked media query with all your type checked rules underneath.&lt;/p&gt;
&lt;p&gt;There are some things that Tailwind simply can not do and in this case developers simply fall back to plain CSS or inline styles with JavaScript. Meaning that, once again, the mental load of understanding the codebase is increased. You need to understand not only that you might be reading tailwind classes or plaain CSS classes and you&#39;ll need to track down where those are in your stylesheets when you do.&lt;/p&gt;
&lt;p&gt;Do you see what I mean when I say the worst of all worlds?&lt;/p&gt;
&lt;h2 id=&quot;okay-so-why-does-everyone-use-tailwind-if-its-the-worst-of-all-worlds&quot;&gt;Okay, so why does everyone use tailwind if it&#39;s the worst of all worlds?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;There are only two kinds of languages: the ones people complain about and the ones nobody uses.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I think the most important factor in Tailwind&#39;s success is that it does one thing very correctly: it demands the developer who installs it set up a config file that lays out all codebase-wide style constants: colors, margin sizes, fonts, border radii, etc. Writing individual styles that do not use a pre-configured constant from the config file is clunky in Tailwind. This is a good thing, an unironic win for Tailwind. More than anything else, this is what a large codebase with multiple frontend devs needs: a rigid set of global constants that everyone is strongly incentivized to use.&lt;/p&gt;
&lt;p&gt;I believe this is where Tailwind has succeeded and other libraries have struggled. Libraries like &lt;a href=&quot;https://vanilla-extract.style/&quot;&gt;vanilla-extract&lt;/a&gt; are legitimately great. But when I set up vanilla-extract in a new codebase I have to set up my own structure for global constants and make sure everyone is importing from that. For me, this works: I like to set up my own structure. I like having Javascript available in those files, for example to generate colors by inverting or tinting other colors. But creating this kind of structure requires foresight and discipline to do it right and ensure it is followed. You must write docs and every developer must read and understand them.&lt;/p&gt;
&lt;p&gt;Since Tailwind has a single magical config file that instantly makes all items inside it globally available, the documentation of how constants work is outsourced to the tailwind docs. Questions of &amp;quot;why do we structure our constants this way?&amp;quot; can be answered simply: that&#39;s just how Tailwind works. There&#39;s no room to bicker about little things, the way we all used to quarrel about where the line breaks should be before prettier came along and let us defer that decision making to the tool. This is great. I like that I no longer format JS manually and I like that I rarely see one-time-use colors in tailwind.&lt;/p&gt;
&lt;p&gt;Most companies adopting Tailwind do so because it offers an out of the box solution for a lack of standards and consistent colors. I think there are better libraries than Tailwind. Libraries that lead to smaller file sizes and better consistency. But to succeed some of these libraries may need to start being more authoritative. I love that vanilla-extract lets me do my own thing, but providing an easy to use default structure, that can be replaced if desired, would help adoption.&lt;/p&gt;
&lt;p&gt;Lastly, Tailwind has also been buoyed by being by being the default styling that just about any LLM or vibe coding tool will produce unless it is explicitly asked not to. This feels like both uncharted and charted territory. Plenty of developers started using jQuery because the first StackOverflow result in their search explained how to solve that problem with jQuery.&lt;/p&gt;
&lt;p&gt;But today we have a much larger set of decisions about codebase design being made all at once by a single tool. Developers who outsource these crucial decisions to LLMs are going to keep getting the worst of all worlds once the codebase becomes to unwieldy for vibes and needs actual developers.&lt;/p&gt;
&lt;h2 id=&quot;so-whats-the-best-of-all-worlds&quot;&gt;So what&#39;s the best of all worlds?&lt;/h2&gt;
&lt;p&gt;I like &lt;a href=&quot;https://vanilla-extract.style/&quot;&gt;vanilla-extract&lt;/a&gt;. It&#39;s a fairly light type safe abstraction that gives you CSS in JS in just about the best way. It&#39;s not the best of all worlds but it&#39;s great at almost everything. I also don&#39;t mind writing apps with plain CSS, though this has problems as it can get hard to keep track of which styles are applying to what. I&#39;ve also written decently sized apps purely using inline styles in React with some basic constants and it works pretty well, though that too has problems like the lack of media queries and increasing JS bundle size. My point here isn&#39;t to say that there is a best of all worlds. My point is to say that Tailwind is the worst of all worlds.&lt;/p&gt;
</content>
  </entry>
</feed>