<?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>Forem: Philip Hern</title>
    <description>The latest articles on Forem by Philip Hern (@shrouwoods).</description>
    <link>https://forem.com/shrouwoods</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%2F3858493%2F9a8ff83c-d5c3-493f-9943-b91a2f0a61d8.jpg</url>
      <title>Forem: Philip Hern</title>
      <link>https://forem.com/shrouwoods</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/shrouwoods"/>
    <language>en</language>
    <item>
      <title>how i automated dev.to and linkedin publishing so visibility stops depending on memory</title>
      <dc:creator>Philip Hern</dc:creator>
      <pubDate>Sun, 05 Apr 2026 14:03:19 +0000</pubDate>
      <link>https://forem.com/shrouwoods/how-i-automated-devto-and-linkedin-publishing-so-visibility-stops-depending-on-memory-2g2i</link>
      <guid>https://forem.com/shrouwoods/how-i-automated-devto-and-linkedin-publishing-so-visibility-stops-depending-on-memory-2g2i</guid>
      <description>&lt;p&gt;after i started writing more consistently, it became obvious that writing is only half the work; distribution is the other half. i wanted a system where i can publish from one canonical source and let automation push the same story to dev.to and linkedin.&lt;/p&gt;

&lt;h2&gt;
  
  
  quick answer
&lt;/h2&gt;

&lt;p&gt;i set up two publish automations that watch my post changes and sync them to dev.to and linkedin. the first publish creates the post on each platform, and later edits update the same external post instead of creating duplicates. this gives me consistent visibility without adding manual publishing steps after every article.&lt;/p&gt;

&lt;h2&gt;
  
  
  who this is for
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;people who publish technical writing and keep forgetting cross-posting&lt;/li&gt;
&lt;li&gt;creators who want one canonical source plus repeatable distribution&lt;/li&gt;
&lt;li&gt;builders who care about discoverability as much as writing quality&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  why this matters
&lt;/h2&gt;

&lt;p&gt;if distribution is manual, it eventually slips. then strong posts sit unread because i forgot to copy, paste, format, and re-share them across platforms. automation solves that by making visibility part of the same delivery path as the content itself.&lt;/p&gt;

&lt;p&gt;this is the same pattern i described in &lt;a href="https://philliant.com/posts/20260319-practical-ai-workflow-jira-github-mcp/" rel="noopener noreferrer"&gt;a practical ai workflow: jira, github, and mcp&lt;/a&gt;, define one clear source of truth, then automate the handoff steps so i can spend more time on thinking and less time on clerical work.&lt;/p&gt;

&lt;h2&gt;
  
  
  step-by-step
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1) define the starting point
&lt;/h3&gt;

&lt;p&gt;i chose my site post as the only canonical source. every external platform receives content from that source, not from separate drafts. this keeps language, links, and updates aligned over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  2) apply the change
&lt;/h3&gt;

&lt;p&gt;i added automation for both targets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;trigger on post updates and support manual runs when i want a full backfill&lt;/li&gt;
&lt;li&gt;create posts when no external mapping exists&lt;/li&gt;
&lt;li&gt;update existing external posts when a mapping already exists&lt;/li&gt;
&lt;li&gt;keep a small state map so each canonical url stays attached to one external post id&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;the practical result is that i can keep writing in one place and trust the sync layer to handle distribution. this complements the writing habits from &lt;a href="https://philliant.com/posts/20260313-my-cursor-setup/" rel="noopener noreferrer"&gt;my cursor setup&lt;/a&gt;, where reusable workflows remove repeated manual work.&lt;/p&gt;

&lt;h3&gt;
  
  
  3) validate the result
&lt;/h3&gt;

&lt;p&gt;i test in three passes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;dry run to confirm detection and decisions without publishing&lt;/li&gt;
&lt;li&gt;publish-all run to verify initial backfill behavior&lt;/li&gt;
&lt;li&gt;normal change-trigger run to verify incremental updates on later edits&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;when all three pass, i know the pipeline is reliable enough for daily use.&lt;/p&gt;

&lt;h2&gt;
  
  
  faq
&lt;/h2&gt;

&lt;h3&gt;
  
  
  what was the biggest setup mistake?
&lt;/h3&gt;

&lt;p&gt;token and redirect mismatches during oauth were the main failure point at first. once i aligned scopes, callback values, and secret placement, the automation became stable.&lt;/p&gt;

&lt;h3&gt;
  
  
  should i keep manual publishing as a fallback?
&lt;/h3&gt;

&lt;p&gt;yes, especially while you are in early setup. after the workflow proves stable, manual publishing becomes a recovery path instead of a default habit.&lt;/p&gt;

&lt;h2&gt;
  
  
  references
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developers.forem.com/api" rel="noopener noreferrer"&gt;dev.to api docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/developers/" rel="noopener noreferrer"&gt;linkedin developer platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/actions" rel="noopener noreferrer"&gt;github actions documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  related reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://philliant.com/posts/20260319-practical-ai-workflow-jira-github-mcp/" rel="noopener noreferrer"&gt;a practical ai workflow: jira, github, and mcp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://philliant.com/posts/20260313-my-cursor-setup/" rel="noopener noreferrer"&gt;my cursor setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://philliant.com/posts/20260315-starter-templates-for-ai-rules-skills-and-commands/" rel="noopener noreferrer"&gt;starter templates for ai rules, skills, and commands&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>automation</category>
      <category>devto</category>
      <category>linkedin</category>
      <category>publishing</category>
    </item>
    <item>
      <title>the future of data engineering workflows with ai</title>
      <dc:creator>Philip Hern</dc:creator>
      <pubDate>Fri, 03 Apr 2026 14:11:59 +0000</pubDate>
      <link>https://forem.com/shrouwoods/the-future-of-data-engineering-workflows-with-ai-42mb</link>
      <guid>https://forem.com/shrouwoods/the-future-of-data-engineering-workflows-with-ai-42mb</guid>
      <description>&lt;h2&gt;
  
  
  quick answer
&lt;/h2&gt;

&lt;p&gt;the future of data engineering workflows with ai is about moving from manual coding to intelligent orchestration. ai agents will handle boilerplate code, pipeline generation, and data quality checks, allowing data engineers to focus on architecture, governance, and business value.&lt;/p&gt;

&lt;h2&gt;
  
  
  who this is for
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;audience: data engineers, analytics engineers, data architects, and technical leaders.&lt;/li&gt;
&lt;li&gt;prerequisites: an understanding of modern data stack concepts and basic ai principles.&lt;/li&gt;
&lt;li&gt;when to use this guide: when planning your data strategy and evaluating how to integrate ai into your engineering practices.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  why this matters
&lt;/h2&gt;

&lt;p&gt;the volume and complexity of data are growing faster than engineering teams can scale. relying solely on manual workflows leads to bottlenecks, technical debt, and delayed insights. embracing ai is not just about efficiency, it is a strategic imperative to remain competitive.&lt;/p&gt;

&lt;h2&gt;
  
  
  step-by-step
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1) define the starting point
&lt;/h3&gt;

&lt;p&gt;traditionally, data engineering has been a highly manual discipline. engineers spend countless hours writing sql, configuring orchestrators like airflow, and debugging failed pipelines. this approach is brittle and scales poorly as the organization grows.&lt;/p&gt;

&lt;h3&gt;
  
  
  2) apply the change
&lt;/h3&gt;

&lt;p&gt;the integration of ai changes this paradigm. large language models can now generate complex sql queries, translate between dialects, and even suggest optimal data models based on source schemas. ai agents can monitor pipeline health, automatically retry transient failures, and alert engineers only when human intervention is necessary. this shift transforms the engineer from a coder into a system architect.&lt;/p&gt;

&lt;h3&gt;
  
  
  3) validate the result
&lt;/h3&gt;

&lt;p&gt;the impact of this transformation is measurable. development cycles shorten, data quality improves through automated testing, and the overall reliability of the platform increases. engineers spend less time firefighting and more time building scalable, resilient architectures that drive business decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  faq
&lt;/h2&gt;

&lt;h3&gt;
  
  
  what is the most important caveat?
&lt;/h3&gt;

&lt;p&gt;ai is a tool, not a replacement for fundamental engineering principles. you still need a strong understanding of data modeling, governance, and security to build a robust platform.&lt;/p&gt;

&lt;h3&gt;
  
  
  what should i do first?
&lt;/h3&gt;

&lt;p&gt;start by identifying the most repetitive tasks in your workflow, such as writing documentation or basic transformations. experiment with ai tools to automate these specific areas before attempting to overhaul your entire architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  references
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://a16z.com/2020/10/15/the-emerging-architectures-for-modern-data-infrastructure/" rel="noopener noreferrer"&gt;the modern data stack&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  related reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://philliant.com/posts/20260318-from-prototype-to-production-ai/" rel="noopener noreferrer"&gt;from prototype to production ai&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>dataengineering</category>
      <category>ai</category>
      <category>workflows</category>
      <category>future</category>
    </item>
    <item>
      <title>how i use cursor and ai agents to write dbt tests and documentation</title>
      <dc:creator>Philip Hern</dc:creator>
      <pubDate>Fri, 03 Apr 2026 14:07:49 +0000</pubDate>
      <link>https://forem.com/shrouwoods/how-i-use-cursor-and-ai-agents-to-write-dbt-tests-and-documentation-46od</link>
      <guid>https://forem.com/shrouwoods/how-i-use-cursor-and-ai-agents-to-write-dbt-tests-and-documentation-46od</guid>
      <description>&lt;h2&gt;
  
  
  quick answer
&lt;/h2&gt;

&lt;p&gt;writing dbt tests and documentation is often the most neglected part of data engineering. i use cursor and custom ai agents to automate this process by reading my sql models, inferring the business logic, and generating the corresponding yaml files. this ensures high-quality data pipelines without the manual overhead.&lt;/p&gt;

&lt;h2&gt;
  
  
  who this is for
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;audience: data engineers, analytics engineers, and developers using dbt&lt;/li&gt;
&lt;li&gt;prerequisites: basic knowledge of dbt, sql, and cursor&lt;/li&gt;
&lt;li&gt;when to use this guide: when you want to scale your data engineering practices and reduce the time spent on writing boilerplate yaml&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  why this matters
&lt;/h2&gt;

&lt;p&gt;documentation and testing are critical for data trust, but they are tedious to write manually. when these steps are skipped, data quality suffers and debugging becomes a nightmare. by automating this with ai, you get the benefits of rigorous testing and clear documentation while freeing up your time for higher-value architectural work.&lt;/p&gt;

&lt;h2&gt;
  
  
  step-by-step
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1) define the starting point
&lt;/h3&gt;

&lt;p&gt;most data engineers start with a raw sql model and a blank slate for their &lt;code&gt;schema.yml&lt;/code&gt; file. the traditional approach requires manually typing out every column name, description, and test. this is prone to human error and inconsistency, plus almost always falls out of sync with current models with the first change.&lt;/p&gt;

&lt;h3&gt;
  
  
  2) apply the change
&lt;/h3&gt;

&lt;p&gt;i use cursor to bridge this gap. by creating specific ai rules and skills, i can highlight a dbt model and ask the agent to generate the documentation. the agent reads the sql, understands the joins and transformations, and produces a complete yaml file with standard tests like &lt;code&gt;not_null&lt;/code&gt; and &lt;code&gt;unique&lt;/code&gt;. it can even infer complex relationships and suggest custom tests based on the data domain.&lt;/p&gt;

&lt;h3&gt;
  
  
  3) validate the result
&lt;/h3&gt;

&lt;p&gt;once the ai generates the yaml, i review it for accuracy. i then run &lt;code&gt;dbt test&lt;/code&gt; and &lt;code&gt;dbt docs generate&lt;/code&gt; to ensure everything compiles correctly. the ai rarely makes syntax errors, so the validation step is mostly about confirming the business logic aligns with the documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  faq
&lt;/h2&gt;

&lt;h3&gt;
  
  
  what is the most important caveat?
&lt;/h3&gt;

&lt;p&gt;you must still review the generated output. ai is excellent at scaffolding and inferring patterns, but it does not possess the full business context that you do.&lt;/p&gt;

&lt;h3&gt;
  
  
  what should i do first?
&lt;/h3&gt;

&lt;p&gt;start by creating a simple cursor skill that defines your team's standards for dbt documentation. feed it a few examples of your best &lt;code&gt;schema.yml&lt;/code&gt; files so it learns your preferred style.&lt;/p&gt;

&lt;h2&gt;
  
  
  references
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.getdbt.com/" rel="noopener noreferrer"&gt;dbt documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  related reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://philliant.com/posts/20260313-my-cursor-setup/" rel="noopener noreferrer"&gt;my cursor setup&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>dbt</category>
      <category>cursor</category>
      <category>ai</category>
      <category>dataengineering</category>
    </item>
    <item>
      <title>sharing is caring</title>
      <dc:creator>Philip Hern</dc:creator>
      <pubDate>Thu, 02 Apr 2026 12:11:05 +0000</pubDate>
      <link>https://forem.com/shrouwoods/sharing-is-caring-2303</link>
      <guid>https://forem.com/shrouwoods/sharing-is-caring-2303</guid>
      <description>&lt;h2&gt;
  
  
  the value of early adoption
&lt;/h2&gt;

&lt;p&gt;i have always found a unique kind of energy in being an early adopter. when a new tool emerges, especially something as transformative as cursor and artificial intelligence, diving in headfirst is not just about personal efficiency. it is about understanding the landscape before the map is fully drawn. by spending the hours required to become a high-level user, i build a deep familiarity with the edges of what the technology can do.&lt;/p&gt;

&lt;p&gt;this mastery translates directly into value for my colleagues. when you understand the high-level nuance of a complex tool, you naturally become the point person for your team. people have onboarding questions, they hit roadblocks, and they need someone who has already navigated those early frustrations. being that resource is incredibly rewarding. it shifts my role from an individual contributor to a multiplier, helping the entire team elevate their workflow and avoid the pitfalls i have already solved.&lt;/p&gt;

&lt;h2&gt;
  
  
  the responsibility to share
&lt;/h2&gt;

&lt;p&gt;this dynamic reminds me of a principle i have heard often over the years regarding the importance of using your voice and your platform to share. this is exactly why i started this website. i wanted a dedicated space to share my voice, my knowledge, my opinions, my experience, and the solutions i have discovered along the way.&lt;/p&gt;

&lt;p&gt;when you hold onto knowledge, its impact is limited to your own output. when you share it, the impact scales infinitely. writing about these tools, documenting my workflows, and answering the nuanced questions my colleagues ask are all extensions of the same core belief. knowledge is meant to be distributed.&lt;/p&gt;

&lt;h2&gt;
  
  
  stepping into mentorship
&lt;/h2&gt;

&lt;p&gt;i have reached a point of mastery and experience where the natural next step for me is to mentor others and deliberately increase my visibility and presence. it is no longer enough to simply be good at what i do behind the scenes. the real work now is in lifting others up.&lt;/p&gt;

&lt;p&gt;in fact, i am starting to feel the weight of this realization. it feels almost selfish not to share what i have learned. when you spend years honing a craft or mastering a paradigm-shifting tool like ai-assisted development, you accumulate a wealth of invisible context. keeping that context locked away serves no one. stepping into a mentorship role, both directly with my colleagues and publicly through this platform, is how i honor the effort it took to gain that experience in the first place.&lt;/p&gt;

&lt;h2&gt;
  
  
  looking forward
&lt;/h2&gt;

&lt;p&gt;my goal is to continue exploring the bleeding edge of these tools, but with a renewed focus on how i can translate those discoveries into accessible guidance for others. whether it is through answering a quick onboarding question about cursor, writing a detailed guide on this site, or simply being a sounding board for a colleague, the objective remains the same. i want to use my experience to make the path easier for those who follow.&lt;/p&gt;

&lt;h2&gt;
  
  
  further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.cursor.com/" rel="noopener noreferrer"&gt;cursor documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  related on this site
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://philliant.com/posts/" rel="noopener noreferrer"&gt;post title&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>mentorship</category>
      <category>ai</category>
      <category>cursor</category>
      <category>earlyadoption</category>
    </item>
    <item>
      <title>what is art?</title>
      <dc:creator>Philip Hern</dc:creator>
      <pubDate>Mon, 30 Mar 2026 23:38:30 +0000</pubDate>
      <link>https://forem.com/shrouwoods/what-is-art-1ofe</link>
      <guid>https://forem.com/shrouwoods/what-is-art-1ofe</guid>
      <description>&lt;h2&gt;
  
  
  thesis
&lt;/h2&gt;

&lt;p&gt;i keep pondering lately, what are we actually defending when we say "ai art is not real art"?&lt;/p&gt;

&lt;p&gt;i do not have a final position yet. i am writing this to think in public, not to close the debate.&lt;/p&gt;

&lt;h2&gt;
  
  
  context
&lt;/h2&gt;

&lt;p&gt;while driving on a family vacation, i asked my wife to fulfill her duty as the passenger and dj some motown bangers. she searched on spotify and found something that seemed to fit the bill. most of the songs were recognizable, memories from my childhood, riding in the backseat listening to my parents' favorites. however, the first song on the playlist was by an artist called the &lt;strong&gt;19s soulers&lt;/strong&gt;, which was an artist i did not recognize. this was a user created playlist so not everything might fit perfectly into the motown mold i was asking for, and that was ok. the song started and it was &lt;strong&gt;SOOO&lt;/strong&gt; good. &lt;strong&gt;TOO&lt;/strong&gt; good. i had my suspicions, but the music caught me so hard that i completely forgot. i asked my boys in the back seat to look up the artist and they did not even search and just responded "AI DAD - IT IS AI". i felt so many conflicting emotions, including one of pride that my boys could tell the difference and they have some defense against being fooled.&lt;/p&gt;

&lt;p&gt;the conversation around ai-generated images and music feels hotter every week, especially when a new ai music act gets attention or a contract. the reaction is often immediate and predictable outrage, fear, dismissal, and arguments about stolen style.&lt;/p&gt;

&lt;p&gt;at the same time, many of us use ai to help write code, review pull requests, or shape architecture notes without the same emotional response. that contrast is interesting to me.&lt;/p&gt;

&lt;p&gt;if i call code a craft, and sometimes an art form, then why does ai help feel acceptable there for so many people, but unacceptable when the ai helps write song lyrics? and if code can be expressive, why is the outrage concentrated in painting, illustration, and music.&lt;/p&gt;

&lt;p&gt;my code has my fingerprints all over it, just as much as this website and the way i speak and write. it defintely qualifies as expressive. i make stylistic, logic, function, etc. choices that suit my style. how is this different from writing a book? but if you asked which one is acceptable to use ai and which one is not, i could guess your answer 99% of the time.&lt;/p&gt;

&lt;h2&gt;
  
  
  argument
&lt;/h2&gt;

&lt;p&gt;i see a few possible reasons, and none of them feel complete on their own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;visual art and music are tied to identity in a very direct way&lt;/li&gt;
&lt;li&gt;audiences often connect to the "maker story", not only the artifact&lt;/li&gt;
&lt;li&gt;creative labor markets in those fields already felt fragile before ai&lt;/li&gt;
&lt;li&gt;software teams have normalized tool-assisted output for decades&lt;/li&gt;
&lt;li&gt;code is often judged by function first, while art is judged by intention and feeling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;still, even with those differences, i cannot shake the inconsistency.&lt;/p&gt;

&lt;p&gt;when i use ai in code, i still feel like the author because i set constraints, reject bad output, and own the result. i do not think that is very different from guiding a visual generator, editing outputs, and curating a final piece. maybe the difference is only social permission, not creative mechanics.&lt;/p&gt;

&lt;p&gt;this question also links to my concern about ownership in &lt;a href="https://philliant.com/posts/20260326-the-danger-of-trusting-the-ai-agent/" rel="noopener noreferrer"&gt;the danger of trusting the ai agent&lt;/a&gt;, where speed is useful but responsibility still has to stay human.&lt;/p&gt;

&lt;h3&gt;
  
  
  tension or counterpoint
&lt;/h3&gt;

&lt;p&gt;there is also a strong counterpoint i take seriously: in code, wrong answers fail in visible ways. tests fail, services break, users complain, and teams can trace accountability. in art, value is less binary, and that makes authorship feel more central and more vulnerable.&lt;/p&gt;

&lt;p&gt;another counterpoint is economic, not philosophical. people may not be reacting to "is this art" at all. they may be reacting to "will this replace my livelihood".&lt;/p&gt;

&lt;p&gt;both of those points feel real to me.&lt;/p&gt;

&lt;p&gt;and i think the later point is one worth exploring because the wide-spread availability of ai has "democratized" creativity, technical endeavors, etc. for people who might have great ideas, but not the musical or technical skill to carry out the plan. well, now they do. and that instant competition that was not present before can certainly feel intimidating and encroaching.&lt;/p&gt;

&lt;p&gt;i am currently mostly pro-ai, but with caution. we should have caution regarding how the models are being trained (and on what data) and regulated. we should exercise caution surrounding &lt;em&gt;who&lt;/em&gt; is doing the regulating, as well. ai is a powerful assistant, and as we all know from spiderman, with great power comes great responsibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  closing
&lt;/h2&gt;

&lt;p&gt;i am left with questions, not conclusions.&lt;/p&gt;

&lt;p&gt;maybe we value human touch most where we believe the human story is the product. maybe we accept ai more where we believe the product is utility. maybe those boundaries are changing and we are all reacting in real time.&lt;/p&gt;

&lt;p&gt;for now, i am trying to keep the question open.....when ai is part of the process, what still makes something mine, yours, or ours?&lt;/p&gt;

&lt;h2&gt;
  
  
  further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.copyright.gov/ai/" rel="noopener noreferrer"&gt;copyright and artificial intelligence, u.s. copyright office&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Generative_art" rel="noopener noreferrer"&gt;generative art&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Computer_music" rel="noopener noreferrer"&gt;computer music&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  related on this site
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://philliant.com/posts/20260318-from-prototype-to-production-ai/" rel="noopener noreferrer"&gt;from prototype to production: my early adopter view of ai&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://philliant.com/posts/20260326-the-danger-of-trusting-the-ai-agent/" rel="noopener noreferrer"&gt;the danger of trusting the ai agent&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://philliant.com/series/commentary/" rel="noopener noreferrer"&gt;commentary series&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>art</category>
      <category>creativity</category>
      <category>music</category>
    </item>
    <item>
      <title>dbt tests</title>
      <dc:creator>Philip Hern</dc:creator>
      <pubDate>Mon, 30 Mar 2026 23:34:36 +0000</pubDate>
      <link>https://forem.com/shrouwoods/dbt-tests-26bb</link>
      <guid>https://forem.com/shrouwoods/dbt-tests-26bb</guid>
      <description>&lt;p&gt;we all know testing is valuable, but almost all dbt projects still underinvest in it. i am guilty of this, shipping the model, promising i will add tests later, then moving on to the next urgent request.&lt;/p&gt;

&lt;p&gt;that pattern feels fast in the moment, but it is expensive over time. dbt tests are one of the easiest ways to protect trust in your data, and the setup cost is usually smaller than people expect.&lt;/p&gt;

&lt;h2&gt;
  
  
  quick answer
&lt;/h2&gt;

&lt;p&gt;dbt tests are assertions about your data that run inside your transformation workflow. they verify assumptions like uniqueness, non-null keys, valid categorical values, and referential integrity. when a test fails, dbt surfaces the exact failing records so you can debug quickly. if you are new to the feature, start with the official &lt;a href="https://docs.getdbt.com/docs/build/data-tests" rel="noopener noreferrer"&gt;dbt data tests documentation&lt;/a&gt; and add a few high-signal tests to your most consumed models first.&lt;/p&gt;

&lt;h2&gt;
  
  
  who this is for
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;analytics engineers who already build dbt models but still rely on manual spot checks&lt;/li&gt;
&lt;li&gt;data teams that have recurring data quality incidents in dashboards or reports&lt;/li&gt;
&lt;li&gt;anyone who wants a practical starting point instead of a perfect testing framework&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  why this matters
&lt;/h2&gt;

&lt;p&gt;the cost of bad data is usually delayed, not immediate. a broken metric can sit in production for days before someone notices, and by then that number has already been used in a deck, decision, or leadership update.&lt;/p&gt;

&lt;p&gt;the frustrating part is that most of these issues are predictable. duplicate primary keys, null foreign keys, unexpected status values, and invalid date ranges are all common failures. dbt tests can catch these early, near the model that introduced the issue.&lt;/p&gt;

&lt;p&gt;i also think testing helps teams move faster, not slower. when tests are in place, i can refactor a model with more confidence because i have a safety net. without tests, every change feels risky and review cycles become slower because everyone is relying on intuition.&lt;/p&gt;

&lt;h2&gt;
  
  
  step-by-step
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1) define the starting point
&lt;/h3&gt;

&lt;p&gt;pick one model that is heavily consumed, for example an order fact table or a customer dimension. identify three assumptions that must always be true:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the model key is unique&lt;/li&gt;
&lt;li&gt;important keys are never null&lt;/li&gt;
&lt;li&gt;status fields only contain known values&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;then encode those assumptions directly in your yml.&lt;/p&gt;

&lt;h3&gt;
  
  
  2) apply the change
&lt;/h3&gt;

&lt;p&gt;start with generic tests in your schema file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;

&lt;span class="na"&gt;models&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fct_orders&lt;/span&gt;
    &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;order_id&lt;/span&gt;
        &lt;span class="na"&gt;tests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;not_null&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;unique&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;customer_id&lt;/span&gt;
        &lt;span class="na"&gt;tests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;not_null&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;relationships&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ref('dim_customers')&lt;/span&gt;
              &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;customer_id&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;order_status&lt;/span&gt;
        &lt;span class="na"&gt;tests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;accepted_values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;placed"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shipped"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cancelled"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;returned"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;this single block gives you strong baseline coverage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;not_null&lt;/code&gt; protects required fields&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;unique&lt;/code&gt; protects grain&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;relationships&lt;/code&gt; protects joins and referential integrity&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;accepted_values&lt;/code&gt; protects enum-like business states&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;next, add one singular test for a business rule that generic tests cannot express cleanly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- tests/orders_non_negative_amount.sql&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;order_amount&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="k"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'fct_orders'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;order_amount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;this test fails only when the query returns rows. singular tests are ideal for custom rules like range checks, cross-column logic, and impossible combinations.&lt;/p&gt;

&lt;h3&gt;
  
  
  3) validate the result
&lt;/h3&gt;

&lt;p&gt;run tests in a tight loop while you are developing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dbt &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--select&lt;/span&gt; fct_orders
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;for a broader gate in CI or before merging, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dbt build &lt;span class="nt"&gt;--select&lt;/span&gt; fct_orders+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;dbt build&lt;/code&gt; runs models and tests together, which is useful when you want to validate both transformation logic and data quality in one pass. the &lt;code&gt;+&lt;/code&gt; after the model name tells dbt to also run any downstream models according to the dag to make sure dependencies also pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  a practical prioritization rule
&lt;/h2&gt;

&lt;p&gt;when time is limited, i prioritize tests in this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;key integrity on high-consumption models (&lt;code&gt;unique&lt;/code&gt; plus &lt;code&gt;not_null&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;foreign key integrity (&lt;code&gt;relationships&lt;/code&gt;) on joins that power dashboards&lt;/li&gt;
&lt;li&gt;controlled fields (&lt;code&gt;accepted_values&lt;/code&gt;) where business logic depends on a finite set of values&lt;/li&gt;
&lt;li&gt;one custom singular test for the highest-risk metric or business rule&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;this sequence catches a large share of real incidents with minimal setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  faq
&lt;/h2&gt;

&lt;h3&gt;
  
  
  what should i test first in a mature project with little coverage?
&lt;/h3&gt;

&lt;p&gt;start where breakage is most expensive, not where modeling is most elegant. choose one or two heavily consumed models and add key integrity plus relationships first. then add one singular test for the business rule that has caused the most historical pain.&lt;/p&gt;

&lt;h3&gt;
  
  
  do dbt tests slow down delivery?
&lt;/h3&gt;

&lt;p&gt;they add some upfront work, but they usually reduce cycle time later. test failures are cheaper during development than after release, and tests make refactors safer because you can verify assumptions continuously instead of rediscovering issues in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  references
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.getdbt.com/docs/build/data-tests" rel="noopener noreferrer"&gt;dbt docs, data tests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.getdbt.com/reference/resource-properties/data-tests" rel="noopener noreferrer"&gt;dbt docs, test properties&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.getdbt.com/best-practices/writing-custom-generic-tests" rel="noopener noreferrer"&gt;dbt docs, writing custom generic data tests&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  related reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://philliant.com/posts/20260330-dbt-docs/" rel="noopener noreferrer"&gt;dbt docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://philliant.com/series/dbt/" rel="noopener noreferrer"&gt;dbt series&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>dbt</category>
      <category>testing</category>
      <category>dataquality</category>
      <category>analyticsengineering</category>
    </item>
    <item>
      <title>dbt docs</title>
      <dc:creator>Philip Hern</dc:creator>
      <pubDate>Mon, 30 Mar 2026 11:39:08 +0000</pubDate>
      <link>https://forem.com/shrouwoods/dbt-docs-3h12</link>
      <guid>https://forem.com/shrouwoods/dbt-docs-3h12</guid>
      <description>&lt;p&gt;most data engineers i know will spend hours getting a model right, then skip the one step that makes it discoverable to everyone else. dbt docs are that step, and they are worth the effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  quick answer
&lt;/h2&gt;

&lt;p&gt;dbt docs is a built-in feature that generates a browsable website from your dbt project. it pulls descriptions from your yml files, renders a searchable model catalog, and draws a lineage graph showing how every model connects. running &lt;code&gt;dbt docs generate&lt;/code&gt; followed by &lt;code&gt;dbt docs serve&lt;/code&gt; gives you a local site instantly. the real payoff is that teammates who never open your sql files can still understand what each model does, what columns it exposes, and where the data comes from. this is especially useful to downstream consumers of your objects because they can see the exact format of the object, including column names and data types, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  who this is for
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;data engineers who use dbt but skip writing descriptions&lt;/li&gt;
&lt;li&gt;analysts, product managers, or business users who need to understand available data without reading sql&lt;/li&gt;
&lt;li&gt;team leads looking for a low-effort way to make data structures discoverable&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  why this matters
&lt;/h2&gt;

&lt;p&gt;when you build a dbt project, the models represent real business concepts. customers, products, sales, inventory, whatever the domain is. the people who consume those models in dashboards or reports often have no involvement in building them and no reason to read raw sql.&lt;/p&gt;

&lt;p&gt;without documentation, those consumers rely on tribal knowledge, slack messages, and guesswork. that does not scale. dbt docs solve this by turning the metadata you already maintain (yml files, project config, source definitions) into a navigable reference that anyone on the team can use. the effort to write a good description is small, and the compound value to the rest of the organization grows with every model you add.&lt;/p&gt;

&lt;p&gt;i think of it like this: if i write a model and do not document it, the only person who truly understands it is me, and even that fades after a few months. if i write a two-sentence description and add column-level context, that knowledge lives in the project permanently and serves everyone who touches the data.&lt;/p&gt;

&lt;h2&gt;
  
  
  what dbt docs generates
&lt;/h2&gt;

&lt;p&gt;when you run &lt;code&gt;dbt docs generate&lt;/code&gt;, dbt produces two main artifacts in your &lt;code&gt;target/&lt;/code&gt; directory:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;manifest.json&lt;/strong&gt; contains the full project graph, including every model, source, seed, snapshot, and macro, along with their descriptions, tags, and configuration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;catalog.json&lt;/strong&gt; contains the schema-level metadata pulled from your warehouse, including column names, data types, and row counts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;together with a bundled &lt;code&gt;index.html&lt;/code&gt;, these files power a static site that you can open locally with &lt;code&gt;dbt docs serve&lt;/code&gt; or host anywhere that serves static files.&lt;/p&gt;

&lt;h3&gt;
  
  
  the site includes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;a searchable list of every model and data source in your project&lt;/li&gt;
&lt;li&gt;model-level and column-level descriptions pulled from your yml files&lt;/li&gt;
&lt;li&gt;the full sql compiled for each model (in each environment)&lt;/li&gt;
&lt;li&gt;a lineage graph (dag - directed acyclic graph) that shows upstream sources, intermediate models, and downstream consumers for any selected node&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;the lineage graph is especially useful when someone asks "where does this column come from" or "what breaks if i change this source table". instead of tracing through sql files manually, the graph answers it visually.&lt;/p&gt;

&lt;h2&gt;
  
  
  how to document your models
&lt;/h2&gt;

&lt;p&gt;dbt reads documentation from yml files that live alongside your models. you are probably already using these for configuration and &lt;a href="https://philliant.com/posts/20260328-what-is-sql-and-why-it-still-works/" rel="noopener noreferrer"&gt;source definitions&lt;/a&gt;, so adding descriptions is a natural extension.&lt;/p&gt;

&lt;h3&gt;
  
  
  model and column descriptions in yml
&lt;/h3&gt;

&lt;p&gt;the most common approach is adding &lt;code&gt;description&lt;/code&gt; fields directly in your yml files. here is what that looks like for a view in an information delivery layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;

&lt;span class="na"&gt;models&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ORDER_SUMMARY_V&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="s"&gt;aggregated view of customer orders with totals and status&lt;/span&gt;
      &lt;span class="s"&gt;breakdowns, consumed by the reporting dashboard and the&lt;/span&gt;
      &lt;span class="s"&gt;customer details page in the application.&lt;/span&gt;
    &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CUSTOMER_ID&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unique&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;identifier&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;customer"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TOTAL_ORDERS&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;count&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;orders&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;placed&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;by&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;this&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;customer"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TOTAL_SPEND&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sum&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;order&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;amounts&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;across&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;completed&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;orders"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LAST_ORDER_DATE&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;most&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;recent&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;order&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;this&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;customer"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;every model and every column can have a description. the more specific you are, the more useful the generated docs become. "id" as a column description does not help anyone. "unique identifier for the customer, sourced from the application database" does.&lt;/p&gt;

&lt;h3&gt;
  
  
  source descriptions
&lt;/h3&gt;

&lt;p&gt;sources benefit from the same treatment. when your project ingests raw data from an external system, describing those sources in your shared sources file makes the lineage graph meaningful from the very first node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;

&lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RAW_ORDERS&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;raw&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;order&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ingested&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;from&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;transactional&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;database&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;via&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;kafka"&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ANALYTICS_DEV_DB&lt;/span&gt;
    &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RAW_DATA&lt;/span&gt;
    &lt;span class="na"&gt;tables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ORDERS&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;one&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;row&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;per&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;order,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;includes&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;order&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;timestamps"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ORDER_ITEMS&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;line&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;items&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;each&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;order,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;one&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;row&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;per&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;product&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;per&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;order"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  doc blocks for longer descriptions
&lt;/h3&gt;

&lt;p&gt;when a model needs more than a sentence or two of context, dbt supports doc blocks. these are markdown files (&lt;code&gt;.md&lt;/code&gt;) that live in your project and can be referenced from yml descriptions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;{% docs order_summary_description %}

this view surfaces the aggregated order history for each customer.
it joins the hub, satellite, and link tables from the refined data
layer to produce a single wide row per customer.

&lt;span class="gs"&gt;**grain:**&lt;/span&gt; one row per customer.

&lt;span class="gs"&gt;**consumers:**&lt;/span&gt; reporting dashboard, customer details api endpoint.

{% enddocs %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then in your yml:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;models&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ORDER_SUMMARY_V&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;doc("order_summary_description")&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;doc blocks are useful when the context is long enough that embedding it inline in yaml becomes awkward. they also let you reuse the same description across multiple references if needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  a few useful options in dbt docs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  persist_docs
&lt;/h3&gt;

&lt;p&gt;by default, dbt docs only live in the generated static site. if you want the descriptions to also appear in your warehouse catalog (so someone querying &lt;a href="https://philliant.com/posts/20260328-the-difference-between-snowflake-and-the-other-databases/" rel="noopener noreferrer"&gt;snowflake&lt;/a&gt; information_schema can see them), you can enable &lt;code&gt;persist_docs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;models&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;my_data_product&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;+persist_docs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;relation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with this enabled, &lt;code&gt;dbt run&lt;/code&gt; pushes your yml descriptions into the &lt;code&gt;COMMENT&lt;/code&gt; property on the table or view and on each column in the warehouse. this is valuable because it means the documentation is available even outside the dbt docs site, directly in the database catalog that tools like snowflake and bi platforms already read.&lt;/p&gt;

&lt;h3&gt;
  
  
  the lineage graph
&lt;/h3&gt;

&lt;p&gt;the generated site includes an interactive dag that visualizes every model, source, and their connections. you can click on any node to see its upstream dependencies and downstream consumers. this is one of the most powerful features in dbt docs because it makes the data flow tangible for people who do not read sql (or maybe they just do not have access to your workspace, but still have a need to understand it).&lt;/p&gt;

&lt;p&gt;when you have a project with dozens or hundreds of models organized into layers (raw, refined, business, information delivery), the lineage graph shows how a raw source table flows through transformations into the final views that analysts query. it replaces the need for manually maintained architecture diagrams that go stale the moment someone adds a new model.&lt;/p&gt;

&lt;h3&gt;
  
  
  exposures
&lt;/h3&gt;

&lt;p&gt;exposures let you document where your dbt models are consumed outside of dbt. dashboards, applications, api endpoints, anything downstream. defining them makes the lineage graph extend beyond the dbt project boundary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;exposures&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;customer_dashboard&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dashboard&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;executive&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;dashboard&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;showing&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;customer&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;order&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;trends&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;retention"&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ref('ORDER_SUMMARY_V')&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ref('CUSTOMER_RETENTION_V')&lt;/span&gt;
    &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;analytics team&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;analytics@example.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;exposures show up in the lineage graph as leaf nodes, making it clear which models are actively consumed and by what. this is helpful when you are deciding whether it is safe to refactor or deprecate a model.&lt;/p&gt;

&lt;h2&gt;
  
  
  hosting dbt docs on github pages
&lt;/h2&gt;

&lt;p&gt;running &lt;code&gt;dbt docs serve&lt;/code&gt; is great for local browsing, but the real value comes from hosting the site where the whole team can access it without installing dbt or cloning the repo. github pages is a straightforward, free option for this.&lt;/p&gt;

&lt;h3&gt;
  
  
  how it works
&lt;/h3&gt;

&lt;p&gt;after &lt;code&gt;dbt docs generate&lt;/code&gt; runs, the &lt;code&gt;target/&lt;/code&gt; directory contains everything needed to serve the site: &lt;code&gt;index.html&lt;/code&gt;, &lt;code&gt;manifest.json&lt;/code&gt;, and &lt;code&gt;catalog.json&lt;/code&gt;. you copy those files to a branch or directory that github pages serves, and the docs are live.&lt;/p&gt;

&lt;h3&gt;
  
  
  a basic github actions workflow
&lt;/h3&gt;

&lt;p&gt;here is a minimal workflow that generates the docs on every push to main and deploys them to github pages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy dbt docs&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
  &lt;span class="na"&gt;pages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
  &lt;span class="na"&gt;id-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github-pages&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.deployment.outputs.page_url }}&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;set up python&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-python@v5&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.11"&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;install dbt&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pip install dbt-snowflake&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;generate docs&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dbt docs generate --profiles-dir .&lt;/span&gt;
        &lt;span class="na"&gt;working-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_dbt_project&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;DBT_SNOWFLAKE_ACCOUNT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SNOWFLAKE_ACCOUNT }}&lt;/span&gt;
          &lt;span class="na"&gt;DBT_SNOWFLAKE_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SNOWFLAKE_USER }}&lt;/span&gt;
          &lt;span class="na"&gt;DBT_SNOWFLAKE_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SNOWFLAKE_PASSWORD }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prepare pages artifact&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;mkdir -p pages&lt;/span&gt;
          &lt;span class="s"&gt;cp my_dbt_project/target/index.html pages/&lt;/span&gt;
          &lt;span class="s"&gt;cp my_dbt_project/target/manifest.json pages/&lt;/span&gt;
          &lt;span class="s"&gt;cp my_dbt_project/target/catalog.json pages/&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;upload pages artifact&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/upload-pages-artifact@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pages&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy to github pages&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deployment&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/deploy-pages@v4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;once this runs, your dbt docs are available at &lt;code&gt;https://&amp;lt;org&amp;gt;.github.io/&amp;lt;repo&amp;gt;/&lt;/code&gt; and automatically update every time someone merges to main. no one needs to install anything or run any commands to browse the documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  keep the credentials out of the repo
&lt;/h3&gt;

&lt;p&gt;the workflow above uses github secrets for warehouse credentials. never commit profiles with real credentials. use environment variables or a ci-specific &lt;code&gt;profiles.yml&lt;/code&gt; that references secrets, and make sure your &lt;code&gt;.gitignore&lt;/code&gt; excludes any local profiles that contain actual passwords or tokens.&lt;/p&gt;

&lt;h2&gt;
  
  
  faq
&lt;/h2&gt;

&lt;h3&gt;
  
  
  do i need to write descriptions for every column?
&lt;/h3&gt;

&lt;p&gt;you do not need to, but the columns that matter most to consumers deserve it. at minimum, describe the primary key, any business key, and any column whose meaning is not obvious from the name alone. over time, filling in the rest pays off as the team grows.&lt;/p&gt;

&lt;h3&gt;
  
  
  can i generate docs without connecting to the warehouse?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;dbt docs generate&lt;/code&gt; pulls catalog metadata from the warehouse, so it does need a connection. however, if you already have a &lt;code&gt;catalog.json&lt;/code&gt; from a previous run, you can serve the site locally with just those files.&lt;/p&gt;

&lt;h3&gt;
  
  
  how is this different from a wiki or confluence page?
&lt;/h3&gt;

&lt;p&gt;dbt docs stay in sync with your code automatically. a wiki page about your data model goes stale the moment someone adds a column or renames a table. dbt docs regenerate from the source of truth (your yml files and your warehouse) every time you run the command, so the documentation and the code never drift apart.&lt;/p&gt;

&lt;h2&gt;
  
  
  references
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.getdbt.com/docs/collaborate/documentation" rel="noopener noreferrer"&gt;dbt docs overview (dbt documentation)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.getdbt.com/reference/commands/cmd-docs" rel="noopener noreferrer"&gt;dbt docs generate command&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.getdbt.com/reference/resource-configs/persist_docs" rel="noopener noreferrer"&gt;persist_docs config&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.getdbt.com/docs/build/exposures" rel="noopener noreferrer"&gt;exposures (dbt documentation)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/pages" rel="noopener noreferrer"&gt;github pages documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  related reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://philliant.com/series/dbt/" rel="noopener noreferrer"&gt;dbt series&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://philliant.com/posts/20260328-what-is-sql-and-why-it-still-works/" rel="noopener noreferrer"&gt;what is sql, and why it still works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://philliant.com/posts/20260328-the-difference-between-snowflake-and-the-other-databases/" rel="noopener noreferrer"&gt;the difference between snowflake and the "other" databases&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>dbt</category>
      <category>documentation</category>
      <category>dataengineering</category>
      <category>githubpages</category>
    </item>
    <item>
      <title>what is sql, and why it still works</title>
      <dc:creator>Philip Hern</dc:creator>
      <pubDate>Sat, 28 Mar 2026 12:48:02 +0000</pubDate>
      <link>https://forem.com/shrouwoods/what-is-sql-and-why-it-still-works-28fd</link>
      <guid>https://forem.com/shrouwoods/what-is-sql-and-why-it-still-works-28fd</guid>
      <description>&lt;p&gt;i wanted this to be an entry point for the &lt;code&gt;sql&lt;/code&gt; series, a clear "what is sql" reference you can scan quickly and return to later. even with constant technology shifts, relational databases and &lt;code&gt;sql&lt;/code&gt; still anchor a huge part of modern software and analytics.&lt;/p&gt;

&lt;h2&gt;
  
  
  quick answer
&lt;/h2&gt;

&lt;p&gt;sql is the language most relational databases use to define, read, update, and manage data. it is declarative, which means i describe the result i want and the database engine decides how to execute it efficiently. this model has held up for decades because sql is expressive, portable, and understandable. vendor dialects differ, but the core grammar is still recognizable almost everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  who this is for
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;people new to data and databases who want a quick introduction&lt;/li&gt;
&lt;li&gt;developers who use databases daily but never got the historical context&lt;/li&gt;
&lt;li&gt;analysts and engineers deciding when sql is the right tool&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  what sql is
&lt;/h2&gt;

&lt;p&gt;at its core, sql works on tables made of rows and columns. tables are connected by keys, and queries combine or reshape those tables to answer questions.&lt;/p&gt;

&lt;p&gt;sql usually appears in four practical categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;querying data&lt;/strong&gt; with &lt;code&gt;select&lt;/code&gt;, &lt;code&gt;where&lt;/code&gt;, &lt;code&gt;join&lt;/code&gt;, &lt;code&gt;group by&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;modifying data&lt;/strong&gt; with &lt;code&gt;insert&lt;/code&gt;, &lt;code&gt;update&lt;/code&gt;, &lt;code&gt;delete&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;defining structures&lt;/strong&gt; with &lt;code&gt;create table&lt;/code&gt;, &lt;code&gt;alter table&lt;/code&gt;, &lt;code&gt;create view&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;controlling access and safety&lt;/strong&gt; with &lt;code&gt;grant&lt;/code&gt;, &lt;code&gt;revoke&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;the most important mindset shift is this: sql is set-based and declarative, not loop-first and imperative.&lt;/p&gt;

&lt;h2&gt;
  
  
  a short history of sql
&lt;/h2&gt;

&lt;p&gt;sql has been around for more than 50 years, and that long runway is one reason it feels "locked in" as a standard.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1970&lt;/strong&gt;: edgar f. codd publishes the relational model at ibm&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;mid 1970s&lt;/strong&gt;: ibm researchers develop sequel, which later becomes sql&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1979&lt;/strong&gt;: oracle ships one of the first commercial sql database implementations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1986&lt;/strong&gt;: ansi publishes the first sql standard (i personally &lt;strong&gt;ONLY&lt;/strong&gt; write ansi standard for maximum portability)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1990s to 2000s&lt;/strong&gt;: major vendors expand features and performance, while the core language stays recognizable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;today&lt;/strong&gt;: sql powers operational databases, analytics warehouses, bi tools, and transformation frameworks&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  why sql is important
&lt;/h2&gt;

&lt;p&gt;sql is still important for practical reasons, not nostalgia.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it is a common language shared by developers, analysts, and data engineers&lt;/li&gt;
&lt;li&gt;it is standard enough that skills transfer across many systems&lt;/li&gt;
&lt;li&gt;it lets database engines optimize queries with indexes and planners&lt;/li&gt;
&lt;li&gt;it models relationships clearly through keys and joins&lt;/li&gt;
&lt;li&gt;it supports transactional consistency for critical business data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;for me, one of the biggest advantages is ease of learning. for common querying and reporting tasks, sql usually has a lower starting barrier than learning a full general-purpose programming language like python.&lt;/p&gt;

&lt;h2&gt;
  
  
  when and where to use sql
&lt;/h2&gt;

&lt;p&gt;sql fits especially well in these environments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;application databases&lt;/strong&gt; like postgresql, mysql, and sql server for transactional systems&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;analytics platforms&lt;/strong&gt; like snowflake, where large-scale reporting and transformation matter most, i cover that context more in &lt;a href="https://philliant.com/posts/20260328-the-difference-between-snowflake-and-the-other-databases/" rel="noopener noreferrer"&gt;the difference between snowflake and the "other" databases&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;data transformation workflows&lt;/strong&gt; where repeatable models and tests are required&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;reporting and business intelligence&lt;/strong&gt; when teams need consistent, auditable metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;sql is usually not the best first tool for heavy imperative algorithms, event orchestration, or specialized graph traversal.&lt;/p&gt;

&lt;p&gt;if you want a practical comparison between snowflake and operational database systems, i break that down in &lt;a href="https://philliant.com/posts/20260328-the-difference-between-snowflake-and-the-other-databases/" rel="noopener noreferrer"&gt;the difference between snowflake and the "other" databases&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  key features worth knowing early
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;joins&lt;/strong&gt; to combine related entities through keys&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;aggregations&lt;/strong&gt; (&lt;code&gt;sum&lt;/code&gt;, &lt;code&gt;count&lt;/code&gt;, &lt;code&gt;avg&lt;/code&gt;) to move from row-level detail to summaries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;window functions&lt;/strong&gt; for ranking, running totals, and partitioned calculations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;transactions&lt;/strong&gt; for safe multi-step writes (&lt;code&gt;begin&lt;/code&gt;, &lt;code&gt;commit&lt;/code&gt;, &lt;code&gt;rollback&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;constraints&lt;/strong&gt; (primary keys, foreign keys, uniqueness) to enforce data quality&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;views and ctes&lt;/strong&gt; to make complex logic readable and reusable&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  a few simple examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  filter rows
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;order_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;total_amount&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;order_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="s1"&gt;'2026-01-01'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  join related data
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_amount&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  aggregate to a useful metric
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_spend&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;total_spend&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  use a transaction for safe updates
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;account_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;account_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  faq
&lt;/h2&gt;

&lt;h3&gt;
  
  
  is sql the same thing as a database?
&lt;/h3&gt;

&lt;p&gt;no. sql is a language, and a database engine is the system that executes it. two engines can both support sql while differing in extensions, performance behavior, and tooling.&lt;/p&gt;

&lt;h3&gt;
  
  
  what is the fastest way to get good at sql?
&lt;/h3&gt;

&lt;p&gt;i suggest learning in this order: filtering, joins, aggregation, then window functions. if you can always explain the grain of one row in your result, your queries become much easier to trust.&lt;/p&gt;

&lt;h3&gt;
  
  
  when should i think twice about using sql?
&lt;/h3&gt;

&lt;p&gt;if your core problem is orchestration, low-latency event handling, or algorithm-heavy logic, sql alone is usually not enough. in those cases, use sql for data access and pair it with application or workflow code.&lt;/p&gt;

&lt;h2&gt;
  
  
  references
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/SQL" rel="noopener noreferrer"&gt;sql (wikipedia)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/SEQUEL_(programming_language)" rel="noopener noreferrer"&gt;sequel (programming language)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/ISO/IEC_9075" rel="noopener noreferrer"&gt;iso/iec 9075 (sql standard)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/History_of_SQL" rel="noopener noreferrer"&gt;history of sql standards (wikipedia)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  related reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://philliant.com/series/sql/" rel="noopener noreferrer"&gt;sql series&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://philliant.com/posts/20260328-the-difference-between-snowflake-and-the-other-databases/" rel="noopener noreferrer"&gt;the difference between snowflake and the "other" databases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://philliant.com/posts/20260324-left-join-effective-satellite-cte/" rel="noopener noreferrer"&gt;left join an effective satellite without duplicating rows (use a cte)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>sql</category>
      <category>database</category>
      <category>relationaldatabases</category>
      <category>history</category>
    </item>
    <item>
      <title>the difference between snowflake and the "other" databases</title>
      <dc:creator>Philip Hern</dc:creator>
      <pubDate>Sat, 28 Mar 2026 11:49:27 +0000</pubDate>
      <link>https://forem.com/shrouwoods/the-difference-between-snowflake-and-the-other-databases-54e0</link>
      <guid>https://forem.com/shrouwoods/the-difference-between-snowflake-and-the-other-databases-54e0</guid>
      <description>&lt;p&gt;when you first step into data engineering, the sheer number of database options can be overwhelming. i spend a lot of my time working in snowflake, but it is definitely not the only tool in the shed.&lt;/p&gt;

&lt;p&gt;to build a solid data platform, you have to understand where your data comes from and how different systems handle it. i want to break down how snowflake compares to two other popular systems you will encounter often: relational engines such as amazon rds and nosql patterns such as amazon dynamodb.&lt;/p&gt;

&lt;p&gt;this is a basic guide written from a data engineer's perspective. i will point out the similarities, the differences, and when you should use each one.&lt;/p&gt;

&lt;h2&gt;
  
  
  the contenders
&lt;/h2&gt;

&lt;p&gt;before we compare them, let us define what we are looking at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;snowflake&lt;/strong&gt;: a cloud-native data warehouse built for analytics (olap - online analytical processing). storage and compute are separate, so you pay for each part independently&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;amazon rds&lt;/strong&gt;: a managed relational database service that runs engines like postgresql, mysql, sql server, and oracle. it is built for transactional app workloads (oltp - online transactional processing)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;amazon dynamodb&lt;/strong&gt;: a fully managed nosql key-value and document store built for very low-latency lookups at high scale&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  similarities and differences
&lt;/h2&gt;

&lt;p&gt;at a high level, snowflake and rds both use sql, which makes them feel familiar. dynamodb is api-first for key-value access. it also offers partiql support, but it does not behave like a relational sql engine with joins and broad ad hoc querying.&lt;/p&gt;

&lt;p&gt;the biggest architectural difference is how they handle storage and compute.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;feature&lt;/th&gt;
&lt;th&gt;snowflake&lt;/th&gt;
&lt;th&gt;amazon RDS&lt;/th&gt;
&lt;th&gt;amazon dynamodb&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;primary use case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;analytics and reporting (olap)&lt;/td&gt;
&lt;td&gt;application backends (oltp)&lt;/td&gt;
&lt;td&gt;high-scale key-value applications&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;query language&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;sql (warehouse dialect)&lt;/td&gt;
&lt;td&gt;sql (engine-specific)&lt;/td&gt;
&lt;td&gt;api-first (partiql support)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;architecture&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;storage and compute decoupled&lt;/td&gt;
&lt;td&gt;storage and compute coupled&lt;/td&gt;
&lt;td&gt;distributed key-value store&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;data structure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;tables and semi-structured data&lt;/td&gt;
&lt;td&gt;relational tables&lt;/td&gt;
&lt;td&gt;key-value and document items&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;indexes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;micro-partition pruning metadata&lt;/td&gt;
&lt;td&gt;user-managed indexes&lt;/td&gt;
&lt;td&gt;partition and sort keys&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;snowflake does not require traditional user-managed indexes for most workloads. it uses micro-partitions and pruning metadata. in rds, you usually create and manage indexes manually to keep queries fast. dynamodb pushes you to define access patterns up front using partition and sort keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  scalability: small, big, and the limits
&lt;/h2&gt;

&lt;p&gt;scalability means something completely different depending on which database you are talking about.&lt;/p&gt;

&lt;h3&gt;
  
  
  snowflake
&lt;/h3&gt;

&lt;p&gt;snowflake scales compute and storage independently. storage is backed by cloud object storage, so it is effectively unbounded for most teams. you can store petabytes of data without thinking about disks.&lt;/p&gt;

&lt;p&gt;compute is handled by virtual warehouses, which come in t-shirt sizes from x-small to 6x-large. if a query is too slow, you can use a larger warehouse. if you have too many concurrent users, you can add clusters.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;small&lt;/strong&gt;: gigabytes of data running on an x-small warehouse&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;big&lt;/strong&gt;: petabytes of data running on a 4x-large warehouse&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;limits&lt;/strong&gt;: practically none for storage; compute is limited only by your budget&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  amazon RDS
&lt;/h3&gt;

&lt;p&gt;rds usually scales write capacity vertically by moving to a larger instance class. depending on engine and change type, this can trigger a restart or failover window. you can also scale read traffic horizontally with read replicas.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;small&lt;/strong&gt;: a few gigabytes on a &lt;code&gt;t3.micro&lt;/code&gt; instance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;big&lt;/strong&gt;: several terabytes on a massive &lt;code&gt;m6g.16xlarge&lt;/code&gt; instance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;limits&lt;/strong&gt;: many engines cap around 64 terabytes per instance (roughly 64 tib), and you still hit single-instance ceilings on cpu, memory, and concurrent connections&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  amazon dynamodb
&lt;/h3&gt;

&lt;p&gt;dynamodb scales horizontally and automatically. it partitions your data across many servers behind the scenes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;small&lt;/strong&gt;: a few megabytes with single-digit read/write capacity units&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;big&lt;/strong&gt;: hundreds of terabytes handling millions of requests per second&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;limits&lt;/strong&gt;: very high scale for key-value access, but full-table scans and broad analytical queries are usually expensive and inefficient&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ease of learning curve and use
&lt;/h2&gt;

&lt;p&gt;as a data engineer, the learning curve dictates how fast you can deliver value.&lt;/p&gt;

&lt;h3&gt;
  
  
  snowflake (easiest for analysts)
&lt;/h3&gt;

&lt;p&gt;if you know sql, you can use snowflake quickly. the learning curve is low because there is very little infrastructure to manage. you do not worry about vacuuming tables, managing indexes, or tuning memory pools. you mostly load data and query it.&lt;/p&gt;

&lt;h3&gt;
  
  
  amazon RDS (moderate)
&lt;/h3&gt;

&lt;p&gt;rds is easy to start with because postgresql and mysql are industry standards. however, the learning curve gets steep when you hit scale. you have to read query plans, manage indexes, handle connection pooling, and tune database parameters.&lt;/p&gt;

&lt;h3&gt;
  
  
  amazon dynamodb (steepest for modeling)
&lt;/h3&gt;

&lt;p&gt;dynamodb has a notoriously steep learning curve for data modeling. because it is not a relational join engine, you usually model around known access patterns (often with single-table design). if those patterns change later, you may need redesign work and backfills.&lt;/p&gt;

&lt;h2&gt;
  
  
  when to use each
&lt;/h2&gt;

&lt;p&gt;choosing the right database is about matching the tool to the workload.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;use snowflake when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you need to analyze massive amounts of data&lt;/li&gt;
&lt;li&gt;you are building dashboards, reports, or a data warehouse&lt;/li&gt;
&lt;li&gt;you need to join data from many different sources&lt;/li&gt;
&lt;li&gt;your queries read millions of rows at a time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;use amazon RDS when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you are building a standard web application&lt;/li&gt;
&lt;li&gt;you need strong transactional guarantees (ACID compliance)&lt;/li&gt;
&lt;li&gt;your queries typically look up or update a single row or a small batch of rows&lt;/li&gt;
&lt;li&gt;your core workload is transactional and fits a single relational instance pattern, even as it grows into multi-terabyte range&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;use amazon dynamodb when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you are building a shopping cart, session store, or gaming leaderboard&lt;/li&gt;
&lt;li&gt;you need guaranteed single-digit millisecond response times&lt;/li&gt;
&lt;li&gt;you have massive, unpredictable spikes in traffic&lt;/li&gt;
&lt;li&gt;you do not need to run complex analytical queries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;as a data engineer, your job is often to extract data from operational systems (like rds and dynamodb) and load it into an analytical system (like snowflake). that is what allows the business to understand what is happening.&lt;/p&gt;

&lt;h2&gt;
  
  
  references
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.snowflake.com/" rel="noopener noreferrer"&gt;snowflake documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/rds/" rel="noopener noreferrer"&gt;amazon RDS documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/dynamodb/" rel="noopener noreferrer"&gt;amazon dynamodb documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  related reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://philliant.com/series/snowflake/" rel="noopener noreferrer"&gt;snowflake series&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>snowflake</category>
      <category>rds</category>
      <category>dynamodb</category>
      <category>database</category>
    </item>
    <item>
      <title>adaptability</title>
      <dc:creator>Philip Hern</dc:creator>
      <pubDate>Fri, 27 Mar 2026 16:45:05 +0000</pubDate>
      <link>https://forem.com/shrouwoods/adaptability-10gc</link>
      <guid>https://forem.com/shrouwoods/adaptability-10gc</guid>
      <description>&lt;h2&gt;
  
  
  thesis
&lt;/h2&gt;

&lt;p&gt;i think adaptability is one of the most useful skills to develop, and its value keeps rising. ai and agents are changing workflows faster than most teams can document them. people who adjust quickly, while staying clear about outcomes, will keep moving when others stall.&lt;/p&gt;

&lt;h2&gt;
  
  
  context
&lt;/h2&gt;

&lt;p&gt;widespread ai adoption is pushing change into almost every part of work. tasks are getting split differently, handoffs are being redefined, and the idea of "the normal way" has a shorter shelf life every month.&lt;/p&gt;

&lt;p&gt;this pace is not only a work story. speed and connectivity shape family logistics, routines, and attention in daily life too. the same mental flexibility that helps at work helps at home, in travel, and during any week that does not look like the one before it.&lt;/p&gt;

&lt;h2&gt;
  
  
  argument
&lt;/h2&gt;

&lt;p&gt;i am writing this from a very rural location, far from my normal office setup. i have no cell service except a satellite connection that can send slow texts, and the wifi is around 10 mbps down on a good day, shared across the whole family. that setup forced me to plan work in batches and communicate constraints early. it is a useful reminder that conditions change, but the work still has to get done.&lt;/p&gt;

&lt;p&gt;for me, adaptability in practice looks like a few simple moves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;shifting to an offline-first workflow when connectivity is inconsistent&lt;/li&gt;
&lt;li&gt;prioritizing high-leverage tasks before synchronous calls&lt;/li&gt;
&lt;li&gt;communicating constraints early so expectations stay realistic&lt;/li&gt;
&lt;li&gt;batching sync-heavy work into the most stable bandwidth windows&lt;/li&gt;
&lt;li&gt;choosing tools that degrade gracefully instead of failing hard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;this is also why i care so much about clear ownership when using ai. adaptable does not mean random. it means adjusting method while protecting intent, quality, and accountability in a rapidly changing workplace (and world). this is a theme i also touched on in &lt;a href="https://philliant.com/posts/20260326-the-danger-of-trusting-the-ai-agent/" rel="noopener noreferrer"&gt;the danger of trusting the ai agent&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  tension or counterpoint
&lt;/h3&gt;

&lt;p&gt;there is a valid concern here: constant adaptation can turn into reactive thrashing. not every new tool deserves adoption, and not every workflow change creates real value.&lt;/p&gt;

&lt;p&gt;the balance i aim for is to adapt principles faster than (or at least as fast as) tools. if i stay anchored on outcomes, quality, and ownership, i can (hopefully) swap tactics without losing direction.&lt;/p&gt;

&lt;h2&gt;
  
  
  closing
&lt;/h2&gt;

&lt;p&gt;i do not see adaptability as a soft trait anymore. i see it as operational leverage. it helps me keep momentum when the environment is unstable, when tools change quickly, or when ideal conditions are not available.&lt;/p&gt;

&lt;p&gt;going forward, i expect this to matter even more. the people and teams that learn quickly, reframe constraints, and keep moving with clarity will have a durable advantage.&lt;/p&gt;

&lt;h2&gt;
  
  
  further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Adaptability" rel="noopener noreferrer"&gt;adaptability&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Organizational_agility" rel="noopener noreferrer"&gt;organizational agility&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  related on this site
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://philliant.com/posts/20260326-the-danger-of-trusting-the-ai-agent/" rel="noopener noreferrer"&gt;the danger of trusting the ai agent&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://philliant.com/series/commentary/" rel="noopener noreferrer"&gt;commentary series&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>adaptability</category>
      <category>ai</category>
      <category>work</category>
      <category>change</category>
    </item>
    <item>
      <title>the danger of trusting the ai agent</title>
      <dc:creator>Philip Hern</dc:creator>
      <pubDate>Thu, 26 Mar 2026 13:31:57 +0000</pubDate>
      <link>https://forem.com/shrouwoods/the-danger-of-trusting-the-ai-agent-ajk</link>
      <guid>https://forem.com/shrouwoods/the-danger-of-trusting-the-ai-agent-ajk</guid>
      <description>&lt;p&gt;i love how fast ai agents can move, but i have learned that speed without ownership can become expensive very quickly. if i let an agent operate in an area i do not understand deeply, i can end up with changes i cannot explain, verify, or recover confidently.&lt;/p&gt;

&lt;p&gt;this is the darker side of the acceleration story i wrote about in &lt;a href="https://philliant.com/posts/from-prototype-to-production-ai/" rel="noopener noreferrer"&gt;from prototype to production: my early adopter view of ai&lt;/a&gt;: ai can &lt;strong&gt;compress execution time&lt;/strong&gt;, but it can also &lt;strong&gt;greatly expand confusion&lt;/strong&gt; if my boundaries are weak.&lt;/p&gt;

&lt;h2&gt;
  
  
  quick answer
&lt;/h2&gt;

&lt;p&gt;if i trust the ai fully in a domain i do not understand, i am borrowing speed against future troubleshooting debt. the agent can still produce plausible progress, but when something breaks, i pay the bill because i do not have the mental model to debug with confidence. my rule now is simple: ai can do my work faster, but it should only do work i truly understand and could perform on my own.&lt;/p&gt;

&lt;h2&gt;
  
  
  who this is for
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;people using agents for coding, data, or operations tasks&lt;/li&gt;
&lt;li&gt;anyone who has watched an agent "do a lot" but struggled to explain what actually changed&lt;/li&gt;
&lt;li&gt;teams where responsibilities cross domains and ownership can get blurry&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  why this matters
&lt;/h2&gt;

&lt;p&gt;automation bias is real. when the output looks polished, it is easy to accept it before understanding it. that is manageable in low-risk tasks, but dangerous in high-impact systems where small assumptions can trigger hard-to-trace regressions.&lt;/p&gt;

&lt;p&gt;the hidden cost is confidence drift. git can say everything is clean while my own understanding says something feels off. when that gap appears, stress goes up, debugging slows down, and trust in the workflow drops.&lt;/p&gt;

&lt;p&gt;and one other note to be fair is that in this case, i tried to use a faster, smaller agent to do work that i should have routed to a stronger, deeper thinking model. i was in a hurry and paid for it. remember the tried and true racing phrase "slow is smooth, smooth is fast".&lt;/p&gt;

&lt;h2&gt;
  
  
  the failure mode i hit
&lt;/h2&gt;

&lt;p&gt;i asked an agent to diagnose an error in an area where i did not have strong depth. during the run, it created files to handle the issue. later, it removed those same files because they were not referenced anywhere. from git's perspective, it was a net zero diff.&lt;/p&gt;

&lt;p&gt;from my perspective, it was not zero at all. i watched a long stream of activity, expected to see resulting changes, and then found a clean tree. i spent significant time retracing the run to understand what happened, and even after reconciling local and remote state i still had low confidence that everything was truly back to a known good place.&lt;/p&gt;

&lt;h2&gt;
  
  
  where i drew the line
&lt;/h2&gt;

&lt;p&gt;the lesson for me is less "never trust ai" and more "do not outsource something you could not do yourself". if i cannot explain the system, i should not delegate high-autonomy changes in that system to an agent.&lt;/p&gt;

&lt;p&gt;that lane discipline applies to people too. if work sits in another colleague's domain, i should route it to them, even if they use an agent themselves. the difference is not whether ai is involved. the difference is whether the person driving understands the domain deeply enough to verify and own the result.&lt;/p&gt;

&lt;h2&gt;
  
  
  the lane check i use now
&lt;/h2&gt;

&lt;p&gt;before i hand work to an agent, i run five checks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;do i understand the domain well enough to review every change with confidence&lt;/li&gt;
&lt;li&gt;if the agent makes a wrong assumption, can i detect it quickly&lt;/li&gt;
&lt;li&gt;do i have explicit stop conditions and verification steps&lt;/li&gt;
&lt;li&gt;will i review the actual diff and command output, not just the narrative in chat&lt;/li&gt;
&lt;li&gt;if this crosses domain ownership, have i handed it to the right colleague&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;if i answer "no" to any of these, i narrow the scope or route the task to someone else.&lt;/p&gt;

&lt;h2&gt;
  
  
  faq
&lt;/h2&gt;

&lt;h3&gt;
  
  
  does this mean ai should only do trivial work?
&lt;/h3&gt;

&lt;p&gt;no. ai can do serious work, but the owner still needs to understand the system and sign off on the result. complexity is fine, unowned complexity is the problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  what do you do when git says clean but confidence is low?
&lt;/h3&gt;

&lt;p&gt;i treat that as a process warning. i retrace the execution log, compare expected outcomes against actual artifacts, and document what happened before continuing. if i still cannot explain it clearly, i escalate to the domain owner instead of pushing forward on uncertainty.&lt;/p&gt;

&lt;h2&gt;
  
  
  references
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Automation_bias" rel="noopener noreferrer"&gt;automation bias&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Out-of-the-loop_performance_problem" rel="noopener noreferrer"&gt;out-of-the-loop performance problem&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  related reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="//../20260318-from-prototype-to-production-ai/index.md"&gt;from prototype to production: my early adopter view of ai&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="//../20260319-practical-ai-workflow-jira-github-mcp/index.md"&gt;a practical ai workflow: jira, github, and mcp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="//../20260314-ai-br-ai-n-fr-ai/index.md"&gt;ai br-ai-n fr-ai&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://philliant.com/series/ai/" rel="noopener noreferrer"&gt;ai series&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>workflow</category>
      <category>trust</category>
      <category>ownership</category>
    </item>
    <item>
      <title>plane wifi: when the cabin forced disconnect</title>
      <dc:creator>Philip Hern</dc:creator>
      <pubDate>Tue, 24 Mar 2026 16:23:21 +0000</pubDate>
      <link>https://forem.com/shrouwoods/plane-wifi-when-the-cabin-forced-disconnect-58eg</link>
      <guid>https://forem.com/shrouwoods/plane-wifi-when-the-cabin-forced-disconnect-58eg</guid>
      <description>&lt;h2&gt;
  
  
  thesis
&lt;/h2&gt;

&lt;p&gt;for a long time, a commercial flight was one of the few places where the world could not reach you unless you paid for a seatback phone nobody used. you were offline by default, not by discipline. now many cabins offer wifi strong enough to treat the plane like a slow office in the sky. that is a small technical change and a large cultural one, because it turns disconnection from a fact into a choice, and sometimes into an expectation. i used to think we should protect those disconnected spaces on purpose. today i am less sure of my own opinion, and this piece is an honest inventory of why.&lt;/p&gt;

&lt;h2&gt;
  
  
  context
&lt;/h2&gt;

&lt;p&gt;the shift did not happen all at once. first it was email that barely worked, then messaging, then enough bandwidth that "i am on a plane" stopped being a credible excuse for silence in some workplaces. the option to connect is not the same as the obligation to connect, but options have a way of becoming norms. if the team assumes you can answer, the cost of opting out is social and professional, not just the price of the wifi pass.&lt;/p&gt;

&lt;p&gt;i notice that i feel different about this than i did five or ten years ago. i used to romanticize the cabin as a rare enforced pause, a moving room where the only honest move was a book, a nap, or staring at clouds. now i sometimes have a different perspective since my brain is already racing and the seat is uncomfortable and sleep is not going to happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  argument
&lt;/h2&gt;

&lt;h3&gt;
  
  
  the case for keeping flights disconnected
&lt;/h3&gt;

&lt;p&gt;there is real relief in a few hours where slack cannot ping you and the news cannot refresh on reflex. the stream of messages and media is designed to feel urgent. a metal tube at thirty thousand feet used to interrupt that design for everyone equally. when you land, nothing has changed except you, and that can be restorative even when it is boring.&lt;/p&gt;

&lt;p&gt;i still believe that boredom and idle time are inputs to thinking, not failures of entertainment. a flight without wifi can be a long, low-stakes walk for your attention, and that has value. it is the same thread i explored in &lt;a href="https://philliant.com/posts/20260320-brain-defrag-time-away-from-screens/" rel="noopener noreferrer"&gt;brain defrag: time away from screens (and from "one more" with ai)&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  the case for wanting the connection
&lt;/h3&gt;

&lt;p&gt;if you cannot sleep on planes, hours in the air can feel like time you are borrowing from your life and not getting back. in that frame, wifi looks less like an intrusion and more like a way to reclaim the block for work, reading you actually chose, or staying in touch with people you care about. the "wasted time" feeling is personal, but it is not irrational.&lt;/p&gt;

&lt;p&gt;cabin class changes how physical that tradeoff is. if you are fortunate enough to sit in first or business, the tray, the elbow room, and the seat pitch can make typing tolerable for a while. in economy, the same work is often inconvenient for you and unfair to the person beside you who did not sign up to be your armrest and your privacy screen. connectivity does not create that squeeze, but it can intensify it when everyone tries to turn a narrow row into an office.&lt;/p&gt;

&lt;h3&gt;
  
  
  tension or counterpoint
&lt;/h3&gt;

&lt;p&gt;the strongest counterpoint to my old "protect the disconnected cabin" instinct is that disconnection was never equally available. people with caregiving responsibilities, unpredictable schedules, or thin margins already paid a tax when flights were black holes on the calendar. wifi can reduce that tax. my nostalgia for a universal offline bubble was partly a privilege story dressed up as a wellness argument.&lt;/p&gt;

&lt;p&gt;the strongest counterpoint to always-on flying is that the cabin is still a shared space, and not every kind of work belongs there without cost to neighbors and to your own nervous system. the technology says you can but your body and the person in the middle seat might wish you would not.&lt;/p&gt;

&lt;h2&gt;
  
  
  closing
&lt;/h2&gt;

&lt;p&gt;i do not have a tidy answer about whether i like in-flight connectivity. some trips i am grateful for it. some trips i wish the excuse to be unreachable still existed without me having to defend it. what i know is that the choice is heavier than the toggle in the portal makes it look, and i will probably keep revisiting it every time the wheels leave the ground.&lt;/p&gt;

&lt;h2&gt;
  
  
  further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/In-flight_connectivity" rel="noopener noreferrer"&gt;in-flight connectivity&lt;/a&gt;, overview of how internet reaches aircraft and how adoption spread&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/Airline_seat" rel="noopener noreferrer"&gt;airline seat&lt;/a&gt;, context on pitch, width, and why economy is a poor default office&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  related on this site
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://philliant.com/series/commentary/" rel="noopener noreferrer"&gt;commentary series&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://philliant.com/posts/20260320-brain-defrag-time-away-from-screens/" rel="noopener noreferrer"&gt;brain defrag: time away from screens (and from "one more" with ai)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>commentary</category>
      <category>travel</category>
      <category>attention</category>
      <category>wifi</category>
    </item>
  </channel>
</rss>
