<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Danilo Assis</title>
    <description>The latest articles on Forem by Danilo Assis (@daniloab).</description>
    <link>https://forem.com/daniloab</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%2F254653%2F7ddeaaa0-1804-4c56-9b97-75a8aa74b2c2.png</url>
      <title>Forem: Danilo Assis</title>
      <link>https://forem.com/daniloab</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/daniloab"/>
    <language>en</language>
    <item>
      <title>Ship Every Day: Progress is a Puzzle Made Piece by Piece</title>
      <dc:creator>Danilo Assis</dc:creator>
      <pubDate>Tue, 10 Mar 2026 12:50:33 +0000</pubDate>
      <link>https://forem.com/daniloab/ship-every-day-progress-is-a-puzzle-made-piece-by-piece-35ml</link>
      <guid>https://forem.com/daniloab/ship-every-day-progress-is-a-puzzle-made-piece-by-piece-35ml</guid>
      <description>&lt;p&gt;&lt;em&gt;Core Leadership Principle #3&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;When I was junior, I watched companies and teams measure success by big releases. The quarterly launch. The major version. The dramatic all-hands demo.&lt;/p&gt;

&lt;p&gt;Everyone celebrated those moments. But between them? Weeks of invisible work. Teams grinding away, not shipping anything, wondering if they were making progress.&lt;/p&gt;

&lt;p&gt;I followed that pattern early on. Work on something for weeks, ship nothing, then finally push everything at once. It felt normal. That's what I saw everywhere.&lt;/p&gt;

&lt;p&gt;Then I joined a team that worked differently. Developers were constantly shipping. Not always full features. Sometimes just a test. Or documentation. Or a bug fix. But every single day, something tangible moved forward.&lt;/p&gt;

&lt;p&gt;I asked one of them how they maintained that pace. His answer changed everything:&lt;/p&gt;

&lt;p&gt;"Shipping to production is a puzzle. You can't place all the pieces at once. But you can place one piece every day. Eventually, you complete the picture."&lt;/p&gt;

&lt;p&gt;That's when I learned: &lt;strong&gt;there is no day where you don't make progress.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What "Ship Every Day" Actually Means
&lt;/h2&gt;

&lt;p&gt;This isn't about posting in Slack: "Here's what I did today!"&lt;/p&gt;

&lt;p&gt;It's not a reporting requirement. It's a mindset.&lt;/p&gt;

&lt;p&gt;Every single day, you should be able to answer three questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;What did I ship today?&lt;/strong&gt; (Code, test, fix, documentation, unblocked issue)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What am I improving today?&lt;/strong&gt; (Skill, process, codebase, understanding)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What's my next piece of the puzzle?&lt;/strong&gt; (Next concrete step)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you can't answer these, you didn't have a productive day.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Puzzle Mindset
&lt;/h2&gt;

&lt;p&gt;Here's what changed for me: I stopped thinking in "features" and started thinking in "pieces."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Old thinking:&lt;/strong&gt;&lt;br&gt;
"I'm working on the authentication system. It'll be done in two weeks."&lt;/p&gt;

&lt;p&gt;Then two weeks pass. Authentication isn't done. Feels like wasted time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New thinking:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monday: Shipped the JWT token generation logic with tests&lt;/li&gt;
&lt;li&gt;Tuesday: Implemented token refresh flow&lt;/li&gt;
&lt;li&gt;Wednesday: Fixed edge case with expired tokens&lt;/li&gt;
&lt;li&gt;Thursday: Added monitoring for auth failures&lt;/li&gt;
&lt;li&gt;Friday: Documented the auth flow for the team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same two weeks. But now I have five concrete things I shipped. Even if "authentication system" isn't 100% done, I made real progress every single day.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Counts as Shipping
&lt;/h2&gt;

&lt;p&gt;You don't need to push to production every day. But you need to complete something tangible:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Merged a PR (even a small one)&lt;/li&gt;
&lt;li&gt;Fixed a bug&lt;/li&gt;
&lt;li&gt;Wrote tests for existing code&lt;/li&gt;
&lt;li&gt;Refactored a confusing module&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Not Code:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identified root cause of a production issue and documented it&lt;/li&gt;
&lt;li&gt;Wrote an RFC for a technical decision&lt;/li&gt;
&lt;li&gt;Updated documentation that was wrong&lt;/li&gt;
&lt;li&gt;Unblocked a teammate by answering their question thoroughly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Progress:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Researched an approach and ruled it out (with evidence)&lt;/li&gt;
&lt;li&gt;Set up monitoring for a feature that didn't have it&lt;/li&gt;
&lt;li&gt;Improved CI/CD pipeline&lt;/li&gt;
&lt;li&gt;Created a runbook for on-call&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these count. They all move the puzzle forward.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Doesn't Count
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;"I worked on stuff today."&lt;/strong&gt;&lt;br&gt;
Too vague. What stuff? What's done now that wasn't done before?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Still working on that feature from last week."&lt;/strong&gt;&lt;br&gt;
What part? What's complete? What's next?&lt;/p&gt;

&lt;p&gt;If you go three days without shipping something tangible, you're stuck. Reset your approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Daily Questions
&lt;/h2&gt;

&lt;p&gt;Every evening, I ask myself:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. What did I ship today?
&lt;/h3&gt;

&lt;p&gt;Good answer: "I fixed the CSS bug blocking the UI team, wrote documentation for the new API endpoint, and got DBA approval for the database migration."&lt;/p&gt;

&lt;p&gt;Bad answer: "Worked on the migration."&lt;/p&gt;

&lt;h3&gt;
  
  
  2. What am I improving today?
&lt;/h3&gt;

&lt;p&gt;Good answer: "Learned how PostgreSQL handles concurrent updates - wrote notes on it."&lt;/p&gt;

&lt;p&gt;Bad answer: "Read some docs."&lt;/p&gt;

&lt;h3&gt;
  
  
  3. What's my next piece?
&lt;/h3&gt;

&lt;p&gt;Good answer: "Tomorrow I'm implementing the rollback plan for the migration."&lt;/p&gt;

&lt;p&gt;Bad answer: "Continue working on migration stuff."&lt;/p&gt;

&lt;p&gt;See the difference? Concrete vs. vague.&lt;/p&gt;

&lt;h2&gt;
  
  
  Small, Mergeable PRs
&lt;/h2&gt;

&lt;p&gt;One practice that helps: keep PRs small.&lt;/p&gt;

&lt;p&gt;Target: under 400 lines when possible.&lt;/p&gt;

&lt;p&gt;Why? Because small PRs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get reviewed faster&lt;/li&gt;
&lt;li&gt;Have fewer bugs&lt;/li&gt;
&lt;li&gt;Are easier to understand&lt;/li&gt;
&lt;li&gt;Can be merged daily&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of one massive PR with your entire feature, break it into pieces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PR 1: Database schema changes&lt;/li&gt;
&lt;li&gt;PR 2: API endpoints (behind feature flag)&lt;/li&gt;
&lt;li&gt;PR 3: Frontend integration&lt;/li&gt;
&lt;li&gt;PR 4: Tests&lt;/li&gt;
&lt;li&gt;PR 5: Enable feature flag&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you're shipping something every day instead of waiting two weeks to ship everything at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature Flags Are Your Friend
&lt;/h2&gt;

&lt;p&gt;Here's the trick: you can ship code to production even if the feature isn't ready.&lt;/p&gt;

&lt;p&gt;Use feature flags.&lt;/p&gt;

&lt;p&gt;Ship the authentication module but keep it disabled. Ship the new API endpoint but don't expose it yet. Ship the UI but hide it behind a flag.&lt;/p&gt;

&lt;p&gt;This lets you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploy daily&lt;/li&gt;
&lt;li&gt;Test in production (with the flag off)&lt;/li&gt;
&lt;li&gt;Enable gradually when ready&lt;/li&gt;
&lt;li&gt;Roll back instantly if needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Shipping to production daily, even with features disabled, keeps you honest. Your code has to actually work. You can't accumulate a month of untested changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  When I Got Stuck
&lt;/h2&gt;

&lt;p&gt;I was once building a complex hospital integration. Two weeks in, I'd written a lot of code but shipped nothing to production. It "wasn't ready yet."&lt;/p&gt;

&lt;p&gt;My manager asked: "What have you shipped this week?"&lt;/p&gt;

&lt;p&gt;I said: "I'm still working on it."&lt;/p&gt;

&lt;p&gt;He said: "Can you ship any piece of it?"&lt;/p&gt;

&lt;p&gt;That question changed everything. I realized:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The data parsing logic worked - I could ship that&lt;/li&gt;
&lt;li&gt;The validation rules were solid - I could ship those&lt;/li&gt;
&lt;li&gt;The error handling was done - I could ship that&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I broke it apart. Started merging pieces daily. Put them behind a feature flag.&lt;/p&gt;

&lt;p&gt;Two weeks later, I flipped the flag on. Everything worked. Because I'd been shipping and testing pieces all along instead of waiting to ship one giant blob.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Compound Effect
&lt;/h2&gt;

&lt;p&gt;Here's what happens when you ship every day:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Week 1:&lt;/strong&gt; Feels slow. Just small pieces.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Week 4:&lt;/strong&gt; You look back and realize you shipped 20 things. Some small, some bigger. But 20 real, complete things.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Month 3:&lt;/strong&gt; Your velocity is higher than people who work in big batches. Because you're not blocked. You're not waiting. You're always moving.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Month 6:&lt;/strong&gt; You've become the person who "always gets things done." Not because you work harder. Because you work in smaller, continuous pieces.&lt;/p&gt;

&lt;h2&gt;
  
  
  This Isn't Micromanagement
&lt;/h2&gt;

&lt;p&gt;Let me be clear: this isn't about reporting to anyone.&lt;/p&gt;

&lt;p&gt;Nobody needs to know what you shipped today. This is for you.&lt;/p&gt;

&lt;p&gt;It's a way of working that keeps you honest and moving forward.&lt;/p&gt;

&lt;p&gt;Some days you'll ship a massive feature. Some days you'll ship one bug fix. Both are fine.&lt;/p&gt;

&lt;p&gt;The point is: no day ends with "I just worked on stuff."&lt;/p&gt;

&lt;h2&gt;
  
  
  The Test
&lt;/h2&gt;

&lt;p&gt;Tonight, before you stop working, answer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What did I ship today?&lt;/li&gt;
&lt;li&gt;What did I improve?&lt;/li&gt;
&lt;li&gt;What's my next piece?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you can't answer, you're drifting. Reset tomorrow.&lt;/p&gt;

&lt;p&gt;If you can answer all three, you had a productive day. Do it again tomorrow.&lt;/p&gt;

&lt;p&gt;String together enough of those days, and you'll ship more than you ever thought possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start Tomorrow
&lt;/h2&gt;

&lt;p&gt;Pick the thing you're working on right now.&lt;/p&gt;

&lt;p&gt;Ask yourself: "What one piece of this can I finish and ship tomorrow?"&lt;/p&gt;

&lt;p&gt;Not the whole thing. One piece.&lt;/p&gt;

&lt;p&gt;Then ship it.&lt;/p&gt;

&lt;p&gt;The next day, ship the next piece.&lt;/p&gt;

&lt;p&gt;That's how you build software. That's how you make progress.&lt;/p&gt;

&lt;p&gt;One piece of the puzzle at a time.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is part 3 of a 14-part series on Core Leadership Principles. Next week: AI as Assistant, Not Autopilot - how to use AI without outsourcing your thinking.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you track daily progress? What helps you maintain momentum when working on long projects?&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Follow me on &lt;a href="https://www.twitter.com/daniloab_" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;br&gt;
If you like and want to support my work &lt;a href="https://www.patreon.com/daniloab" rel="noopener noreferrer"&gt;check my patreon&lt;/a&gt;&lt;br&gt;
See more in &lt;a href="https://linktr.ee/daniloab" rel="noopener noreferrer"&gt;https://linktr.ee/daniloab&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@ttepavac?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Tanja Tepavac&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/a-piece-of-a-puzzle-with-a-missing-piece-cWMhxNmQVq0?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>leadership</category>
      <category>career</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Ownership &amp; Accountability: You Own Your Code from Concept to Production</title>
      <dc:creator>Danilo Assis</dc:creator>
      <pubDate>Thu, 19 Feb 2026 12:22:14 +0000</pubDate>
      <link>https://forem.com/daniloab/ownership-accountability-you-own-your-code-from-concept-to-production-42do</link>
      <guid>https://forem.com/daniloab/ownership-accountability-you-own-your-code-from-concept-to-production-42do</guid>
      <description>&lt;p&gt;&lt;em&gt;Core Leadership Principle #2&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;It was 2 AM. Production was down. The team was scrambling in Slack trying to figure out what broke.&lt;/p&gt;

&lt;p&gt;Someone deployed a feature that afternoon. Everything looked fine in staging. But in production, it was causing a cascade of failures across our integrations.&lt;/p&gt;

&lt;p&gt;The developer who wrote it? Offline. Asleep. Had no monitoring set up. No alerts configured. No documentation about how the feature worked or what could go wrong.&lt;/p&gt;

&lt;p&gt;We spent four hours debugging someone else's code, in the dark, with real users unable to process data.&lt;/p&gt;

&lt;p&gt;That's when I learned what ownership actually means.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Ownership Looks Like
&lt;/h2&gt;

&lt;p&gt;Ownership isn't just writing code. It's owning that code from the moment you have the idea until it's retired from production.&lt;/p&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You understand the problem you're solving&lt;/li&gt;
&lt;li&gt;You design the solution&lt;/li&gt;
&lt;li&gt;You write the code&lt;/li&gt;
&lt;li&gt;You test it&lt;/li&gt;
&lt;li&gt;You deploy it&lt;/li&gt;
&lt;li&gt;You monitor it&lt;/li&gt;
&lt;li&gt;You fix it when it breaks&lt;/li&gt;
&lt;li&gt;You improve it over time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you write code and throw it over the wall for someone else to deal with, you're not owning anything. You're just coding.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Two-Hour Rule
&lt;/h2&gt;

&lt;p&gt;Here's a pattern I see all the time:&lt;/p&gt;

&lt;p&gt;Developer hits a bug. Stares at it for six hours. Finally asks for help at 5 PM: "Hey, this isn't working, can you take a look?"&lt;/p&gt;

&lt;p&gt;Six hours of being stuck. Zero communication.&lt;/p&gt;

&lt;p&gt;Better approach: &lt;strong&gt;If you're blocked for more than 2 hours, raise it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But here's the key - don't just say "it's broken, help me." Show what you tried:&lt;/p&gt;

&lt;p&gt;"I'm stuck on this auth bug. I've tried:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Checking JWT token expiration - tokens are valid&lt;/li&gt;
&lt;li&gt;Validating signature algorithm - matches what we configured&lt;/li&gt;
&lt;li&gt;Using Claude to review the middleware - found nothing&lt;/li&gt;
&lt;li&gt;Here are my logs [paste]&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I think it's in the token refresh flow, but I need another set of eyes."&lt;/p&gt;

&lt;p&gt;See the difference? You did the work. You exhausted your options. You documented what you tried. You have a theory about where the problem is.&lt;/p&gt;

&lt;p&gt;Now when someone helps you, they can actually help - not spend an hour reproducing what you should have already done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before You Ask for Help
&lt;/h2&gt;

&lt;p&gt;Use everything available to you first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read the documentation&lt;/li&gt;
&lt;li&gt;Use AI tools (Claude, ChatGPT, Gemini, Cursor)&lt;/li&gt;
&lt;li&gt;Write failing tests that show exactly what's broken&lt;/li&gt;
&lt;li&gt;Check similar issues in the codebase&lt;/li&gt;
&lt;li&gt;Google the error message&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've done all this and you're still stuck after 2 hours - perfect. Ask for help. Just show what you tried.&lt;/p&gt;

&lt;p&gt;"Can you help?" without context is not taking ownership. It's asking someone else to own your problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Things Break in Production
&lt;/h2&gt;

&lt;p&gt;This is where ownership really shows.&lt;/p&gt;

&lt;p&gt;Bad ownership:&lt;br&gt;
"The deployment failed. Someone should look at it."&lt;/p&gt;

&lt;p&gt;Good ownership:&lt;br&gt;
"The deployment failed. I own it. Root cause: database migration timed out because I didn't account for the table size. I'm rolling back now. Fix: breaking the migration into smaller batches. Should be ready to redeploy in 30 minutes. Here's how we prevent this next time..."&lt;/p&gt;

&lt;p&gt;Notice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Takes responsibility immediately&lt;/li&gt;
&lt;li&gt;Explains what went wrong&lt;/li&gt;
&lt;li&gt;Has a fix plan&lt;/li&gt;
&lt;li&gt;Knows how to prevent it&lt;/li&gt;
&lt;li&gt;Gives timeline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's ownership.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up for Success
&lt;/h2&gt;

&lt;p&gt;When you ship a feature, you should have:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monitoring:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Metrics tracking the important stuff (latency, errors, throughput)&lt;/li&gt;
&lt;li&gt;Dashboards showing health at a glance&lt;/li&gt;
&lt;li&gt;Logs that actually help you debug&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Alerts:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configured before you ship&lt;/li&gt;
&lt;li&gt;With context - not just "something broke"&lt;/li&gt;
&lt;li&gt;Routed to the right people&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Documentation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How does this work?&lt;/li&gt;
&lt;li&gt;What can go wrong?&lt;/li&gt;
&lt;li&gt;How do you fix common issues?&lt;/li&gt;
&lt;li&gt;Who built this and how do you reach them?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your feature goes down at 3 AM and someone else is on-call, they should be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand what broke from the alert&lt;/li&gt;
&lt;li&gt;Find your runbook&lt;/li&gt;
&lt;li&gt;Fix it or know how to reach you&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If they can't, you didn't finish shipping.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Deployment That Failed
&lt;/h2&gt;

&lt;p&gt;A dev once deployed a payment refactor. It tested thoroughly. Everything worked in staging. It pushed to production Friday afternoon.&lt;/p&gt;

&lt;p&gt;Saturday morning: alerts firing. Payment processing slowed to a crawl. Users couldn't complete transactions.&lt;/p&gt;

&lt;p&gt;The old dev might have said "worked fine in staging" and waited for someone else to investigate.&lt;/p&gt;

&lt;p&gt;Instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It jumped on immediately&lt;/li&gt;
&lt;li&gt;It found the issue: hadn't tested with production data volume&lt;/li&gt;
&lt;li&gt;It rolled back in 10 minutes&lt;/li&gt;
&lt;li&gt;It wrote a post-mortem&lt;/li&gt;
&lt;li&gt;It fixed the actual issue&lt;/li&gt;
&lt;li&gt;It added load testing to our CI/CD&lt;/li&gt;
&lt;li&gt;It redeployed Monday with confidence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's ownership. Not "it worked on my machine." Not "not my problem on weekends." Just: I own this, I'll fix it, here's how we prevent it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ownership Makes You Better
&lt;/h2&gt;

&lt;p&gt;Here's what surprised me: taking full ownership made me a better engineer faster.&lt;/p&gt;

&lt;p&gt;Why? Because when you know you're responsible for something in production, you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Think through edge cases&lt;/li&gt;
&lt;li&gt;Write better tests&lt;/li&gt;
&lt;li&gt;Set up proper monitoring&lt;/li&gt;
&lt;li&gt;Document your decisions&lt;/li&gt;
&lt;li&gt;Care about performance&lt;/li&gt;
&lt;li&gt;Plan for failure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can't just write code and move on. You have to think about the whole lifecycle.&lt;/p&gt;

&lt;p&gt;This makes you better at every part of engineering.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Junior Developer Who Got It
&lt;/h2&gt;

&lt;p&gt;I had a junior developer on my team. First significant feature. He was nervous about deploying.&lt;/p&gt;

&lt;p&gt;But he did everything right:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wrote comprehensive tests&lt;/li&gt;
&lt;li&gt;Set up monitoring and alerts&lt;/li&gt;
&lt;li&gt;Created a runbook&lt;/li&gt;
&lt;li&gt;Deployed behind a feature flag&lt;/li&gt;
&lt;li&gt;Watched metrics for the first hour&lt;/li&gt;
&lt;li&gt;Posted an update in Slack&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The feature worked perfectly. But more importantly, he owned it completely. When a question came up two weeks later, he knew exactly how it worked and why we built it that way.&lt;/p&gt;

&lt;p&gt;That's the mindset. Not "I wrote some code." But "I own this feature."&lt;/p&gt;

&lt;h2&gt;
  
  
  Start Small
&lt;/h2&gt;

&lt;p&gt;Pick one thing you're working on right now. Before you ship it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set up basic monitoring&lt;/li&gt;
&lt;li&gt;Add one alert for the most critical failure mode&lt;/li&gt;
&lt;li&gt;Write a simple runbook: "If X breaks, here's how to fix it"&lt;/li&gt;
&lt;li&gt;Document your reasoning: "Why we built it this way"&lt;/li&gt;
&lt;li&gt;Tell your team: "I'm shipping X, here's what to watch for"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's ownership.&lt;/p&gt;

&lt;p&gt;Do this consistently, and you'll notice: people trust your work more. You grow faster. Your code gets better.&lt;/p&gt;

&lt;p&gt;Not because you're perfect. Because you own the whole thing, not just the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Test
&lt;/h2&gt;

&lt;p&gt;Next time something you built has an issue, watch your first reaction.&lt;/p&gt;

&lt;p&gt;Is it: "Well, it worked in my environment"?&lt;br&gt;
Or is it: "I'll look into that right now"?&lt;/p&gt;

&lt;p&gt;Is it: "Someone should probably fix that"?&lt;br&gt;
Or is it: "I'm on it, here's my plan"?&lt;/p&gt;

&lt;p&gt;The difference between those reactions is the difference between a code writer and an engineer who ships.&lt;/p&gt;

&lt;p&gt;Choose the second one. Every time.&lt;/p&gt;

&lt;p&gt;That's ownership.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is part 2 of a 14-part series on Core Leadership Principles. Next week: &lt;a href="https://dev.to/daniloab/ship-every-day-progress-is-a-puzzle-made-piece-by-piece-35ml"&gt;Ship Every Day - why making progress daily beats bursts of heroic effort.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you practice ownership in your work? What's helped you take full responsibility for your code in production?&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Follow me on &lt;a href="https://www.twitter.com/daniloab_" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;br&gt;
If you like and want to support my work &lt;a href="https://www.patreon.com/daniloab" rel="noopener noreferrer"&gt;check my patreon&lt;/a&gt;&lt;br&gt;
See more in &lt;a href="https://linktr.ee/daniloab" rel="noopener noreferrer"&gt;https://linktr.ee/daniloab&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@amutiomi?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Miguel A Amutio&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/a-security-camera-attached-to-a-pole-DSuFVGUHjrg?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>leadership</category>
      <category>career</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Core Leadership Principles: A 14-Part Series on Building Better Engineering Teams</title>
      <dc:creator>Danilo Assis</dc:creator>
      <pubDate>Sat, 14 Feb 2026 16:46:21 +0000</pubDate>
      <link>https://forem.com/daniloab/core-leadership-principles-a-14-part-series-on-building-better-engineering-teams-4nll</link>
      <guid>https://forem.com/daniloab/core-leadership-principles-a-14-part-series-on-building-better-engineering-teams-4nll</guid>
      <description>&lt;p&gt;Over the next 14 posts, I'm sharing the leadership principles I've developed while building and leading engineering teams in my career. These aren't academic theories. They're battle-tested principles that emerged from real problems: missed deadlines, broken production systems, developers who couldn't grow, and teams that couldn't ship.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here's what this series is not:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A management framework you must follow&lt;/li&gt;
&lt;li&gt;A comprehensive guide to leadership&lt;/li&gt;
&lt;li&gt;The "right" way to do things&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Here's what it is:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;14 principles I believe make engineers better&lt;/li&gt;
&lt;li&gt;Concrete examples of what works and what doesn't&lt;/li&gt;
&lt;li&gt;Practical actions you can take today&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You won't agree with everything. You might not agree with anything. That's fine. This is about you, not me. Take what's useful. Ignore the rest. Use AI to summarize it, text-to-speech to listen while you commute, or just skim for the bold text.&lt;/p&gt;

&lt;p&gt;What matters is this: &lt;strong&gt;How much do you want to get better at what you do?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The 14 Principles
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Intellectual Honesty&lt;/strong&gt; - Always have a defensible theory for why you're building something&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ownership &amp;amp; Accountability&lt;/strong&gt; - You own your code from concept to production&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ship Every Day&lt;/strong&gt; - Progress is a puzzle made piece by piece&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI as Assistant, Not Autopilot&lt;/strong&gt; - Use AI to accelerate learning, not replace thinking&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Come Prepared&lt;/strong&gt; - Study before the class, debate during the class&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learn in Public&lt;/strong&gt; - No DMs for technical problems&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Continuous Learning&lt;/strong&gt; - You are responsible for your own growth&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quality Without Paralysis&lt;/strong&gt; - Ship safely, but ship&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open Discourse&lt;/strong&gt; - Best idea wins, regardless of rank&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Raise the Bar&lt;/strong&gt; - Meet it, then raise it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Professional, Not Personal&lt;/strong&gt; - Be effective, not nice&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bias for Action&lt;/strong&gt; - Done and shipped beats perfect and theoretical&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Active Observability&lt;/strong&gt; - Shipping is just the beginning&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Product Builders, Not Code Writers&lt;/strong&gt; - Talk to stakeholders before building&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Who This Is For
&lt;/h2&gt;

&lt;p&gt;This series is for engineers who want to be more than just code writers. It's for people who want to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ship great software, not just write code&lt;/li&gt;
&lt;li&gt;Build products that solve real problems&lt;/li&gt;
&lt;li&gt;Grow faster in their careers&lt;/li&gt;
&lt;li&gt;Lead teams without losing their technical edge&lt;/li&gt;
&lt;li&gt;Work with a championship mentality&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're comfortable where you are, this isn't for you. If you want to get better, keep reading.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Foundation: Championship Mentality
&lt;/h2&gt;

&lt;p&gt;Every principle in this series comes from one core belief: &lt;strong&gt;We're building a team of champions.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Champions ship. Champions improve. Champions raise the bar.&lt;/p&gt;

&lt;p&gt;You have two paths:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Path A:&lt;/strong&gt; Be comfortable, be safe, be forgettable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Path B:&lt;/strong&gt; Be the professional who delivers, who raises the bar, who ships great work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This series is for people who choose Path B.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use This Series
&lt;/h2&gt;

&lt;p&gt;Each post focuses on one principle with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The problem&lt;/strong&gt; - why this matters&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What good looks like&lt;/strong&gt; - concrete examples&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What bad looks like&lt;/strong&gt; - what to avoid&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How to practice it&lt;/strong&gt; - actionable steps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The test&lt;/strong&gt; - how to know if you're doing it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read them in order. Read them out of order. Read one and stop. Come back in six months. Whatever works for you.&lt;/p&gt;

&lt;p&gt;The only thing I ask: &lt;strong&gt;If you read it, apply it.&lt;/strong&gt; Don't just collect knowledge. Use it. Test it. See if it makes you better. Come back and let's discuss what didn't work, what worked, let's improve the next version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's Start
&lt;/h2&gt;

&lt;p&gt;The first principle is &lt;strong&gt;Intellectual Honesty&lt;/strong&gt; - the foundation of everything else. Because if you can't be honest about what you know and don't know, you can't learn, you can't grow, and you can't build anything great.&lt;/p&gt;

&lt;p&gt;Let's go.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is the introduction to a 14-part series on Core Leadership Principles for engineering teams. Next up: &lt;a href="https://dev.to/daniloab/intellectual-honesty-the-foundation-of-great-engineering-1dph"&gt;Intellectual Honesty - The Foundation of Great Engineering&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Follow me on &lt;a href="https://www.twitter.com/daniloab_" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;br&gt;
If you like and want to support my work &lt;a href="https://www.patreon.com/daniloab" rel="noopener noreferrer"&gt;check my patreon&lt;/a&gt;&lt;br&gt;
See more in &lt;a href="https://linktr.ee/daniloab" rel="noopener noreferrer"&gt;https://linktr.ee/daniloab&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@pockethustle?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;TheMarketingHustle.com&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/a-statue-of-a-woman-with-her-hands-on-her-face-rGgTB7hYBnM?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>leadership</category>
      <category>career</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Intellectual Honesty - The Foundation of Great Engineering</title>
      <dc:creator>Danilo Assis</dc:creator>
      <pubDate>Sat, 14 Feb 2026 16:46:11 +0000</pubDate>
      <link>https://forem.com/daniloab/intellectual-honesty-the-foundation-of-great-engineering-1dph</link>
      <guid>https://forem.com/daniloab/intellectual-honesty-the-foundation-of-great-engineering-1dph</guid>
      <description>&lt;p&gt;&lt;em&gt;Core Leadership Principle #1&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;You're in a technical meeting. Someone asks why you chose MongoDB over PostgreSQL for the new feature. You freeze. The truth? You picked it because a tutorial you followed used it. Or because it seemed "more modern." Or because you didn't want to deal with schema migrations.&lt;/p&gt;

&lt;p&gt;But you can't say that. So you mumble something about "scalability" and "flexibility" and hope no one asks follow-up questions.&lt;/p&gt;

&lt;p&gt;We've all been there. And it's killing our ability to build great software.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Intellectual Honesty?
&lt;/h2&gt;

&lt;p&gt;Intellectual honesty means you always have a defensible theory for why you're building something a certain way. Not a guess. Not a vibe. A theory you can defend with data, reasoning, or experience.&lt;/p&gt;

&lt;p&gt;It means three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;You know why you made a decision&lt;/strong&gt; - not just what you decided&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You're willing to change your mind&lt;/strong&gt; - when evidence shows you're wrong&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You admit what you don't know&lt;/strong&gt; - instead of pretending&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The gap between "I don't know" and "I don't know and didn't try to find out" is the difference between an honest engineer and a dangerous one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Bad Decisions Compound
&lt;/h3&gt;

&lt;p&gt;That MongoDB choice? It seemed small at the time. But six months later, you're fighting with transactions, your team is debugging race conditions, and migration to PostgreSQL would take weeks. All because you picked a tool without understanding the tradeoffs.&lt;/p&gt;

&lt;p&gt;One intellectually dishonest decision leads to another. You can't admit the database choice was wrong (because you never had a real reason for it), so you build workarounds. Then you build workarounds for the workarounds. Technical debt piles up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Teams Can't Learn
&lt;/h3&gt;

&lt;p&gt;When you pretend to have reasons you don't have, your team can't learn from your decisions. They can't evaluate the tradeoffs. They can't apply the reasoning to future problems.&lt;/p&gt;

&lt;p&gt;Worse, you create a culture where everyone pretends. Junior developers copy your behavior. Soon, nobody knows why anything works the way it does. You're cargo culting your own codebase.&lt;/p&gt;

&lt;h3&gt;
  
  
  You Stop Growing
&lt;/h3&gt;

&lt;p&gt;If you never admit you don't know something, you never learn it. If you never acknowledge mistakes, you never improve. Intellectual honesty is the foundation of growth.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Looks Like in Practice
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Good: Defendable Decisions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;"I'm building this with Redis instead of our current in-memory cache because we're having latency problems under load. I've tested both approaches with 10k concurrent requests and Redis reduced our p95 latency from 800ms to 120ms. Here's my benchmark data. I want to validate this works in our actual environment before we commit."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Notice what's here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clear problem statement (latency under load)&lt;/li&gt;
&lt;li&gt;Specific comparison (tested both)&lt;/li&gt;
&lt;li&gt;Actual data (10k requests, p95 from 800ms to 120ms)&lt;/li&gt;
&lt;li&gt;Next step (validate in real environment)&lt;/li&gt;
&lt;li&gt;Humility (want to confirm before committing)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This engineer did the work. They have a theory. They tested it. They're ready to be wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"I initially thought GraphQL was the right choice, but after prototyping, the complexity doesn't justify the benefits for our use case. Here's why I'm switching to REST endpoints."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is intellectual honesty in action: changing your mind when reality doesn't match your theory. No ego. No doubling down. Just adaptation.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Bad: Intellectual Dishonesty
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;"I just think this way is better."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Better how? For what? Compared to what? This is a non-answer masquerading as a decision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"That's how we've always done it."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is worse than "I don't know" because it pretends to be a reason. Tradition isn't reasoning. It's the absence of reasoning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"The AI told me to do it this way."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Outsourcing your thinking to AI is outsourcing your judgment. If you can't explain why the AI's suggestion solves your problem better than alternatives, you don't understand what you're building.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Practice Intellectual Honesty
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Write RFCs (Request for Comments)
&lt;/h3&gt;

&lt;p&gt;Before building anything significant, write 1-2 pages explaining:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What problem you're solving&lt;/li&gt;
&lt;li&gt;What alternatives you considered
&lt;/li&gt;
&lt;li&gt;Why you chose this approach&lt;/li&gt;
&lt;li&gt;What the tradeoffs are&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This forces you to think. You can't hide behind jargon in writing. If your reasoning is weak, it'll be obvious.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Build POCs (Proof of Concepts)
&lt;/h3&gt;

&lt;p&gt;Don't just theorize. Test your assumptions. Build a small version. Measure real results.&lt;/p&gt;

&lt;p&gt;"I think X will be faster" → Build both X and Y, benchmark them, show data.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Show Your Work
&lt;/h3&gt;

&lt;p&gt;When you make a decision, document it. Not for bureaucracy. For clarity. Future you will forget why you chose this path. Your teammate needs to understand the reasoning to maintain it.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;PRDs that explain the problem and why it matters&lt;/li&gt;
&lt;li&gt;Test plans that show what you're validating and why&lt;/li&gt;
&lt;li&gt;Benchmarks with real numbers, not hunches&lt;/li&gt;
&lt;li&gt;Implementation plans that break work into reviewable pieces&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Practice Saying "I Don't Know"
&lt;/h3&gt;

&lt;p&gt;Make it a habit. When you don't know something, say it out loud. Then immediately follow with: "Let me find out."&lt;/p&gt;

&lt;p&gt;This does two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It keeps you honest&lt;/li&gt;
&lt;li&gt;It models good behavior for your team&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The best engineers I know say "I don't know" constantly. The worst engineers never do.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Embrace Being Wrong
&lt;/h3&gt;

&lt;p&gt;When evidence contradicts your theory, update your theory. Don't defend it. Don't rationalize. Don't save face.&lt;/p&gt;

&lt;p&gt;Say: "I was wrong about X. Here's what I learned. Here's what I'm doing differently."&lt;/p&gt;

&lt;p&gt;This is a superpower. Teams that can admit mistakes fix them fast. Teams that can't admit mistakes hide them until they explode.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Test
&lt;/h2&gt;

&lt;p&gt;Before your next technical decision, ask yourself:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"If someone asks me why I chose this approach, can I give them a clear, data-backed answer? Or will I bullshit?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the answer is bullshit, you're not ready to build yet. Go do the work. Test your assumptions. Build the POC. Get the data.&lt;/p&gt;

&lt;p&gt;Then decide.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strong Opinions, Loosely Held
&lt;/h2&gt;

&lt;p&gt;This phrase gets thrown around a lot, but here's what it actually means:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strong opinions:&lt;/strong&gt; You have a clear point of view based on reasoning and evidence. You're not wishy-washy. You can articulate why you think X is better than Y.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Loosely held:&lt;/strong&gt; You're ready to change your mind when new evidence emerges. You're not attached to being right. You're attached to finding the truth.&lt;/p&gt;

&lt;p&gt;The combination is powerful: You move fast with conviction, but you adapt quickly when you're wrong.&lt;/p&gt;

&lt;p&gt;The opposite is deadly: Weak opinions, strongly held. You don't really know why you believe something, but you'll fight to defend it anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Culture Shift
&lt;/h2&gt;

&lt;p&gt;When you practice intellectual honesty, you change the culture around you.&lt;/p&gt;

&lt;p&gt;Junior developers learn they can say "I don't know" without being judged. They learn to show their work. They learn to change their minds.&lt;/p&gt;

&lt;p&gt;Senior developers stop pretending to know everything. They start testing their assumptions. They start asking "why" instead of just "what."&lt;/p&gt;

&lt;p&gt;Code reviews become about ideas, not egos. The best argument wins, not the loudest voice or the highest rank.&lt;/p&gt;

&lt;p&gt;This is how you build a team that ships great software. Not by pretending to have all the answers. By doing the work to find them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start Today
&lt;/h2&gt;

&lt;p&gt;Pick one decision you're about to make. Before you make it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write down why you're considering this approach&lt;/li&gt;
&lt;li&gt;List at least two alternatives&lt;/li&gt;
&lt;li&gt;Explain the tradeoffs&lt;/li&gt;
&lt;li&gt;Test your top choice if possible&lt;/li&gt;
&lt;li&gt;Be ready to change your mind&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it. That's intellectual honesty.&lt;/p&gt;

&lt;p&gt;Do this consistently, and you'll build better software, grow faster, and earn the respect of everyone around you.&lt;/p&gt;

&lt;p&gt;Not because you're always right. Because you're always honest.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is part of a series on Core Leadership Principles for engineering teams. Next up: Ownership &amp;amp; Accountability - You own your code from concept to production.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Follow me on &lt;a href="https://www.twitter.com/daniloab_" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;br&gt;
If you like and want to support my work &lt;a href="https://www.patreon.com/daniloab" rel="noopener noreferrer"&gt;check my patreon&lt;/a&gt;&lt;br&gt;
See more in &lt;a href="https://linktr.ee/daniloab" rel="noopener noreferrer"&gt;https://linktr.ee/daniloab&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@brett_jordan?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Brett Jordan&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/brown-wooden-blocks-on-white-table-Pd3ml1YRPlg?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>leadership</category>
      <category>productivity</category>
      <category>career</category>
    </item>
    <item>
      <title>How to Integrate Multiple LLM Providers Without Turning Your Codebase Into a Mess - Provider Strategy in Practice</title>
      <dc:creator>Danilo Assis</dc:creator>
      <pubDate>Fri, 09 Jan 2026 02:24:50 +0000</pubDate>
      <link>https://forem.com/daniloab/how-to-integrate-multiple-llm-providers-without-turning-your-codebase-into-a-mess-provider-36g9</link>
      <guid>https://forem.com/daniloab/how-to-integrate-multiple-llm-providers-without-turning-your-codebase-into-a-mess-provider-36g9</guid>
      <description>&lt;h1&gt;
  
  
  Provider Strategy in Practice
&lt;/h1&gt;

&lt;h2&gt;
  
  
  How to Integrate Multiple LLM Providers Without Turning Your Codebase Into a Mess
&lt;/h2&gt;

&lt;p&gt;Most companies eventually hit the same wall.&lt;/p&gt;

&lt;p&gt;They start with &lt;strong&gt;one external provider&lt;/strong&gt; — payments, maps, messaging, or LLMs. Everything works fine… until the day they need a second provider.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The main vendor goes down
&lt;/li&gt;
&lt;li&gt;Pricing changes
&lt;/li&gt;
&lt;li&gt;A specific feature exists only in another provider
&lt;/li&gt;
&lt;li&gt;Latency or regional availability becomes a problem
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And suddenly, what used to be a clean integration becomes a maintenance nightmare.&lt;/p&gt;

&lt;p&gt;This post walks through a &lt;strong&gt;practical, production-ready pattern&lt;/strong&gt; to integrate multiple LLM providers while keeping your core platform clean, scalable, and resilient.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Common Pain of Multi-Provider Integrations
&lt;/h2&gt;

&lt;p&gt;When teams integrate providers directly into the domain logic, two types of complexity tend to leak into the system.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Provider-specific data leaks into the platform
&lt;/h3&gt;

&lt;p&gt;Each provider has its own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;request and response formats&lt;/li&gt;
&lt;li&gt;error codes&lt;/li&gt;
&lt;li&gt;retry behavior&lt;/li&gt;
&lt;li&gt;streaming protocols&lt;/li&gt;
&lt;li&gt;rate limits&lt;/li&gt;
&lt;li&gt;latency characteristics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Soon your core logic starts looking like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// handle OpenAI logic&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gemini&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// handle Gemini logic&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, your domain layer is no longer about business rules — it is about vendor behavior.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Provider behavior leaks into the product flow
&lt;/h3&gt;

&lt;p&gt;Different providers behave differently under load:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;good p75 but terrible p99&lt;/li&gt;
&lt;li&gt;aggressive throttling&lt;/li&gt;
&lt;li&gt;partial outages&lt;/li&gt;
&lt;li&gt;inconsistent output quality&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without isolation, every new provider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;increases regression risk&lt;/li&gt;
&lt;li&gt;slows down development&lt;/li&gt;
&lt;li&gt;makes refactoring scary&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is predictable: &lt;strong&gt;giant integration files, fragile logic, and painful maintenance.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Solution: Provider Strategy
&lt;/h2&gt;

&lt;p&gt;To solve this problem, I like to use what I call &lt;strong&gt;Provider Strategy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It is not a single pattern, but a &lt;strong&gt;combination of well-known patterns&lt;/strong&gt; working together:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strategy&lt;/strong&gt; – each provider implements the same interface&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adapter&lt;/strong&gt; – providers translate external formats into internal contracts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Registry&lt;/strong&gt; – a central place to resolve the active provider&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failover Policy&lt;/strong&gt; – logic to switch providers when the primary is degraded&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The core principle is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Treat external systems as external.&lt;br&gt;&lt;br&gt;
Your platform should only work with &lt;strong&gt;its own internal contract&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  A Real Example: Services Marketplace Using LLMs
&lt;/h2&gt;

&lt;p&gt;Imagine a platform that connects customers with professionals within a geographic range.&lt;/p&gt;

&lt;p&gt;User input:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ZIP code&lt;/li&gt;
&lt;li&gt;service type (e.g., &lt;em&gt;mason&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;desired date&lt;/li&gt;
&lt;li&gt;optional notes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The platform:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Searches the database for available professionals&lt;/li&gt;
&lt;li&gt;Aggregates ratings, availability, and pricing signals&lt;/li&gt;
&lt;li&gt;Uses an LLM to generate a short, friendly proposal message&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is exactly the kind of flow that often starts with one provider and later needs multiple.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Define a Stable Internal Contract
&lt;/h2&gt;

&lt;p&gt;Your domain must not care &lt;strong&gt;which provider&lt;/strong&gt; is being used.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ProviderId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gemini&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;claude&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;LLMGenerateRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;maxTokens&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;LLMGenerateResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;providerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProviderId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;inputTokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;outputTokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This contract becomes the backbone of your platform.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Provider Strategy + Adapter
&lt;/h2&gt;

&lt;p&gt;Each provider implements the same interface and adapts its SDK internally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;LLMProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProviderId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LLMGenerateRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LLMGenerateResult&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example: OpenAI Provider
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;openAIProvider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LLMProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;openAIGenerate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;temperature&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;maxTokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxTokens&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;providerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example: Gemini Provider
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;geminiProvider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LLMProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gemini&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;geminiGenerate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;temperature&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;maxOutputTokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxTokens&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;providerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gemini&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3: Provider Registry
&lt;/h2&gt;

&lt;p&gt;Providers are registered at startup and resolved dynamically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createProviderRegistry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LLMProvider&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProviderId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Provider not registered: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;resolve&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;h2&gt;
  
  
  Step 4: Failover Policy + Circuit Breaker
&lt;/h2&gt;

&lt;p&gt;Provider selection becomes a policy decision, not scattered logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pickProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fallbacks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;health&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;candidates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;fallbacks&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;health&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;healthy&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;This allows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;automatic failover&lt;/li&gt;
&lt;li&gt;controlled retries&lt;/li&gt;
&lt;li&gt;predictable p99 behavior&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Putting It All Together
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;generateProposal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getPlatformSettings&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;registry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createProviderRegistry&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;openAIProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;geminiProvider&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;providerId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pickProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;primaryProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fallbacks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fallbackProviders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;health&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;getProviderHealth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;providerId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;buildProposalPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;maxTokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;220&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Performance, p75, p99, and Cost Control
&lt;/h2&gt;

&lt;p&gt;LLM systems often show:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;good p75&lt;/li&gt;
&lt;li&gt;poor p99&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To control this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;aggressive timeouts&lt;/li&gt;
&lt;li&gt;retry budgets&lt;/li&gt;
&lt;li&gt;deterministic caching&lt;/li&gt;
&lt;li&gt;async processing with queues&lt;/li&gt;
&lt;li&gt;circuit breakers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Measure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;provider latency percentiles&lt;/li&gt;
&lt;li&gt;retry rate&lt;/li&gt;
&lt;li&gt;cache hit rate&lt;/li&gt;
&lt;li&gt;token usage and cost&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Monolith First, Microservices Later
&lt;/h2&gt;

&lt;p&gt;This pattern works well in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a single monolith&lt;/li&gt;
&lt;li&gt;background workers&lt;/li&gt;
&lt;li&gt;fully distributed systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The internal contract stays stable while the architecture evolves.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Provider Strategy allows teams to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;isolate external complexity&lt;/li&gt;
&lt;li&gt;scale integrations safely&lt;/li&gt;
&lt;li&gt;improve reliability and p99&lt;/li&gt;
&lt;li&gt;avoid vendor lock-in&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of fighting providers, your platform becomes &lt;strong&gt;provider-aware but provider-independent&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;Follow me on &lt;a href="https://www.twitter.com/daniloab" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;br&gt;
See more in &lt;a href="https://linktr.ee/daniloab" rel="noopener noreferrer"&gt;https://linktr.ee/daniloab&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@dnevozhai?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Denys Nevozhai&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/aerial-photography-of-concrete-roads-7nrsVjvALnA?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>javascript</category>
      <category>node</category>
      <category>architecture</category>
    </item>
    <item>
      <title>How do you know you are moving forward?</title>
      <dc:creator>Danilo Assis</dc:creator>
      <pubDate>Mon, 20 Oct 2025 17:32:35 +0000</pubDate>
      <link>https://forem.com/daniloab/how-do-you-know-you-are-moving-forward-47ce</link>
      <guid>https://forem.com/daniloab/how-do-you-know-you-are-moving-forward-47ce</guid>
      <description>&lt;h2&gt;
  
  
  TLDR
&lt;/h2&gt;

&lt;p&gt;Progress isn’t just about effort, it’s about direction. You move forward when you recognize patterns, adapt existing solutions to your context, and focus on the few actions that create the biggest results. Growth comes from learning, feedback, and alignment, not repetition.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you know you are moving forward?
&lt;/h2&gt;

&lt;p&gt;Human life is surrounded by the anxiety of knowing if things are going as expected. Whether in childhood, youth, adulthood, or elderhood, it doesn’t matter. Humans will always seek dopamine as a way of reward. The human body and mind are strongly influenced by it.&lt;/p&gt;

&lt;p&gt;As Arthur Schopenhauer said, “&lt;em&gt;Life is a constant oscillation between the desire to have and the boredom of possessing.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But how do you know you are moving forward? That’s a good question, and let’s dissect it below.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Executive Challenge
&lt;/h2&gt;

&lt;p&gt;In the executive field, you are constantly bombarded by deadlines and expectations, either from your leaders or yourself. Strategies, metrics, and plans must be well defined for each situation, although it is not always easy to achieve that.&lt;/p&gt;

&lt;p&gt;For example, imagine a junior business analyst who has been requested to bring numbers about a specific company that seniors were studying for a case. By the time he presented his report, he had already reviewed three similar cases inside the company.&lt;/p&gt;

&lt;p&gt;But the question is: Was he able to find the right approach? Which strategies could he pursue to achieve the expected result?&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem Pattern Matching
&lt;/h2&gt;

&lt;p&gt;Most problems have already been solved by someone before, and nowadays, with the internet, AIs, YouTube videos, and TikTok, solutions are widely available.&lt;/p&gt;

&lt;p&gt;That is the moment when the ideas of Problem Pattern Matching and Smart Copy and Paste come into play.&lt;/p&gt;

&lt;p&gt;Let’s imagine another situation: someone who has never cooked rice. What would that person do? They would probably look for references and sources on the internet, ask an AI, or watch a short video.&lt;/p&gt;

&lt;p&gt;This is the core idea of &lt;strong&gt;Problem Pattern Matching&lt;/strong&gt;: searching for patterns that already exist. That junior analyst could have applied this by looking at previous internal cases and how they had been approached.&lt;/p&gt;

&lt;h2&gt;
  
  
  Smart Copy and Paste
&lt;/h2&gt;

&lt;p&gt;Once something relevant is found, it is time to apply the Smart Copy and Paste.&lt;/p&gt;

&lt;p&gt;You might be wondering: “Okay, am I just going to copy and use someone else’s idea?”&lt;/p&gt;

&lt;p&gt;Not exactly. The junior analyst should have adapted the model to his context instead of simply copying it. If he had just copied the previous report, the outcome would have been rejected by his leaders.&lt;/p&gt;

&lt;p&gt;Instead, the solution had to be tested, adjusted, and improved. Just like I heard one: “You must adapt, not adopt.”&lt;/p&gt;

&lt;p&gt;This is where feedback loops become essential. Solutions are improved when coworkers, leaders, or clients provide insights that help refine the final product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond the Limits
&lt;/h2&gt;

&lt;p&gt;So, how do you know you are moving forward?&lt;/p&gt;

&lt;p&gt;You know it when you can transform existing patterns into new solutions that fit your scenario, when you adapt instead of blindly copying, and when you use checkpoints and feedback loops to validate progress.&lt;/p&gt;

&lt;p&gt;Moving forward means climbing beyond your limits, prioritizing the 20% of actions that generate 80% of results (Pareto Principle), and aligning with people who raise the bar with you.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/The_World_as_Will_and_Representation" rel="noopener noreferrer"&gt;The World as Will and Representation – Wikipedia&lt;/a&gt;&lt;br&gt;
&lt;a href="https://en.wikipedia.org/wiki/Pareto_principle" rel="noopener noreferrer"&gt;Pareto Principle – Wikipedia&lt;/a&gt;&lt;br&gt;
&lt;a href="https://en.wikipedia.org/wiki/Pattern_matching" rel="noopener noreferrer"&gt;Pattern Matching – Wikipedia&lt;/a&gt;&lt;br&gt;
&lt;a href="https://dev.to/daniloab/learning-faster-with-cone-of-learning-16hm"&gt;Learning Faster with the Cone of Learning – dev.to&lt;/a&gt;&lt;br&gt;
&lt;a href="https://dev.to/daniloab/getting-ready-for-daily-meeting-583j"&gt;Getting Ready for Daily Meeting – dev.to&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Follow me on Twitter&lt;br&gt;
If you like and want to support my work be my patreon&lt;br&gt;
See more in &lt;a href="https://linktr.ee/daniloab" rel="noopener noreferrer"&gt;https://linktr.ee/daniloab&lt;/a&gt;.&lt;br&gt;
Photo by &lt;a href="https://unsplash.com/@maplerockdesign?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Richard Bell&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/a-black-and-white-photo-of-an-arrow-painted-on-a-road-PMQ4Qd9Fu4I?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>management</category>
      <category>programming</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>What does it take to be a leader?</title>
      <dc:creator>Danilo Assis</dc:creator>
      <pubDate>Fri, 05 Sep 2025 13:36:29 +0000</pubDate>
      <link>https://forem.com/daniloab/what-does-it-take-to-be-a-leader-45b0</link>
      <guid>https://forem.com/daniloab/what-does-it-take-to-be-a-leader-45b0</guid>
      <description>&lt;h2&gt;
  
  
  What is a Leader?
&lt;/h2&gt;

&lt;p&gt;Being a leader is not an easy position, whether in the workplace, on a sports team, or within a family. Some might argue that leadership is innate, embedded in one’s character, disposition, and mindset. However, leadership can also be developed through deliberate practice. No one is born knowing everything; it is a journey that involves continuous learning, experience, and reflection. Those who aspire to lead effectively must first understand what leadership truly means.&lt;/p&gt;

&lt;p&gt;It is essential to recognize that leadership is not only about taking the lead, but also about offering consistent support. A true leader is someone who understands the needs of their team and prioritizes collective success. Within a company, does it make sense to focus solely on individual performance, or should it also consider team performance? Leadership is not about acting as an independent contributor. It requires observing the team, identifying areas of difficulty, and assisting individuals in improving. This is precisely where many organizations have gone wrong, they often adopt short-term strategies, such as terminating underperformers, rather than investing in coaching and growth.&lt;/p&gt;

&lt;p&gt;If a team of ten people is continuously reduced through dismissals instead of being supported and guided, the result will be the eventual breakdown of cohesion and productivity. Leaders should focus on strengthening the group, not weakening it. A successful team is built through collaboration, not competition. Effective leadership means committing to the development of others, enhancing performance and work quality, and guiding individuals toward their goals. Teams that receive support and feedback are often stronger and more resilient in the face of challenges.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategies and Resources
&lt;/h2&gt;

&lt;p&gt;One of the most impactful resources on this topic is the book Radical Candor: Be a Kick-Ass Boss Without Losing Your Humanity, which provides a practical guide for leading without sacrificing empathy. Written by Kim Scott, a leadership expert who has worked with companies like Google and Apple, the book introduces the Radical Candor framework. This approach encourages leaders to care personally while challenging directly, striking a balance between compassion and accountability. Through real-world examples and actionable insights, Scott demonstrates how honest communication, when delivered with respect and clarity, can build trust, improve performance, and drive long-term results.&lt;/p&gt;

&lt;p&gt;Organizations should aim to create environments where feedback is encouraged, people feel valued, and leadership is seen as a responsibility to serve the team. Otherwise, talent will continue to be lost, and the cycle of disengagement and high turnover will persist. After all, the best leaders are those who uplift others, not those who lead alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bad Leadership Example
&lt;/h2&gt;

&lt;p&gt;Consider a scenario where poor leadership had a direct negative impact on a team. A startup with ten developers, who were growing quickly and fully engaged, suddenly had new milestones added to their sprint. However, the team did not believe in the direction the project was taking and began discussing their concerns privately. Once leadership was made aware of these conversations, they faced a crucial decision. They could have responded by initiating open dialogue to clarify the vision and rebuild confidence, or they could have reacted defensively, assuming the team was being uncooperative.&lt;/p&gt;

&lt;p&gt;Unfortunately, the leadership chose the second option, responding aggressively and treating the concerns as a threat rather than a warning. This decision broke the chain of trust and engagement. One by one, team members started to leave, seeking better environments where they could contribute meaningfully and be heard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson
&lt;/h2&gt;

&lt;p&gt;What is the lesson here? Effective leadership is not just about setting goals; it is about supporting the people who are expected to reach them. A leader should have encouraged open communication, asked the right questions, and addressed doubts with transparency. Instead, the company has paid the price, both financially and culturally, by losing valuable talent, spending resources on hiring and training, and damaging team morale.&lt;/p&gt;

&lt;p&gt;Leadership is an art. On one hand, a leader must be firm and decisive; on the other, they need to listen, care personally, and challenge directly, a core principle of Radical Candor. There is no need to be harsh or distant. With respect, clear feedback, and genuine care, leaders can build strong, motivated teams capable of overcoming challenges together. &lt;/p&gt;

&lt;p&gt;Books remain one of the most valuable tools for learning, and leadership is no exception. In addition to Radical Candor, another powerful read is The Phoenix Project: A Novel About IT, DevOps, and Helping Your Business Win. Although written as a novel, it offers practical and insightful lessons about leadership in high-pressure environments. The story follows Bill, an IT manager unexpectedly promoted to VP of Operations at a struggling company. Faced with missed deadlines, team dysfunction, and a failing initiative, Bill has just 90 days to turn things around. Through mentorship and experience, he learns how collaboration, process improvement, and effective leadership can drive real change. The book provides valuable takeaways on how leaders can navigate complex challenges, build trust, and enable their teams to succeed, no matter the industry.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.radicalcandor.com/" rel="noopener noreferrer"&gt;https://www.radicalcandor.com/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.amazon.com.br/Phoenix-Project-DevOps-Helping-Business/dp/0988262592" rel="noopener noreferrer"&gt;https://www.amazon.com.br/Phoenix-Project-DevOps-Helping-Business/dp/0988262592&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Follow me on &lt;a href="https://x.com/daniloab_" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you like and want to support my work, become my &lt;a href="https://www.patreon.com/daniloab" rel="noopener noreferrer"&gt;Patreon&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Want to boost your career? Start now with my mentorship through the Twitter DM!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mentor.daniloassis.dev" rel="noopener noreferrer"&gt;https://mentor.daniloassis.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See more at &lt;a href="https://linktr.ee/daniloab" rel="noopener noreferrer"&gt;https://linktr.ee/daniloab&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Photo of &lt;a href="https://unsplash.com/pt-br/@tobiasmrzyk?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Tobias Mrzyk&lt;/a&gt; in &lt;a href="https://unsplash.com/pt-br/fotografias/silhueta-das-pessoas-iuqmGmst5Po?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>leadership</category>
      <category>career</category>
    </item>
    <item>
      <title>How to Use Google Sheets API with Node.js (Without SDKs or Consent Screen)</title>
      <dc:creator>Danilo Assis</dc:creator>
      <pubDate>Tue, 05 Aug 2025 14:24:44 +0000</pubDate>
      <link>https://forem.com/daniloab/how-to-use-google-sheets-api-with-nodejs-without-sdks-or-consent-screen-3k7o</link>
      <guid>https://forem.com/daniloab/how-to-use-google-sheets-api-with-nodejs-without-sdks-or-consent-screen-3k7o</guid>
      <description>&lt;p&gt;Recently, I've needed to integrate with Google Sheet APIs, and even though it's part of my normal daily routine, the knowledge I gained from that experience has motivated me to write this piece of code.&lt;/p&gt;

&lt;p&gt;Mostly, people are used to connecting with Google Tools through the well-known consent page:&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%2F6kkif38xrtovtml62qar.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%2F6kkif38xrtovtml62qar.png" alt="Gmail consent page example" width="800" height="938"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But, how does Google manage API requests for server-to-server services?&lt;/p&gt;

&lt;p&gt;Let's take a deep dive and understand how to integrate Google Sheets without OAuth popups, SDKs, or real user interaction. In this post, I’ll show you how to use the Google Sheets API with a &lt;strong&gt;Service Account&lt;/strong&gt;, generate your own &lt;strong&gt;JWT manually&lt;/strong&gt;, and append data using native &lt;strong&gt;Node.js&lt;/strong&gt; modules only — no SDKs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Use Case
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Internal tools&lt;/li&gt;
&lt;li&gt;Server-side automations (e.g., CRON jobs)&lt;/li&gt;
&lt;li&gt;No user login or consent screen&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1: Set Up a Google Service Account
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1.1. Create a Google Cloud Project
&lt;/h3&gt;

&lt;p&gt;Go to &lt;a href="https://console.cloud.google.com/" rel="noopener noreferrer"&gt;Google Cloud Console&lt;/a&gt;, create (or select) a project.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.2. Enable the Sheets API
&lt;/h3&gt;

&lt;p&gt;Search for “Google Sheets API” in the &lt;a href="https://console.cloud.google.com/apis/library" rel="noopener noreferrer"&gt;API Library&lt;/a&gt;, click &lt;strong&gt;Enable&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.3. Create a Service Account
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;strong&gt;IAM &amp;amp; Admin &amp;gt; Service Accounts&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create Service Account&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;After creation, go to the &lt;strong&gt;"Keys"&lt;/strong&gt; tab and:

&lt;ul&gt;
&lt;li&gt;Click &lt;strong&gt;Add Key &amp;gt; Create new key&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;JSON&lt;/strong&gt; and download it&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;**It's very important to save this JSON file because the code relies on it to save data to the spreadsheet through backend calls.&lt;/p&gt;

&lt;p&gt;DON'T FORGET THIS STEP!**&lt;/p&gt;

&lt;h3&gt;
  
  
  1.4. Share the Spreadsheet
&lt;/h3&gt;

&lt;p&gt;Open the JSON file, copy the &lt;code&gt;client_email&lt;/code&gt; field, and share with your Google Sheet giving &lt;strong&gt;Editor&lt;/strong&gt; access:&lt;br&gt;
&lt;code&gt;your-service-account@your-project.iam.gserviceaccount.com&lt;/code&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 2: Write Code to Append Data
&lt;/h2&gt;

&lt;p&gt;We'll create a script responsible for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generating the &lt;code&gt;access_token&lt;/code&gt; responsible for claiming the API with the respective scopes on it.&lt;/li&gt;
&lt;li&gt;Process the Google Spreadsheet API call.&lt;/li&gt;
&lt;li&gt;It is expected you to serve the JSON file in some way. I'm using AWS secret manager with my keys already processed and just passing below to my google script file.&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  2.1 Google Service
&lt;/h3&gt;

&lt;p&gt;Here, you can name it as you want. I will move forward, declaring it as a service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;GoogleServiceAccountJsonObjectKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;project_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;private_key_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;private_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;client_email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;auth_uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;token_uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;auth_provider_x509_cert_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;client_x509_cert_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;universe_domain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;generateGoogleApiJWT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;googleServiceAccountJsonObjectKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GoogleServiceAccountJsonObjectKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Starting generation of google api jwt token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;client_email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;clientEmail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;private_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;privateKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;private_key_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;kid&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;googleServiceAccountJsonObjectKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;alg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;RS256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;typ&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;JWT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;kid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;iss&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;clientEmail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.googleapis.com/auth/spreadsheets&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;aud&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://oauth2.googleapis.com/token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;iat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;ONE_HOUR_LATER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;encodedHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;base64url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;encodedPayload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;base64url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;unsignedToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;encodedHeader&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;encodedPayload&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;sign&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createSign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;RSA-SHA256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;unsignedToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error in signing token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;privateKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;unsignedToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getAccessToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Getting google api access token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;grant_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;urn:ietf:params:oauth:grant-type:jwt-bearer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;assertion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://oauth2.googleapis.com/token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&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;Content-Type&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;application/x-www-form-urlencoded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No access_token in response&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;submitGoogleSpreadsheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;googleSheetsAPIKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GoogleServiceAccountJsonObjectKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;spreadSheetId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Submitting google spreadsheet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;googleSheetsAPIKey&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;spreadSheetId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Missing google sheets api key or spreadsheet id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;jwtToken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;jwtToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateGoogleApiJWT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;googleSheetsAPIKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to generate google api jwt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;accessToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getAccessToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jwtToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to get google api access token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bodyAsArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;value&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;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;boolean&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;yes&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;no&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createdAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toLocaleDateString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en-US&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;numeric&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;month&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2-digit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;day&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2-digit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;bodyAsArray&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Spreadsheet fields&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://sheets.googleapis.com/v4/spreadsheets/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;spreadSheetId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/values/Sheet1!A2:append?valueInputOption=USER_ENTERED`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&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;Content-Type&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;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;h3&gt;
  
  
  2.2 Calling the function
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;MyType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;spreadsheetId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MY_SPREADSHEET_ID&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;googleSpreadsheetPayload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;submitGoogleSpreadsheet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MyType&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;googleSheetsAPIKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;spreadsheetId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Google spreadsheet submitted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;googleSpreadsheetPayload&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Google spreadsheet error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Security Tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Never commit the Service Account JSON object to the file in Git.&lt;/li&gt;
&lt;li&gt;Only expose as envs the values that you need.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;.gitignore&lt;/code&gt;, &lt;code&gt;secret managers&lt;/code&gt;, or &lt;code&gt;environment variables&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Limit the spreadsheet access by not granting Drive-wide permissions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;This approach gives you full control of the Google Sheets API using standard Node.js, with no SDKs and no OAuth pop-up. It’s ideal for back-end scripts, internal dashboards, and server-to-server data automation.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developers.google.com/workspace/sheets/api/reference/rest" rel="noopener noreferrer"&gt;Google Sheets API Reference&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developers.google.com/identity/protocols/oauth2/service-account" rel="noopener noreferrer"&gt;Google OAuth 2.0 for Service Accounts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Follow me on &lt;a href="https://x.com/daniloab_" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you like and want to support my work, become my &lt;a href="https://www.patreon.com/daniloab" rel="noopener noreferrer"&gt;Patreon&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Want to boost your career? Start now with my mentorship through the Twitter DM!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mentor.daniloassis.dev" rel="noopener noreferrer"&gt;https://mentor.daniloassis.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See more at &lt;a href="https://linktr.ee/daniloab" rel="noopener noreferrer"&gt;https://linktr.ee/daniloab&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>javascript</category>
      <category>googlecloud</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Leveraging Native HTTP Caching in React Native (and Overriding It When Needed)</title>
      <dc:creator>Danilo Assis</dc:creator>
      <pubDate>Fri, 20 Jun 2025 15:43:25 +0000</pubDate>
      <link>https://forem.com/daniloab/leveraging-native-http-caching-in-react-native-and-overriding-it-when-needed-40jb</link>
      <guid>https://forem.com/daniloab/leveraging-native-http-caching-in-react-native-and-overriding-it-when-needed-40jb</guid>
      <description>&lt;p&gt;Improving performance in mobile apps is always a balancing act — especially when it comes to data fetching. You want to keep things fast, avoid unnecessary API calls, and ensure your users always see the most up-to-date information.&lt;/p&gt;

&lt;p&gt;Recently, I ran into this exact challenge while working on a React Native app. I had a screen that fetched a list of specific entity based on a user ID. The data didn't change often, and yet every time the screen loaded, we made a fresh network call.&lt;/p&gt;

&lt;p&gt;So, can we cache this response natively and skip the request unless it's really needed? The answer: yes — and React Native already gives us what we need.&lt;/p&gt;

&lt;p&gt;Let’s walk through what I did.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Imagine this:&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://api.example.com/entity/123`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You make this call when a user opens the app or navigates to a screen. Even if the data hasn't changed, the app hits the backend every time. This not only slows down the UI but also increases server load.&lt;/p&gt;

&lt;p&gt;React Native, under the hood, uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OkHttp on Android&lt;/li&gt;
&lt;li&gt;NSURLSession on iOS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both of these support &lt;strong&gt;HTTP caching&lt;/strong&gt; — meaning you can cache GET responses natively based on headers like Cache-Control.&lt;/p&gt;

&lt;p&gt;But by default, React Native doesn't configure OkHttp for disk caching. So we first needed to get our backend and frontend talking the same cache language.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Let the Backend Control the Cache
&lt;/h2&gt;

&lt;p&gt;We updated our API to include a proper &lt;code&gt;Cache-Control&lt;/code&gt; header:&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="nx"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Control&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;public&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;must&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;revalidate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What does that mean?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;public&lt;/code&gt;: this response can be cached by clients and shared caches (like proxies or CDNs)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;max-age=3600&lt;/code&gt;: cache is fresh for 1 hour&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;must-revalidate&lt;/code&gt;: after 1 hour, the client must check with the server before reusing it&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That tells native clients like OkHttp and NSURLSession: &lt;strong&gt;“You can safely cache this response for an hour.”&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Overriding the Cache When Needed
&lt;/h2&gt;

&lt;p&gt;Now here’s the twist.&lt;/p&gt;

&lt;p&gt;Sometimes, we wanted to force a fresh request, even if the cache was still valid — like when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The app is reopened&lt;/li&gt;
&lt;li&gt;The user manually refreshes the screen&lt;/li&gt;
&lt;li&gt;We suspect stale data for any reason&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We solved this by adding a &lt;code&gt;Cache-Control: no-cache&lt;/code&gt; header on demand:&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.example.com/entity/123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&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;Cache-Control&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;no-cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Fresh response:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Fetch error:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Caching can feel like a black box — especially in mobile apps — but understanding how native caching works under the hood unlocks a lot of power.&lt;/p&gt;

&lt;p&gt;By:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Letting the backend control cache timing&lt;/li&gt;
&lt;li&gt;Letting React Native honor the native cache&lt;/li&gt;
&lt;li&gt;And providing a clean way to override it when needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We got the best of both worlds: performance + flexibility.&lt;/p&gt;

&lt;p&gt;This flow took a bit to learn, but once everything clicked, it became one of those “ohhh, this is super useful” moments.&lt;/p&gt;

&lt;p&gt;Let me know if you want a full code example or a working demo!&lt;/p&gt;

&lt;p&gt;Photo of &lt;a href="https://unsplash.com/pt-br/@_rabuto?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Wouter R&lt;/a&gt; in &lt;a href="https://unsplash.com/pt-br/fotografias/um-monte-de-caixas-de-correio-amarelas-sentadas-ao-lado-de-uma-parede-de-tijolos-nE-6sYdNFVE?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>What is a Ledger and Why Are Names Harder?</title>
      <dc:creator>Danilo Assis</dc:creator>
      <pubDate>Wed, 26 Jun 2024 16:20:54 +0000</pubDate>
      <link>https://forem.com/woovi/what-is-a-ledger-and-why-are-names-harder-25jn</link>
      <guid>https://forem.com/woovi/what-is-a-ledger-and-why-are-names-harder-25jn</guid>
      <description>&lt;p&gt;&lt;a href="https://daniloab.substack.com/p/o-que-e-ledger-e-por-que-nomes-sao" rel="noopener noreferrer"&gt;Portugues Version&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Ledger Series
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://dev.to/woovi/what-is-ledger-and-why-does-it-need-idempotence-18n9"&gt;What is a Ledger and why you need to learn about it?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/woovi/what-is-ledger-and-why-does-it-need-idempotence-18n9"&gt;What is Ledger and why does it need Idempotence?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/woovi/what-is-a-ledger-and-why-floating-points-are-not-recommended-1f4l"&gt;What is a Ledger and Why Floating Points Are Not Recommended?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/woovi/what-is-a-ledger-and-why-are-names-harder-25jn"&gt;What is a Ledger and Why Are Names Harder?&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In software development, choosing appropriate names for variables, functions, and data structures is a crucial skill. Clear and consistent names are essential for code maintainability and for promoting a healthy development culture. Let's explore why names are important and how we can improve the readability and maintenance of our ledger example.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Importance of Names
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Clarity&lt;/strong&gt;: Well-chosen names make the code easier to understand, both for the author and for other developers who will work on the project in the future.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainability&lt;/strong&gt;: Readable and clear code facilitates maintenance and updates, reducing the time needed to fix bugs or add new features.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Communication&lt;/strong&gt;: Good names act as implicit documentation, helping to communicate the code's intent more effectively.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt;: Consistent names help establish patterns within the codebase, making it more predictable and less prone to errors.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Example Improvement: Changing transactionId to idempotencyId
&lt;/h2&gt;

&lt;p&gt;In our previous example, we used transactionId to ensure transaction idempotency. While this name is clear, idempotencyId can be a more descriptive and precise name, reflecting its exact purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Updating the Code
&lt;/h2&gt;

&lt;p&gt;Let's update the code example to use idempotencyId instead of transactionId:&lt;/p&gt;

&lt;p&gt;Defining the ledger structure in MongoDB:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;60c72b2f9b1d8e4d2f507d3a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ISODate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2023-06-13T12:00:00Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Deposit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Value in cents&lt;/span&gt;
  &lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Balance in cents&lt;/span&gt;
  &lt;span class="nx"&gt;idempotencyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;abc123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;// Idempotency ID&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Function to add a new entry to the ledger and calculate the balance using idempotency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MongoClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongodb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;idempotencyId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongodb://localhost:27017&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MongoClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;finance&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ledger&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;existingTransaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;idempotencyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;idempotencyId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existingTransaction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Transaction already exists:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;existingTransaction&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastEntry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastBalance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lastEntry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;lastEntry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newBalance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lastBalance&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newEntry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newBalance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;idempotencyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;idempotencyId&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newEntry&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Transaction successfully added:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newEntry&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;addTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Deposit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unique-idempotency-id-001&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Value in cents&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Choosing appropriate names is an essential part of software development. Clear, descriptive, and consistent names facilitate code readability, maintenance, and communication. In the example above, we changed transactionId to idempotencyId, making the variable's purpose clearer.&lt;/p&gt;

&lt;p&gt;In our next post, we'll explore more about the importance of naming best practices and how they can positively impact code quality and development team efficiency.&lt;/p&gt;

&lt;p&gt;Stay tuned for more insights on building reliable financial systems and development best practices!&lt;/p&gt;




&lt;p&gt;Visit us &lt;a href="https://woovi.com/" rel="noopener noreferrer"&gt;Woovi&lt;/a&gt;!&lt;/p&gt;




&lt;p&gt;Follow me on &lt;a href="https://x.com/daniloab_" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you like and want to support my work, become my &lt;a href="https://www.patreon.com/daniloab" rel="noopener noreferrer"&gt;Patreon&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Want to boost your career? Start now with my mentorship through the link&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mentor.daniloassis.dev" rel="noopener noreferrer"&gt;https://mentor.daniloassis.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See more at &lt;a href="https://linktr.ee/daniloab" rel="noopener noreferrer"&gt;https://linktr.ee/daniloab&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Photo of &lt;a href="https://unsplash.com/pt-br/@lucaonniboni?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Luca Onniboni&lt;/a&gt; in &lt;a href="https://unsplash.com/pt-br/fotografias/maquina-de-escrever-teal-e-preta-4v9Kk01mEbY?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>What is a Ledger and Why Floating Points Are Not Recommended?</title>
      <dc:creator>Danilo Assis</dc:creator>
      <pubDate>Fri, 21 Jun 2024 20:37:29 +0000</pubDate>
      <link>https://forem.com/woovi/what-is-a-ledger-and-why-floating-points-are-not-recommended-1f4l</link>
      <guid>https://forem.com/woovi/what-is-a-ledger-and-why-floating-points-are-not-recommended-1f4l</guid>
      <description>&lt;p&gt;&lt;a href="https://daniloab.substack.com/p/o-que-e-um-ledger-e-por-que-pontos" rel="noopener noreferrer"&gt;Portugue Version&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Ledger Series
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://dev.to/woovi/what-is-ledger-and-why-does-it-need-idempotence-18n9"&gt;What is a Ledger and why you need to learn about it?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/woovi/what-is-ledger-and-why-does-it-need-idempotence-18n9"&gt;What is Ledger and why does it need Idempotence?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/woovi/what-is-a-ledger-and-why-floating-points-are-not-recommended-1f4l"&gt;What is a Ledger and Why Floating Points Are Not Recommended?&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the realm of financial transactions and ledgers, accuracy is paramount. However, using floating point numbers to represent monetary values can lead to significant issues due to their inherent imprecision. This is why it's crucial to understand the problems with floats and consider alternative approaches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Floating Points Are Problematic
&lt;/h2&gt;

&lt;p&gt;Floating point numbers are a common way to represent real numbers in computing. However, they are not suitable for precise financial calculations due to rounding errors. Here are a few examples illustrating the issue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Expected: 0.3, Actual: 0.30000000000000004&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Expected: true, Actual: false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These small inaccuracies can accumulate over many transactions, leading to significant errors in financial records.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Cents or Decimal128
&lt;/h2&gt;

&lt;p&gt;To avoid these pitfalls, it's recommended to represent monetary values in the smallest units (like cents) or use a data type designed for high precision, such as Decimal128 in MongoDB. This ensures that all calculations are exact, maintaining the integrity of financial data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Updating the Example to Use Cents
&lt;/h2&gt;

&lt;p&gt;We'll update our previous example to store amounts in cents instead of dollars, ensuring precision in our calculations.&lt;/p&gt;

&lt;p&gt;Define the structure of the ledger in MongoDB using cents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;60c72b2f9b1d8e4d2f507d3a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ISODate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2023-06-13T12:00:00Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Deposit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Amount in cents&lt;/span&gt;
  &lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Balance in cents&lt;/span&gt;
  &lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;abc123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Function to add a new entry to the ledger and calculate the balance using cents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MongoClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongodb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongodb://localhost:27017&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MongoClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;finance&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ledger&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;existingTransaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transactionId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existingTransaction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Transaction already exists:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;existingTransaction&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastEntry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastBalance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lastEntry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;lastEntry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newBalance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lastBalance&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newEntry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newBalance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transactionId&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newEntry&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Transaction successfully added:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newEntry&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;addTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Deposit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unique-transaction-id-001&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Amount in cents&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, if you are in prod making a change like this one it will need a migration of database. With mongo, this is easier. You can check how we handle this in our blogpost &lt;a href="https://dev.to/woovi/zero-downtime-database-migrations-at-woovi-cc7"&gt;Zero Downtime Database Migrations at Woovi&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The goal here is show what you can learn from common mistakes&lt;/p&gt;

&lt;h2&gt;
  
  
  Woovi APIs Use Cents
&lt;/h2&gt;

&lt;p&gt;Woovi, a modern financial services platform, utilizes cents in their APIs to ensure precision and avoid the pitfalls of floating-point arithmetic. By doing so, they maintain the accuracy and reliability of financial transactions, which is crucial for both their operations and their customers' trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Implementing idempotency in your ledger system is crucial for maintaining accurate and reliable financial records. By ensuring that each transaction is only recorded once, you can prevent duplicate entries and maintain the integrity of your data.&lt;/p&gt;

&lt;p&gt;As we have seen, idempotency is not just a technical detail but a fundamental principle that helps build robust and fault-tolerant systems. Similarly, using cents instead of floating points ensures precise financial calculations. In our next blog post, we will explore more advanced topics in ledger management and how to handle other challenges such as concurrency and eventual consistency.&lt;/p&gt;

&lt;p&gt;Stay tuned for more insights into building reliable financial systems!&lt;/p&gt;




&lt;p&gt;Visit us &lt;a href="https://woovi.com/" rel="noopener noreferrer"&gt;Woovi&lt;/a&gt;!&lt;/p&gt;




&lt;p&gt;Follow me on &lt;a href="https://x.com/daniloab_" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you like and want to support my work, become my &lt;a href="https://www.patreon.com/daniloab" rel="noopener noreferrer"&gt;Patreon&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Want to boost your career? Start now with my mentorship through the link&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mentor.daniloassis.dev" rel="noopener noreferrer"&gt;https://mentor.daniloassis.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See more at &lt;a href="https://linktr.ee/daniloab" rel="noopener noreferrer"&gt;https://linktr.ee/daniloab&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Photo of &lt;a href="https://unsplash.com/pt-br/@iam_aspencer?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Andrew Spencer&lt;/a&gt; in &lt;a href="https://unsplash.com/pt-br/fotografias/silhueta-no-homem-flutuante-eY7ioRbk2sY?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>What is Ledger and why does it need Idempotence?</title>
      <dc:creator>Danilo Assis</dc:creator>
      <pubDate>Thu, 20 Jun 2024 22:14:43 +0000</pubDate>
      <link>https://forem.com/woovi/what-is-ledger-and-why-does-it-need-idempotence-18n9</link>
      <guid>https://forem.com/woovi/what-is-ledger-and-why-does-it-need-idempotence-18n9</guid>
      <description>&lt;p&gt;&lt;a href="https://daniloab.substack.com/p/o-que-e-ledger-e-por-que-precisa" rel="noopener noreferrer"&gt;PTBR Version&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Ledger Series
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://dev.to/woovi/what-is-ledger-and-why-does-it-need-idempotence-18n9"&gt;What is a Ledger and why you need to learn about it?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/woovi/what-is-ledger-and-why-does-it-need-idempotence-18n9"&gt;What is Ledger and why does it need Idempotence?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/woovi/what-is-a-ledger-and-why-floating-points-are-not-recommended-1f4l"&gt;What is a Ledger and Why Floating Points Are Not Recommended?&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In our previous &lt;a href="https://dev.to/woovi/what-is-a-ledger-and-why-you-need-to-learn-about-it-4d0g"&gt;blog post&lt;/a&gt;, we discussed what a ledger is and why it is essential to learn about it. We explored its origins, how it works, and its importance in financial institutions. Now, we will delve into a critical concept in building robust and reliable ledgers: idempotency.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Idempotency?
&lt;/h2&gt;

&lt;p&gt;Idempotency is the property of certain operations that can be applied multiple times without changing the result beyond the initial application. In other words, if an operation is idempotent, performing it once or multiple times has the same effect. This concept is crucial in distributed systems, APIs, and financial transactions to prevent duplicate processing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is Idempotency Important in a Ledger?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Preventing Duplicate Transactions&lt;/strong&gt;: In a ledger, idempotency ensures that if a transaction is accidentally submitted more than once, it does not get recorded multiple times, which could lead to incorrect balances.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt;: Idempotency helps maintain the consistency and integrity of financial records, which is vital for audits and regulatory compliance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Handling&lt;/strong&gt;: Systems can safely retry operations without the risk of applying the same transaction multiple times, making the system more robust and fault-tolerant.
Implementing Idempotency in a Ledger
To illustrate how to implement idempotency in a ledger, we will update our previous example to include a transaction ID. This transaction ID will be used to ensure that each transaction is only recorded once.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Define the structure of the ledger in MongoDB:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;60c72b2f9b1d8e4d2f507d3a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ISODate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2023-06-13T12:00:00Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Deposit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1000.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1000.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;abc123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Function to add a new entry to the ledger and calculate the balance with idempotency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MongoClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongodb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongodb://localhost:27017&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MongoClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;finance&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ledger&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Check if the transaction already exists&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;existingTransaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transactionId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existingTransaction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Transaction already exists:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;existingTransaction&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Get the last entry in the ledger&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastEntry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastBalance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lastEntry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;lastEntry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Calculate the new balance&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newBalance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lastBalance&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Create a new entry in the ledger&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newEntry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newBalance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transactionId&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// Insert the new entry into the ledger&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newEntry&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Transaction successfully added:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newEntry&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Example usage&lt;/span&gt;
&lt;span class="nf"&gt;addTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Deposit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;500.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unique-transaction-id-001&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to keep the same transactionId?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: Your system needs to ensure that the transactionId is always created as expected to ensure that the transaction is unique. How to do this?&lt;/p&gt;

&lt;p&gt;Let's assume you have an employee payment system and you cannot double pay employees. To do this, you can have a helper model that initiates a payment, a PaymentIntent. This PaymentIntent has the following model&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;60c72b2f9b1d8e4d2f508d82&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;taxID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;12345678990&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// cpf do funcionário&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Salário&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;3000.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PENDING&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;456&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;companyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123&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;Once you call the entry creation function in the ledger you can create the transactionId by combining: company id + paymentIntent transactionId. Thus, every time you process the payment for that PaymentIntent, the same transactionId will always be created, ensuring that the Ledger can identify that entry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Implementing idempotency in your ledger system is crucial for maintaining accurate and reliable financial records. By ensuring that each transaction is only recorded once, you can prevent duplicate entries and maintain the integrity of your data.&lt;/p&gt;

&lt;p&gt;As we have seen, idempotency is not just a technical detail but a fundamental principle that helps build robust and fault-tolerant systems. In our next blog post, we will explore more advanced topics in ledger management and how to handle other challenges such as concurrency and eventual consistency.&lt;/p&gt;

&lt;p&gt;Stay tuned for more insights into building reliable financial systems!&lt;/p&gt;




&lt;p&gt;Visit us &lt;a href="https://woovi.com/" rel="noopener noreferrer"&gt;Woovi&lt;/a&gt;!&lt;/p&gt;




&lt;p&gt;Follow me on &lt;a href="https://x.com/daniloab_" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you like and want to support my work, become my &lt;a href="https://www.patreon.com/daniloab" rel="noopener noreferrer"&gt;Patreon&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Want to boost your career? Start now with my mentorship through the link&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mentor.daniloassis.dev" rel="noopener noreferrer"&gt;https://mentor.daniloassis.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See more at &lt;a href="https://linktr.ee/daniloab" rel="noopener noreferrer"&gt;https://linktr.ee/daniloab&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Photo of &lt;a href="https://unsplash.com/pt-br/@thecreativv?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;The Creativv&lt;/a&gt; in &lt;a href="https://unsplash.com/pt-br/fotografias/edificio-de-concreto-marrom-e-branco-qLEKtlHvGfo?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
