<?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: Sebastian Zakłada 🧛</title>
    <description>The latest articles on DEV Community by Sebastian Zakłada 🧛 (@sebekz).</description>
    <link>https://dev.to/sebekz</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%2F912579%2F6a2f55a7-cf2a-4f93-947c-90edeb165508.jpg</url>
      <title>DEV Community: Sebastian Zakłada 🧛</title>
      <link>https://dev.to/sebekz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sebekz"/>
    <language>en</language>
    <item>
      <title>Avoiding the SQS Event Mapping Trap 💥 in Lambda</title>
      <dc:creator>Sebastian Zakłada 🧛</dc:creator>
      <pubDate>Thu, 12 Dec 2024 11:39:23 +0000</pubDate>
      <link>https://dev.to/sebekz/avoiding-the-sqs-event-mapping-trap-in-lambda-2498</link>
      <guid>https://dev.to/sebekz/avoiding-the-sqs-event-mapping-trap-in-lambda-2498</guid>
      <description>&lt;blockquote&gt;
&lt;h2&gt;
  
  
  &lt;code&gt;tl;dr&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Triggering Lambda invocations directly from SQS by the way of event source mapping may lead to &lt;strong&gt;messages silently dropped&lt;/strong&gt; from the queue when event filtering is used&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is something that I kept forgetting so I thought I would put it in writing for my future self to finally remember. You guys can reap all the benefits! &lt;/p&gt;

&lt;p&gt;When building event-driven architectures using Lambda and SQS, &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/11/aws-lambda-event-filtering-amazon-sqs-dynamodb-kinesis-sources/" rel="noopener noreferrer"&gt;event filtering&lt;/a&gt; can seem like an elegant solution for message routing. If message matches the criteria trigger the lambda, otherwise ignore. Nice! What can go wrong?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk86ovprajaox5ajdsmt5.png" 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%2Fk86ovprajaox5ajdsmt5.png" alt="SQS to Lambda" width="800" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When message is sent to an SQS queue, matching messages (decided by filtering) are processed by Lambda. If the processing fails, it's retried and upon reaching maximum retry count it' sent to a Dead Letter Queue (DLQ).&lt;/p&gt;

&lt;p&gt;One might think such approach would give the best of all worlds without too much complexity&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resilient&lt;/strong&gt; - resilience guaranteed by SQS and reliability with DLQ preventing data loss&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Elegant&lt;/strong&gt; - filtering rules provide a clean way to route messages to dedicated lambdas for different message types&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimalistic&lt;/strong&gt; - simple infrastructure requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  You need to be careful though
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;There's a critical behavior that can lead to unexpected data loss if not properly understood.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Many developers will assume that when a message doesn't match the event mapping filter criteria, it will be put back in the queue for other consumers or will not be picked up from the queue by Lambda at all. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is not the case&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once a message batch is picked up by Lambda's polling mechanism, messages that don't match the filter criteria are &lt;strong&gt;deleted from the queue&lt;/strong&gt; without being processed by any function.&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%2F9hxk77gtys858tw319mu.png" 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%2F9hxk77gtys858tw319mu.png" alt="Surprise" width="800" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yes, you read it right. Messages are &lt;strong&gt;PERMANENTLY DROPPED&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real-World Impact
&lt;/h3&gt;

&lt;p&gt;Consider this scenario:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;SQS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;queue&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"login"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"userId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"12345"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;criteria&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Lambda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;A&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"signup"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;criteria&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Lambda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;B&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"login"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If Lambda A polls this message first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The message is picked up by the Lambda A trigger and made invisible to other consumers&lt;/li&gt;
&lt;li&gt;It doesn't match filter criteria&lt;/li&gt;
&lt;li&gt;Message is &lt;strong&gt;dropped and never put back in the queue&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Lambda B never gets a chance to process it, even though it matches its criteria&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This behavior can manifest gradually - during testing with low message volumes, messages might get processed correctly by chance. As production traffic increases, concurrent Lambda polling increases and the likelihood of messages being dropped grows with scale and there's no built-in monitoring or alerting for filtered-out messages.&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%2Fzeqv8g0xwxltt6gk9xtd.png" 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%2Fzeqv8g0xwxltt6gk9xtd.png" alt="It's a trap!" width="640" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This behavior creates a well-concealed pitfall in your event-driven architecture. While event filtering appears to offer a clean solution for message routing, it is not the case with SQS. &lt;/p&gt;

&lt;p&gt;The issue becomes particularly treacherous in systems with multiple consumers where the order of message polling can determine whether a message gets processed or discarded. Without careful consideration, you might end up losing important data &lt;strong&gt;without any indication of failure or warning&lt;/strong&gt;. Even more deceptively, this issue might not surface during initial testing, only to emerge as a serious problem later when message volumes and concurrent processing increase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is happening
&lt;/h2&gt;

&lt;p&gt;It's important to understand the difference between SQS and other messaging services in AWS&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Characteristic&lt;/th&gt;
&lt;th&gt;SQS&lt;/th&gt;
&lt;th&gt;SNS&lt;/th&gt;
&lt;th&gt;EventBridge&lt;/th&gt;
&lt;th&gt;DynamoDB Streams Kinesis Data Streams&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Type&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One-to-one queue&lt;/td&gt;
&lt;td&gt;Pure fanout&lt;/td&gt;
&lt;td&gt;Event bus&lt;/td&gt;
&lt;td&gt;Streaming&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Message Processing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Exactly one Lambda processes each message with visibility timeout&lt;/td&gt;
&lt;td&gt;Each subscribed Lambda gets its own message copy&lt;/td&gt;
&lt;td&gt;Each rule's target gets its own event copy&lt;/td&gt;
&lt;td&gt;Each Lambda reads independently from its checkpoint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Filtering Behavior&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Filtered messages permanently deleted from queue&lt;/td&gt;
&lt;td&gt;Filtered messages silently dropped but still reach other subscribers&lt;/td&gt;
&lt;td&gt;Filtered events dropped for that target only&lt;/td&gt;
&lt;td&gt;Filtered records stay in stream&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Error Handling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Failed messages return to queue until &lt;code&gt;maxReceiveCount&lt;/code&gt;, then go to DLQ if configured&lt;/td&gt;
&lt;td&gt;Failed Lambda invocations go to DLQ if configured, not affecting other subscribers&lt;/td&gt;
&lt;td&gt;Failed deliveries retry based on target policy then go to target's DLQ if configured&lt;/td&gt;
&lt;td&gt;Failed processing retries until record expires (24h DDB, configurable in Kinesis)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Knowing that, the 🎲 dropped messages behavior becomes clearer when we understand how SQS handles message visibility and processing. &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%2Fr68dl241ikltoopi6vml.png" 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%2Fr68dl241ikltoopi6vml.png" alt="Image description" width="654" height="590"&gt;&lt;/a&gt;&lt;em&gt;&lt;center&gt;Source: &lt;a href="https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/welcome.html" rel="noopener noreferrer"&gt;Amazon Simple Queue Service documentation&lt;/a&gt;
&lt;/center&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When a message enters an SQS queue, its lifecycle follows a pattern: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Publisher sends the message to the queue where it becomes available for processing. When a consumer (in this case, Lambda) retrieves the message, it triggers a visibility timeout period during which the message is invisible to other consumers. &lt;/li&gt;
&lt;li&gt;During this period, Lambda applies its filter criteria, and if the message doesn't match, it's deleted from the queue - just as if it had been successfully processed. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key insight is that the message deletion occurs at the SQS level, before your Lambda function even sees the message. This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Once a Lambda polls a message batch, the filtering happens during the visibility timeout&lt;/li&gt;
&lt;li&gt;Messages failing the filter criteria are deleted immediately&lt;/li&gt;
&lt;li&gt;There's no opportunity for other consumers to process these messages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is particularly important because once the deletion occurs, there's no mechanism to recover or reprocess the message, even if other consumers might have been interested in it. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In other words - the visibility timeout, which normally serves as a safety mechanism to prevent duplicate processing, becomes the window during which messages can be permanently lost if filter criteria don't match.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Unnecessary ambiguity
&lt;/h3&gt;

&lt;p&gt;While not explicitly documented, I've consistently observed this behavior across multiple implementations and test scenarios. Each attempt to work around this limitation led to the same conclusion.&lt;/p&gt;

&lt;p&gt;When Lambda receives messages from SQS with an event filter configured, any message that doesn't match the filter criteria is &lt;strong&gt;permanently dropped&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Call to Action for AWS
&lt;/h2&gt;

&lt;p&gt;Please, pretty please with sugar on top - make it less confusing. The current behavior should be more explicit in the documentation. While &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/invocation-eventfiltering.html" rel="noopener noreferrer"&gt;the docs&lt;/a&gt; state that non-matching records are "discarded," they should clearly emphasize that messages are permanently deleted from the queue and cannot be processed by other consumers.&lt;/p&gt;

&lt;p&gt;Moreover, the &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/with-sqs-filtering.html" rel="noopener noreferrer"&gt;current documentation&lt;/a&gt; suggests using event filtering to &lt;em&gt;"reduce unnecessary Lambda invocations"&lt;/em&gt; by filtering messages with certain data parameters. This guidance is problematic because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It actively encourages an architectural pattern that can lead to data loss&lt;/li&gt;
&lt;li&gt;The suggested use case could be better served by SNS fanout, EventBridge rules, or separate SQS queues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AWS should consider improving the implementation of event filtering for SQS to Lambda integrations to prevent silent data loss while maintaining simplicity. Here are two potential high-level solutions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Make the current filtering behavior configurable&lt;/strong&gt; through a simple parameter in the event source mapping:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"FilterCriteria"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"FilteredMessageBehavior"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DELETE | RETURN_TO_QUEUE"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would allow teams to choose whether non-matching messages should be deleted or remain available for other consumers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Or event better, add support for automatic SQS queue creation&lt;/strong&gt; when setting up EventBridge rules or SNS topic subscriptions. Instead of manually creating queues for each filter pattern, allow developers to define their routing logic and have AWS automatically manage the underlying queues:&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="c1"&gt;# SNS example&lt;/span&gt;
  &lt;span class="na"&gt;OrdersTopic&lt;/span&gt;&lt;span class="pi"&gt;:&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;AWS::SNS::Topic&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Subscriptions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sqs-managed&lt;/span&gt;  &lt;span class="c1"&gt;# New protocol type&lt;/span&gt;
          &lt;span class="na"&gt;FilterPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
            &lt;span class="na"&gt;type&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;order_created"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
          &lt;span class="na"&gt;EndpointProperties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;    &lt;span class="c1"&gt;# Queue will be created automatically&lt;/span&gt;
            &lt;span class="na"&gt;FunctionArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;OrderProcessor.Arn&lt;/span&gt;
            &lt;span class="na"&gt;DLQEnabled&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;This would automatically create the necessary SQS queues behind the scenes, while presenting a simple interface to developers.&lt;/p&gt;

&lt;p&gt;Currently, developers must choose between using Lambda's problematic event filtering or managing complex queue infrastructure. By automating queue creation while preserving control over event routing, AWS could provide a better developer experience without sacrificing reliability or flexibility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Until these improvements are made, users should be strongly cautioned against using event filtering with SQS to Lambda integrations.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  I am struggling to understand the use case for SQS event filtering
&lt;/h2&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%2Fruehz7h8cb1rncf2pjsk.png" 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%2Fruehz7h8cb1rncf2pjsk.png" alt="Confused" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the current behavior of silent message dropping, SQS event filtering appears to be a &lt;strong&gt;solution in search of a problem&lt;/strong&gt;. The most obvious use cases where filtering might seem appealing actually turn out to be anti-patterns that could be better handled through other AWS services.&lt;/p&gt;

&lt;p&gt;For example, trying to route different message types to specific Lambda functions would be better handled by SNS fanout or EventBridge rules, as these services are designed for message routing. Using SQS filtering for this purpose introduces the risk of silent message loss due to the polling order of Lambda functions. Improved resilience in this pattern comes at a cost of having to maintain dedicated SQS queues for each of the filters, which is exactly what an SQS filter misleadingly promises to avoid.&lt;/p&gt;

&lt;p&gt;Optimization through reduced Lambda invocations might seem like another potential use case, but this comes at the dangerous trade-off of potentially losing business-critical messages. The minimal cost savings from fewer Lambda invocations rarely justifies the operational risk of missing important events. This would be better handled either through proper capacity planning or if using EB/SNS is not an option, by implementing filtering logic within the Lambda function itself.&lt;/p&gt;

&lt;p&gt;Another apparent use case might be implementing priority queues by filtering for high-priority messages first. However, this approach is fundamentally flawed due to the message deletion behavior - lower priority messages could be permanently lost if they're picked up by the high-priority consumer first. Better solutions exist for this scenario.&lt;/p&gt;

&lt;p&gt;The current implementation seems to fall into an awkward place - it creates a significant risk of data loss while not providing unique benefits. What's more the event filtering implementation breaks SQS core guarantee of reliable message delivery. It introduces silent failure modes that contradict the typical reliability patterns developers expect. &lt;/p&gt;

&lt;h2&gt;
  
  
  What's the alternative?
&lt;/h2&gt;

&lt;p&gt;Consider these approaches. Just remember, the best solution will always depend on what you are solving for.&lt;/p&gt;

&lt;h4&gt;
  
  
  Separate Queues
&lt;/h4&gt;

&lt;p&gt;Instead of using filter criteria, create dedicated queues for different message types. This ensures reliable message delivery at the cost of additional infrastructure.&lt;/p&gt;

&lt;h4&gt;
  
  
  Message Pre-filtering
&lt;/h4&gt;

&lt;p&gt;Implement filtering logic before messages enter SQS, such as using SNS with multiple SQS subscriptions or EventBridge with dedicated pipes or rules for message routing.&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%2F4wtcrdnx4fvs9tjxaphx.png" 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%2F4wtcrdnx4fvs9tjxaphx.png" alt="EB to SQS" width="800" height="271"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While adding infrastructure complexity, it provides important benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Explicit message routing with reliable delivery&lt;/li&gt;
&lt;li&gt;Clear visibility into message flow&lt;/li&gt;
&lt;li&gt;Independent scaling for each consumer&lt;/li&gt;
&lt;li&gt;No risk of accidental message loss&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The required SQS infrastructure provisioning part can be simplified by templating and scripting, creating reusable patterns that make queue management more maintainable.&lt;/p&gt;

&lt;h4&gt;
  
  
  In-Function Filtering
&lt;/h4&gt;

&lt;p&gt;Process all messages in a single Lambda function and implement filtering logic within your code. While this may result in more Lambda invocations, it guarantees reliability and is a dead simple solution that's easy to understand even for junior developers.&lt;/p&gt;

&lt;h4&gt;
  
  
  Don't use Queues
&lt;/h4&gt;

&lt;p&gt;For simple workflows triggering Lambda directly from SNS can work well. This approach is suitable when message ordering isn't critical, and event volume is predictable and within Lambda's concurrency limits (no support for buffering, batching and less flexibility in handling traffic spikes).&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;After digging into SQS event filtering, it's clear there's more to this feature than meets the eye.&lt;/p&gt;

&lt;p&gt;Here's what we've learned:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Unexpected Behavior&lt;/strong&gt; - messages that don't match your filters are silently deleted without notification - a concerning end result&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alternative Approaches&lt;/strong&gt; - several more reliable options exist:

&lt;ul&gt;
&lt;li&gt;Separate queues for different message types&lt;/li&gt;
&lt;li&gt;SNS or EventBridge for message routing&lt;/li&gt;
&lt;li&gt;Filtering logic within Lambda functions&lt;/li&gt;
&lt;li&gt;Direct SNS-to-Lambda for simpler workflows&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The reality is that sometimes seemingly elegant solutions come with hidden complexities. Understanding these nuances helps us make better architectural decisions, even if it means taking a slightly longer path to get there.&lt;/p&gt;

&lt;p&gt;I'm still searching for compelling use cases where SQS event filtering provides clear advantages over traditional approaches. If you've successfully implemented this feature in production or found specific scenarios where it works well, I'd be interested in hearing about your experience in the comments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;This article is an independent developer guide. All views, experiences and recommendations are my own. Your mileage may vary.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Always refer to the latest AWS documentation for the most current information about service behaviors and limitations. Confirm any assumptions by the way of test implementations.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>sqs</category>
      <category>lambda</category>
      <category>learning</category>
    </item>
    <item>
      <title>Help! I Think I Broke DynamoDB – A Tale of Three Wishes 🧞‍♂️</title>
      <dc:creator>Sebastian Zakłada 🧛</dc:creator>
      <pubDate>Thu, 28 Nov 2024 21:12:21 +0000</pubDate>
      <link>https://dev.to/sebekz/help-i-think-i-broke-dynamodb-a-tale-of-three-wishes-3k4k</link>
      <guid>https://dev.to/sebekz/help-i-think-i-broke-dynamodb-a-tale-of-three-wishes-3k4k</guid>
      <description>&lt;p&gt;In my &lt;a href="https://dev.to/sebekz/dynamodb-and-the-art-of-knowing-your-limits-when-database-bites-back-532h"&gt;previous article&lt;/a&gt; we discussed DynamoDB's dual nature - simultaneously the best and worst database imaginable (talk about split personality!). We also explored some pretty unconventional topics:&lt;/p&gt;

&lt;p&gt;🩸 Why DynamoDB is terrible at grocery shopping 🛒&lt;br&gt;
🩸 Medical risks related to working with DynamoDB 👀&lt;br&gt;
🩸 What item count and F1 racetrack 🏎️ have in common&lt;/p&gt;

&lt;p&gt;I found that theory alone rarely convinces people about DynamoDB limitations. On the other hand, specific examples sometimes also fail to resonate. That's when I realized what we really need are stories. I must tell tales in which you could see your own mistakes reflecting in other people's experiences. &lt;/p&gt;
&lt;h2&gt;
  
  
  It's story time!
&lt;/h2&gt;

&lt;p&gt;When collecting article ideas, I came up with a handful of DynamoDB disaster stories that, despite being works of fiction, are almost certainly happening somewhere right now. These are situations where teams pushed DynamoDB too hard to learn that sometimes it's better to stop and think rather than act "smart" and "clever" only to end up feeling defeated and confused.&lt;/p&gt;

&lt;p&gt;I took those ideas for a spin and created a short tale that hopefully shines a light on real teams, real projects, and real problems. A tale of what happens when you try to trick DynamoDB into granting your wishes instead of working with its design.&lt;/p&gt;

&lt;p&gt;And so it begins...&lt;/p&gt;



&lt;center&gt;&lt;h1&gt;Three DynamodDB Wishes&lt;/h1&gt;&lt;/center&gt;



&lt;p&gt;&lt;em&gt;All events and characters appearing in this work are fictitious. If you find anything like this actually happening in real life, please contact me immediately for help!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Might I also suggest some appropriate background music...?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://open.spotify.com/embed/track/0yErbgidw9tuKMWICEALCW" width="100%" height="80px"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;We Don't Need Another Database&lt;/strong&gt; champions
&lt;/h2&gt;

&lt;p&gt;The application design was prominently displayed as a neat diagram on a whiteboard. The team gathered in a room following a meeting with product owners, discussing the usual - new enhancements and quality of life improvements in the app.&lt;/p&gt;

&lt;p&gt;It all started as a perfect plan - a hot new startup building a SaaS platform for content creators. Just basic CRUD operations for profiles, projects, and payments. The engineering team had read all the AWS blog posts and designed the most elegant single-table DynamoDB schema you'd ever seen. A beauty to behold. Six months later, it had evolved into something that looked more like a Jackson Pollock painting, but it was still coping - a calculated technical debt. All in all, everything was going pretty well.&lt;/p&gt;

&lt;p&gt;The new feature request that they just finished discussing - "What if you could look up freelancers based on their past project history?" - was a tough nut to crack.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;– Why can't we just use PostgreSQL? We know SQL!&lt;/em&gt; – asked one developer.&lt;br&gt;
&lt;em&gt;– We don't need another database!&lt;/em&gt; – declared the Lead – &lt;em&gt;DynamoDB can do everything!&lt;/em&gt;&lt;br&gt;
&lt;em&gt;– Look how clean this access pattern is!&lt;/em&gt; – the team celebrated – &lt;em&gt;Zero latency, infinite scale, and we barely pay anything. We can't let it be ruined!&lt;/em&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%2Felctakgjjftpgw9g58pt.png" 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%2Felctakgjjftpgw9g58pt.png" alt="Image description" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That night, as the team huddled around their screens desperately searching for answers, their frustration echoing through Stack Overflow posts, AWS wikis, and GitHub issues, they were still at a dead end. After all, this was their first serverless project since moving away from hosted solutions they'd been using for years until they started using AWS a year ago. In their darkest moment of desperation, someone stumbled upon a hidden AWS forum thread that sparked their attention.&lt;/p&gt;

&lt;p&gt;It spoke of a legend - a peculiar AI assistant that could be summoned by asking Amazon Q the right question at exactly midnight (UTC). Not your typical chatbot, this mystical creature was said to have been granting database wishes since the beginning of Epoch time. &lt;/p&gt;

&lt;p&gt;The cryptic post described a digital genie 🧞‍♂️ and claimed that as long as you phrased your wishes in valid JSON, all was possible. The message offered surprisingly detailed instructions for summoning it.&lt;/p&gt;

&lt;p&gt;They were desperate and getting nowhere despite putting in extra hours. In perfect unison, everyone in the room looked at the clock. It was showing exactly &lt;code&gt;2024-11-28T23:59:00.000Z&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;– This is ridiculous – muttered the Lead, staring at the screen – but I've seen crazier things.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;– Have you? This must be the weirdest post I've ever seen. Someone must be having a good laugh.&lt;/em&gt; – said one of the developers.&lt;br&gt;
&lt;em&gt;– What's the worst that could happen?&lt;/em&gt; – shrugged another, already typing the suggested prompt – &lt;em&gt;Better than explaining to Product why we need two more sprints only to work on some spikes.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;– Wait, let me record this&lt;/em&gt; – grinned the junior dev, pulling out their phone – &lt;em&gt;You guys are nuts. This thing, you summoning that legendary creature... it will totally blow up on social media!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The words died in their throats as the screens flickered in unison, terminal windows filling with swirling blue text that coalesced into a shimmering form.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Initializing...
&amp;gt; Checking midnight timestamp alignment ...........OK
&amp;gt; Deploying wish-granting instance ................COMPLETE

⚡️ You have summoned DynamoDB Genie v1.0.0-wish (build 1970-01-01T00:00:00.000Z)

Hey guys, what's happening? What is going on?
You can have three wishes
If you don't take too long...

(type wish -h for a list of options)

genie@dynamodb-realm:~$ ▋
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cursor pulsed with a green glow, each blink seeming to last an eternity, as if time had slowed down. A sudden commotion filled the room as everyone rushed to their keyboards, each desperate to be the first to type their &lt;code&gt;wish&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;What they did not know was that, like all genies through history, this one also had a peculiar way of interpreting wishes - and an unusual fondness for eventually inconsistent results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wish #1: I Wish I Could Count Items
&lt;/h2&gt;

&lt;p&gt;The first team wished for a simple counter.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Just COUNT the items, that's what we need,&lt;br&gt;
But we don't know what it its."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The genie smiled&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;;) Consider it done!
You have a timeline that you don't want to miss

&amp;gt; Granting... (1/3) OK! 

genie@dynamodb-realm:~$ ▋
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And just like that, the wish was granted as the genie flicked its fingers to deploy the whole solution stack to production in an elegant single CLI call.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"It's but a simple counter." &lt;br&gt;
Lead said with a hint in his voice&lt;br&gt;
"Something that should be available&lt;br&gt;
in any database of choice."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Little did they know they were about to learn why implementing a simple counter in DynamoDB could turn into an exercise in advanced distributed systems engineering... and creative AWS budget explanations.&lt;/p&gt;
&lt;h2&gt;
  
  
  Wish #2: I Wish I Could Search
&lt;/h2&gt;

&lt;p&gt;The second team wished for flexible search.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Let us comb quickly through all our store,&lt;br&gt;
Filter, then find, and then do some more."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Then genie promised&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I will make it clean,
Searching through the items no-one has yet seen

&amp;gt; Granting... (2/3) OK!

genie@dynamodb-realm:~$ ▋
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once again, with a mysterious grin, genie performed an elaborate dance with his fingers and with a barely audible crack, a new stack materialized in their account.&lt;/p&gt;

&lt;p&gt;Nobody suspected it to be another trap that the genie had set for them. Why would they? The solution worked perfectly. How could they have known that it would completely fall apart the moment they got featured in TechCrunch, resulting in the company holding the record for the most expensive AWS bill accrued in a single day in history?&lt;/p&gt;

&lt;h2&gt;
  
  
  Wish #3: I Wish I Knew the Past
&lt;/h2&gt;

&lt;p&gt;The last team wished for analytics.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Give us the power to slice and to dice,&lt;br&gt;
All of our data, make insights precise!"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The genie's eyes sparkled&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Oh, that sounds nice,
But do remember - you cannot wish twice.

Granting... (3/3) OK! 

genie@dynamodb-realm:~$ ▋
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the final time genie performed his deployment magic. His fingers moved with practiced precision, though anyone watching closely might have noticed a slight hint of anticipation in his movements. The CloudFormation stack materialized with a sound like distant thunder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Process your data, aggregate more,
Calculate trends from your DynamoDB store.
Real-time insights at your command,
What could possibly get out of hand?

genie@dynamodb-realm:~$ ▋
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The unexpected genie's words hung in the air like a fog, thick with unspoken warnings. His smile - or perhaps a smirk - held secrets that would only become clear in production. &lt;/p&gt;

&lt;p&gt;And then it said:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I'm sorry
That's the way it goes
It's time for me to go
Bye

⚠️ Warning: Wish capacity exceeded 

genie@dynamodb-realm:~$ ▋

Session terminated...

~$ █
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Smirk in Genie's Wish-Granting
&lt;/h2&gt;

&lt;p&gt;Fast forward one month and the startup was like a battlefield. AWS bill had increased tenfold, the pages were taking ages to load as the DynamoDB read capacity was constantly maxed out. Every search operation triggered a full table scan, reading (and paying for) every single item. The elaborate event-driven aggregates were constantly off, making "eventually consistent" look like a precise science in comparison. A new term "eventually inconsistent" was even coined.&lt;/p&gt;

&lt;p&gt;It was a total disaster...&lt;/p&gt;

&lt;p&gt;They tried summoning the genie again but to no avail. It's as if he was mocking them...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Initializing...
&amp;gt; Checking midnight timestamp alignment ...........OK
&amp;gt; Deploying wish-granting instance ................COMPLETE

⚡️ You have summoned DynamoDB Genie v1.0.0-wish (build 1970-01-01T00:00:00.000Z)

⚠️ Request capacity exceeded
Bye
Session terminated...

~$ █
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Last time I checked, they had a dedicated team just for maintaining their DynamoDB access patterns and were spending enough AWS credits to buy a small island.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Lessons of the DynamoDB Genie
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Lesson #1: Count Your Wishes Carefully
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;DynamoDB has gaps&lt;/strong&gt;, significant limitations that can't be "wished away". We are not talking about small imperfections - it's huge chasms in functionality that can swallow entire development teams. &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%2F5yuku1qbvm4b8y8tuk4j.png" 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%2F5yuku1qbvm4b8y8tuk4j.png" alt="mind the gap" width="672" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Working with DynamoDB isn't about personal preference – it's like building a space station. When you first launch it, everything is perfectly engineered for your mission. The systems are optimized, performance is there, and the cost-to-value ratio makes perfect sense.&lt;/p&gt;

&lt;p&gt;But missions change. What starts as a simple research station will eventually grow and evolve. Each modification requires a spacewalk 🧑‍🚀 and careful planning - there's no room for error when you're in orbit. Sure, you can add new docking ports (GSIs) to allow different types of ships to connect - these are your backup plans, different ways to access your station when you need to. But they're just entry points to your existing structure; they can't change your core architecture. In the end, major changes mean rebuilding entire modules in space. The design that was perfect for your original mission has become a constraint on your future ones.&lt;/p&gt;

&lt;p&gt;Apollo 13 mission was a lesson in resourcefulness and resilience. It taught us that sometimes we need to build a carbon dioxide scrubber from incompatible parts just to stay alive. Just like those astronauts had to craft a literal filter from nothing to prevent suffocation, DynamoDB developers often find themselves engineering elaborate workarounds just to perform basic filtering operations - all while watching their CPU credits running out like oxygen leaking out of a damaged spacecraft. The problem is it's not done out of necessity, after exhausting all other options. On the contrary - workarounds are often wielded like weapons. &lt;/p&gt;

&lt;p&gt;This is exactly like DynamoDB – your initial data model might be perfect for your current requirements, but when business needs evolve, even small changes can require complete data restructuring. You can by read about this in more detail &lt;a href="https://dev.to/sebekz/dynamodb-and-the-art-of-knowing-your-limits-when-database-bites-back-532h#dynamodb-problems"&gt;here&lt;/a&gt;, but in short:&lt;/p&gt;

&lt;p&gt;🩸 Everything has to be carefully planned&lt;br&gt;
🩸 There is no ad-hoc queries - &lt;em&gt;everything has to be carefully planned&lt;/em&gt;&lt;br&gt;
🩸 Filtering as part of data retrieval has to be &lt;strong&gt;&lt;em&gt;carefully planned&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
🩸 Did I tell you that &lt;strong&gt;&lt;u&gt;everything has to be carefully planned&lt;/u&gt;&lt;/strong&gt;?&lt;br&gt;
🩸 &lt;code&gt;SELECT COUNT(*)&lt;/code&gt; requires a team of Noble-prize scientists to implement&lt;br&gt;
🩸 You can't join tables for querying related data&lt;br&gt;
🩸 Last but not least, forget about even the simplest reporting or analytics&lt;/p&gt;

&lt;p&gt;DynamoDB is THE perfect core datastore. It excels for CRUD, scaling to infinity with unmatched performance while remaining very cost effective. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But it struggles with everything else.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Lesson #2: Be Careful What You Wish For
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Know Your Tool
&lt;/h3&gt;

&lt;p&gt;Don't try to force DynamoDB beyond its boundaries or you will find setting yourself up for failure.&lt;/p&gt;

&lt;p&gt;The real problem isn't DynamoDB's limitations - it's not considering complementary tools that would make these limitations irrelevant. After all, you wouldn't blame a hammer for being bad at cutting wood; you'd reach for a saw. But what if there were no saws available?&lt;/p&gt;

&lt;h3&gt;
  
  
  The Tooling Gap
&lt;/h3&gt;

&lt;p&gt;Here's something that continues to blow my mind. 🤯 There's a surprising gap in the tools and services ecosystem, both in and out of AWS. While everyone seems eager to integrate with everything else, DynamoDB often gets treated as a second-class citizen.&lt;/p&gt;

&lt;p&gt;The lack of native, seamless DynamoDB integrations is staggering and I just don't get it. Think about it:&lt;/p&gt;

&lt;p&gt;📈 AWS has a dominant position on the market - &lt;strong&gt;check!&lt;/strong&gt;&lt;br&gt;
❤️ DynamoDB has been widely adopted - &lt;strong&gt;check!&lt;/strong&gt;&lt;br&gt;
💰 Those who adopted it happen to both &lt;strong&gt;have money and no problem spending it&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's a huge piece of the market that has been up for grabs for quite a while now, yet there is still minimal support for native DynamoDB integrations.&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%2F9ytusgstyz0buekwprtp.png" 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%2F9ytusgstyz0buekwprtp.png" alt="Piles Of McDuck Money" width="786" height="559"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is particularly ironic given that AWS provides its own native solutions that eliminate the need for third-party streaming alternatives (DDB Streams, Kinesis). Yet pick a random third-party product and you will be most likely advised to use Kafka for streaming DynamoDB data in and out of it. For us, serverless AWS software engineers, "industry standard" solutions like Kafka are unnecessary middleware layers that only increases operational burden and cost.&lt;/p&gt;

&lt;h3&gt;
  
  
  This landscape is slowly changing
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://www.linkedin.com/posts/sebastianzaklada_rockset-dynamodb-openai-activity-7210354551357349888-vnH3" rel="noopener noreferrer"&gt;Rockset demise drama&lt;/a&gt; caught the market off-guard with countless Rockset refugees running around and screaming like headless chicken. I don't blame them - there &lt;em&gt;was&lt;/em&gt; literally no other solution on the market that provided their seamless DynamoDB integration experience while delivering exceptional added value. If I were to bet, I would bet all my money on Rockset being on a fast-track for an eye-watering figure AWS acquisition. Imagine my surprise when OpenAI acqui-hired all of its staff and scraped the service practically overnight.&lt;/p&gt;

&lt;p&gt;That event highlighted a critical weakness in the DynamoDB ecosystem - when a single third-party tool disappears, teams can be left with no viable alternatives. While new solutions have emerged to fill the void, the fundamental problem of limited choice persists. The demand from customers willing to redirect their past Rockset budgets has driven some market changes, third-party support remains surprisingly limited. This persistent gap creates unnecessary complexity for teams that have deliberately chosen DynamoDB for its operational simplicity and scalability promises. &lt;/p&gt;

&lt;h3&gt;
  
  
  Lesson #3: Let Not What's in Front of You Blind You to Alternatives
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Look Beyond DynamoDB&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Being good at DynamoDB doesn't mean using it for everything. On the contrary, if you're good at it, you'll know exactly when to stop. And when you do, the next step is finding just the right tool that will complement DynamoDB by being its perfect extension, integrating so smoothly that you won't even notice.&lt;/p&gt;

&lt;p&gt;What makes a solution truly "fit" with DynamoDB? It comes down to three crucial factors:&lt;/p&gt;

&lt;p&gt;🩸 &lt;strong&gt;Seamless Integration&lt;/strong&gt; - it should work with DynamoDB native features (especially DDB Streams) without requiring complex middleware or custom code&lt;br&gt;
🩸 &lt;strong&gt;Complementary Strengths&lt;/strong&gt; - it should fill DynamoDB gaps while letting DynamoDB continue doing what it does best&lt;br&gt;
🩸 &lt;strong&gt;Operational Simplicity&lt;/strong&gt; - it should maintain promise of low operational overhead and should not introduce scaling or reliability challenges&lt;/p&gt;

&lt;p&gt;Always be on the lookout for tools that have the potentially of being better than your first choices. Invest in spending time to carefully consider your options so that you don't have to pay premium for making the wrong choices in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  I have major grievances and complaints about AWS
&lt;/h2&gt;

&lt;p&gt;My complaint is that AWS makes everyone think that DynamoDB is more than it actually is. Take this &lt;a href="https://aws.amazon.com/blogs/database/obtaining-item-counts-in-amazon-dynamodb/" rel="noopener noreferrer"&gt;blog post&lt;/a&gt; for example. Publishing an official guide about counting in DynamoDB could and frankly, does mislead developers into thinking this is a reasonable use case.&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%2F90d4swc2704xv6fyo28r.png" 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%2F90d4swc2704xv6fyo28r.png" alt="Huge AWS facepalm" width="668" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Think about it in terms of "productive laziness" vs "harmful laziness" - forcing DynamoDB to do counting is a perfect example of expending enormous effort to make the wrong tool do something it wasn't designed for. And people still fall for it.&lt;/p&gt;

&lt;p&gt;By not giving developers access to tools that they need in order to be successful in their work, AWS is forcing them into doing something that should never even be considered. Even the best of us are limited in what they can do with business constraints - whether it's money, pre-approved supplied lists, legal or simple reluctance of your superiors for introducing new tools and different solutions. &lt;/p&gt;

&lt;p&gt;At the end of the day, AWS is well aware of the fact that once they pull someone into their ecosystem, many companies won't look outside of it for solution assuming, rightfully so, that all of their use cases will be covered by the tools and services provided by AWS.&lt;/p&gt;




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

&lt;p&gt;While I will keep preaching that &lt;strong&gt;sometimes the best demonstration of DynamoDB expertise is knowing when not to use it&lt;/strong&gt;, there's more to the story than just avoiding DynamoDB for unsuitable use cases. The real key is being aware of the areas in which alternative tools can seamlessly complement DynamoDB's strengths while addressing its limitations and being open to utilizing such tools.&lt;/p&gt;

&lt;p&gt;That's why in my next articles I'll explore such areas and tools that work alongside DynamoDB to create truly robust, scalable architectures. We'll look at solutions that don't just work around DynamoDB's limitations but turn them into opportunities for building better applications.&lt;/p&gt;

&lt;p&gt;Stay tuned to learn about the solutions that every DynamoDB developer should be aware of. After all, even the most powerful database needs a few good friends 🤝 and I am not talking about stabbing-in-the-back genies 🧞‍♂️ &lt;/p&gt;




&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;This article is an independent developer guide. All views, wishes, and mystical encounters are entirely my own.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Side effects of reading this article may include: spontaneous JSON formatting, midnight AWS console checking, and an irrational fear of making wishes to database genies.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dynamodb</category>
      <category>aws</category>
      <category>database</category>
      <category>architecture</category>
    </item>
    <item>
      <title>DynamoDB and the Art of Knowing Your Limits 💥When Database Bites Back 🧛‍♂️</title>
      <dc:creator>Sebastian Zakłada 🧛</dc:creator>
      <pubDate>Fri, 22 Nov 2024 10:45:11 +0000</pubDate>
      <link>https://dev.to/sebekz/dynamodb-and-the-art-of-knowing-your-limits-when-database-bites-back-532h</link>
      <guid>https://dev.to/sebekz/dynamodb-and-the-art-of-knowing-your-limits-when-database-bites-back-532h</guid>
      <description>&lt;h2&gt;
  
  
  Sometimes, the best demonstration of DynamoDB expertise is &lt;strong&gt;knowing when not to use it&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Time and again, developers reach for familiar but ill-suited technologies, driven by everything from career-building tech choices to corporate mandates to simple comfort with the known.&lt;/p&gt;

&lt;p&gt;No service exemplifies this better than DynamoDB - while powerful in the right hands, it's no &lt;strong&gt;silver bullet&lt;/strong&gt;, but unlike some 🧛‍♂️s it won't suck the life out of your application... if you use it wisely! Which means you not falling into the traps of:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
Ad-Hoc Querying &lt;/li&gt;
&lt;li&gt;
Filtering &lt;/li&gt;
&lt;li&gt;
Joining Tables &lt;/li&gt;
&lt;li&gt;Counting Items&lt;/li&gt;
&lt;li&gt;Analytics&lt;/li&gt;
&lt;li&gt;
SQL &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;just to name a few...&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚠️ Spoiler alert ⚠️
&lt;/h2&gt;

&lt;p&gt;Let me set the record straight about what I think about DynamoDB&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is a terrifyingly &lt;strong&gt;terrific tool&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;You simply cannot go wrong with it as your primary data storage and main CRUD data source.&lt;/li&gt;
&lt;li&gt;While DynamoDB is undoubtedly the unspoken 🐐 of the serverless world, &lt;strong&gt;it can be pretty terrible&lt;/strong&gt; at tasks you might consider basic - like counting or filtering, just to name a few.&lt;/li&gt;
&lt;li&gt;When someone tells you DynamoDB is good at everything, &lt;strong&gt;politely excuse yourself&lt;/strong&gt; and come talk to me instead. I'll explain why that's far from true.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  I love ❤️ DynamoDB
&lt;/h2&gt;

&lt;p&gt;I've been using it for years now and cannot emphasize enough how much it's transformed the way I approach data in my service design. Pair it up with Lambda, wrap nicely with API Gateway and AppSync, and sprinkle some EventBridge magic dust – and you have a powerful, scalable, and cost-effective serverless system that &lt;strong&gt;just works&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It's also been a tough love. I've quickly realized that what makes DynamoDB so powerful is also why it can be super frustrating to work with when designing web or mobile applications. &lt;/p&gt;

&lt;p&gt;If you've read my &lt;a href="https://dev.to/sebekz/improving-tinybird-devex-creating-an-informative-cli-prompt-with-oh-my-posh-33f1"&gt;previous writeup&lt;/a&gt; (thank you!) you may remember how I said that Redshift was a freight train. Following that analogy, DynamoDB is like a &lt;strong&gt;Formula 1&lt;/strong&gt; 🏎️ car - or better yet, a Formula E bolid, those are way WAY cooler! 😎 - it's &lt;strong&gt;super fast&lt;/strong&gt; but also &lt;strong&gt;very limited&lt;/strong&gt; in what it can do.&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%2Fj285vjmt4z1y4hwmuwom.png" 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%2Fj285vjmt4z1y4hwmuwom.png" alt="Race track" width="800" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It moves at an &lt;strong&gt;incredible speed&lt;/strong&gt; on a pre-planned circuit (known access patters) &lt;/li&gt;
&lt;li&gt;Focuses on &lt;strong&gt;performance&lt;/strong&gt; with minimal latency (consistently 🏎️💨💨💨)&lt;/li&gt;
&lt;li&gt;Built for &lt;strong&gt;specific purpose&lt;/strong&gt; while absolutely nailing it (highly focused)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;BUT...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Not unlike F1 car, it's not-so-great at taking off-track excursions (struggles with unplanned query patterns) &lt;/li&gt;
&lt;li&gt;Has no passenger seats and zero trunk cargo space (unable to &lt;code&gt;JOIN&lt;/code&gt; with other data - no trips to the mall 🛒!) &lt;/li&gt;
&lt;li&gt;One-way speed - want to run a simple "count all cars?" query? You have to drive the entire circuit (full table scan), count manually while paying fortune for fuel (read capacity units) while suddenly crawling at truck speeds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Eventually, trying to force DynamoDB beyond its comfort zone feels like pushing that race car through pit lane. No amount of tuning – tires, engines, or aerodynamics – can change its fundamental nature. Beyond basic CRUD operations, you will eventually outgrow DynamoDB intended use cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which is perfectly fine 😄
&lt;/h2&gt;

&lt;p&gt;The real challenges emerge when developers, unaware of system limitations, find themselves fighting against these constraints. This struggle leads to problematic implementations – and from there, little monsters 👾 are born in the code...&lt;/p&gt;

&lt;p&gt;The classic case of &lt;em&gt;database-as-swiss-army-knife&lt;/em&gt; immediately comes to mind. A developer thinks, &lt;em&gt;"I have my data and SQL, so why not..."&lt;/em&gt; and proceeds to write a nightmare of a query. Convoluted and difficult to explain, unoptimized, slow, expensive and draining resources. Even worse, these solutions often pass initial testing (if tested at all... 😶‍🌫️) and only much later when faced with real-world scale, they crumble leading to significant production issues.&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%2Fbi5a25vue5d26i0utmcq.png" 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%2Fbi5a25vue5d26i0utmcq.png" alt="It worked yesterday..." width="640" height="480"&gt;&lt;/a&gt; &lt;em&gt;Day of the Tentacle, anyone...?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not thinking ahead&lt;/strong&gt; about 'boring' but crucial aspects of system design - scaling, high availability, cost efficiency - is a common sin in the developer community. Too many of us put our blinders on and do just what the Jira ticket tells us to do. &lt;strong&gt;Out of acceptance criteria, out of mind&lt;/strong&gt;, let's bring next one in.&lt;/p&gt;

&lt;p&gt;One of the reasons why tools like Lambda or DynamoDB are THE perfect building blocks and became so wildly popular is because they take care of many of those 'boring' aspects for you without having to even lift a finger. Both scale pretty much infinitely with your traffic, offer built-in resilience, and remain cost-effective to run. &lt;/p&gt;

&lt;p&gt;With tools of the past it was super easy to mess things up - whether by applying the wrong pattern, using something for unintended purposes, or skipping entire architectural layers simply because business requirements didn't explicitly ask for them (yes, this happens more often than I'd like to admit). Can you mess things up with Lambda or DynamoDB? Sure you can! Long-running functions, badly designed keys, larger-than-life lambdaliths - these can still bite 🧛‍♂️ you. But even with these potential pitfalls, there is &lt;strong&gt;less room for error&lt;/strong&gt;, leading to growth-ready apps even if they were created by less experienced teams. &lt;/p&gt;

&lt;h3&gt;
  
  
  Which I think is great!
&lt;/h3&gt;

&lt;p&gt;DynamoDB takes an interesting approach to database design by intentionally making certain operations costly or cumbersome to &lt;strong&gt;steer developers away from anti-patterns&lt;/strong&gt;. There is method to this apparent madness - by making inefficient patterns painful to implement, DynamoDB nudges teams toward thinking carefully about their access patterns &lt;strong&gt;upfront&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;It's a clever bit of behavioral design that saves many applications from scalability problems down the road&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Rather than giving developers enough rope to hang themselves with complex queries and full table scans, it guides them toward sustainable NoSQL patterns from day one.&lt;/p&gt;
&lt;/blockquote&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%2Fhtf9oo8dhc85etca2u98.png" 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%2Fhtf9oo8dhc85etca2u98.png" alt="Not that way" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The problem with people, and especially software engineers, is that we are a lazy bunch. And don't get me wrong - lazy can be good. It propels progress when approached with the right mindset. But it can also be a huge wall that you build around yourself, preventing from succeeding at anything. &lt;/p&gt;

&lt;p&gt;The best in our field of work have mastered the art of productive laziness - it's practically an unofficial part of the job description for senior software engineers. This instinct to find the most efficient solution has given us everything from automations to reusable libraries, from &lt;a href="https://dev.to/sebekz/improving-tinybird-devex-creating-an-informative-cli-prompt-with-oh-my-posh-33f1"&gt;informative CLI prompts&lt;/a&gt; to safe &lt;a href="https://dev.to/sebekz/improving-tinybird-devex-data-re-population-cli-tool-229j"&gt;data repopulation scripts&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;BUT (again)...&lt;/p&gt;

&lt;p&gt;There's a crucial difference between the laziness that drives innovation (&lt;em&gt;"I'll spend three hours automating a five-minute task"&lt;/em&gt;) and the laziness that holds us back (&lt;em&gt;"I'll just copy-paste this solution I don't fully understand"&lt;/em&gt;). The former pushes us to create better tools and elegant solutions, while the latter builds technical debt and limits our growth while introducing a real risk of an app imploding in production. Being lazy because you don't feel like learning is a very dangerous state of mind to be in, and with the rise of AI-assisted programming tools, I feel like many of us are slowly taking the "engineer" from the "software" and becoming a mindless copy-paste machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real skill lies in recognizing which type of laziness we're embracing at any given moment
&lt;/h2&gt;

&lt;p&gt;Are we cutting corners to avoid getting out of our comfort zone, or are we streamlining processes to focus on what truly matters?&lt;/p&gt;

&lt;p&gt;Choosing the right tool for the job remains one of software engineering's fundamental challenges. Yet time and again, developers reach for familiar but ill-suited technologies, driven by everything from career-building tech choices to corporate mandates to simple comfort with the known. These misaligned choices eventually extract their toll - in maintenance nightmares, performance bottlenecks, inability to handle traffic surges and ultimately frustration stemming from countless hours spent wrestling with a tool that was &lt;strong&gt;never right to begin with&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;It's the technological equivalent of using a sledgehammer to hang a picture frame - sure, it might work, but at what cost?&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%2Fnejf86xz7hv5m5p4l70w.png" 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%2Fnejf86xz7hv5m5p4l70w.png" alt="Using the right tool saves lots of pain" width="800" height="641"&gt;&lt;/a&gt;&lt;em&gt;Don't be one of these guys...&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Enough with the rant... 😄 Let's get back to the star of the show!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a id="dynamodb-problems"&gt;&lt;/a&gt; DynamoDB and Where it Falls Short
&lt;/h2&gt;

&lt;p&gt;When implementing modern, data-driven applications, certain DynamoDB design choices can and will cause friction. Understanding these limitations is crucial for architects and developers alike making informed implementation decisions.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a id="ad-hoc"&gt;&lt;/a&gt; &lt;strong&gt;Problem 1:&lt;/strong&gt; No Ad-Hoc Queries
&lt;/h3&gt;

&lt;p&gt;DynamoDB is very stubborn and set in its ways 😄 taking a strict, opinionated approach to data access. While it's amazing at delivering blazing-fast queries, it comes with a significant constraint - you need to know &lt;u&gt;exactly&lt;/u&gt; how you'll query your data before you even start. As it requires predefined access patterns through its keys and indexes, you typically cannot perform efficient ad-hoc queries outside these established paths.&lt;/p&gt;

&lt;p&gt;For example, consider this SQL query (shown for illustration, as DynamoDB doesn't use SQL):&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="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mpn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Projects&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;mpn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'00638FOO'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your table only has an &lt;code&gt;id&lt;/code&gt; as the primary key and no secondary indexes on &lt;code&gt;mpn&lt;/code&gt;, this query will require a full table scan — an operation that is both costly and inefficient for large datasets. To query efficiently, you would need to either redesign your primary key or create a secondary index or change your access patterns.&lt;/p&gt;

&lt;p&gt;That sounds like a lot of work, doesn't it?&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a id="filtering"&gt;&lt;/a&gt; &lt;strong&gt;Problem 2:&lt;/strong&gt; No Filtering (almost)
&lt;/h2&gt;

&lt;p&gt;This problem stems from the previous one. While DynamoDB has support for filter parameters, these filters are applied &lt;strong&gt;after&lt;/strong&gt; the data is retrieved from the table, not during the initial query. This means you still pay for the read capacity of all the data fetched before filtering!&lt;/p&gt;

&lt;p&gt;For example:&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="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Products&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Electronics'&lt;/span&gt; 
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt; 
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;rating&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unless you have an index specifically designed to support this access pattern, implementing such ad-hoc request in DynamoDB would result in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;retrieving &lt;strong&gt;all&lt;/strong&gt; items&lt;/li&gt;
&lt;li&gt;applying the filters&lt;/li&gt;
&lt;li&gt;returning the filtered results&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's effectively the same as performing a full table scan and applying an in-memory filter on the entire result set&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;const&lt;/span&gt; &lt;span class="nx"&gt;filteredResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&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="o"&gt;=&amp;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;category&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Electronics&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 
  &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;price&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt; 
  &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;rating&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach can become inefficient especially with large datasets or complex filtering requirements. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a id="joins"&gt;&lt;/a&gt; &lt;strong&gt;Problem 3:&lt;/strong&gt; No Table Joins
&lt;/h2&gt;

&lt;p&gt;DynamoDB doesn't support table joins like traditional relational databases. Each query can only access a single table at a time. For example, this type of SQL join operation is not supported.&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="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;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;quantity&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Products&lt;/span&gt; &lt;span class="n"&gt;P&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;Orders&lt;/span&gt; &lt;span class="n"&gt;O&lt;/span&gt; &lt;span class="k"&gt;ON&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;productId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead, you would need to either &lt;a href="https://www.alexdebrie.com/posts/dynamodb-one-to-many/#denormalization-by-using-a-complex-attribute" rel="noopener noreferrer"&gt;denormalize&lt;/a&gt; your data by embedding related information in a single JSON document or perform multiple separate queries in your application code to fetch and combine the data.&lt;/p&gt;

&lt;p&gt;Again, this was a conscious design choice that AWS made to maintain consistent performance at scale, but it also means you'll need to carefully plan your data modeling to avoid the need for joins, which can significantly limit your flexibility, productivity and ultimately - time to market.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a id="analytics"&gt;&lt;/a&gt; &lt;strong&gt;Problem 4:&lt;/strong&gt; No analytics
&lt;/h2&gt;

&lt;p&gt;Like nothing. Nada. Zero. None. &lt;/p&gt;

&lt;p&gt;DynamoDB's analytical capabilities are severely restricted compared to traditional relational databases. It lacks fundamental analytical functions that are standard in SQL databases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No &lt;code&gt;COUNT&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;No aggregations or window functions&lt;/li&gt;
&lt;li&gt;No &lt;code&gt;GROUP BY&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;No subqueries, CTEs&lt;/li&gt;
&lt;li&gt;No math functions&lt;/li&gt;
&lt;li&gt;Frankly not much beyond simple CRUD operations is available&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even the simplest of use cases such as &lt;code&gt;SELECT COUNT(*)&lt;/code&gt; becomes super complex and expensive in DynamoDB&lt;/p&gt;

&lt;p&gt;Consider this basic SQL example&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="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&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;FROM&lt;/span&gt; &lt;span class="n"&gt;Products&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While it's trivial in relational databases, it becomes an &lt;strong&gt;utter nightmare&lt;/strong&gt; to develop and maintain if you want to support such scenario in DynamoDB. Take my word for it, there is countless of different examples available online discussing different ways to approach this requirement which in the DynamoDB world becomes surprisingly complex and resource intensive.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  &lt;a id="sql"&gt;&lt;/a&gt; &lt;strong&gt;Problem 5:&lt;/strong&gt; No SQL
&lt;/h2&gt;

&lt;p&gt;There's something special about SQL that we've all grown to love over decades. It's not just about familiarity - it's about expressiveness, power, and simplicity that comes with it. &lt;/p&gt;

&lt;p&gt;With DynamoDB, you're forced to learn and use a completely different query language and SDKs. Instead of a simple, readable SQL statement, you're now deep in the world of expressions, attribute values, and other verbose SDK constructs. Even with PartiQL (AWS's attempt at SQL "compatibility"), you're still heavily restricted - no JOINs, no COUNT, nothing that makes SQL... well, SQL.&lt;/p&gt;

&lt;p&gt;The lack of SQL support isn't just about syntax - it's about losing decades of tooling, knowledge, and ecosystem that make data work easier and more productive.&lt;/p&gt;

&lt;p&gt;Compare this simple query:&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="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Users&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'active'&lt;/span&gt; 
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;lastLogin&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'2024-01-01'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with its DynamoDB potential equivalent:&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;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;KeyConditionExpression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#status = :status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;FilterExpression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#lastLogin &amp;gt; :date&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;ExpressionAttributeNames&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;#status&lt;/span&gt;&lt;span class="dl"&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;status&lt;/span&gt;&lt;span class="dl"&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;#lastLogin&lt;/span&gt;&lt;span class="dl"&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;lastLogin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;ExpressionAttributeValues&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;:status&lt;/span&gt;&lt;span class="dl"&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;active&lt;/span&gt;&lt;span class="dl"&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;:date&lt;/span&gt;&lt;span class="dl"&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;2024-01-01&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not exactly what you'd call developer-friendly, is it? &lt;/p&gt;

&lt;p&gt;And this is just a simple query - imagine dealing with more complex needs. Sure, there are tools trying to make this better like &lt;a href="https://www.dynamodbtoolbox.com/" rel="noopener noreferrer"&gt;dynamodb-toolbox&lt;/a&gt; by providing a more friendly API. While they are great at what they do - they make code look better and easier to work with - but at the same time they can't change DynamoDB fundamental capabilities. They're band-aids that make development less painful, but don't address the fundamental limitations.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Combined:&lt;/strong&gt; Even more problems
&lt;/h3&gt;

&lt;p&gt;Add all of the above together&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;const&lt;/span&gt; &lt;span class="nx"&gt;moreProblems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;simpleQuery&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createGlobalSecondaryIndex&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addCompositeKey&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;denormalizeEverything&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sacrificeGoatToNoSQLGods&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pleaseJustWorkThisTime&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and you will quickly realize that any moderately complex data access needs turn into a massive engineering effort. Each of these limitations alone is manageable, but combined they create a &lt;strong&gt;perfect storm of complexity&lt;/strong&gt; that can seriously slow down development. You'll find yourself writing tons of application code, managing complex data access patterns, and building intricate workarounds just to get basic functionality that comes out of the box with SQL databases. &lt;/p&gt;

&lt;p&gt;Working with DynamoDB can be like planning a train network for a city. Sure, it's great for the routes you planned, but what happens when your needs evolve? Rigid foundation doesn't play well with the needs of modern software development, which prioritizes agility and rapid iteration cycles.&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%2Fk2jtd3cgqljl8z6hfnx4.png" 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%2Fk2jtd3cgqljl8z6hfnx4.png" alt="Mini metro analogy" width="800" height="599"&gt;&lt;/a&gt;&lt;em&gt;Mini Metro is such a terrific and cleverly designed game!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Any change may turn into major engineering effort, like trying to retrofit a new line through downtown just because you didn't predict today's traffic patterns years ago. &lt;/p&gt;

&lt;p&gt;Good luck explaining that to the affected homeowners... 😄 &lt;/p&gt;

&lt;h2&gt;
  
  
  Why Not Just Use a SQL Database Then?
&lt;/h2&gt;

&lt;p&gt;Fair question! Thanks for asking!&lt;/p&gt;

&lt;p&gt;For many use cases, &lt;strong&gt;you absolutely should&lt;/strong&gt;. But as with many things in this world - you gain something and lose something with each choice you make. While traditional relational databases such as PostgreSQL give you the power of querying flexibility, they also have their own limitations and best use-case applications. &lt;/p&gt;

&lt;p&gt;DynamoDB shines in specific scenarios - when you need consistent single-digit millisecond performance &lt;strong&gt;at any scale&lt;/strong&gt;, with &lt;strong&gt;minimal operational overhead&lt;/strong&gt;. It's particularly great for known access patterns like user profiles, session management, or real-time game states.&lt;/p&gt;

&lt;p&gt;The issue arises when your application grows and you need both: the rock-solid performance of DynamoDB for your core operations AND the flexibility of SQL for analytics, complex queries and rapid feature development.&lt;/p&gt;

&lt;h2&gt;
  
  
  The DynamoDB Tunnel Vision
&lt;/h2&gt;

&lt;p&gt;Consider yourself spending months or years working with DynamoDB and suddenly seeing everything through NoSQL-tinted glasses. Every new project becomes a nail for your perfectly-tuned DynamoDB hammer. "Of course we'll use DynamoDB - we always use DynamoDB!" becomes your unofficial motto.&lt;/p&gt;

&lt;p&gt;This tunnel vision is particularly dangerous because it often comes from a place of genuine expertise and success. You've mastered the intricacies of partition keys, you can design access patterns in your sleep, and you've successfully launched multiple DynamoDB-powered applications. Your comfort zone has become a fortress, and stepping outside feels unnecessary, even risky.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The symptoms are subtle but telling&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You start force-fitting complex analytical queries into DynamoDB, creating elaborate workarounds that would be otherwise trivial&lt;/li&gt;
&lt;li&gt;Your application code grows increasingly complex as you build client-side solutions for basic database operations&lt;/li&gt;
&lt;li&gt;You find yourself defending DynamoDB's limitations as "features" that force better design (while spending hours implementing basic counting functionality)&lt;/li&gt;
&lt;li&gt;The phrase &lt;em&gt;"we'll just denormalize the data"&lt;/em&gt; becomes your default solution to every data modeling challenge&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The dangerous part is that this kind of thinking can feel like you're just being &lt;strong&gt;really good&lt;/strong&gt; at your job. &lt;em&gt;"I know DynamoDB's limitations!"&lt;/em&gt; you might say, &lt;em&gt;"and I can work around them!"&lt;/em&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  But there's a fine line between working around limitations and fighting against the fundamental nature of your tools
&lt;/h2&gt;

&lt;p&gt;Remember our Formula 1 car analogy? This is &lt;em&gt;&lt;strong&gt;exactly&lt;/strong&gt;&lt;/em&gt; like insisting on using your racing car for grocery shopping, daily commutes, and moving furniture, then spending countless hours engineering elaborate solutions to make it work. Do you really think strapping a cargo container to your F1 car is a good idea? 😅&lt;/p&gt;

&lt;p&gt;The real danger isn't just the technical debt you're accumulating - it's the cost of not exploring better solutions. While you're busy implementing complex DynamoDB workarounds, you could be leveraging purpose-built tools that solve your problems out of the box. That analytics query that took you three days to optimize in DynamoDB? It could have been a simple SQL statement in Tinybird.&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%2Fd0c15fexryjxsem8h1ga.png" 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%2Fd0c15fexryjxsem8h1ga.png" alt="These aren't the databases you're looking for" width="800" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Breaking free from this mindset requires humility and a willingness to admit that sometimes, just &lt;em&gt;sometimes&lt;/em&gt;, your favorite tool isn't the right one for the job. It means acknowledging that expertise in one technology shouldn't limit your architectural choices. Most importantly, it means remembering that our job isn't to use DynamoDB&lt;/p&gt;

&lt;h2&gt;
  
  
  Our job is to solve problems effectively
&lt;/h2&gt;

&lt;p&gt;The next time you find yourself reflexively reaching for DynamoDB, pause and ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Am I choosing this because it's the best tool for the job, or because it's the tool I know best?&lt;/li&gt;
&lt;li&gt;What would this solution look like with a different database?&lt;/li&gt;
&lt;li&gt;Does this solution need a database at all (&lt;a href="https://dev.to/aws-builders/using-aws-s3-as-a-database-17l0"&gt;S3&lt;/a&gt;, anyone?)&lt;/li&gt;
&lt;li&gt;Am I building workarounds for functionality that comes standard in other databases?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Is my comfort with DynamoDB blinding me to better alternatives?&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sometimes, the best demonstration of DynamoDB expertise is knowing when not to use it.&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%2Ft1682dvsblumujedmqht.png" 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%2Ft1682dvsblumujedmqht.png" alt="To be continued..." width="800" height="450"&gt;&lt;/a&gt;&lt;em&gt;(to be continued...)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Have you faced similar challenges with DynamoDB? How did you solve them? Share your experiences in the comments!&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Up
&lt;/h2&gt;

&lt;p&gt;The landscape of data engineering has evolved. Tools like OpenSearch (or Algolia), Redshift (or Databricks), Tinybird (or Rockset... oh, &lt;a href="https://www.linkedin.com/posts/sebastianzaklada_rockset-dynamodb-openai-activity-7210354551357349888-vnH3/" rel="noopener noreferrer"&gt;wait&lt;/a&gt;) have emerged to bridge the gap between purpose-built databases. &lt;/p&gt;

&lt;p&gt;In the upcoming article I will explore how we can leverage these specialized tools alongside DynamoDB to build robust, scalable systems without breaking the bank and spending months implementing features. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time-to-Market is king! 👑&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;This article is an independent developer guide. All views, rants and recommendations expressed are my own.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;No databases were harmed in the making of this article. Just some egos and a few AWS accounts over the years of me using DynamoDB.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I took this article as an excuse to dive into my beloved World of Words&lt;/em&gt; 😄&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Gosh, I never realized how much I missed writing...&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dynamodb</category>
      <category>aws</category>
      <category>database</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Improving Tinybird DevEx 🚀 Data Re-Population CLI Tool</title>
      <dc:creator>Sebastian Zakłada 🧛</dc:creator>
      <pubDate>Wed, 20 Nov 2024 10:49:35 +0000</pubDate>
      <link>https://dev.to/sebekz/improving-tinybird-devex-data-re-population-cli-tool-229j</link>
      <guid>https://dev.to/sebekz/improving-tinybird-devex-data-re-population-cli-tool-229j</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Quote of the day&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The key isn't blindly following "best practices" - it's about &lt;strong&gt;understanding&lt;/strong&gt; your tools well enough to know exactly &lt;strong&gt;when to use them&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We continue exploring ways to improve Tinybird developer experience in the command line. In my &lt;a href="https://dev.to/sebekz/improving-tinybird-devex-creating-an-informative-cli-prompt-with-oh-my-posh-33f1"&gt;last article&lt;/a&gt;, we explored enhancing the Tinybird CLI with &lt;strong&gt;Oh My Posh&lt;/strong&gt;, creating a custom prompt that displays critical workspace and environment information. That improvement was aimed at preventing accidental deployments and keeping aware of the current terminal session context. &lt;/p&gt;

&lt;p&gt;Today, we're diving deeper into another crucial aspect of Tinybird development - managing data in your downstream data sources efficiently.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Data Management Dilemma
&lt;/h2&gt;

&lt;p&gt;Ever found yourself neck-deep in Tinybird (or any other data-centric) development, making sweeping changes to your processing pipelines and materialization logic, only to realize you need a fresh start? Any early stage feature development is prone to changing requirements and that makes data structures a moving target. &lt;/p&gt;

&lt;p&gt;Or perhaps you've encountered that heart-stopping moment when production data becomes corrupted, and you need a reliable and fast way to recover? &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%2Fx2uyvm76yb48bq40u9p4.png" 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%2Fx2uyvm76yb48bq40u9p4.png" alt="Data corruption" width="700" height="301"&gt;&lt;/a&gt;&lt;em&gt;Can you guess my age from this image...?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I've been there, and today I'll share how we've tackled these challenges at my &lt;a href="https://esub.com/" rel="noopener noreferrer"&gt;eSUB&lt;/a&gt; mothership.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Can Trigger a Full Refresh
&lt;/h2&gt;

&lt;p&gt;Analytics platforms such as Tinybird, where you ingest and transform data to produce materialized views tuned for best performance and cost efficiency, they all come with their own sets of challenges. During rapid development cycles, you might find yourself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Making changes to pipelines&lt;/li&gt;
&lt;li&gt;Implementing major shifts in business logic &lt;/li&gt;
&lt;li&gt;Recovering from unexpected data corruption in an environment &lt;/li&gt;
&lt;li&gt;Periodically refreshing development environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of that may demand a clean slate as it's no longer possible to support new data models or changed requirements when there is breaking changes involved. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Pitfalls of Destructive Full Refreshes
&lt;/h2&gt;

&lt;p&gt;Before we get into the weeds and I can explain the tool that I've built, let me get one thing straight - full data re-builds are super bad, and &lt;strong&gt;YOU SHOULD AVOID DOING THAT AT ALL COST&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I just threw a wrench into my whole "the-best-ever-o-matic new CLI tool coming up!" sales pitch, didn't I?&lt;/em&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%2F2b1uy1un1gd5geoa1fyn.png" 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%2F2b1uy1un1gd5geoa1fyn.png" alt="Image description" width="768" height="768"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Jokes aside, and I am a jolly person, it has to be said out loud and clear - when faced with the need to refresh data, performing a destructive full refresh in &lt;strong&gt;production&lt;/strong&gt; environments carries &lt;strong&gt;significant risks&lt;/strong&gt; that should make you pause and re-consider your approach.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Service Interruption&lt;/strong&gt; - during a full refresh, your analytical queries and dashboards may become unavailable or return incomplete results, potentially impacting business operations and decision-making.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource Intensive&lt;/strong&gt; - full refreshes typically consume substantial computational resources, which lead to cost increase, performance hit of other running processes and even potentially exceeding service quotas or limits&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Consistency Challenges&lt;/strong&gt; - full refresh throws your whole system into partially inconsistent state, if it receives new real-time events during that time, that data may be lost or you risk having inconsistent states between different parts of your system as a result of downstream pipes and even other systems receiving incomplete or incorrect data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recovery Complexity&lt;/strong&gt; - if something goes wrong during the refresh you may not have an easy way to roll back and the original state could be permanently lost&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's just to name a few.&lt;/p&gt;

&lt;p&gt;Instead of destructive full refreshes, consider these alternatives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Implementing incremental updates&lt;/strong&gt; where possible. Sometimes all it takes to avoid a breaking change from happening is a little bit of thinking ahead and preparation. It's possible more often than you think.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Versioning&lt;/strong&gt; your pipes and datasets. Can't emphasize this enough. If it hits production, then it's your contract and unless you really know what you are doing (like, &lt;em&gt;really REALLY&lt;/em&gt;) making any breaking changes to an already published contract is a big no-no. Think of your Tinybird data carrying structures (pipes, data sources) as APIs. In the end, most often than not you are going to publish results of your user-facing pipes as APIs. And what do you do when you have to change a contract in the existing API? You &lt;a href="https://medium.com/apache-apisix/api-versioning-f8c662588768" rel="noopener noreferrer"&gt;version it&lt;/a&gt;. The same principle applies in Tinybird or really to any part of the system you are building.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With proper versioning you can maintain parallel data structures during transitions, empowering you with the capability to do many cool things such as&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A/B testing and canaries (NOT the birds!)&lt;/li&gt;
&lt;li&gt;super easy rollback to proven-and-tested previous versions&lt;/li&gt;
&lt;li&gt;making sure that your new contracts are stable before fully committing&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Going Against the Flow
&lt;/h2&gt;

&lt;p&gt;You must be pretty confused at this point, asking yourself the question... why building the tool?! After all I just said that triggering full data rebuild is the last thing you should do!&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%2Fhaqxdqmkym6nkeqonk2h.png" 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%2Fhaqxdqmkym6nkeqonk2h.png" alt="Going against the flow" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Look... while I just spent a good chunk of this post telling you why full rebuilds are generally a terrible idea (and they really are!), sometimes you just need the right tool for the job. You know what they say about rules being made to be broken? Well, not exactly broken, but more like... carefully bent when you really know what you're doing! 😉&lt;/p&gt;

&lt;p&gt;Sometimes you DO need that nuclear option - maybe you're in development, testing some wild new ideas, or dealing with a situation where a full rebuild is actually the safest bet. The key isn't blindly following "best practices" - it's about &lt;strong&gt;understanding&lt;/strong&gt; your tools well enough to know exactly &lt;strong&gt;when to use them&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;And hey, that's exactly why I built this tool - for those specific situations where a full rebuild is exactly what the doctor has prescribed! &lt;/p&gt;

&lt;h2&gt;
  
  
  The Manual Console-driven Method
&lt;/h2&gt;

&lt;p&gt;For simple use-case scenarios where all you need is trashing data in a single data source it's perfectly fine to trigger rebuilds from the Tinybird Console as all it takes is just a few clicks to get data flowing again. &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%2Fqhfbw4m524b1ei4etuyv.png" 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%2Fqhfbw4m524b1ei4etuyv.png" alt="TB Console - Truncate" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But let's be real - when you're dealing with anything but a sample app, you will be dealing with dozens if not hundreds of data sources and a complex web of dependencies. Clicking through the UI becomes a pain real quick. You also need to consider orchestrating the order of operations as you definitely don't want to refresh that downstream pipe before its dependencies are ready, right?&lt;/p&gt;

&lt;p&gt;With more than just a handful of pipes and data sources present in a project these scenarios can quickly turn into time-consuming ordeals. Lack of proper tooling can turn into a significant operational burden and before you know it, you're spending half your day just clicking through the UI or trying to remember which pipe needs to be refreshed first. Been there, done that, got the t-shirt!&lt;/p&gt;

&lt;p&gt;There's an old engineering wisdom that states: if you find yourself doing something manually more than twice, it's time to automate it. This principle became crystal clear when I was recently working on Tinybird implementation, where I found myself repeatedly resetting data sources during rapid prototyping stages. The first time, it was a novelty. The second time, it felt like &lt;em&gt;déjà vu&lt;/em&gt;. By the third time, it was clear&lt;/p&gt;

&lt;h2&gt;
  
  
  I Needed Automation
&lt;/h2&gt;

&lt;p&gt;That's when the data re-population script was born - &lt;strong&gt;forged&lt;/strong&gt; from the necessity of automation, &lt;strong&gt;refined and polished&lt;/strong&gt; through repeated use.&lt;/p&gt;

&lt;p&gt;One tool to bring them all and in the darkness bind them 😉😅&lt;/p&gt;

&lt;p&gt;Utilizing the native Tinybird CLI capability to manage pipes and data sources from the command line, I crafted a specialized script that handled truncating and repopulating Tinybird data sources in a carefully orchestrated pattern.&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%2Flyd5raswqos3rkm7m8o3.png" 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%2Flyd5raswqos3rkm7m8o3.png" alt="My preciousss..." width="640" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My preciousss... 💍 &lt;/p&gt;

&lt;h2&gt;
  
  
  Let's Dive In
&lt;/h2&gt;

&lt;p&gt;After acknowledging all the risks and considerations we discussed earlier, I am giving you a tool that transforms what could be a hazardous operation into a controlled, deliberate process. Think of it as your "break glass in case of emergency" tool - but one that actually knows what it's doing! &lt;/p&gt;

&lt;p&gt;The tool provides two core capabilities, each wrapped in multiple layers of safety controls. First, it manages your data sources through selective truncation based on configurable rules, with built-in protection for critical sources through prefix-based exclusions. Second, it handles orchestrated repopulation using a three-phase strategy that respects data dependencies: starting with core reference data, moving through standard operational pipes, and finishing with dependent calculations (that part you should adjust to match your use-case).&lt;/p&gt;

&lt;p&gt;Getting started is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Clone the repository&lt;/span&gt;
git clone https://github.com/sebekz/tinybird-devex-plus.git

&lt;span class="c"&gt;# Navigate to script location&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;tinybird-devex-plus/tinybird/scripts

&lt;span class="c"&gt;# Make it executable (Unix/Linux/macOS)&lt;/span&gt;
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x repopulateAllDataSources.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also pull only the script from the &lt;a href="https://github.com/sebekz/tinybird-devex-plus/blob/main/tinybird/scripts/repopulateAllDataSources.sh" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt; both approaches are fine.&lt;/p&gt;

&lt;p&gt;One must not forget about all the required &lt;strong&gt;dependencies&lt;/strong&gt;. You'll need the following tools installed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.tinybird.co/docs/cli/install" rel="noopener noreferrer"&gt;Tinybird CLI&lt;/a&gt; - obviously... &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/bobbyiliev/introduction-to-bash-scripting/blob/main/ebook/en/content/018-working-with-json-in-bash-using-jq.md" rel="noopener noreferrer"&gt;jq&lt;/a&gt; - for all the JSON parsing that the script is doing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once it's done you are ready to run it 💥!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Usage:       . scripts/repopulateAllDataSources.sh [COMMAND]&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# Commands:&lt;/span&gt;
&lt;span class="c"&gt;#              dryrun     - Performs a dry run without mutating any data sources&lt;/span&gt;
&lt;span class="c"&gt;#              repopulate - DESTRUCTIVE OPERATION! Truncates and populates &lt;/span&gt;
&lt;span class="c"&gt;#                           matching data sources&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# Example:     . scripts/repopulateAllDataSources.sh dryrun&lt;/span&gt;
&lt;span class="c"&gt;#              . scripts/repopulateAllDataSources.sh repopulate&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# Note:        You must be in /tinybird folder for this script to be able to &lt;/span&gt;
&lt;span class="c"&gt;#              use tb authentication details&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But wait... didn't we forget about something?&lt;/p&gt;

&lt;h2&gt;
  
  
  Safety First: The Art of Not Breaking Things
&lt;/h2&gt;

&lt;p&gt;Remember how we talked about the &lt;strong&gt;risks of full rebuilds&lt;/strong&gt;? Remember that scene in Jurassic Park where Samuel L. Jackson's character says "Hold onto your butts" before rebooting the park's systems? Well, data repopulation isn't quite as dramatic, but it deserves the same level of respect.&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%2F564omc7tijz2nrtoaizh.png" 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%2F564omc7tijz2nrtoaizh.png" alt="Hold onto" width="600" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I added a bunch of extra safety measures ensuring, that anytime you run the script, you must turn all the required safety keys first.&lt;/p&gt;

&lt;p&gt;By default, this script protects your critical data sources - landing sources (with &lt;code&gt;source_&lt;/code&gt; prefix), operational data (&lt;code&gt;ops_&lt;/code&gt; prefix), and snapshots (&lt;code&gt;snapshot_&lt;/code&gt; prefix) remain untouched. These particular prefixes resulted from the use-case scenarios that I was working on, yours may and most likely will be different so feel free to adjust that part to your liking!&lt;/p&gt;

&lt;p&gt;Protection goes beyond just prefixes. The tool implements what I like to call "progressive do-not-break-everything safety" - you need to explicitly confirm your intentions at several checkpoints before any action is taken.&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%2F2gc90zuqalgmux898lwt.png" 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%2F2gc90zuqalgmux898lwt.png" alt="Safety script measures" width="800" height="277"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The script enforces several safety measures:&lt;/p&gt;

&lt;p&gt;🔑 Mandatory workspace verification before execution&lt;br&gt;
🔑 Configuration review prompts that demand explicit confirmation&lt;br&gt;
🔑 Dry run capability for risk-free testing&lt;br&gt;
🔑 Final Warning and confirmation&lt;br&gt;
🔑 Built-in exclusions for critical data sources&lt;/p&gt;

&lt;p&gt;Each step requires explicit &lt;strong&gt;"y"&lt;/strong&gt; or &lt;strong&gt;"Y"&lt;/strong&gt; confirmation. Any other response (including empty) &lt;u&gt;aborts&lt;/u&gt; the operation.&lt;/p&gt;

&lt;p&gt;You can see that it's not just a simple reset button - it provides a &lt;strong&gt;safe&lt;/strong&gt; and configurable way for dropping nukes on your data. Think of it as a "reset button with guardrails". Safety is enforced through:&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%2F4anspwugsjqmuw9gubc2.png" 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%2F4anspwugsjqmuw9gubc2.png" alt="Turn safety keys to ON position" width="400" height="258"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;Run script without any parameters to see all the available commands.&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%2F91pgwm73l30hl5k296q3.png" 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%2F91pgwm73l30hl5k296q3.png" alt="No parameters" width="800" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;YES. I have OCD when it comes to nice looking tools. Can't help myself with all the colors, formatting, etc. Some call it an utter waste of time, but I find that good looking tool or good-looking code is just better - as you put more thought to how it looks you end up putting more thought into how it WORKS.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I guess that's a good topic for a separate article&lt;/em&gt; 😊 &lt;em&gt;and I digressed again. That's also something I am good at&lt;/em&gt; 😊&lt;/p&gt;

&lt;p&gt;When you run the tool, it first walks you through a configuration review. You'll see exactly what data sources would be affected and what the execution plan looks like. It verifies your current workspace context and makes sure you're operating in the environment you intend to. &lt;/p&gt;

&lt;p&gt;As mentioned earlier - do not rush to press Y, take your time and triple check the settings.&lt;/p&gt;

&lt;p&gt;Always start with &lt;code&gt;dryrun&lt;/code&gt;. It's not just a suggestion - it's best practice. This mode lets you validate every step of the process without touching your data. Only when you're absolutely certain about the changes should you proceed with the actual repopulation.&lt;/p&gt;

&lt;p&gt;During execution, the tool orchestrates all the required steps. It first truncates the selected data sources, then executes priority pipes to establish your core data foundation. From there, it processes standard operational pipes, and finally handles any dependent calculations. This ordered approach ensures data consistency throughout the rebuild process.&lt;/p&gt;

&lt;p&gt;Here is how it looks like in action, starting with the truncation&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%2Ft5rogqwon6ykst7an33k.png" 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%2Ft5rogqwon6ykst7an33k.png" alt="Processing data sources" width="611" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;after which data sources are re-populated from their respective pipes&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%2Fnqumbfpxl2wubn8vwbz6.png" 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%2Fnqumbfpxl2wubn8vwbz6.png" alt="Image description" width="761" height="807"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The result? A tool that respects the destructive nature of data rebuilds while providing the automation we need for those rare but necessary occasions. It's not about making rebuilds easy - it's about making them safe when they're unavoidable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Room for improvement
&lt;/h2&gt;

&lt;p&gt;Like any tool born from practical needs, this one has its own wishlist for the future. Two main items are keeping me up at night (along with my usual vampire schedule 🧛):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Moving configuration to script parameters - because hardcoding configuration is so last century&lt;/li&gt;
&lt;li&gt;Migrating the code to Python - as much as I enjoy the old-school Bash routine, Python would make this tool much more maintainable and extensible&lt;/li&gt;
&lt;li&gt;Removing repetitions, shortcuts and unnecessary code - a result of iterative approach and YAGNI as soon as I got what I needed back when I needed it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'll probably not make the time to tackle any of these improvements, so if you are feeling adventurous - go for it and share! I would love to see what you were able to come up with! But for now, the current version gets the job done while keeping your data safe from accidental nuclear launches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to Next?
&lt;/h2&gt;

&lt;p&gt;In the upcoming articles, I will explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tinybird Console Pro Tips&lt;/strong&gt; - unlocking the full-width view and most importantly, the dark mode - vampires like me 🧛 fear the light!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IaC Blueprint&lt;/strong&gt; - structuring your Tinybird projects like a pro&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-tenancy Guide&lt;/strong&gt; - implementing secure and scalable multi-tenant analytics with Tinybird and AWS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production Checklist&lt;/strong&gt; - everything you need for &lt;em&gt;truly&lt;/em&gt; production-ready analytics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB with Tinybird&lt;/strong&gt; - tips and tricks learned when working on a real-world implementation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node.js SDK&lt;/strong&gt; - Tinybird SDK for Node.js that you never knew you needed!&lt;/li&gt;
&lt;li&gt;and many more!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stay tuned!&lt;/p&gt;

&lt;h2&gt;
  
  
  Enjoying this content? 🧛
&lt;/h2&gt;

&lt;p&gt;If you found this article helpful, consider &lt;a href="https://dev.to/sebekz"&gt;following me&lt;/a&gt; for more similarly engaging posts! &lt;/p&gt;

&lt;h3&gt;
  
  
  Note
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;I'd love to hear your thoughts, suggestions, or even gentle&lt;/em&gt; 😄 &lt;em&gt;corrections - don't hesitate to drop a comment.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Your feedback, whether it's a nod of approval or pointing out areas for improvement, will help me craft better content in the future!&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Disclaimer
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;This article is an independent developer guide. I am not affiliated with, sponsored by, or officially connected to Tinybird in any way. All views and recommendations expressed are my own.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;While these customizations are generally safe, if your coding adventures somehow result in your workstation spontaneously combusting, I must respectfully decline any responsibility for damage to you or your property. However, as a courtesy to your friendly &lt;a href="https://www.linkedin.com/in/sebastianzaklada/" rel="noopener noreferrer"&gt;Engineering Vampire&lt;/a&gt;&lt;/em&gt; 🧛 &lt;em&gt;please do give advance notice of any potential... mishaps. It would be terribly wasteful to let good blood&lt;/em&gt; 🩸 &lt;em&gt;go to waste. Just saying!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>tinybird</category>
      <category>devex</category>
      <category>cli</category>
      <category>engineeringvampire</category>
    </item>
    <item>
      <title>Improving Tinybird DevEx 🚀 Creating an Informative CLI Prompt with Oh My Posh</title>
      <dc:creator>Sebastian Zakłada 🧛</dc:creator>
      <pubDate>Tue, 19 Nov 2024 13:34:28 +0000</pubDate>
      <link>https://dev.to/sebekz/improving-tinybird-devex-creating-an-informative-cli-prompt-with-oh-my-posh-33f1</link>
      <guid>https://dev.to/sebekz/improving-tinybird-devex-creating-an-informative-cli-prompt-with-oh-my-posh-33f1</guid>
      <description>&lt;p&gt;If you are working with Tinybird regularly, you know how important it is to stay aware of your current workspace and environment. Instead of running multiple commands to check these, wouldn't it be great to have this information right in your prompt? &lt;/p&gt;

&lt;p&gt;In this guide, I'll help you to enhance your CLI experience by creating a custom &lt;strong&gt;Oh My Posh&lt;/strong&gt; prompt that displays Tinybird-specific information.&lt;/p&gt;

&lt;p&gt;Here is how your customized prompt will look once configured:&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%2F79bnz3kxnwu5tugjrhsb.png" 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%2F79bnz3kxnwu5tugjrhsb.png" alt="Oh My Posh final prompt" width="800" height="52"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Shortcut:&lt;/strong&gt; If Tinybird and Oh My Posh are not news to you, feel free to skip directly to here for information on how to add Tinybird to your existing prompt.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Tinybird? Never heard of it before!
&lt;/h2&gt;

&lt;p&gt;Let me fix that for you right away! 😄 &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.tinybird.co/" rel="noopener noreferrer"&gt;Tinybird&lt;/a&gt; is a real-time data platform that lets developers build SQL-based APIs for working with massive datasets. It builds on ClickHouse, which is widely recognized as one of the fastest analytical databases for real-time data processing workloads. &lt;/p&gt;

&lt;p&gt;While traditional data warehouses like Redshift excel at batch analytics and BI workloads - think of them as freight trains hauling massive amounts of data - Tinybird is more like a high-speed maglev train, optimized for real-time, user-facing analytics that require instant responses.&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%2F4lu4bakwxzk7mfvx8lxe.png" 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%2F4lu4bakwxzk7mfvx8lxe.png" alt="Maglev Train" width="644" height="746"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When building data products with Tinybird, you'll typically work across multiple contexts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Different workspaces for various projects&lt;/li&gt;
&lt;li&gt;Multiple environments (development, staging, production)&lt;/li&gt;
&lt;li&gt;Distinct configurations and data pipelines per context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Managing these different contexts efficiently is crucial for a smooth development workflow. An informative CLI prompt becomes invaluable here - it helps you maintain awareness of your current workspace and environment without repeatedly running status-checking commands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Customize Your Prompt?
&lt;/h2&gt;

&lt;p&gt;As developers, we spend significant time in the terminal. When working with Tinybird, you need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check which workspace you're in&lt;/li&gt;
&lt;li&gt;Verify your current environment&lt;/li&gt;
&lt;li&gt;Ensure you're using the right AWS account&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How often have you run a &lt;code&gt;tb&lt;/code&gt; command only to realize you were in the wrong workspace? Or deployed to production (I know, I know... it was &lt;em&gt;"someone else"&lt;/em&gt; on the team... right? 😉) while your AWS credentials were pointing to a different account? &lt;/p&gt;

&lt;p&gt;Having this information directly in your prompt eliminates these "oops" moments and helps prevent mistakes like accidentally pushing to the wrong workspace.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We'll set up &lt;a href="https://ohmyposh.dev/" rel="noopener noreferrer"&gt;Oh My Posh&lt;/a&gt; to display Tinybird information in your prompt&lt;/li&gt;
&lt;li&gt;You'll see your current workspace, environment, and other helpful information at a glance&lt;/li&gt;
&lt;li&gt;This works with bash, zsh, and PowerShell&lt;/li&gt;
&lt;li&gt;The configuration is also fully customizable to your needs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You'll need the following tools installed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.tinybird.co/docs/cli/install" rel="noopener noreferrer"&gt;Tinybird CLI&lt;/a&gt; - configured and ready to use&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ohmyposh.dev/docs/installation/windows" rel="noopener noreferrer"&gt;Oh My Posh&lt;/a&gt; - for prompt customization&lt;/li&gt;
&lt;li&gt;A terminal that supports Unicode characters like the one bundled with Visual Studio Code&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ohmyposh.dev/docs/installation/fonts#nerd-fonts" rel="noopener noreferrer"&gt;Nerd Font&lt;/a&gt; - required for icons&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting Up Oh My Posh
&lt;/h2&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%2Ffc8jbj54240kt2rcsdf7.png" 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%2Ffc8jbj54240kt2rcsdf7.png" alt="oh-my-posh" width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before diving into Tinybird-specific customizations, let's set up Oh My Posh correctly. The installation process varies depending on your operating system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Windows Installation
&lt;/h3&gt;

&lt;p&gt;If you're using Windows, you have several installation options:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Option 1: Using winget (Recommended)&lt;/span&gt;
winget &lt;span class="nb"&gt;install &lt;/span&gt;JanDeDobbeleer.OhMyPosh

&lt;span class="c"&gt;# Option 2: Using Scoop&lt;/span&gt;
scoop &lt;span class="nb"&gt;install &lt;/span&gt;https://github.com/JanDeDobbeleer/oh-my-posh/releases/latest/download/oh-my-posh.json

&lt;span class="c"&gt;# Option 3: Using Chocolatey&lt;/span&gt;
choco &lt;span class="nb"&gt;install &lt;/span&gt;oh-my-posh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  macOS Installation
&lt;/h3&gt;

&lt;p&gt;For macOS users:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Option 1: Using Homebrew (Recommended)&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;jandedobbeleer/oh-my-posh/oh-my-posh

&lt;span class="c"&gt;# Option 2: Using MacPorts&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;port selfupdate
&lt;span class="nb"&gt;sudo &lt;/span&gt;port &lt;span class="nb"&gt;install &lt;/span&gt;oh-my-posh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Linux Installation
&lt;/h3&gt;

&lt;p&gt;For Linux systems:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Option 1: Using curl (Universal)&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://ohmyposh.dev/install.sh | bash &lt;span class="nt"&gt;-s&lt;/span&gt;

&lt;span class="c"&gt;# Option 2: Using homebrew&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;jandedobbeleer/oh-my-posh/oh-my-posh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For detailed installation instructions tailored to your specific setup and additional configuration options, consult the &lt;a href="https://ohmyposh.dev/docs/installation/linux" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;. The docs cover all platforms and special cases like WSL environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Nerd Fonts
&lt;/h2&gt;

&lt;p&gt;Oh My Posh needs a &lt;a href="https://ohmyposh.dev/docs/installation/fonts#nerd-fonts" rel="noopener noreferrer"&gt;Nerd Font&lt;/a&gt; to display icons correctly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nerd Fonts&lt;/strong&gt; are modified versions of popular programming fonts that add a vast collection of icons (glyphs) to the original font. Think of them as regular programming fonts supercharged with extra symbols - like the Git branch icon, folder symbols 📂, or platform-specific icons.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;Oh My Posh has a CLI to help you select and install a Nerd Font:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# To install the recommended font&lt;/span&gt;
oh-my-posh font &lt;span class="nb"&gt;install &lt;/span&gt;meslo

&lt;span class="c"&gt;# For manual font selection&lt;/span&gt;
oh-my-posh font &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installing a Nerd Font, you'll need to configure your terminal to use it. The configuration process varies depending on your terminal emulator.&lt;/p&gt;

&lt;p&gt;Remember that each terminal maintains its own font settings. Configuring VS Code integrated terminal will not affect your standalone terminal emulator, and vice versa. For detailed configuration steps for each terminal type, refer to the official Oh My Posh &lt;a href="https://ohmyposh.dev/docs/installation/fonts#configuration" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's how to set it up in VS Code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;settings.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"terminal.integrated.fontFamily"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MesloLGM Nerd Font"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Optional:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;enable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;font&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ligatures&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"terminal.integrated.fontLigatures"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;a id="shortcut"&gt;&lt;/a&gt; Creating the Tinybird Prompt Segment
&lt;/h2&gt;

&lt;p&gt;To display Tinybird workspace information in your prompt, we'll create a custom segment that integrates with Oh My Posh. This involves two steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creating a script to fetch Tinybird information&lt;/li&gt;
&lt;li&gt;Adding the segment to your Oh My Posh configuration&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 1: Create the Tinybird Status Script
&lt;/h3&gt;

&lt;p&gt;Create a new file called &lt;code&gt;tb-prompt.sh&lt;/code&gt; in your &lt;code&gt;.aws&lt;/code&gt; directory. On different systems, this will be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Windows:&lt;/strong&gt; &lt;code&gt;C:\Users\YOUR_USERNAME\.aws\tb-prompt.sh&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mac/Linux:&lt;/strong&gt; &lt;code&gt;~/.aws/tb-prompt.sh&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add the following content to the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;".tinyb"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then        
    &lt;/span&gt;&lt;span class="nv"&gt;branch_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'"name":'&lt;/span&gt; .tinyb | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; : &lt;span class="nt"&gt;-f&lt;/span&gt; 2 | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'"'&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 2&lt;span class="sb"&gt;`&lt;/span&gt;
    &lt;span class="nv"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'"host":'&lt;/span&gt; .tinyb | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; / &lt;span class="nt"&gt;-f&lt;/span&gt; 3 | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 2 | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; : &lt;span class="nt"&gt;-f&lt;/span&gt; 1&lt;span class="sb"&gt;`&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$region&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tinybird"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nv"&gt;region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'"host":'&lt;/span&gt; .tinyb | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; / &lt;span class="nt"&gt;-f&lt;/span&gt; 3 | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 1&lt;span class="sb"&gt;`&lt;/span&gt;
    &lt;span class="k"&gt;fi    

    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"tb:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;branch_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make the script executable (Mac/Linux only):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x ~/.aws/tb-prompt.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checks for a &lt;code&gt;.tinyb&lt;/code&gt; configuration file in the current directory&lt;/li&gt;
&lt;li&gt;Extracts the branch name and region information&lt;/li&gt;
&lt;li&gt;Outputs the branch name in the format &lt;code&gt;tb:branch_name&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2: Configure Oh My Posh
&lt;/h3&gt;

&lt;p&gt;Add the Tinybird segment to your Oh My Posh configuration by including this segment configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"style"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"powerline"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"powerline_symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;e0b0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"foreground"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#00b7ff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"background"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#193549"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"shell"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"script"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"~/.aws/tb-prompt.sh"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"template"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;" &lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;f1c0 {{ .Output }} "&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runs the &lt;code&gt;tb-prompt.sh&lt;/code&gt; script to get Tinybird information&lt;/li&gt;
&lt;li&gt;Uses a database icon (&lt;code&gt;\uf1c0&lt;/code&gt;) from Nerd Fonts&lt;/li&gt;
&lt;li&gt;Displays the output in a clean format with distinctive colors&lt;/li&gt;
&lt;li&gt;Integrates with the powerline style of the prompt&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The segment will only show Tinybird information when you're in a directory containing a &lt;code&gt;.tinyb&lt;/code&gt; configuration file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's look at a complete Oh My Posh configuration that combines some of the tools a developer may need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS Profile &amp;amp; Region&lt;/strong&gt; - shows your current AWS credentials context&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment Stage&lt;/strong&gt; - displays the current stage (like dev/prod)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node.js Version&lt;/strong&gt; - shows Node version and package manager
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Current Path&lt;/strong&gt; - displays your working directory&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Git Status&lt;/strong&gt; - shows branch, changes, stash count, and more&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tinybird Workspace&lt;/strong&gt; - displays current Tinybird workspace (from our script)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NET Version&lt;/strong&gt; - shows the .NET SDK version if applicable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Root Indicator&lt;/strong&gt; - shows when you're running as administrator/root&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution Time&lt;/strong&gt; - displays how long the last command took&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exit Code&lt;/strong&gt; - shows success/failure of the last command&lt;/li&gt;
&lt;/ul&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%2F79bnz3kxnwu5tugjrhsb.png" 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%2F79bnz3kxnwu5tugjrhsb.png" alt="Oh My Posh final prompt" width="800" height="52"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Save this as &lt;code&gt;~/.ohmyposh.json&lt;/code&gt; (or your preferred config location) and customize the colors, symbols, and segments to match your preferences:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"blocks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"alignment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"left"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"segments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"background"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#FFA400"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"foreground"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#193549"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"leading_diamond"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;e0b6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"powerline_symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;e0b0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"style"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"diamond"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"template"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;" {{ .Profile }}{{ if .Region }}@{{ .Region }}{{ end }} "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"trailing_diamond"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;e0b0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aws"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"style"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"powerline"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"powerline_symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;E0B0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"foreground"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#ffffff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"background"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#f792c1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"template"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{{ if eq &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;devtest&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; .Env.STAGE }} {{ else }} &lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;f635 {{ .Env.STAGE }} {{ end }}"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"style"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"powerline"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"powerline_symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;E0B0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"foreground"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#ffffff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"background"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#6CA35E"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"template"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{{ if .PackageManagerIcon }}{{ .PackageManagerIcon }} {{ end }}{{ .Full }} "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"fetch_package_manager"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"background"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#ff479c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"foreground"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#ffffff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"powerline_symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;e0b0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"style"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"folder"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"style"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"powerline"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"template"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;" &lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;e5ff {{ .Path }} "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"path"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"background"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#fffb38"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"foreground"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#193549"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"powerline_symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;e0b0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"fetch_stash_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"fetch_status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"fetch_upstream_icon"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"style"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"powerline"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"template"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;" {{ .HEAD }}{{if .BranchStatus }} {{ .BranchStatus }}{{ end }}{{ if .Working.Changed }} &lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;f044 {{ .Working.String }}{{ end }}{{ if and (.Staging.Changed) (.Working.Changed) }} |{{ end }}{{ if .Staging.Changed }} &lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;f046 {{ .Staging.String }}{{ end }}{{ if gt .StashCount 0}} &lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;f692 {{ .StashCount }}{{ end }}{{ if gt .WorktreeCount 0}} &lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;f1bb {{ .WorktreeCount }}{{ end }} "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"git"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"style"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"powerline"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"powerline_symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;e0b0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"foreground"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#00b7ff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"background"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#193549"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"shell"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"script"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"~/.aws/tb-prompt.sh"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"template"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;" &lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;f1c0 {{ .Output }} "&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"background"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#6CA35E"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"foreground"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#ffffff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"powerline_symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;e0b0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"fetch_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"style"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"powerline"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"template"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;" &lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;e70c {{ if .Unsupported }}&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;f071{{ else }}{{ .Full }}{{ end }} "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dotnet"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"background"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#ffff66"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"foreground"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#ffffff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"powerline_symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;e0b0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"style"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"powerline"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"template"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;" &lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;f0e7 "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"root"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"background"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#8800dd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"foreground"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#ffffff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"powerline_symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;e0b0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"style"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"austin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"threshold"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"style"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"powerline"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"template"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;" &amp;lt;#fefefe&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;fbab&amp;lt;/&amp;gt; {{ .FormattedMs }} "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"executiontime"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"background"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#2e9599"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"background_templates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"{{ if gt .Code 0 }}#f1184c{{ end }}"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"foreground"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#ffffff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"powerline_symbol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;e0b0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"always_enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"style"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"powerline"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"template"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;" {{ if gt .Code 0 }}&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;f00d{{ else }}&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;f42e{{ end }} "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"trailing_diamond"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;e0b4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"exit"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prompt"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"final_space"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Impact of a Better Prompt
&lt;/h2&gt;

&lt;p&gt;Configuring your command prompt goes beyond aesthetics - it's about developer productivity and safety. Having instant visibility into your workspace and environment status helps prevent common mistakes and speeds up your workflow.&lt;/p&gt;

&lt;p&gt;It's about making your daily work smoother and safer. &lt;/p&gt;

&lt;p&gt;Think of it as your development cockpit - just as pilots rely on their (super cool!) instruments, developers need clear visibility of their working context. This becomes especially crucial when working with multiple Tinybird workspaces and AWS environments.&lt;/p&gt;

&lt;p&gt;With instant visibility into your workspace and environment status, you can confidently avoid those costly mistakes that happen when context isn't clear.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to Next?
&lt;/h2&gt;

&lt;p&gt;This is just the beginning of improving your Tinybird development experience. In the upcoming articles, I will explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tinybird Console Pro Tips&lt;/strong&gt; - unlocking the full-width view and most importantly, the dark mode - vampires like me 🧛 fear the light!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IaC Blueprint&lt;/strong&gt; - structuring your Tinybird projects like a pro&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-tenancy Guide&lt;/strong&gt; - implementing secure and scalable multi-tenant analytics with Tinybird and AWS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production Checklist&lt;/strong&gt; - everything you need for &lt;em&gt;truly&lt;/em&gt; production-ready analytics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node.js SDK&lt;/strong&gt; - Tinybird SDK for Node.js that you never knew you needed!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Re-population CLI Tool&lt;/strong&gt; - for efficiently managing and reloading your datasets&lt;/li&gt;
&lt;li&gt;and many more!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stay tuned!&lt;/p&gt;

&lt;h3&gt;
  
  
  Note
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;This is my first technical writeup in quite a while (I usually write about craft beer, and in my native language at that!). I'd love to hear your thoughts, suggestions, or even gentle&lt;/em&gt; 😄 &lt;em&gt;corrections - don't hesitate to drop a comment.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Your feedback, whether it's a nod of approval or pointing out areas for improvement, will help me craft better content in the future!&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Disclaimer
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;This article is an independent developer guide. I am not affiliated with, sponsored by, or officially connected to Tinybird in any way. All views and recommendations expressed are my own.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;While these customizations are generally safe, if your coding adventures somehow result in your workstation spontaneously combusting, I must respectfully decline any responsibility for damage to you or your property. However, as a courtesy to your friendly &lt;a href="https://www.linkedin.com/in/sebastianzaklada/" rel="noopener noreferrer"&gt;Engineering Vampire&lt;/a&gt;&lt;/em&gt; 🧛 &lt;em&gt;please do give advance notice of any potential... mishaps. It would be terribly wasteful to let good blood&lt;/em&gt; 🩸 &lt;em&gt;go to waste. Just saying!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>tinybird</category>
      <category>devex</category>
      <category>ohmyposh</category>
      <category>engineeringvampire</category>
    </item>
  </channel>
</rss>
