<?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: Tohru Yaginuma</title>
    <description>The latest articles on DEV Community by Tohru Yaginuma (@tohru_yaginuma_4f041533c6).</description>
    <link>https://dev.to/tohru_yaginuma_4f041533c6</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%2F4070197%2Fde54a3d2-386e-4e56-9ace-97700c7acafa.jpg</url>
      <title>DEV Community: Tohru Yaginuma</title>
      <link>https://dev.to/tohru_yaginuma_4f041533c6</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tohru_yaginuma_4f041533c6"/>
    <language>en</language>
    <item>
      <title>Web APIs and Batch Jobs Communicate Failure Differently</title>
      <dc:creator>Tohru Yaginuma</dc:creator>
      <pubDate>Sat, 22 Aug 2026 23:58:22 +0000</pubDate>
      <link>https://dev.to/tohru_yaginuma_4f041533c6/web-apis-and-batch-jobs-communicate-failure-differently-2mga</link>
      <guid>https://dev.to/tohru_yaginuma_4f041533c6/web-apis-and-batch-jobs-communicate-failure-differently-2mga</guid>
      <description>&lt;h2&gt;
  
  
  Long Story Short
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Web APIs can communicate failure through HTTP status codes, but batch jobs need to terminate in a way that allows the execution environment to recognize that the process has failed.&lt;/li&gt;
&lt;li&gt;In this case, I changed the error handling to throw an exception. This caused failed Lambda invocations to be reflected in the CloudWatch &lt;code&gt;Errors&lt;/code&gt; metric, which allowed the alarm to trigger.&lt;/li&gt;
&lt;li&gt;Error handling does not end inside the application. We also need to think about whether failures can be correctly observed by the monitoring and alerting systems outside it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;Recently, I had the opportunity to implement a mechanism that sends a Slack notification when a batch job running on AWS fails.&lt;/p&gt;

&lt;p&gt;The batch job itself was an existing Lambda function that was already running in production, so I initially thought the task would be relatively simple.&lt;/p&gt;

&lt;p&gt;The setup was to monitor Lambda execution errors with CloudWatch and send notifications to Slack through AWS notification services.&lt;/p&gt;

&lt;p&gt;The AWS-side configuration itself was not particularly complicated.&lt;/p&gt;

&lt;p&gt;However, when I looked at the existing batch code that we wanted to monitor, I found a problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  An Error Was Happening, but the Job Wasn't Failing
&lt;/h2&gt;

&lt;p&gt;The existing error handling included code like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;return false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;return { status: 500 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The intention was probably to indicate that an error had occurred.&lt;/p&gt;

&lt;p&gt;From Lambda's perspective, however, that is not what happens.&lt;/p&gt;

&lt;p&gt;Returning &lt;code&gt;false&lt;/code&gt; or &lt;code&gt;{ status: 500 }&lt;/code&gt; simply means that the Lambda function returned a value and completed successfully.&lt;/p&gt;

&lt;p&gt;Just because the returned value contains the number &lt;code&gt;500&lt;/code&gt; does not mean that Lambda automatically treats the invocation as a failure.&lt;/p&gt;

&lt;p&gt;As a result, even though the application code intended to handle the situation as an error, the execution environment still saw it as a successful invocation.&lt;/p&gt;

&lt;p&gt;Because of that, the invocation was not treated as a function error and was not reflected in CloudWatch's &lt;code&gt;Errors&lt;/code&gt; metric.&lt;/p&gt;

&lt;p&gt;The CloudWatch Alarm therefore did not trigger, and no Slack notification was sent.&lt;/p&gt;

&lt;p&gt;Building the monitoring mechanism made me realize that the existing error handling was not communicating the failure correctly to the systems outside the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  I Couldn't Apply the Same Mental Model as a Web API
&lt;/h2&gt;

&lt;p&gt;What I found interesting was the difference between how Web APIs and batch jobs communicate failure.&lt;/p&gt;

&lt;p&gt;In a Web API, we use HTTP status codes to communicate the result of a request to the client.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP 500 Internal Server Error
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By returning an HTTP 500 response, the client can recognize that the request has failed.&lt;/p&gt;

&lt;p&gt;When you spend a lot of time working with Web APIs, it becomes natural to think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If something goes wrong, return 500.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But what I was working with this time was not a Web API responding to an HTTP request. It was a batch job running on Lambda.&lt;/p&gt;

&lt;p&gt;There was no client waiting for an HTTP response.&lt;/p&gt;

&lt;p&gt;What mattered was not returning a value that contained &lt;code&gt;500&lt;/code&gt;, but &lt;strong&gt;making the Lambda invocation itself fail&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this case, I refactored the code to throw an exception when an error occurred:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;throw new Error(...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This caused the Lambda invocation itself to fail, which was then reflected in the CloudWatch &lt;code&gt;Errors&lt;/code&gt; metric.&lt;/p&gt;

&lt;h2&gt;
  
  
  Error Handling Doesn't End Inside the Application
&lt;/h2&gt;

&lt;p&gt;When I started this task, I thought I was simply adding a Slack notification for batch failures.&lt;/p&gt;

&lt;p&gt;But once I followed the notification flow back to the existing code, I ended up asking a more fundamental question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is this application actually communicating its failures correctly to the outside world?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Writing an error log.&lt;/p&gt;

&lt;p&gt;Returning &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Returning a value containing &lt;code&gt;500&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you only look at the application code, all of these can appear to be forms of error handling.&lt;/p&gt;

&lt;p&gt;But if the Lambda runtime and CloudWatch cannot observe the execution as a failure, that error handling does not connect to monitoring or alerting.&lt;/p&gt;

&lt;p&gt;This experience made me realize that when thinking about error handling, I should consider not only:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"How is the error handled inside the code?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;but also:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Who needs to observe this failure, and how will they know that it happened?"&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Web APIs communicate success and failure to clients through HTTP status codes.&lt;/p&gt;

&lt;p&gt;Batch jobs are different. We need to think about how the execution environment determines whether a job succeeded or failed.&lt;/p&gt;

&lt;p&gt;Implementing this Slack notification mechanism made me realize that the familiar Web API mindset of "return 500 when something fails" cannot simply be applied to batch jobs.&lt;/p&gt;

&lt;p&gt;Error handling is not only an application-level concern.&lt;/p&gt;

&lt;p&gt;I learned that part of error handling is also making sure that a failure can be correctly observed by the execution environment and the monitoring systems around the application.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>aws</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>Designing HTML Page Caching Changed How I Think About Caching</title>
      <dc:creator>Tohru Yaginuma</dc:creator>
      <pubDate>Sun, 16 Aug 2026 15:09:34 +0000</pubDate>
      <link>https://dev.to/tohru_yaginuma_4f041533c6/designing-html-page-caching-changed-how-i-think-about-caching-4k8f</link>
      <guid>https://dev.to/tohru_yaginuma_4f041533c6/designing-html-page-caching-changed-how-i-think-about-caching-4k8f</guid>
      <description>&lt;h2&gt;
  
  
  Long Story Short
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Designing HTML page caching in practice connected several concepts I had previously understood separately, such as TTL, browser caching, and where cache policies should live.&lt;/li&gt;
&lt;li&gt;In our architecture, where HTML pages are cached with CloudFront, the choice between CSR and SSR also affects whether data can benefit from the HTML cache.&lt;/li&gt;
&lt;li&gt;Starting from the simple goal of “caching pages” made me think more broadly about what should be cached and where, including API responses and backend caching.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;Recently, I had the opportunity to design HTML page caching for a web platform at work.&lt;/p&gt;

&lt;p&gt;It was my first time designing HTML page caching in practice, and we used AWS CloudFront as our CDN.&lt;/p&gt;

&lt;p&gt;I already had a basic understanding of caching.&lt;/p&gt;

&lt;p&gt;Store a response once, reuse it later, reduce server-side processing, and improve response times.&lt;/p&gt;

&lt;p&gt;At least conceptually, I thought I understood it.&lt;/p&gt;

&lt;p&gt;The task also seemed relatively simple at first.&lt;/p&gt;

&lt;p&gt;If we cache HTML pages in CloudFront, requests don’t need to reach the application every time.&lt;/p&gt;

&lt;p&gt;Then we just need to decide which pages to cache and how long their TTLs should be.&lt;/p&gt;

&lt;p&gt;Once I actually started working on the design, however, I realized there was more to think about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking at CSR and SSR Through the Lens of Caching
&lt;/h2&gt;

&lt;p&gt;One of the things I found particularly interesting was the relationship between CSR, SSR, and HTML page caching.&lt;/p&gt;

&lt;p&gt;I had previously thought about CSR and SSR mainly in terms of where rendering happens.&lt;/p&gt;

&lt;p&gt;With CSR, rendering happens in the browser. With SSR, HTML is generated on the server.&lt;/p&gt;

&lt;p&gt;From there, I would usually think about things like SEO and initial page-load performance.&lt;/p&gt;

&lt;p&gt;None of that was new to me.&lt;/p&gt;

&lt;p&gt;But designing HTML page caching gave me another perspective on CSR and SSR.&lt;/p&gt;

&lt;p&gt;In our case, CloudFront caches HTML pages.&lt;/p&gt;

&lt;p&gt;This means that when data is fetched during SSR and included in the HTML, the resulting HTML — including that data — can be served from the CloudFront cache.&lt;/p&gt;

&lt;p&gt;With CSR, on the other hand, the browser fetches data from an API after receiving the HTML. That data is therefore outside the HTML page cache.&lt;/p&gt;

&lt;p&gt;Of course, this doesn’t mean that “SSR is cacheable” and “CSR is not.”&lt;/p&gt;

&lt;p&gt;SSR-generated HTML doesn’t have to be cached, and API responses fetched through CSR can be cached at other layers.&lt;/p&gt;

&lt;p&gt;But in our architecture, the choice between CSR and SSR affects whether that data can benefit from the HTML page cache.&lt;/p&gt;

&lt;p&gt;I had previously viewed CSR and SSR primarily as rendering strategies, so seeing this connection in practice was interesting to me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caching Exists Beyond HTML
&lt;/h2&gt;

&lt;p&gt;Thinking about CSR led me to another question:&lt;/p&gt;

&lt;p&gt;What about the API responses fetched by the client?&lt;/p&gt;

&lt;p&gt;For this project, the scope was HTML page caching with CloudFront. I hadn't really considered API response caching as part of the same problem at that point.&lt;/p&gt;

&lt;p&gt;But while thinking about CSR and SSR, I realized that API responses can also be cached at different layers, such as a CDN or the backend itself.&lt;/p&gt;

&lt;p&gt;That made me realize that I had been thinking about caching too narrowly, mostly in terms of HTML pages.&lt;/p&gt;

&lt;p&gt;Do we cache the HTML?&lt;/p&gt;

&lt;p&gt;Do we cache API responses?&lt;/p&gt;

&lt;p&gt;Do we cache something in the browser?&lt;/p&gt;

&lt;p&gt;At the CDN?&lt;/p&gt;

&lt;p&gt;On the backend?&lt;/p&gt;

&lt;p&gt;They are all forms of caching, but each comes with different considerations.&lt;/p&gt;

&lt;p&gt;I didn’t design API caching as part of this project.&lt;/p&gt;

&lt;p&gt;Still, thinking about HTML page caching made me look beyond HTML and think more deeply about caching across the system. That itself was an important learning for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  TTL Needs a Reason Too
&lt;/h2&gt;

&lt;p&gt;TTL was another area I hadn’t thought deeply about until I had to design it myself.&lt;/p&gt;

&lt;p&gt;A longer TTL allows a cached response to be reused for longer.&lt;/p&gt;

&lt;p&gt;But it also means that stale content can remain visible for longer after the underlying page has been updated.&lt;/p&gt;

&lt;p&gt;Not every page has the same traffic or update frequency either.&lt;/p&gt;

&lt;p&gt;Some pages receive a lot of traffic but rarely change. Others may change relatively frequently.&lt;/p&gt;

&lt;p&gt;Once I started thinking about actual pages, I realized that TTL wasn’t simply a matter of choosing a “long” or “short” value.&lt;/p&gt;

&lt;p&gt;It was a trade-off involving how fresh each page needed to be.&lt;/p&gt;

&lt;p&gt;I used to think of TTL mostly as a configuration value. This experience made me realize that the value itself should have a reason behind it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Should Caching Decisions Live?
&lt;/h2&gt;

&lt;p&gt;Another question was where the decision about which pages to cache should live.&lt;/p&gt;

&lt;p&gt;We could define rules in CloudFront based on URLs and other conditions.&lt;/p&gt;

&lt;p&gt;But the application often has the most context about whether a particular page is safe to cache.&lt;/p&gt;

&lt;p&gt;In our design, we decided to keep information about cacheable pages on the application side and use that information to control caching behavior in CloudFront.&lt;/p&gt;

&lt;p&gt;Before this project, I partly thought of HTML page caching as CDN configuration.&lt;/p&gt;

&lt;p&gt;Working on the design made me realize that it can also be an application design question:&lt;/p&gt;

&lt;p&gt;Where should the responsibility for caching decisions live?&lt;/p&gt;

&lt;h2&gt;
  
  
  A Clearer Mental Model of Caching
&lt;/h2&gt;

&lt;p&gt;I don’t feel that I learned one particular new caching technology from this experience.&lt;/p&gt;

&lt;p&gt;The bigger learning was that concepts I had previously understood separately started to connect.&lt;/p&gt;

&lt;p&gt;CSR and SSR.&lt;/p&gt;

&lt;p&gt;CDNs.&lt;/p&gt;

&lt;p&gt;TTL.&lt;/p&gt;

&lt;p&gt;Browser caching.&lt;/p&gt;

&lt;p&gt;API response caching.&lt;/p&gt;

&lt;p&gt;Backend caching.&lt;/p&gt;

&lt;p&gt;I already knew these terms and concepts individually.&lt;/p&gt;

&lt;p&gt;But designing HTML page caching myself forced me to think about them in terms of a real system: What should we do here?&lt;/p&gt;

&lt;p&gt;The questions I ask about caching have also changed.&lt;/p&gt;

&lt;p&gt;What are we caching?&lt;/p&gt;

&lt;p&gt;Where are we caching it?&lt;/p&gt;

&lt;p&gt;Who can safely share the cached response?&lt;/p&gt;

&lt;p&gt;How fresh does it need to be?&lt;/p&gt;

&lt;p&gt;There is still plenty I don’t know.&lt;/p&gt;

&lt;p&gt;But I now have a much clearer idea of what questions I should be asking.&lt;/p&gt;

&lt;p&gt;Designing HTML page caching gave me a much clearer mental model of caching than I had before.&lt;/p&gt;

</description>
      <category>cloudfront</category>
      <category>webdev</category>
      <category>architecture</category>
      <category>web</category>
    </item>
    <item>
      <title>Async-First Doesn’t Mean Async-Only</title>
      <dc:creator>Tohru Yaginuma</dc:creator>
      <pubDate>Sun, 09 Aug 2026 20:27:34 +0000</pubDate>
      <link>https://dev.to/tohru_yaginuma_4f041533c6/async-first-doesnt-mean-async-only-ebm</link>
      <guid>https://dev.to/tohru_yaginuma_4f041533c6/async-first-doesnt-mean-async-only-ebm</guid>
      <description>&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Asynchronous discussions can lose momentum because participants are focused on different tasks. For complex or important topics, it’s often better to switch to synchronous communication.&lt;/li&gt;
&lt;li&gt;At least in my experience working in a Japanese-speaking organization, AI-generated messages are often still too verbose to send as-is. As writing becomes cheaper, it’s even more important to reduce the cognitive load on readers.&lt;/li&gt;
&lt;li&gt;Async-first does not mean async-only. Keeping written records while introducing short meetings when necessary can reduce the overall cost of communication.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;I currently work from Vancouver, Canada, for a fully remote and fully flexible organization based in Japan.&lt;/p&gt;

&lt;p&gt;Since everyone works on their own schedule, much of our day-to-day communication, decision-making, and discussion happens asynchronously.&lt;/p&gt;

&lt;p&gt;There are many benefits to this way of working. People can think at their own pace, and discussions naturally leave a written record.&lt;/p&gt;

&lt;p&gt;As someone who is fairly introverted, I also appreciate having time to think through my ideas before sharing them.&lt;/p&gt;

&lt;p&gt;Recently, however, I’ve started to realize that keeping every discussion asynchronous is not always the most efficient approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complex discussions are expensive to read
&lt;/h2&gt;

&lt;p&gt;When discussing multiple options, I usually start by sharing my recommendation, then document the reasoning behind it and the pros and cons of alternative approaches.&lt;/p&gt;

&lt;p&gt;The more complicated the topic becomes, the longer the document becomes.&lt;/p&gt;

&lt;p&gt;Writing requires effort, but so does reading. Someone has to understand the background, process the trade-offs, form an opinion, and respond.&lt;/p&gt;

&lt;p&gt;Lately, I’ve become more aware of the reader’s cost than the writer’s.&lt;/p&gt;

&lt;p&gt;In our company, Japanese is the shared language, and much of our written communication is now assisted by AI. While AI makes it easier to produce long documents, the resulting text can still be unnecessarily verbose or difficult to follow.&lt;/p&gt;

&lt;p&gt;AI makes writing cheaper. It does not necessarily make reading cheaper.&lt;/p&gt;

&lt;p&gt;On top of that, everyone already has their own priorities.&lt;/p&gt;

&lt;p&gt;When a message arrives, people are often in the middle of something else. Replies become spread out over hours or days. Discussions can lose momentum, and important but non-urgent topics may eventually fade away without reaching a conclusion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Write first, then talk
&lt;/h2&gt;

&lt;p&gt;To address this, I’ve started scheduling short meetings whenever a discussion becomes too complex to resolve asynchronously.&lt;/p&gt;

&lt;p&gt;This doesn’t mean abandoning written communication.&lt;/p&gt;

&lt;p&gt;I still document the background, context, and available options. AI helps me draft these documents quickly, but I always review and simplify them before sharing.&lt;/p&gt;

&lt;p&gt;Once everyone has the necessary context, a short meeting allows the team to focus on the same problem at the same time.&lt;/p&gt;

&lt;p&gt;I’ve found that questions that would otherwise require many rounds of messages can often be resolved in just a few minutes of conversation.&lt;/p&gt;

&lt;p&gt;There’s another benefit as well.&lt;/p&gt;

&lt;p&gt;In a fully remote, fully flexible environment, opportunities to talk to teammates naturally become less frequent. Even when the purpose is purely technical, real-time conversations can help build mutual understanding and trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Async-first doesn’t mean avoiding meetings
&lt;/h2&gt;

&lt;p&gt;I used to believe that if a team was designed around asynchronous communication, then most discussions should stay asynchronous.&lt;/p&gt;

&lt;p&gt;I don’t think that anymore.&lt;/p&gt;

&lt;p&gt;Asynchronous communication is a tool, not the goal.&lt;/p&gt;

&lt;p&gt;If a topic is simple, asynchronous communication works well. If a discussion becomes complex, stalls, or requires multiple rounds of clarification, a short synchronous conversation can be the more efficient option.&lt;/p&gt;

&lt;p&gt;I’ve learned not to hesitate to schedule a meeting when it helps move an important discussion forward.&lt;/p&gt;

&lt;p&gt;Being async-first doesn’t mean avoiding synchronous communication.&lt;/p&gt;

&lt;p&gt;It means choosing the communication style that minimizes the overall cost for the team.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>remote</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
