<?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: hans</title>
    <description>The latest articles on DEV Community by hans (@hanswys).</description>
    <link>https://dev.to/hanswys</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%2F3804880%2F2df71985-72af-4134-8ba1-fa3c1d1bfee7.jpg</url>
      <title>DEV Community: hans</title>
      <link>https://dev.to/hanswys</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hanswys"/>
    <language>en</language>
    <item>
      <title>Bridging the Tracking Gap: Implementing Manual Returns for Retailers and Customers</title>
      <dc:creator>hans</dc:creator>
      <pubDate>Mon, 16 Mar 2026 03:15:00 +0000</pubDate>
      <link>https://dev.to/hanswys/bridging-the-tracking-gap-implementing-manual-returns-for-retailers-and-customers-2il5</link>
      <guid>https://dev.to/hanswys/bridging-the-tracking-gap-implementing-manual-returns-for-retailers-and-customers-2il5</guid>
      <description>&lt;p&gt;In the world of e-commerce returns, "Mail Returns" (where a customer ships a parcel themselves) are often a black hole. Because the system doesn't generate a label, there is no automatic tracking. No one knows where the package is, and the "he-said-she-said" over shipping proof becomes a support nightmare.&lt;/p&gt;

&lt;p&gt;We recently tackled this by building a manual tracking flow that serves two distinct masters: &lt;strong&gt;Retailers&lt;/strong&gt; needing oversight and &lt;strong&gt;Customers&lt;/strong&gt; needing peace of mind. Here is how we built a unified tracking system across two different UIs with a shared backend.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: The "Mail Return" Black Hole
&lt;/h2&gt;

&lt;p&gt;When a return is marked as &lt;strong&gt;Mail&lt;/strong&gt;, the customer uses their own shipping method (e.g., national post or a local courier).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;For Staff:&lt;/strong&gt; They can't see when a return is coming or if it's even been sent.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;For Customers:&lt;/strong&gt; They have no way to "prove" they shipped it within the platform.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Technical Gap:&lt;/strong&gt; Since no label is generated by our system, no tracking data is auto-filled.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We needed a way for both parties to manually input &lt;strong&gt;Tracking Numbers&lt;/strong&gt; and &lt;strong&gt;Courier Names&lt;/strong&gt; into a shared source of truth.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Architecture: One Logic, Two Gates
&lt;/h2&gt;

&lt;p&gt;Instead of building two separate tracking services, we opted for a &lt;strong&gt;shared form object&lt;/strong&gt; pattern. This ensures that whether a retailer updates a number or a customer does it, the validation and persistence logic remain identical.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Backend Foundation
&lt;/h3&gt;

&lt;p&gt;We leveraged a &lt;code&gt;Shipment::UpdateForm&lt;/code&gt; object to handle the heavy lifting. This kept our controllers thin and our models clean.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;colgroup&gt;
&lt;col&gt;
&lt;col&gt;
&lt;col&gt;
&lt;/colgroup&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;strong&gt;Feature&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;strong&gt;Implementation Detail&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;strong&gt;Endpoints&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;PATCH .../customer/shipment/tracking&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;PUT .../retailer/shipment_tracking&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;strong&gt;Validation&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Max 64 chars for tracking; Max 128 for courier; Alphanumeric only.&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;strong&gt;State Guard&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Only allows updates if the return is in &lt;code&gt;pending&lt;/code&gt; or &lt;code&gt;pending_action&lt;/code&gt;.&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;strong&gt;Data Model&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Updates &lt;code&gt;tracking_number&lt;/code&gt; and &lt;code&gt;custom_courier_name&lt;/code&gt; on the &lt;code&gt;Shipment&lt;/code&gt; model.&lt;/p&gt;&lt;/td&gt;
&lt;td colspan="1" rowspan="1"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro-Tip:&lt;/strong&gt; Avoid heavy model callbacks. By performing the update within the Form Object’s &lt;code&gt;submit&lt;/code&gt; method, we ensured that side effects (like invalidating cache) only happened when the form was actually valid.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Frontend Implementation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Retailer Experience (React + Polaris)
&lt;/h3&gt;

&lt;p&gt;In the retailer dashboard, we focused on speed and clarity for support agents.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Contextual UI:&lt;/strong&gt; If the shipping method is "Mail," an "Add Tracking Number" button appears.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Modal Flow:&lt;/strong&gt; Clicking the button triggers a Polaris modal that pre-fills existing data if the agent is editing rather than adding.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Immediate Feedback:&lt;/strong&gt; Upon saving, the app invalidates the return order query, triggering an instant UI refresh to show the new "Track" button (which links to the external tracking URL).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Customer Experience (Remix + React)
&lt;/h3&gt;

&lt;p&gt;The customer-facing side needed to be more "walk-through" style. We integrated this directly into the &lt;strong&gt;Return Summary&lt;/strong&gt; page.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Progressive Disclosure:&lt;/strong&gt; We show the "Tracking Number" and "Courier" labels even if empty to signal that this information is expected.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Inline Editing:&lt;/strong&gt; Instead of a modal, we used an inline form. The "Save" button remains disabled until both fields are filled to ensure data integrity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;I18n Ready:&lt;/strong&gt; Since we operate globally, all labels (&lt;code&gt;add_tracking_button_label&lt;/code&gt;, etc.) are served via the API to support multi-language storefronts.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Key Technical Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Shared Validation is King:&lt;/strong&gt; By using a single Form Object, we prevented "data drift" where a retailer might be able to enter a character that a customer’s UI would reject.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Status-Awareness:&lt;/strong&gt; Don't let users edit tracking after a return is "Completed" or "Cancelled." Hard-coding these guards into the service layer prevents API abuse.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The "Track" Button Logic:&lt;/strong&gt; We implemented a fallback mechanism. The UI displays the &lt;code&gt;custom_courier_name&lt;/code&gt; if present, but falls back to the system's &lt;code&gt;courierName&lt;/code&gt; for legacy data, ensuring the "Track Shipment" link never breaks.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Manual tracking isn't just about adding a text field; it's about creating a reliable audit trail. By building a unified backend and tailoring the UI to the specific needs of retailers and customers, we turned a "black hole" into a transparent part of the return lifecycle.&lt;/p&gt;

</description>
      <category>returns</category>
      <category>ecommerce</category>
      <category>api</category>
      <category>learning</category>
    </item>
    <item>
      <title>Beyond Manual Coding: Implementing an Agentic CI/CD Workflow at PostCo</title>
      <dc:creator>hans</dc:creator>
      <pubDate>Thu, 12 Mar 2026 03:15:00 +0000</pubDate>
      <link>https://dev.to/hanswys/beyond-manual-coding-implementing-an-agentic-cicd-workflow-at-postco-1nc3</link>
      <guid>https://dev.to/hanswys/beyond-manual-coding-implementing-an-agentic-cicd-workflow-at-postco-1nc3</guid>
      <description>&lt;p&gt;In a high-growth startup environment like PostCo, the goal is always the same: &lt;strong&gt;Ship high-quality features, faster.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As a Junior Software Engineer, I quickly realized that traditional "manual" workflows—where every line of boilerplate and every initial refactor is typed by hand—are no longer the most efficient way to deliver value. Instead, I’ve been developing an "Agentic Workflow" that uses AI agents as force multipliers within our CI/CD pipeline.&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%2Flrx37weice76tr1ap7e8.jpg" 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%2Flrx37weice76tr1ap7e8.jpg" alt="CI/CD Diagram" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  The Problem: The "Boilerplate" Bottleneck
&lt;/h4&gt;

&lt;p&gt;Most developers spend a significant portion of their day on repetitive tasks: setting up file structures, writing unit tests, or refactoring existing methods to fit new requirements. These tasks are necessary but often delay the critical "thinking" work.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Solution: The 4-Stage Agentic Loop
&lt;/h4&gt;

&lt;p&gt;Our workflow at PostCo is designed to automate the execution phase while keeping the "Intent" and "Quality Control" firmly in human hands.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Requirements and Context (Notion + Linear)&lt;/strong&gt; Every feature starts with a deep dive. We use Notion for Implementation Docs that define the &lt;em&gt;why&lt;/em&gt; and &lt;em&gt;how&lt;/em&gt;. These are synced to Linear, providing a clear map for the development process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Agentic Execution (GitHub + Claude)&lt;/strong&gt; The moment a PR is created, a &lt;strong&gt;Claude Agent (Planner/Executor)&lt;/strong&gt; analyzes the Implementation Doc and the existing codebase. It doesn’t just suggest code; it executes it, pushing a functional starting point to the branch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The "Checker" Loop&lt;/strong&gt; Code shouldn't just be written; it should be verified. We employ a second &lt;strong&gt;Claude Agent (Code Checker)&lt;/strong&gt; that reviews the first agent’s work. They loop bidirectionally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Executor&lt;/strong&gt; pushes code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Checker&lt;/strong&gt; identifies logic gaps or style inconsistencies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Executor&lt;/strong&gt; refines the code.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. The Human-in-the-Loop: Tech Lead Review&lt;/strong&gt; Once the agents have iterated to a point of stability, the "Review" phase begins. I take the refined code and walk through it with my &lt;strong&gt;Tech Lead&lt;/strong&gt;. This is where we discuss the "Big Picture"—scalability, edge cases the agents might have missed, and alignment with our broader engineering goals at PostCo.&lt;/p&gt;

&lt;p&gt;By the time my Tech Lead sees a PR, the "noise" has been filtered out. The code is already linted, tested, and structurally aligned with the requirements. This allows our senior engineers to spend their time mentoring and architecting rather than correcting syntax or boilerplate.&lt;/p&gt;

&lt;p&gt;In 2026, being a "good developer" isn't just about how well you write code—it's about how well you manage the systems that produce it. This agentic workflow is just the beginning of how we’re scaling our engineering efforts.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>beginners</category>
      <category>devops</category>
    </item>
    <item>
      <title>Syncing Rails Validations with Formik: A Practical Approach</title>
      <dc:creator>hans</dc:creator>
      <pubDate>Tue, 10 Mar 2026 03:12:00 +0000</pubDate>
      <link>https://dev.to/hanswys/syncing-rails-validations-with-formik-a-practical-approach-nhh</link>
      <guid>https://dev.to/hanswys/syncing-rails-validations-with-formik-a-practical-approach-nhh</guid>
      <description>&lt;p&gt;In many SaaS apps, we allow "placeholder" data during onboarding to reduce friction. But what happens when that data needs to be validated later? I recently tackled this at PostCo by building a bridge between our Rails backend and React frontend.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Backend Validator:&lt;/strong&gt; Don't just rely on &lt;code&gt;presence: true&lt;/code&gt;. I created a &lt;code&gt;PlaceholderValidator&lt;/code&gt; to check against specific strings like "Address pending" or "00000".&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Error Bridge:&lt;/strong&gt; Formik expects a specific shape. I wrote a utility to transform Rails' &lt;code&gt;errors.messages&lt;/code&gt; into a flattened object that Formik can consume, including a case-mapping layer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The UX Trick:&lt;/strong&gt; If a field has a placeholder, the user shouldn't have to delete it manually. I implemented a "clear on edit" utility that treats placeholders as &lt;code&gt;""&lt;/code&gt; in the UI, triggering immediate Yup validation.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Handling nested errors is messy, but a little bit of mapping logic goes a long way in making a Junior-built feature feel Senior-grade.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>react</category>
      <category>learning</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
