<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Deborah Fabusuyi</title>
    <description>The latest articles on DEV Community by Deborah Fabusuyi (@deborah_fabusuyi_355942b2).</description>
    <link>https://dev.to/deborah_fabusuyi_355942b2</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2014724%2Fab93fe49-29ec-4d6c-bea0-d08dafbb9a72.png</url>
      <title>DEV Community: Deborah Fabusuyi</title>
      <link>https://dev.to/deborah_fabusuyi_355942b2</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/deborah_fabusuyi_355942b2"/>
    <language>en</language>
    <item>
      <title>The API Contract I Didn't Know I Needed</title>
      <dc:creator>Deborah Fabusuyi</dc:creator>
      <pubDate>Tue, 29 Sep 2026 08:35:00 +0000</pubDate>
      <link>https://dev.to/deborah_fabusuyi_355942b2/the-api-contract-i-didnt-know-i-needed-l00</link>
      <guid>https://dev.to/deborah_fabusuyi_355942b2/the-api-contract-i-didnt-know-i-needed-l00</guid>
      <description>&lt;p&gt;One thing I've learned while working on production applications is that writing the frontend code is often not the hardest part.&lt;/p&gt;

&lt;p&gt;Sometimes, the harder problem is understanding exactly what you're building.&lt;/p&gt;

&lt;p&gt;Recently, I was working on a feature that depended on a backend endpoint. My initial approach was straightforward: inspect what was available, make a few assumptions about the response, start building the UI, and resolve the gaps as they came up.&lt;/p&gt;

&lt;p&gt;It worked.&lt;/p&gt;

&lt;p&gt;But it also created unnecessary back-and-forth.&lt;/p&gt;

&lt;p&gt;During an engineering conversation, I came across the idea of an &lt;strong&gt;API contract&lt;/strong&gt; and realised it described exactly what was missing from my approach.&lt;/p&gt;

&lt;p&gt;An API contract creates a shared understanding between the frontend and backend about how they communicate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What data is sent&lt;/li&gt;
&lt;li&gt;What data is returned&lt;/li&gt;
&lt;li&gt;The structure and types of that data&lt;/li&gt;
&lt;li&gt;Required and optional fields&lt;/li&gt;
&lt;li&gt;Possible states and status values&lt;/li&gt;
&lt;li&gt;How errors and edge cases are handled&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of building around assumptions, both sides have an agreed interface to build against.&lt;/p&gt;

&lt;p&gt;That immediately made me think about the feature I was working on.&lt;/p&gt;

&lt;p&gt;So I sat down with the backend developer and we walked through the endpoint together.&lt;/p&gt;

&lt;p&gt;We clarified what each field represented, what the response would look like, which states we needed to account for, and what should happen when certain data wasn't available.&lt;/p&gt;

&lt;p&gt;The impact was simple but significant: &lt;strong&gt;the ambiguity disappeared.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The backend developer could focus on the endpoint and business logic. I could focus on consuming the response and designing the UI around the states we had agreed on.&lt;/p&gt;

&lt;p&gt;More importantly, we could work in parallel without constantly making assumptions about each other's work.&lt;/p&gt;

&lt;h3&gt;
  
  
  The mental model changed for me
&lt;/h3&gt;

&lt;p&gt;I used to think about API integration roughly as:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backend builds → frontend consumes → UI gets built&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm starting to see it more as:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frontend + Backend → agree on the interface → build independently → integrate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That middle step doesn't need to be a formal ceremony.&lt;/p&gt;

&lt;p&gt;Sometimes it's just asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What exactly does this field mean?"&lt;/p&gt;

&lt;p&gt;"What values can this status have?"&lt;/p&gt;

&lt;p&gt;"What happens when this data isn't available?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those questions can prevent hours of unnecessary implementation and rework.&lt;/p&gt;

&lt;h3&gt;
  
  
  My takeaway
&lt;/h3&gt;

&lt;p&gt;I'm learning that becoming a stronger frontend engineer isn't only about getting better at React, TypeScript, or the tools we use to build interfaces.&lt;/p&gt;

&lt;p&gt;It's also about understanding the systems around the interface.&lt;/p&gt;

&lt;p&gt;That means communicating clearly with backend engineers, identifying ambiguity early, thinking through edge cases, and creating enough shared understanding for different parts of a team to move independently.&lt;/p&gt;

&lt;p&gt;Sometimes the thing blocking you isn't a difficult technical problem.&lt;/p&gt;

&lt;p&gt;It's an undefined assumption.&lt;/p&gt;

&lt;p&gt;And sometimes the best next step after receiving a ticket isn't opening your editor.&lt;/p&gt;

&lt;p&gt;It's talking to the person building the other side of the system.&lt;/p&gt;

</description>
      <category>api</category>
      <category>architecture</category>
      <category>softwareengineering</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The Bug That Only Existed Between Two Systems</title>
      <dc:creator>Deborah Fabusuyi</dc:creator>
      <pubDate>Thu, 03 Sep 2026 11:55:08 +0000</pubDate>
      <link>https://dev.to/deborah_fabusuyi_355942b2/the-bug-that-only-existed-between-two-systems-4095</link>
      <guid>https://dev.to/deborah_fabusuyi_355942b2/the-bug-that-only-existed-between-two-systems-4095</guid>
      <description>&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;I was working on a system with two separate dashboards: one used internally by a review team, and one used by external users. They're built and maintained somewhat separately, but they share data.&lt;/p&gt;

&lt;p&gt;A flow I worked on recently tied them together: when the review team needs someone to resubmit something, they leave a comment explaining why. That comment is supposed to travel all the way to the external dashboard, show up as the reason for the request, and then travel back automatically once the resubmission happens, so the review side sees their own original comment again against the new submission.&lt;/p&gt;

&lt;p&gt;QA flagged that sometimes the comment just wasn't showing up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tracing the Loop
&lt;/h2&gt;

&lt;p&gt;This wasn't a bug I could find by staring at one component. The comment is meant to flow from the review side, through a notification the external dashboard picks up, back out automatically on resubmission, and land again on the review side against the new submission, a round trip across two separate apps, not a single feature.&lt;/p&gt;

&lt;p&gt;To find where it broke, I had to actually follow data across both apps rather than trust that "my component looks correct." That meant reading through a part of the codebase I didn't normally touch.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Actual Bug
&lt;/h2&gt;

&lt;p&gt;The relevant piece of code handled two kinds of submissions differently — one path for file uploads, one for text-based responses. And they'd each gone wrong in a different way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;file upload path&lt;/strong&gt; just... didn't include the comment field at all when building the payload. It wasn't broken, it was omitted.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;text path&lt;/strong&gt; included a comment, but a hardcoded placeholder string, not the actual comment that had come in from the notification.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neither path was doing the one thing that mattered: pass through the real &lt;code&gt;comment&lt;/code&gt; value it had already received, and only when one actually existed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix
&lt;/h2&gt;

&lt;p&gt;The fix itself was small once the actual cause was clear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read the real comment from the incoming data in both paths.&lt;/li&gt;
&lt;li&gt;Only include it in the payload when it's actually present — omit the field entirely rather than sending an empty string when there's nothing to say.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last part mattered more than it sounds. Sending an empty string where the field should be absent is a subtly different contract, and downstream code that checks for the field's presence (rather than its truthiness) would have handled it wrong.&lt;/p&gt;

&lt;p&gt;To illustrate the shape of the fix (not the real code, just the pattern):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before: comment field always present, sometimes with a placeholder&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="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;otherFields&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;comment&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;N/A&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;// After: comment field only present when there's something real to say&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="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;otherFields&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;comment&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;comment&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference looks small, but &lt;code&gt;payload.comment === undefined&lt;/code&gt; (field absent) and &lt;code&gt;payload.comment === ''&lt;/code&gt; (field present but empty) are not the same thing to a consumer checking &lt;code&gt;'comment' in payload&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Reinforced
&lt;/h2&gt;

&lt;p&gt;This bug couldn't have been caught by looking at either side in isolation — one side's code was fine, and the other side's file-path failure only mattered in combination with what the first side expected back. It only became visible once I treated it as one data flow spanning two codebases, not two separate features that happened to be adjacent.&lt;/p&gt;

&lt;p&gt;The other small thing worth remembering: "the field is missing" and "the field is empty" are not the same bug, and conflating them means you can fix one and leave the other standing. Being precise about that distinction — omit vs. empty — was what actually closed the loop.&lt;/p&gt;

</description>
      <category>debugging</category>
      <category>softwaredevelopment</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
