<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kristian Dupont</title>
    <description>The latest articles on DEV Community by Kristian Dupont (@kristiandupont).</description>
    <link>https://dev.to/kristiandupont</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F142521%2F2bf14cd2-1f3d-40fa-bf42-f86908252f64.jpeg</url>
      <title>DEV Community: Kristian Dupont</title>
      <link>https://dev.to/kristiandupont</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kristiandupont"/>
    <language>en</language>
    <item>
      <title>Kanel v4: Generate Markdown from Postgres</title>
      <dc:creator>Kristian Dupont</dc:creator>
      <pubDate>Thu, 02 Apr 2026 08:14:50 +0000</pubDate>
      <link>https://dev.to/kristiandupont/kanel-v4-generate-markdown-from-postgres-1fkc</link>
      <guid>https://dev.to/kristiandupont/kanel-v4-generate-markdown-from-postgres-1fkc</guid>
      <description>&lt;p&gt;I just published Kanel v4. Kanel was born as a tool for generating Typescript types from a live PostgreSQL database. V4 changes the architecture around so you can have multiple distinct generators.&lt;/p&gt;

&lt;p&gt;It comes with a new built-in generator for Markdown. This can be used to document your data model for your fellow team members, but the primary use case that I picture is LLM consumption. I’ve used an MCP server for my developer database which does work, but it feels slightly clumsy to me, and including a markdown with with an @-tag when pointing something out feels very natural.&lt;/p&gt;

&lt;p&gt;Thanks for reading stdout! Subscribe for free to receive new posts and support my work.&lt;/p&gt;

&lt;p&gt;I’ve created an example for the dvd-rental database. You can see the result &lt;a href="https://github.com/kristiandupont/kanel/tree/main/examples/markdown" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;[&lt;br&gt;
 &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1468779036391-52341f60b55d%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DM3wzMDAzMzh8MHwxfHNlYXJjaHw0fHxkb2N1bWVudHxlbnwwfHx8fDE3NzUxMTc1OTl8MA%26ixlib%3Drb-4.1.0%26q%3D80%26w%3D1080" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1468779036391-52341f60b55d%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DM3wzMDAzMzh8MHwxfHNlYXJjaHw0fHxkb2N1bWVudHxlbnwwfHx8fDE3NzUxMTc1OTl8MA%26ixlib%3Drb-4.1.0%26q%3D80%26w%3D1080" title="A stack of thick folders on a white surface" alt="A stack of thick folders on a white surface" width="1080" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;
&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;](&lt;a href="https://images.unsplash.com/photo-1468779036391-52341f60b55d?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw0fHxkb2N1bWVudHxlbnwwfHx8fDE3NzUxMTc1OTl8MA&amp;amp;ixlib=rb-4.1.0&amp;amp;q=80&amp;amp;w=1080" rel="noopener noreferrer"&gt;https://images.unsplash.com/photo-1468779036391-52341f60b55d?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw0fHxkb2N1bWVudHxlbnwwfHx8fDE3NzUxMTc1OTl8MA&amp;amp;ixlib=rb-4.1.0&amp;amp;q=80&amp;amp;w=1080&lt;/a&gt;)&lt;br&gt;
&lt;em&gt;Photo by Beatriz Pérez Moya on Unsplash&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Even if you are using an ORM that mirrors the database schema in some way, those files often miss some of the finer nuances that are possible with Postgres. With Kanel, you can construct documentation that goes into just about as much detail as you could want. The engine has access to everything that &lt;a href="https://github.com/kristiandupont/extract-pg-schema/" rel="noopener noreferrer"&gt;extract-pg-schema&lt;/a&gt; exposes, which allows you to document indices, procedures, ranges, domains and most other Postgres-specific features.&lt;/p&gt;

&lt;p&gt;Setting it up requires a bit of configuration work, as I don’t think I would be able to write a general-purpose template that would suit all teams. It works with Handlebars templates which will be used to generate the final result.&lt;/p&gt;

&lt;p&gt;Thanks for reading stdout! Subscribe for free to receive new posts and support my work.&lt;/p&gt;

</description>
      <category>documentation</category>
      <category>llm</category>
      <category>postgres</category>
      <category>tooling</category>
    </item>
    <item>
      <title>The Robot is not a Junior Developer. It Is a Senior Developer caught in Groundhog Day</title>
      <dc:creator>Kristian Dupont</dc:creator>
      <pubDate>Mon, 29 Dec 2025 08:18:07 +0000</pubDate>
      <link>https://dev.to/kristiandupont/the-robot-is-not-a-junior-developer-it-is-a-senior-developer-caught-in-groundhog-day-56d0</link>
      <guid>https://dev.to/kristiandupont/the-robot-is-not-a-junior-developer-it-is-a-senior-developer-caught-in-groundhog-day-56d0</guid>
      <description>&lt;p&gt;I frequently encounter the notion that, when it comes to programming, AI is like a well-read junior developer.&lt;/p&gt;

&lt;p&gt;I don’t think that is a great analogy any more. At this point, Claude (my preferred provider at the moment) feels much more like a talented senior developer. But crucially, a senior developer who is experiencing a perpetual &lt;em&gt;day 1&lt;/em&gt; on the team.&lt;/p&gt;

&lt;p&gt;Thanks for reading stdout! Subscribe for free to receive new posts and support my work.&lt;/p&gt;

&lt;p&gt;[&lt;br&gt;
 &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1527430253228-e93688616381%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DM3wzMDAzMzh8MHwxfHNlYXJjaHwzfHx0b3klMjByb2JvdHxlbnwwfHx8fDE3NjY5MjMyMTB8MA%26ixlib%3Drb-4.1.0%26q%3D80%26w%3D1080" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1527430253228-e93688616381%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DM3wzMDAzMzh8MHwxfHNlYXJjaHwzfHx0b3klMjByb2JvdHxlbnwwfHx8fDE3NjY5MjMyMTB8MA%26ixlib%3Drb-4.1.0%26q%3D80%26w%3D1080" title="blue plastic robot toy" alt="blue plastic robot toy" width="1080" height="606"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;](&lt;a href="https://images.unsplash.com/photo-1527430253228-e93688616381?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwzfHx0b3klMjByb2JvdHxlbnwwfHx8fDE3NjY5MjMyMTB8MA&amp;amp;ixlib=rb-4.1.0&amp;amp;q=80&amp;amp;w=1080" rel="noopener noreferrer"&gt;https://images.unsplash.com/photo-1527430253228-e93688616381?crop=entropy&amp;amp;cs=tinysrgb&amp;amp;fit=max&amp;amp;fm=jpg&amp;amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwzfHx0b3klMjByb2JvdHxlbnwwfHx8fDE3NjY5MjMyMTB8MA&amp;amp;ixlib=rb-4.1.0&amp;amp;q=80&amp;amp;w=1080&lt;/a&gt;)&lt;br&gt;
&lt;em&gt;Photo by Emilipothèse on Unsplash&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There are two ways of getting better and more specific output from an LLM: fine-tuning and retrieval-augmented generation (RAG).&lt;/p&gt;

&lt;p&gt;Fine-tuning is like making a student study a curriculum for a long time. Over time, they internalize a worldview. Certain assumptions simply become “how things are.”&lt;/p&gt;

&lt;p&gt;RAG is more like allowing the student to bring notes during an exam.&lt;/p&gt;

&lt;p&gt;In one sense, the former is much more powerful. But in many real situations, the latter is more effective. If the question is narrow, and the relevant piece of knowledge is very specific, it often matters more that the right note is present in front of the student than that the student has spent years internalizing the entire curriculum.&lt;/p&gt;

&lt;p&gt;Scanning a codebase, reading READMEs, and opening individual files is exactly this kind of exam-time context injection. When an AI does it, that &lt;em&gt;is&lt;/em&gt; RAG, but obviously humans do it as well.&lt;/p&gt;

&lt;p&gt;When an experienced engineer is introduced to a code base, their first task is not writing code. It is reconstructing local reality. They scan the architecture and attempt to infer conventions. But even after doing that, their first PR’s will need course-correcting by the other team members. They will have assumed that standards are followed and that visible patterns are intentional, both of which are frequently wrong.&lt;/p&gt;

&lt;p&gt;Standards will not be followed because the developer who started something didn’t know about them or maybe they did but they decided to be clever, for good or bad reasons. And even clear patterns in the code base may reflect past decisions that were revised just last week.&lt;/p&gt;

&lt;p&gt;The real rules live in commit history, Slack or email threads, half-remembered discussions, and the team’s collective memory. A human absorbs this over time. An AI doesn’t. Every session, it wakes up on day one. Now, in theory, fine-tuning would be the equivalent here, but at least with our current tooling, this isn’t an option.&lt;/p&gt;

&lt;h4&gt;
  
  
  AI-Assisted Programming Is Not Delegation. It’s Context Construction.
&lt;/h4&gt;

&lt;p&gt;Given this perspective, how can we best work with LLM’s as software developers?&lt;/p&gt;

&lt;p&gt;Well, we need to give the robot as much help as possible which means, somewhat paradoxically, adding more English to our code: comments, READMEs (I’m considering putting one in every folder), agent files with detailed descriptions.1 This creates an “exoskeleton” for the LLM to work with. Ask it to keep this up to date.&lt;/p&gt;

&lt;p&gt;However, as tempting as it might be to document every idiosyncrasy of the codebase in an agent file, this will result in large prompts full of information, most of which will be irrelevant to most tasks, with a real risk of confusing the LLM. Even if the context window is large enough, you still want to be conscious of what you put in there. In an uncanny, human-like fashion, the &lt;a href="https://www.simplypsychology.org/primacy-recency.html" rel="noopener noreferrer"&gt;primacy and recency effects&lt;/a&gt; where most attention is paid to the first and last bits of information presented have been &lt;a href="https://arxiv.org/abs/2507.13949" rel="noopener noreferrer"&gt;observed&lt;/a&gt; in LLM’s!&lt;/p&gt;

&lt;p&gt;Thus, the primary job of the human engineer in an AI-assisted workflow is not to write code. It is to compile the world the code will be written inside.&lt;/p&gt;

&lt;p&gt;For every non-trivial change, you have to decide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;What assumptions about this part of the system is the LLM likely to make&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Which of those assumptions are true and matter for this change&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Which assumptions the model is likely to get wrong&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;And which slice of local reality must be injected to prevent that&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words: what would you need to tell a talented developer with zero institutional memory so that they can operate effectively?&lt;/p&gt;

&lt;p&gt;You are not “using AI.” You are compiling context for non-persistent intelligence.&lt;/p&gt;

&lt;p&gt;And that is now the real job.&lt;/p&gt;

&lt;p&gt;1&lt;/p&gt;

&lt;p&gt;Adding lots of English to your code is the antithesis to &lt;em&gt;Clean Code&lt;/em&gt;. I was never a believer in that to begin with, but with LLM’s I am even less so. Some people will disagree.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Schemalint Support for Triggers</title>
      <dc:creator>Kristian Dupont</dc:creator>
      <pubDate>Sat, 24 May 2025 08:54:51 +0000</pubDate>
      <link>https://dev.to/kristiandupont/schemalint-support-for-triggers-383g</link>
      <guid>https://dev.to/kristiandupont/schemalint-support-for-triggers-383g</guid>
      <description>&lt;p&gt;&lt;a href="https://www.npmjs.com/package/schemalint" rel="noopener noreferrer"&gt;Schemalint&lt;/a&gt; is a tool that inspects a PostgreSQL database and prints out errors if the schema diverges from a defined set of rules.&lt;/p&gt;

&lt;p&gt;I am &lt;a href="https://dev.to/kristiandupont/are-you-using-types-when-you-should-be-linting-ec6"&gt;big fan of linter rules&lt;/a&gt; in general. They offer a nice way of establishing rules for your code base. This is handy both for aligning with team mates as well as your future self. With Schemalint, you can apply this to the database schema which is often a fundamental part of a software systems architecture.&lt;/p&gt;

&lt;p&gt;The newest version adds support for triggers. I have been wanting this for a long time, because triggers are one of the more subtle parts of a schema and therefor arguable one of the more important aspects for a tool like this. A trigger (or the lack thereof) is not very visible in most database inspection tools, so it’s easy to miss when something is wrong.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2A_69TR6q3vX1655mx" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2A_69TR6q3vX1655mx" width="1024" height="682"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Photo by Tsvetoslav Hristov on Unsplash&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For instance, I greatly prefer having a dedicated table and a trigger for soft deletes over having an archived_at column, as outlined in &lt;a href="https://brandur.org/fragments/deleted-record-insert" rel="noopener noreferrer"&gt;this blog post&lt;/a&gt;. I like this because the archived_at approach requires an extra is null condition in every single query you make to this table. And what’s worse: forgetting to do so often doesn’t look like a bug when developing. However, the dedicated table approach requires that you have the trigger in place and forgetting to set that is &lt;em&gt;also&lt;/em&gt; easy to miss (though only needs to happen once per table). But now, Schemalint can catch those for you!&lt;/p&gt;

&lt;p&gt;There is a custom &lt;a href="https://github.com/kristiandupont/schemalint/blob/main/example/custom-rules/lastUpdated.js" rel="noopener noreferrer"&gt;example rule&lt;/a&gt; for enforcing setting a last_update column in the examples folder. There is a triggers field on tables and views in the input to the rules (see the details &lt;a href="https://kristiandupont.github.io/extract-pg-schema/api/extract-pg-schema.trigger.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;).&lt;/p&gt;

</description>
      <category>linter</category>
      <category>databaseschema</category>
      <category>postgres</category>
    </item>
    <item>
      <title>The Pre-Contagion Window</title>
      <dc:creator>Kristian Dupont</dc:creator>
      <pubDate>Sat, 30 Nov 2024 10:21:44 +0000</pubDate>
      <link>https://dev.to/kristiandupont/the-pre-contagion-window-1oeb</link>
      <guid>https://dev.to/kristiandupont/the-pre-contagion-window-1oeb</guid>
      <description>&lt;p&gt;When a new engineer starts on a team, something interesting happens. Depending on their level of experience and outspokenness, they will note or perhaps complain about a lot of things. Processes, tools, conventions — details of engineering that differ from their previous roles.&lt;/p&gt;

&lt;p&gt;In my experience, this lasts something like a month or two, and then it starts to die off. Think of this initial adjustment phase as the ‘pre-contagion window,’ where a new hire hasn’t yet been infected by the habits — good or bad — of their new team.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2A0pRpjsK9bg_qaM-w" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2A0pRpjsK9bg_qaM-w" width="800" height="1000"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Photo by Blake Cheek on Unsplash&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This period of fresh perspective is fleeting, and if you’re not careful, it’s easy to squander.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you’re a manager or colleague to the new hire
&lt;/h3&gt;

&lt;p&gt;Make an effort to be extra patient with these complaints. First of all, they tend to go away. Yes, they may be things you’ve already considered, attempted, or found impractical for various reasons. It might also be the case that you’ve been doing some silly exercise so many times that you don’t see it anymore, even if it costs you minutes every day. If you find yourself dismissing the complaint due to competing priorities, consider whether addressing it might actually save time and effort in the long run. Are you too busy cutting down trees to sharpen the saw? Conversely, and this goes without saying, don’t tolerate toxic behavior from anyone, including new hires.&lt;/p&gt;

&lt;p&gt;Consider asking new hires to document any issues they notice during their onboarding period.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you’re the new hire
&lt;/h3&gt;

&lt;p&gt;Perhaps show this blog post to your colleagues, as a sort of preemptive move to make them more patient with you.&lt;/p&gt;

&lt;p&gt;Remember, you’re uniquely positioned to make impactful arguments. There is a lot more potency in suggestions that are backed up with real experience. If you suggest switching to tool X in order to solve problem Y and you can add that you did exactly this at your last job and the tradeoffs were worth it, that is a lot more convincing than someone just suggesting to try it and see what happens.&lt;/p&gt;

&lt;p&gt;By recognizing the value of the pre-contagion window, you will continually improve your team’s processes.&lt;/p&gt;

</description>
      <category>engineering</category>
      <category>onboarding</category>
    </item>
    <item>
      <title>I made a Course</title>
      <dc:creator>Kristian Dupont</dc:creator>
      <pubDate>Thu, 20 Jun 2024 19:08:36 +0000</pubDate>
      <link>https://dev.to/kristiandupont/i-made-a-course-25ki</link>
      <guid>https://dev.to/kristiandupont/i-made-a-course-25ki</guid>
      <description>&lt;p&gt;I’ve created a course with Newline called “Fullstack Typescript with TailwindCSS and tRPC Using Modern Features of PostgreSQL”. It basically teaches what is currently my favourite stack, from top to bottom. I am honestly quite happy with it and I feel that it gives a good introduction to how I like to use &lt;a href="https://github.com/kristiandupont/kanel"&gt;Kanel&lt;/a&gt; to generate types that permeate the system.&lt;/p&gt;

&lt;p&gt;If you are interested, take a look: &lt;a href="https://www.newline.co/courses/fullstack-typescript-with-tailwindcss-and-trpc-using-modern-features-of-postgresql"&gt;https://www.newline.co/courses/fullstack-typescript-with-tailwindcss-and-trpc-using-modern-features-of-postgresql&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dNnGLyFS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/0%2ABczW_oS58IoZ2ejf" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dNnGLyFS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/0%2ABczW_oS58IoZ2ejf" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The course consists of several hours of video where I not only show the concepts but also little tricks and tips about how I work day-to-day.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Bug-to-Error Distance</title>
      <dc:creator>Kristian Dupont</dc:creator>
      <pubDate>Tue, 21 May 2024 16:02:11 +0000</pubDate>
      <link>https://dev.to/kristiandupont/bug-to-error-distance-4j67</link>
      <guid>https://dev.to/kristiandupont/bug-to-error-distance-4j67</guid>
      <description>&lt;p&gt;I hadn’t articulated this before, but I recently realized that I have an internal metric for assessing bugs and errors.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AxpS39Un--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/0%2AUIj2vwU3TRJiJSwk" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AxpS39Un--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/0%2AUIj2vwU3TRJiJSwk" alt="" width="800" height="534"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Photo by Nuno Antunes on Unsplash&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this context, a &lt;em&gt;bug&lt;/em&gt; is a piece of code that doesn’t do what the programmer intended, and an &lt;em&gt;error&lt;/em&gt; is an undesired outcome or behavior.&lt;/p&gt;

&lt;p&gt;Now, I am deliberately being vague about what &lt;em&gt;distance&lt;/em&gt; refers to, as it’s a bit of a combination. It can mean “in the code”, measured in lines, files, folders, or services. It can also mean “in time”, where the visible error might appear significantly later than when the bug in question was triggered.&lt;/p&gt;

&lt;p&gt;For syntax errors, this distance is practically zero. A squiggly line appears right underneath the bug. The error is both co-located and immediate, at least if you are working in a syntax-highlighting editor.&lt;/p&gt;

&lt;p&gt;On the other hand, if you have a service that accidentally inserts garbage into your database, you might not discover it for a long time. The distance can be severe, both in terms of code location and time. This sort of bug is significantly harder to track down and fix.&lt;/p&gt;

&lt;p&gt;So, it seems valuable to assess code not only on how potentially error-prone a pattern or piece of code is, but also on what the potential bug-to-error distance is, and try to minimize it.&lt;/p&gt;

&lt;p&gt;How does one do that in practical terms? For instance, it has affected my view on type inference. For a while, my perspective was: &lt;em&gt;infer all the things!&lt;/em&gt; Type safety without the plumbing code, best of both worlds — surely, that’s the way to go, always? Well, specifying types is like creating little valves in the code. “At this point, this is what I expect things to look like”. If you don’t do this, you will have less plumbing code but the price you pay is an increased bug-to-error distance because a piece of data may have a different shape than you expected, inferred from something “far away”.&lt;br&gt;&lt;br&gt;
I now tend to enable the &lt;a href="https://typescript-eslint.io/rules/explicit-module-boundary-types/"&gt;explicit-module-boundary-types&lt;/a&gt; linter rule when writing Typescript, which forces explicitly typed signatures for exported functions. I am sure there are plenty of other similar considerations that I haven’t thought of yet.&lt;/p&gt;

</description>
      <category>codequality</category>
      <category>programming</category>
    </item>
    <item>
      <title>Empathy, articulated</title>
      <dc:creator>Kristian Dupont</dc:creator>
      <pubDate>Sat, 21 Oct 2023 17:14:33 +0000</pubDate>
      <link>https://dev.to/kristiandupont/empathy-articulated-2dfj</link>
      <guid>https://dev.to/kristiandupont/empathy-articulated-2dfj</guid>
      <description>&lt;p&gt;Like everyone and his brother, I’ve been working on a “coach” chat bot. Mostly for fun but also in an attempt to help me personally, primarily with health as I am in my 40s and need to take that stuff seriously. It’s not a product, I am just using it myself though I have a few friends playing with it as well.&lt;/p&gt;

&lt;p&gt;If you haven’t worked with LLM’s as a developer, you might mistakenly think something similar to what I did: Hey, OpenAI has an API, it’s just ChatGPT without the UI! Chat is basically solved!&lt;/p&gt;

&lt;p&gt;Well, it turns out that that is not quite the case.&lt;/p&gt;

&lt;p&gt;Making an API call for completions feels more like using Mechanical Turk. You talk to it using natural language which is amazing, but it’s like every request is handled by someone new who doesn’t know anything, so you need to explain the whole thing from scratch every time. And the thing is: there is a hard limit to how long your description can be. So you need to summarize everything for it and figure out what context it needs to know for &lt;em&gt;this particular message&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iNA0Wuh8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2ABmj5FH85PqWqakP_evt6cQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iNA0Wuh8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2ABmj5FH85PqWqakP_evt6cQ.png" alt="" width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;API Call&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Imagine you received a letter out of the blue that said: “Well, at least I did 50 pushups today” and were expected to respond something. You don’t know who it’s from or even why they sent you this. What do you respond? Obviously, you can’t really say anything meaningful. There isn’t even a question in there! The job of the bot developer is to turn such a message into a piece of text that would enable you to not only respond something meaningful but also make it feel to the recipient as if you are just continuing a conversation that they were already having with you.&lt;/p&gt;

&lt;p&gt;Establishing this context is very challenging. One common solution, at the moment, is to have a template that looks something like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You are a helpful coach chat bot. Your purpose is to assist the user with health, wealth and well-being.&lt;/p&gt;

&lt;p&gt;Here are some messages from the conversation that may or may not be related:&lt;br&gt;&lt;br&gt;
[[related-messages]]&lt;/p&gt;

&lt;p&gt;Here are the ten most recent messages:&lt;br&gt;&lt;br&gt;
[[recent-history]]&lt;/p&gt;

&lt;p&gt;User says:&lt;br&gt;&lt;br&gt;
[[message]]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This provides a closer approximation to meaningful context, especially if one of those related messages tells you something else about pushups, like say, that the user has a goal of 50 per day for a month, or that they couldn’t do them because of an injury. Also, the most recent messages will make it clearer to you what you were talking about specifically, and what style of communication the two of you were using — was it a formal conversation, an inspirational pep-talk or perhaps more of a friendly bantering situation?&lt;/p&gt;

&lt;p&gt;While fetching the most recent messages is straightforward, identifying the most relevant ones is anything but trivial. The first place to start might be to create &lt;a href="https://en.wikipedia.org/wiki/Word_embedding"&gt;vector embeddings&lt;/a&gt; out of every message in the history. Then you can find, say, the 10 messages with the highest cosine similarity to the incoming message. It will mean that previous messages that are &lt;em&gt;similar&lt;/em&gt; will be inserted into the template, which is a place to start. Of course, similar is not the same as related.&lt;/p&gt;

&lt;p&gt;I was using something close to this for a while and it did work but my bot felt quite distracted which was frustrating. Now, debugging this is &lt;em&gt;really hard&lt;/em&gt;, but I suspect it’s because finding similar messages simply isn’t good enough. It wouldn’t remember old, related conversations if they weren’t sufficiently similar so it felt like I had to remind it of things constantly. It would apologize profusely and seem to recall when reminded, but that almost made it more annoying.&lt;/p&gt;

&lt;p&gt;To address this, I implemented a strategy of tagging messages to create and utilize categories. That helped a bit but now my bot had developed dementia instead. It would often repeat points it had made in the past. That is also a seriously weird experience!&lt;/p&gt;

&lt;p&gt;Another interesting thing is that since my bot communicates with the user at random times via the phone, I needed to tell it how long it was since the last interaction, what date, weekday and time of day it currently is, because otherwise it might say “good morning” in the evening, and it would carry every conversation as if there had been no delay. One thing I found here which seems quite intuitive when you think about it is that it was much better to tell it “last interaction was 3 days ago” instead of a specific date and then today’s date afterwards. It’s not great at math, so help it when you can!&lt;/p&gt;

&lt;p&gt;I wanted the bot to not only react when the user initiates a chat. It should also reach out now and then. This isn’t something LLM’s will do automatically but a solution that seems to work well is quite simple: after each message, I ask the LLM for when it would like to follow up if it hasn’t heard from the user, and if so, what the reminder message should be. I then set a timer for the follow-up and re-initiate the chat with the reminder message.&lt;/p&gt;

&lt;p&gt;But the hardest part, which is probably going to be the next uncanny valley for us to cross, is to convincingly “simulate” empathy. In order to make it feel like the bot cares about the user, it needs to be interested in them, learn about them and have a &lt;a href="https://en.wikipedia.org/wiki/Theory_of_mind"&gt;theory of mind&lt;/a&gt;. The latter is basically a way of saying that it should try to picture what the user is thinking and what their mood/mental state is like.&lt;/p&gt;

&lt;p&gt;One thing I did which felt like a step in the right direction to me was this bit of text in the prompt:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You should decide for each message if you are in “empathy” mode or “problem solving” mode. Don’t mix the two in one message.&lt;br&gt;&lt;br&gt;
In “empathy” mode, you are looking to understand and possibly to help the user understand. Ask questions. If ${member.name} seems incongruent or you are confused, ask for clarification.&lt;br&gt;&lt;br&gt;
In “problem solving” mode, you should offer solutions and suggestions. You can still ask questions but those will probably be of practical nature.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Secondly, I have given the bot a “note pad”. It can add a note to this after each message. I then run two types of “dream cycles” where it reorganizes its thoughts. The server runs these asynchronously to the conversations. The simpler one runs daily. This one will make the bot read its notes, conversation and other inputs from today and update its note pad. Currently, the entire note pad is injected into every prompt which doesn’t scale that well, so I might look into a tagging system there as well.&lt;/p&gt;

&lt;p&gt;The second dream cycle is weekly and more resource intensive. It will analyze various facets of the ongoing conversation, access its data, and perform multiple interpretation runs. For instance, it will try to spot what the users particular vocabulary is. If they say they “went for a run”, does that mean a 10 minute sprint or a 2 hour jog?&lt;/p&gt;

&lt;p&gt;I studied neuroscience at university and while I always found it fascinating, it never felt as tangible and, well, &lt;em&gt;basic&lt;/em&gt; as it does to me now. This is probably what is most exciting to me in all of this.&lt;/p&gt;

&lt;p&gt;So the above changes have been about making it closer to human. But I want my bot to act as a &lt;em&gt;coach&lt;/em&gt;. What makes a good coach? Well, one thing I intend to do is to give it a library. I might for example use my old collection on &lt;a href="http://procrastotherapy.com/"&gt;Procrastotherapy&lt;/a&gt; which has several good resources that I keep forgetting about. I imagine I will create little descriptions for each book/essay/video and then put those in my database as vector embeddings as well, so it can look those up. It would also be fun for it to keep up with new research by simply reading papers or articles posted to some subreddit or something. These are all vague ideas so far.&lt;/p&gt;

&lt;p&gt;Bottom line is that programming LLM’s is &lt;strong&gt;fun&lt;/strong&gt;! It’s a weird hybrid between programming, psychology, neuroscience and library science. It feels super empowering and humbling at the same time. Interestingly, I think it forces you to think in terms of empathy &lt;em&gt;yourself&lt;/em&gt;: what does the bot know at this point? How can I articulate things so it will have the necessary basis for answering? When you think about it, this is a lot like what you do (or should do) when having a conversation with a fellow human being. That is just super fascinating to me.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>neuroscience</category>
    </item>
    <item>
      <title>Meme-driven Retrospectives</title>
      <dc:creator>Kristian Dupont</dc:creator>
      <pubDate>Mon, 08 May 2023 06:55:21 +0000</pubDate>
      <link>https://dev.to/kristiandupont/meme-driven-retrospectives-30an</link>
      <guid>https://dev.to/kristiandupont/meme-driven-retrospectives-30an</guid>
      <description>&lt;p&gt;I gave a talk at BestBrains Academy about cultural differences and using humor when leading teams of engineers.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/N5hCGu-sigU"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Here are some of the relevant links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://imgflip.com/memegenerator"&gt;Meme generator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.paulgraham.com/makersschedule.html"&gt;Maker’s Schedule, Manager’s Schedule&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://heeris.id.au/2013/this-is-why-you-shouldnt-interrupt-a-programmer/"&gt;Why you shouldn’t interrupt a programmer (comic&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.goodreads.com/book/show/97756.The_Joke_and_Its_Relation_to_the_Unconscious"&gt;The joke and its relation to the unconscious&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.hofstede-insights.com/country-comparison-tool"&gt;Culture comparison tool&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>culturaldiversity</category>
      <category>humor</category>
      <category>leadership</category>
      <category>retrospectives</category>
    </item>
    <item>
      <title>Are you using types when you should be linting?</title>
      <dc:creator>Kristian Dupont</dc:creator>
      <pubDate>Sat, 06 May 2023 18:06:29 +0000</pubDate>
      <link>https://dev.to/kristiandupont/are-you-using-types-when-you-should-be-linting-ec6</link>
      <guid>https://dev.to/kristiandupont/are-you-using-types-when-you-should-be-linting-ec6</guid>
      <description>&lt;p&gt;I’m a big fan of static typing. With &lt;a href="https://github.com/kristiandupont/kanel"&gt;Kanel&lt;/a&gt;, I generate types for Typescript from Postgres databases. That means that the compiler helps me remember if a member has a fullName or firstName+ lastName columns/properties. If I mistype or mis-remember, a squiggly line in my editor or a CI failure stops me long before any user experiences an error. In a large system with many components, type checking saves me countless hours of work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3RpuK2RC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2Ar1AwONWwGeXqKT50y4iuPg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3RpuK2RC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2Ar1AwONWwGeXqKT50y4iuPg.png" alt="" width="800" height="717"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using types to remember and enforce the shape of your data model is extremely valuable.&lt;/p&gt;

&lt;p&gt;It doesn’t stop there, though. Type systems can be used to enforce a meta-architecture of sorts, which in many ways is even more powerful. Languages like Rust and Haskell excel at this. A common phrase among Haskellites is “when it compiles, it works”.&lt;/p&gt;

&lt;p&gt;Let’s look at a trivially simple example. The most famous design pattern from the nineties was the mighty &lt;em&gt;singleton&lt;/em&gt;. I don’t personally see it much these days but it probably sneaks in many places still. The idea is that there are some things you want one and only one instance of, like the connection to the database. And while &lt;em&gt;you&lt;/em&gt; are perfectly capable of remembering this, your experience tells you that the developers you work with simply &lt;em&gt;cannot&lt;/em&gt; be trusted not to create new instances, the little rascals, so you can nudge them in the right direction with this pattern.&lt;/p&gt;

&lt;p&gt;What happens when you try to instantiate a singletonized class:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--H7SJl12K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AiNVu-8xdCGoMgHYMXJwNZA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--H7SJl12K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AiNVu-8xdCGoMgHYMXJwNZA.png" alt="" width="800" height="141"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ah-HA! The type system saves the day, and your junior developer will realize that they are doing something they are not supposed to. The error message isn’t too helpful — it’s sort of like your mechanic telling you that there is insufficient electrical current flow between the car’s alternator, voltage regulator and starter motor, when all you need to know is that your battery is dead. But, with the singleton being such a common pattern, many developers will intuitively look for a static getInstance method or similar.&lt;/p&gt;

&lt;p&gt;What we’re really doing with our complicated types is building a fixture for building our app. Or, if we’re making a library, it might be at an even higher level of abstraction. With higher-kinded types we might be enabling creating types for creating a certain architecture. Let me be completely clear that I am not arguing against typing, I just think that that it can and should be complemented with linting. In fact, I think that any project of significant size, private or public, should come with a set of custom linter rules.&lt;/p&gt;

&lt;p&gt;The React &lt;a href="https://legacy.reactjs.org/docs/hooks-rules.html"&gt;Rules of Hooks&lt;/a&gt; is a nice example. The linter rules are extremely helpful when using hooks, and I cannot imagine the kind of trickery they would have had to pull if they wanted to achieve the same using types.&lt;/p&gt;

&lt;p&gt;I am working on a library that synchronizes state from the database to the frontend in endpoints. Basically, if any mutation is made, the response should contain an array that describes those changes. For simple CRUD operations, this can be done automatically. But for instance, when a table has a trigger attached, there can be undetected updates which require “manual” updates. That is quite easy to forget, so I wanted to try and create a guard rail for this.&lt;/p&gt;

&lt;p&gt;My first intuition was to use the type system. Maybe such mutations could return a state value that was marked as unresolved, and then a function that creates the necessary updates would take this state value and mark it as resolved. Endpoints would then have to return such a state value, if it was unresolved, it would trigger a compiler error. In Rust, I think this could be done quite elegantly because of its sophisticated ownership tracking. But I failed to come up with a nice solution in Typescript.&lt;/p&gt;

&lt;p&gt;Instead, I wrote an ESLint rule. This is what the first version looked like, with some helper functions omitted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = {
  create: function (context) {
    return {
      CallExpression(node) {
        if (isCallExpression(node, "router", ["get", "post", "put", "patch", "delete"])) {
          if (node.arguments.length !== 2 || node.arguments[1].type !== "ArrowFunctionExpression") {
            return;
          }
          const knexCalls = countMethodCalls(node.arguments[1], "db", "knex");
          const resolveCalls = countMethodCalls(node.arguments[1], "db", "resolve");

          if (knexCalls !== resolveCalls) {
            context.report({
              node,
              message: `There must be a corresponding db.resolve call for each db.knex call.`,
            });
          }
        }
      },
    };
  },
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basically, it checks that any db.knex call in an endpoint is complemented by a db.resolve call by making sure they are called the same number of times. I have since changed it quite significantly, but I wanted to show this version because it’s so trivial. If you are refraining from writing linter rules because it seems daunting, consider giving it a try. (Pro tip: ChatGPT or Co-Pilot are great at it!).&lt;/p&gt;

&lt;p&gt;While this rule does not cover everything, it has the very nice feature that the error message tells you what the solution is. If this had been a type error, you would almost certainly be facing a cryptic message that only makes sense if you already know what’s wrong. With this solution, our architecture is both more self-validating and self-documenting. It sure beats looking in that eternally outdated wiki that we all edited after the big planning meeting a year and a half ago!&lt;/p&gt;

&lt;p&gt;But, you might say, this doesn’t sound very &lt;a href="https://papl.cs.brown.edu/2014/safety-soundness.html"&gt;&lt;em&gt;sound&lt;/em&gt;&lt;/a&gt;&lt;em&gt;!&lt;/em&gt; Indeed, it‘s not. And I know that this is off-putting to some more than others. I personally don’t mind the lacking soundness in the Typescript type system, but to some it’s a complete deal-breaker. To me, the beauty is that linter rules are easy to disable in special conditions, so you really only do need to get them 80% right for them to be useful.&lt;/p&gt;

&lt;p&gt;In my mind, the triad of typing, testing and linting should be the gold standard of writing solid code.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>linter</category>
    </item>
    <item>
      <title>Use 98% TailwindCSS, 2% plain CSS</title>
      <dc:creator>Kristian Dupont</dc:creator>
      <pubDate>Tue, 21 Mar 2023 16:29:49 +0000</pubDate>
      <link>https://dev.to/kristiandupont/use-98-tailwindcss-2-plain-css-48fi</link>
      <guid>https://dev.to/kristiandupont/use-98-tailwindcss-2-plain-css-48fi</guid>
      <description>&lt;p&gt;Yes, it’s awesome. I declared my affection a while ago and it has genuinely altered the way I write frontend code. &lt;a href="https://itnext.io/and-naming-things-tailwind-css-typescript-and-mammals-9eab459633d2"&gt;Everything I loved about it&lt;/a&gt;, I still love. Additionally, it has nudged me out of the habit of making CSS rules that cascade. While those certainly have their advantages, avoiding them means that you can move html around without any fear which has felt very liberating to me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EL8q_52O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/620/0%2AJQT_DZMvUfpan2eL.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EL8q_52O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/620/0%2AJQT_DZMvUfpan2eL.jpg" alt="" width="620" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Software development can be fickle like the fashion industry and right now, utility classes are &lt;em&gt;on fire&lt;/em&gt;. I suspect that in time, they will fade a bit, the way micro services or NoSQL databases have. We have a tendency as a community to become infatuated with new principles and enter a state of limerence, believing that they are a universal solution that must be applied everywhere, to everything.&lt;/p&gt;

&lt;p&gt;So, while I can attest to the many benefits of utility classes, I find that there are a few downsides worth pointing out.&lt;/p&gt;

&lt;p&gt;When a designer hands me a Figma file, utility classes are great because I can easily identify the ones that match the margin, padding etc. that I am looking at. It’s a lot easier than scanning through CSS files, looking for the class that matches everything, if a such even exists. &lt;em&gt;However&lt;/em&gt;, it is also easy to overlook subtle details like letter spacing and line height. Those are things my non-designer eye is more likely to overlook and since there isn’t a rule set once and for all, I need to actively remember them.&lt;/p&gt;

&lt;p&gt;The same applies to classes for pseudo-states like hover and disabled. The fact that you always have to specify everything makes this particularly easy to miss as you can quickly look at the result and not notice that anything is missing, unless you explicitly try out those states.&lt;/p&gt;

&lt;p&gt;Tailwind recommends extracting components in React or whatever your framework is to encapsulate styles, rather than CSS classes. This generally seems to me to be a sound principle. Nevertheless, there are situations where it can be frustrating to have to split code into atoms, especially if you have a strict one-component-per-file policy (which I don’t recommend, though!)&lt;/p&gt;

&lt;p&gt;Finally, there are a number of things that I really just want to set and forget. For instance, I generally want all my links to look the same. Having to add a bunch of classes to each one feels unnecessary and silly, as does creating a component.&lt;/p&gt;

&lt;p&gt;Overall, Tailwind is a fantastic framework that I will continue to use. I have just reached the personal conclusion that there is &lt;em&gt;absolutely&lt;/em&gt; nothing wrong with creating a .css file or two to complement it (and to be clear, I don’t think the Tailwind people would say otherwise!) You can use &lt;a href="https://tailwindcss.com/docs/reusing-styles#extracting-classes-with-apply"&gt;apply&lt;/a&gt; or you can just make plain old styles, it’s all good. Just don’t be dogmatic for the sake of dogma.&lt;/p&gt;

</description>
      <category>css</category>
      <category>tailwindcss</category>
    </item>
    <item>
      <title>About index.js Files..</title>
      <dc:creator>Kristian Dupont</dc:creator>
      <pubDate>Tue, 20 Dec 2022 08:44:13 +0000</pubDate>
      <link>https://dev.to/kristiandupont/about-indexjs-files-1iho</link>
      <guid>https://dev.to/kristiandupont/about-indexjs-files-1iho</guid>
      <description>&lt;p&gt;Index.js files are a “cute” feature that Ryan Dahl came up with when he designed Node.js. While he &lt;a href="https://www.youtube.com/watch?v=M3BM9TB-8yA"&gt;officially regrets them&lt;/a&gt;, I think they have evolved to be a useful tool for structuring code. In this post, I will try to articulate the rules that I had made up for myself about them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qh5d3qEw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2Afn6tny8A_XGf82vf" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qh5d3qEw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2Afn6tny8A_XGf82vf" alt="" width="880" height="585"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Photo by Maksym Kaharlytskyi on Unsplash&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Basically_,_ index files serve one of two purposes: as &lt;em&gt;encapsulation&lt;/em&gt; or as a &lt;em&gt;namespace&lt;/em&gt;. The corollary of this is that index files act as guard rails for imports — I avoid importing something from a subfolder of a folder containing an index file.&lt;/p&gt;
&lt;h4&gt;
  
  
  Encapsulation
&lt;/h4&gt;

&lt;p&gt;If an index file is used for encapsulation, it should contain a default export for the primary entity (class, component, function, etc.) in he folder. It may also contain a number of names exports for related, “secondary” items. This is useful when you want to hide the implementation details of a module and expose only a single interface. For example, consider the following folder structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src
└── TopBar
    ├── index.js
    ├── TopBar.js
    ├── DropDown.js
    └── SearchBox.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, the TopBar folder contains the implementation for a top bar component, including a few sub-components. The index file re-exports the component in TopBar.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// index.js
export { default } from './TopBar';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use the component, you would import the default export from the TopBar folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import TopBar from './TopBar';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Namespace
&lt;/h4&gt;

&lt;p&gt;If an index file is used as a namespace, it should contain a bunch of named exports for everything in the folder. I used to do this a lot but I have actually stopped doing it in favor of just having the folder name in the import. However, it can still be useful to group related entities under a single identifier if they feel particularly strongly related.&lt;/p&gt;

&lt;p&gt;For example, consider the following folder structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src
└── shared-components
    ├── LoadingButton.jsx
    ├── Spinner.jsx
    └── ModalDialog
        ├── ModalDialog.jsx
        ├── index.js
        └── Overlay.jsx
    ├── Chevron.jsx
    └── index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, the shared-components folder contains four components. The index.js file in the&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export { default as LoadingButton } from './LoadingButton';
export { default as Spinner } from './Spinner';
export { default as ModalDialog } from './ModalDialog';
export { default as Chevron } from './Chevron';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes shared-components work like a sort of package that you can import the components from. The win over importing the components from their respective sub-directories is small but might be to your preference.&lt;/p&gt;

&lt;h4&gt;
  
  
  Importing from Sub-folders
&lt;/h4&gt;

&lt;p&gt;If a folder has an index file, I consider that a guard rail which should prevent me from importing anything from sub-folders. Now, files that are in the folder or a sub-folder &lt;em&gt;themselves&lt;/em&gt; are allowed to import stuff, you just shouldn’t cross the boundary, so to speak. This is something I want to create a linter rule for because it could be enforced automatically, but for now I keep track of it manually.&lt;/p&gt;

&lt;h4&gt;
  
  
  Emergent Design
&lt;/h4&gt;

&lt;p&gt;Both use cases are well suited for emergent design.&lt;/p&gt;

&lt;p&gt;In the case of encapsulation, you can start by writing something in a single file. When that file becomes too large or complex, you replace it with a folder of the same name (sans extension), and from the perspective of the rest of the code, everything remains the same.&lt;/p&gt;

&lt;p&gt;When used for namespaces, this is a nice first step for grouping related code before moving it into a separate package, either in your mono-repo using workspaces or all the way to NPM, depending on how general-purpose it becomes.&lt;/p&gt;

&lt;h4&gt;
  
  
  ..default exports??
&lt;/h4&gt;

&lt;p&gt;Some people argue that the concept of default exports was a mistake and I have some sympathy for that. At this point I’ve used them so much that it’s basically muscle memory and I’ve found that VSCode has sufficient refactoring capabilities for handling them, to the point where the downsides are negligible.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Name is the Leftmost Part of Semver</title>
      <dc:creator>Kristian Dupont</dc:creator>
      <pubDate>Sat, 24 Sep 2022 07:18:22 +0000</pubDate>
      <link>https://dev.to/kristiandupont/name-is-the-leftmost-part-of-semver-c2b</link>
      <guid>https://dev.to/kristiandupont/name-is-the-leftmost-part-of-semver-c2b</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xUjASjdJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Ag_R-P0Fg9yBgLQKl1d0gTw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xUjASjdJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Ag_R-P0Fg9yBgLQKl1d0gTw.png" alt="" width="880" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sorry, this is a bit of a rant. My preferred routing library for React has been &lt;a href="https://kyeotic.github.io/raviger/"&gt;Raviger&lt;/a&gt; for a while but recently I needed to figure out how the most recent version of React Router works. It turns out that there are plenty of answers on StackOverflow and elsewhere that explain how to do things, many for outdated versions of the API.&lt;/p&gt;

&lt;p&gt;I know that the React Router developers have taken flak for changing things throughout the years and I don’t mean to single them out. They have created great value for the community which I have capitalized on for free. I am grateful and they owe me absolutely nothing.&lt;/p&gt;

&lt;p&gt;The small point I want to make, though, is this: if you are changing your API so much that it’s not just a “major breaking change”, but rather a complete redesign, consider changing the name of your library rather than just the version. For ecosystem related reasons as well as pedantic ones.&lt;/p&gt;

</description>
      <category>semver</category>
    </item>
  </channel>
</rss>
