<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jose Torreblanca</title>
    <description>The latest articles on DEV Community by Jose Torreblanca (@josewhitetower).</description>
    <link>https://dev.to/josewhitetower</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%2F31016%2F60c6d2a1-760c-4402-9da0-35eb9a6d9059.jpeg</url>
      <title>DEV Community: Jose Torreblanca</title>
      <link>https://dev.to/josewhitetower</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/josewhitetower"/>
    <language>en</language>
    <item>
      <title>Building a Modern Analytics Stack: UI Cube.js dbt Athena Integration</title>
      <dc:creator>Jose Torreblanca</dc:creator>
      <pubDate>Thu, 21 Aug 2025 20:19:33 +0000</pubDate>
      <link>https://dev.to/josewhitetower/building-a-modern-analytics-stack-ui-cubejs-dbt-athena-integration-29o2</link>
      <guid>https://dev.to/josewhitetower/building-a-modern-analytics-stack-ui-cubejs-dbt-athena-integration-29o2</guid>
      <description>&lt;p&gt;&lt;em&gt;How we built a seamless data pipeline from AWS Athena to React dashboards using dbt and Cube.js&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge: From Raw Events to Dashboard Insights
&lt;/h2&gt;

&lt;p&gt;Picture this: You have millions of ridepooling trip events flowing into AWS Athena, and you need to serve them up as beautiful, interactive dashboards to your stakeholders. The data needs to be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fast to query&lt;/strong&gt; (sub-second response times)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always fresh&lt;/strong&gt; (reflecting recent events)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexible&lt;/strong&gt; (various time ranges and filters)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalable&lt;/strong&gt; (handling growing data volumes)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the story of how we built a modern analytics stack that connects all these pieces seamlessly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture: Four Layers, One Goal
&lt;/h2&gt;

&lt;p&gt;Our stack consists of four key layers, each solving a specific problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UI Layer (React) 
    ↕️ 
Semantic Layer (Cube.js)
    ↕️
Transformation Layer (dbt)
    ↕️
Storage Layer (AWS Athena)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's walk through each layer and see how they work together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 1: AWS Athena - The Foundation
&lt;/h2&gt;

&lt;p&gt;At the bottom, we have &lt;strong&gt;AWS Athena&lt;/strong&gt; storing our raw event data in S3 with a multitenant architecture:&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;-- Raw trip events table&lt;/span&gt;
&lt;span class="n"&gt;ridepooling__moia__service__trip_entities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ent__trip&lt;/span&gt;
&lt;span class="n"&gt;ridepooling__tenant_1__service__trip_entities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ent__trip&lt;/span&gt;
&lt;span class="n"&gt;ridepooling__tenant_2__service__trip_entities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ent__trip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each tenant gets isolated data storage, but the schema remains consistent. Events flow in continuously from our ridepooling services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Athena's role:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Serverless SQL engine over S3 data lakes&lt;/li&gt;
&lt;li&gt;Cost-effective for large datasets&lt;/li&gt;
&lt;li&gt;Handles complex queries with good performance&lt;/li&gt;
&lt;li&gt;Integrates natively with AWS ecosystem&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Layer 2: dbt - The Transformation Engine
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;dbt&lt;/strong&gt; sits on top of Athena, transforming raw events into analytics-ready models:&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;-- stop_network_usage.sql&lt;/span&gt;
&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;materialized&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;"view"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;

&lt;span class="k"&gt;select&lt;/span&gt;
    &lt;span class="n"&gt;stops&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;stops&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;service_area_uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;stops&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name_en&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;stop_name_en&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;stops&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name_de&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;stop_name_de&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trip_usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;occurred_at&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;trip_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;distinct&lt;/span&gt; &lt;span class="n"&gt;trip_usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trip_id&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;daily_trip_count&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt;
    &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;multitenancy_source&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"ridepooling"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"service__service_area_entities"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"att__stop__details_latest"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="n"&gt;stops&lt;/span&gt;
&lt;span class="k"&gt;left&lt;/span&gt; &lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;link__stop_id__pickup&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;stop_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trip_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;occurred_at&lt;/span&gt;
    &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;multitenancy_source&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"ridepooling"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"service__trip_entities"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"ent__trip"&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;link__stop_id__pickup&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
    &lt;span class="k"&gt;union&lt;/span&gt;
    &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;link__stop_id__delivery&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;stop_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trip_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;occurred_at&lt;/span&gt;  
    &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;multitenancy_source&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"ridepooling"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"service__trip_entities"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"ent__trip"&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;link__stop_id__delivery&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;trip_usage&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;stops&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;trip_usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop_id&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;stops&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stops&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;service_area_uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stops&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name_en&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stops&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name_de&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trip_usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;occurred_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key dbt features we leverage:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multitenancy macros&lt;/strong&gt;: Dynamic table routing per tenant&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jinja templating&lt;/strong&gt;: Conditional logic and variables&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;View materialization&lt;/strong&gt;: Always fresh data without staleness&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modular design&lt;/strong&gt;: Reusable transformations across tenants&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The multitenant magic:&lt;/strong&gt;&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;-- This macro resolves to tenant-specific tables at runtime&lt;/span&gt;
&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;multitenancy_source&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"ridepooling"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"service__trip_entities"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"ent__trip"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="c1"&gt;-- Becomes: ridepooling__moia__service__trip_entities.ent__trip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Layer 3: Cube.js - The Semantic Layer
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Cube.js&lt;/strong&gt; acts as our semantic layer, defining business metrics and providing a consistent API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// stop-network-usage.js&lt;/span&gt;
&lt;span class="nf"&gt;cube&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`StopNetworkUsage`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;sql_table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`ridepooling__&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;COMPILE_CONTEXT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tenant_name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;__service__stop_network_data_products__&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;COMPILE_CONTEXT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.stop_network_usage`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="na"&gt;dimensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;stopId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`stop_id`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`string`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;primary_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="na"&gt;stopNameEn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`stop_name_en`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`string`&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="na"&gt;tripDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`trip_date`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`time`&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="na"&gt;measures&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;totalTrips&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`daily_trip_count`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`sum`&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="na"&gt;avgDailyTrips&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`daily_trip_count`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`avg`&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cube.js superpowers:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Semantic definitions&lt;/strong&gt;: Business users think "total trips," not SQL aggregations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic SQL generation&lt;/strong&gt;: Converts REST/GraphQL requests to optimized SQL&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching layer&lt;/strong&gt;: Sub-second response times for repeated queries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-tenant aware&lt;/strong&gt;: Same schema definition works across all tenants&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time dimension intelligence&lt;/strong&gt;: Handles date ranges, comparisons, and time series&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Layer 4: React UI - The Experience
&lt;/h2&gt;

&lt;p&gt;Finally, our &lt;strong&gt;React dashboard&lt;/strong&gt; consumes Cube.js APIs to create beautiful visualizations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// StopUsageDashboard.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useCubeQuery&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@cubejs-client/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;StopUsageDashboard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;resultSet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCubeQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;measures&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;StopNetworkUsage.totalTrips&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;timeDimensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
      &lt;span class="na"&gt;dimension&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;StopNetworkUsage.tripDate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;granularity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;day&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;dateRange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;last 30 days&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="na"&gt;dimensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;StopNetworkUsage.stopNameEn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LoadingSpinner&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Stop Usage Over Time&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LineChart&lt;/span&gt; 
        &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;resultSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chartPivot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; 
        &lt;span class="na"&gt;xAxis&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"time"&lt;/span&gt;
        &lt;span class="na"&gt;yAxis&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"StopNetworkUsage.totalTrips"&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;UI benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Declarative queries&lt;/strong&gt;: Describe what you want, not how to get it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic optimization&lt;/strong&gt;: Cube.js handles SQL generation and caching&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time updates&lt;/strong&gt;: Data stays fresh through view materialization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rich interactions&lt;/strong&gt;: Time range pickers, filters, drill-downs work out of the box&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Data Flow: From Event to Insight
&lt;/h2&gt;

&lt;p&gt;Here's how a single trip event becomes a dashboard insight:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Event arrives&lt;/strong&gt;: Trip booked with pickup stop A and delivery stop B&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Athena stores&lt;/strong&gt;: Raw event in tenant-specific S3 partition&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;dbt transforms&lt;/strong&gt;: View aggregates daily usage per stop, handling both pickup and delivery&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cube.js semantics&lt;/strong&gt;: Converts to business metrics like "total trips per stop"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI displays&lt;/strong&gt;: Beautiful chart showing stop usage trends&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The magic is that steps 3-5 happen in &lt;strong&gt;real-time&lt;/strong&gt; during user queries, ensuring fresh data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Design Decisions &amp;amp; Trade-offs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ✅ Why Views Over Tables?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Always fresh data&lt;/strong&gt; vs. potential performance hit&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity&lt;/strong&gt; vs. automation complexity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Perfect for our use case&lt;/strong&gt;: Daily monitoring with reasonable data volumes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ✅ Why Cube.js Over Direct SQL?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Semantic consistency&lt;/strong&gt; across different front-ends&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic optimization&lt;/strong&gt; and caching&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer productivity&lt;/strong&gt; - no more manual SQL writing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-tenant support&lt;/strong&gt; built-in&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ✅ Why dbt Over Raw Athena?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Version control&lt;/strong&gt; for transformations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing and documentation&lt;/strong&gt; built-in&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modularity and reusability&lt;/strong&gt; across tenants&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Software engineering practices&lt;/strong&gt; for data&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ✅ Why This Stack?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Serverless&lt;/strong&gt;: Scales automatically, pay-per-use&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modern&lt;/strong&gt;: Embraces current best practices&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexible&lt;/strong&gt;: Each layer can be swapped independently&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer-friendly&lt;/strong&gt;: Code-first approach throughout&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Performance Characteristics
&lt;/h2&gt;

&lt;p&gt;Our stack delivers impressive performance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Query latency&lt;/strong&gt;: 200-800ms for typical dashboard queries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data freshness&lt;/strong&gt;: Real-time (views query source directly)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;: Handles millions of events, hundreds of concurrent users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost efficiency&lt;/strong&gt;: Serverless architecture scales to zero&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Performance tips:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cube.js pre-aggregations for frequently accessed metrics&lt;/li&gt;
&lt;li&gt;Athena partition pruning with date filters&lt;/li&gt;
&lt;li&gt;Strategic use of dbt incremental models for large fact tables&lt;/li&gt;
&lt;li&gt;Smart caching strategies in the UI layer&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Operational Considerations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Monitoring &amp;amp; Observability
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cube.js&lt;/strong&gt;: Built-in query performance metrics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;dbt&lt;/strong&gt;: Model run logs and test results&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Athena&lt;/strong&gt;: CloudWatch metrics for query performance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI&lt;/strong&gt;: User experience monitoring and error tracking&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Multi-tenant Isolation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data isolation&lt;/strong&gt;: Separate databases per tenant&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schema consistency&lt;/strong&gt;: Same models work across tenants&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime flexibility&lt;/strong&gt;: Switch tenants via variables&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: Row-level security enforced at storage layer&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Development Workflow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;dbt development&lt;/strong&gt;: Local development with sample data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cube.js iteration&lt;/strong&gt;: Schema development with dev APIs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI prototyping&lt;/strong&gt;: Component development with mock data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration testing&lt;/strong&gt;: End-to-end testing across layers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment&lt;/strong&gt;: GitOps with environment promotion&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🎯 Start Simple
&lt;/h3&gt;

&lt;p&gt;We initially over-engineered with complex incremental models and scheduling. Views solved our freshness problem elegantly.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎯 Semantic Layer is Worth It
&lt;/h3&gt;

&lt;p&gt;Cube.js transformed our development velocity. No more custom SQL endpoints or manual optimization.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎯 Multi-tenancy From Day One
&lt;/h3&gt;

&lt;p&gt;Planning for multiple tenants upfront saved massive refactoring later.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎯 Choose Your Materializations Wisely
&lt;/h3&gt;

&lt;p&gt;Not everything needs to be a table. Views, incremental, and tables each have their place.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;Our stack continues to evolve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real-time streaming&lt;/strong&gt;: Moving from batch to stream processing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced analytics&lt;/strong&gt;: ML models integrated into the pipeline
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-service&lt;/strong&gt;: Empowering business users to create their own metrics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Global scale&lt;/strong&gt;: Multi-region deployment strategies&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;Building a modern analytics stack isn't just about picking the right tools—it's about creating a &lt;strong&gt;coherent data experience&lt;/strong&gt; that serves everyone from data engineers to business stakeholders.&lt;/p&gt;

&lt;p&gt;Our UI → Cube.js → dbt → Athena stack delivers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fast insights&lt;/strong&gt; for decision makers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Productive development&lt;/strong&gt; for engineers
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalable architecture&lt;/strong&gt; for growing data needs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainable codebase&lt;/strong&gt; for long-term success&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The secret sauce isn't any single technology—it's how they work together to create something greater than the sum of their parts.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What's your experience with modern analytics stacks? Have you found different combinations that work well for your use cases? I'd love to hear about your architecture decisions and trade-offs.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>cubejs</category>
      <category>dbt</category>
      <category>athena</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Jose Torreblanca</dc:creator>
      <pubDate>Thu, 21 Aug 2025 20:06:44 +0000</pubDate>
      <link>https://dev.to/josewhitetower/-30j5</link>
      <guid>https://dev.to/josewhitetower/-30j5</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/josewhitetower/when-simple-beats-complex-solving-stale-data-with-dbt-views-m7j" class="crayons-story__hidden-navigation-link"&gt;When Simple Beats Complex: Solving Stale Data with dbt Views&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/josewhitetower" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F31016%2F60c6d2a1-760c-4402-9da0-35eb9a6d9059.jpeg" alt="josewhitetower profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/josewhitetower" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Jose Torreblanca
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Jose Torreblanca
                
              
              &lt;div id="story-author-preview-content-2788286" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/josewhitetower" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F31016%2F60c6d2a1-760c-4402-9da0-35eb9a6d9059.jpeg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Jose Torreblanca&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/josewhitetower/when-simple-beats-complex-solving-stale-data-with-dbt-views-m7j" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Aug 21 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/josewhitetower/when-simple-beats-complex-solving-stale-data-with-dbt-views-m7j" id="article-link-2788286"&gt;
          When Simple Beats Complex: Solving Stale Data with dbt Views
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/dbt"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;dbt&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/dataengineering"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;dataengineering&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sql"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sql&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/analytics"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;analytics&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/josewhitetower/when-simple-beats-complex-solving-stale-data-with-dbt-views-m7j#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>dbt</category>
      <category>dataengineering</category>
      <category>sql</category>
      <category>analytics</category>
    </item>
    <item>
      <title>When Simple Beats Complex: Solving Stale Data with dbt Views</title>
      <dc:creator>Jose Torreblanca</dc:creator>
      <pubDate>Thu, 21 Aug 2025 20:05:20 +0000</pubDate>
      <link>https://dev.to/josewhitetower/when-simple-beats-complex-solving-stale-data-with-dbt-views-m7j</link>
      <guid>https://dev.to/josewhitetower/when-simple-beats-complex-solving-stale-data-with-dbt-views-m7j</guid>
      <description>&lt;p&gt;&lt;em&gt;A story about discovering the right materialization for the right use case&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: "Will My Data Be Stale in 90 Days?"
&lt;/h2&gt;

&lt;p&gt;Picture this: You're building a daily monitoring dashboard for ridepooling stop usage. Your stakeholder asks a simple question: &lt;em&gt;"What happens if I query this data in 90 days? Will I get stale data?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That question led me down a rabbit hole that taught me more about dbt materializations than any tutorial ever could.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Discovery: Everything Goes Stale
&lt;/h2&gt;

&lt;p&gt;My initial &lt;code&gt;stop_network_usage&lt;/code&gt; model was materialized as a &lt;strong&gt;table&lt;/strong&gt; with a hard-coded 30-day lookback window:&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="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;materialized&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;"table"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;

&lt;span class="k"&gt;select&lt;/span&gt;
    &lt;span class="n"&gt;stops&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;date_trunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'day'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trips&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;occurred_at&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;usage_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trips&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trip_id&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;daily_usage&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;stops&lt;/span&gt;
&lt;span class="k"&gt;inner&lt;/span&gt; &lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="n"&gt;trips&lt;/span&gt; &lt;span class="k"&gt;on&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;trips&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;occurred_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="k"&gt;current_date&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="s1"&gt;'30'&lt;/span&gt; &lt;span class="k"&gt;day&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The harsh reality hit: &lt;strong&gt;without regular dbt runs, every table in our data warehouse goes stale.&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fresh events flow into source systems continuously&lt;/li&gt;
&lt;li&gt;dbt tables only update when manually executed
&lt;/li&gt;
&lt;li&gt;Queries read stale data until the next dbt run&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If I didn't run dbt for 90 days, my dashboard would show data from 120 days ago (90 days since last run + 30-day lookback window). Ouch.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Complex" Solution Path
&lt;/h2&gt;

&lt;p&gt;My first instinct was to over-engineer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Convert to incremental materialization&lt;/strong&gt; ✓&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add scheduling infrastructure&lt;/strong&gt; (cron jobs, Airflow, AWS EventBridge)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implement monitoring and alerting&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Add sliding window logic for data retention&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set up error handling and recovery procedures&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I started down this path, converting my model to incremental:&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="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;materialized&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;"incremental"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unique_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;"stop_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"usage_date"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;

&lt;span class="c1"&gt;-- Complex incremental logic with is_incremental() conditions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;if&lt;/span&gt; &lt;span class="n"&gt;is_incremental&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;date_trunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'day'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trips&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;occurred_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;usage_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="p"&gt;}})&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;trips&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;occurred_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="k"&gt;current_date&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="s1"&gt;'90'&lt;/span&gt; &lt;span class="k"&gt;day&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;endif&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While learning about incremental models was valuable (they're genuinely powerful for large datasets), I realized I was building a complex solution for a simple problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Simple Solution: Views to the Rescue
&lt;/h2&gt;

&lt;p&gt;Then it hit me. For my use case, I had a much simpler option: &lt;strong&gt;views&lt;/strong&gt;.&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="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;materialized&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;"view"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;

&lt;span class="k"&gt;select&lt;/span&gt;
    &lt;span class="n"&gt;stops&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;stops&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;service_area_uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;stops&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name_en&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;stop_name_en&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trip_usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;occurred_at&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;trip_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;distinct&lt;/span&gt; &lt;span class="n"&gt;trip_usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trip_id&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;daily_trip_count&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;stops&lt;/span&gt;
&lt;span class="k"&gt;left&lt;/span&gt; &lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;link__stop_id__pickup&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;stop_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trip_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;occurred_at&lt;/span&gt;
    &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;trips&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;link__stop_id__pickup&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
    &lt;span class="k"&gt;union&lt;/span&gt;
    &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;link__stop_id__delivery&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;stop_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trip_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;occurred_at&lt;/span&gt;  
    &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;trips&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;link__stop_id__delivery&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;trip_usage&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;stops&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;trip_usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop_id&lt;/span&gt;
&lt;span class="k"&gt;group&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="p"&gt;[...]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Problem solved.&lt;/strong&gt; Views query the source data directly every time, so they're always fresh—no automation required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Views vs. Tables vs. Incremental: Choose Your Fighter
&lt;/h2&gt;

&lt;p&gt;This experience taught me that dbt materializations aren't just technical choices—they're &lt;strong&gt;architectural decisions&lt;/strong&gt; with real business implications:&lt;/p&gt;

&lt;h3&gt;
  
  
  📊 Views: Always Fresh, Simple
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best for&lt;/strong&gt;: Small-medium datasets, infrequent queries, freshness &amp;gt; performance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pros&lt;/strong&gt;: Always fresh data, no scheduling complexity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cons&lt;/strong&gt;: Slower queries, higher compute costs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;My use case&lt;/strong&gt;: ✅ Perfect for daily dashboard monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🗄️ Tables: Fast Performance, Can Go Stale
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best for&lt;/strong&gt;: Large datasets, frequent queries, acceptable staleness&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pros&lt;/strong&gt;: Pre-computed results, fast queries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cons&lt;/strong&gt;: Requires regular refresh, can go stale&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;My use case&lt;/strong&gt;: ❌ Would need complex automation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ⚡ Incremental: Best of Both Worlds (When You Need It)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best for&lt;/strong&gt;: Very large datasets, regular updates, historical accumulation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pros&lt;/strong&gt;: Efficient processing, fast subsequent runs, accumulates history&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cons&lt;/strong&gt;: Complex logic, still needs scheduling, overkill for simple use cases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;My use case&lt;/strong&gt;: ❌ Over-engineered for my requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Key Insight: Match the Tool to the Problem
&lt;/h2&gt;

&lt;p&gt;The temptation in data engineering is to always reach for the most sophisticated solution. Incremental models are powerful, automated pipelines are impressive, but sometimes the simple answer is the right answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My requirements:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Daily UI monitoring ✓&lt;/li&gt;
&lt;li&gt;Always fresh data ✓
&lt;/li&gt;
&lt;li&gt;Reasonable data volumes ✓&lt;/li&gt;
&lt;li&gt;Simple aggregations ✓&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Views delivered exactly what I needed&lt;/strong&gt; without any of the operational overhead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Question your assumptions&lt;/strong&gt;: Not every data model needs to be a table or incremental&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Understand the trade-offs&lt;/strong&gt;: Performance vs. freshness vs. complexity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Size your solution to your problem&lt;/strong&gt;: Views are underrated for the right use cases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Think about operations&lt;/strong&gt;: Who will maintain your complex automation?&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;Sometimes the best engineering decision is choosing the simplest solution that meets your requirements. Views might not be glamorous, but they solved my stale data problem elegantly.&lt;/p&gt;

&lt;p&gt;The next time someone asks "Will my data be stale in 90 days?", I'll confidently answer: "Not if you use views."&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What materialization strategies have you found work best for your use cases? I'd love to hear about times when simple solutions beat complex ones in your data projects.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dbt</category>
      <category>dataengineering</category>
      <category>sql</category>
      <category>analytics</category>
    </item>
    <item>
      <title>Understanding Big O Notation: A Beginner's Reflection on Writing Better Programs</title>
      <dc:creator>Jose Torreblanca</dc:creator>
      <pubDate>Tue, 17 Dec 2024 09:09:22 +0000</pubDate>
      <link>https://dev.to/josewhitetower/understanding-big-o-notation-a-beginners-reflection-on-writing-better-programs-35od</link>
      <guid>https://dev.to/josewhitetower/understanding-big-o-notation-a-beginners-reflection-on-writing-better-programs-35od</guid>
      <description>&lt;p&gt;When I started learning about programming, I focused on writing code that simply worked. But as I continued to grow as a developer, I realized that writing efficient programs is just as important as making them functional. One of the tools that helped me gain this understanding is &lt;strong&gt;Big O Notation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this article, I'll share what I have learned about Big O Notation, why it matters, and how to measure it. I'll focus on the three most common time complexities: &lt;strong&gt;O(1)&lt;/strong&gt;, &lt;strong&gt;O(n)&lt;/strong&gt;, and &lt;strong&gt;O(n²)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsl7crzs4s1kksq3xm2r2.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsl7crzs4s1kksq3xm2r2.jpeg" alt="Big O graph" width="800" height="556"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  What is Big O Notation?
&lt;/h3&gt;

&lt;p&gt;Big O Notation is a way to measure how the performance of an algorithm changes as the size of the input grows. It tells us about the &lt;strong&gt;time complexity&lt;/strong&gt; (how long it takes to run) or &lt;strong&gt;space complexity&lt;/strong&gt; (how much memory it uses).&lt;/p&gt;

&lt;p&gt;By understanding Big O, developers can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compare the efficiency of different solutions.&lt;/li&gt;
&lt;li&gt;Identify bottlenecks in code.&lt;/li&gt;
&lt;li&gt;Write scalable programs that perform well even with large inputs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of Big O Notation as a measure of how an algorithm "scales" rather than how fast it runs on your specific machine. It gives a general idea of performance trends.&lt;/p&gt;




&lt;h3&gt;
  
  
  Key Time Complexities: O(1), O(n), O(n²)
&lt;/h3&gt;

&lt;p&gt;Let’s dive into the three time complexities I have learned so far.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. &lt;strong&gt;O(1) - Constant Time&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;An algorithm has &lt;strong&gt;O(1)&lt;/strong&gt; time complexity if it always takes the same amount of time to complete, regardless of the input size.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getFirstElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the function retrieves the first element of an array. No matter how large the array is, this operation happens in constant time.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why it matters&lt;/strong&gt;: O(1) operations are the most efficient because their execution time does not increase with the size of the input.&lt;/li&gt;
&lt;/ul&gt;




&lt;h4&gt;
  
  
  2. &lt;strong&gt;O(n) - Linear Time&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;An algorithm has &lt;strong&gt;O(n)&lt;/strong&gt; time complexity if the execution time grows linearly with the input size.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;printAllElements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the function iterates through every element of the array. If the array has 10 elements, the loop runs 10 times. If the array has 1,000 elements, it runs 1,000 times.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why it matters&lt;/strong&gt;: O(n) operations scale reasonably well but can become slower with very large inputs.&lt;/li&gt;
&lt;/ul&gt;




&lt;h4&gt;
  
  
  3. &lt;strong&gt;O(n²) - Quadratic Time&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;An algorithm has &lt;strong&gt;O(n²)&lt;/strong&gt; time complexity if its execution time grows quadratically with the input size. Typically, this happens when we have nested loops.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;printPairs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, for each element in the outer loop, the inner loop runs through the entire array. If the array has 10 elements, this results in 100 operations (10 * 10).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why it matters&lt;/strong&gt;: O(n²) operations can quickly become inefficient as input size grows. For example, with 1,000 elements, this would require 1,000,000 operations!&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  How to Measure Big O Notation
&lt;/h3&gt;

&lt;p&gt;To determine the Big O of a function:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Count the basic operations&lt;/strong&gt; in your code, like loops, function calls, and calculations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus on the dominant term&lt;/strong&gt; — the part of the code that grows the fastest as input size increases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ignore constants and smaller terms&lt;/strong&gt; because they become negligible as the input size grows.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s analyze an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;exampleFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;       &lt;span class="c1"&gt;// O(1)&lt;/span&gt;

    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;   &lt;span class="c1"&gt;// O(n)&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;   &lt;span class="c1"&gt;// O(n)&lt;/span&gt;
        &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;   &lt;span class="c1"&gt;// O(n)&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;The first operation &lt;code&gt;console.log(array[0])&lt;/code&gt; runs in &lt;strong&gt;O(1)&lt;/strong&gt; time.&lt;/li&gt;
&lt;li&gt;The first loop runs in &lt;strong&gt;O(n)&lt;/strong&gt; time.&lt;/li&gt;
&lt;li&gt;The nested loop runs in &lt;strong&gt;O(n²)&lt;/strong&gt; time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To find the overall time complexity, we &lt;strong&gt;combine&lt;/strong&gt; these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O(1) + O(n) + O(n²)&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;dominant term&lt;/strong&gt; is O(n²), so the time complexity of this function is &lt;strong&gt;O(n²)&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;Learning about Big O Notation has been a game-changer for me. It helps me think critically about the efficiency of my code and make better decisions when choosing data structures and algorithms. By understanding time complexities like O(1), O(n), and O(n²), I can now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identify the strengths and weaknesses of my solutions.&lt;/li&gt;
&lt;li&gt;Optimize my code for better performance.&lt;/li&gt;
&lt;li&gt;Avoid writing programs that work but scale poorly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As I continue learning, I'll explore more complex time complexities and data structures. For now, this reflection gives me a solid foundation to write better and more efficient programs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Happy coding!
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaways:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Big O Notation measures how algorithms scale with input size.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;O(1)&lt;/strong&gt;: Constant time - execution time remains the same.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;O(n)&lt;/strong&gt;: Linear time - execution time grows proportionally with input size.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;O(n²)&lt;/strong&gt;: Quadratic time - execution time grows quadratically due to nested loops.&lt;/li&gt;
&lt;li&gt;Always focus on the &lt;strong&gt;dominant term&lt;/strong&gt; when analysing code.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I hope this reflection helps other beginners like me! Understanding these basics will pave the way for solving more challenging problems efficiently.&lt;/p&gt;

</description>
      <category>bigo</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
