<?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: Mellina Yonashiro</title>
    <description>The latest articles on DEV Community by Mellina Yonashiro (@yogmel).</description>
    <link>https://dev.to/yogmel</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%2F384906%2F28c8a9d8-c6c7-476d-bd97-03624e3c12ff.jpeg</url>
      <title>DEV Community: Mellina Yonashiro</title>
      <link>https://dev.to/yogmel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yogmel"/>
    <language>en</language>
    <item>
      <title>Use case: Creating an agentic workflow from frontend to test automation</title>
      <dc:creator>Mellina Yonashiro</dc:creator>
      <pubDate>Wed, 30 Sep 2026 09:04:55 +0000</pubDate>
      <link>https://dev.to/yogmel/use-case-creating-an-agentic-workflow-from-frontend-to-test-automation-35m7</link>
      <guid>https://dev.to/yogmel/use-case-creating-an-agentic-workflow-from-frontend-to-test-automation-35m7</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Every time our Frontend (FE) changed a UI component, a little &lt;em&gt;nightmare&lt;/em&gt; started: tests broke, the Test Automation (TA) team was called and what seemed to be endless back-and-forth happened.&lt;/p&gt;

&lt;p&gt;I saw an opportunity to reduce this friction by &lt;strong&gt;introducing a headless agent into our workflow&lt;/strong&gt; - one that picks up the broken tests and tries to fix them.&lt;/p&gt;

&lt;p&gt;Developing automations like this is a hard process though - designing a meaningful flow, dealing with cross-dependencies and accesses that might turn into dead-ends, and other unknown issues along the way. In this article, I'll walk you through the problem, the design decisions and trade-offs, and how it was implemented end-to-end.&lt;/p&gt;

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

&lt;p&gt;When working in frontend, we have some CI steps set up. One of them is to run test automation. More often than not, &lt;em&gt;these tests fail because frontend changed the HTML structure or user flow altogether&lt;/em&gt;. When this happens, we have to go through these steps shown in the diagram.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2fagdc060629o9vn6n58.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2fagdc060629o9vn6n58.jpg" alt="Flow diagram showing a cross-team loop between Frontend and UI Test Automation when UI tests fail. A Frontend pull request triggers tests A, B, and C, which fail. UI Test Automation responds by skipping tests A–C and later creating and merging a PR to address them. Meanwhile, Frontend’s CI passes because the tests are skipped, so the Frontend PR is merged to main. Afterward, UI Test Automation runs again and creates a fix PR; during this period, there’s a warning state where automation “passes” but tests remain skipped on main, until the fixes are merged and tests are stabilized on main." width="799" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Summarizing: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;FE: makes a breaking change. Tests break.&lt;/li&gt;
&lt;li&gt;TA: skip related tests.&lt;/li&gt;
&lt;li&gt;FE: CI pipeline now passes and merges the feature branch.&lt;/li&gt;
&lt;li&gt;TA: unskip the test, fix them, open a pull request and merge.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This workflow triggers for every feature branch opened by FE. The steps have to be done &lt;em&gt;in sequence&lt;/em&gt;, and the flow involves at least &lt;em&gt;four engineers&lt;/em&gt; (the ones writing the code and the ones reviewing it), &lt;em&gt;context switching&lt;/em&gt;, and &lt;em&gt;cross-team dependency&lt;/em&gt;. Also, there is a period in between that we &lt;em&gt;lose test coverage:&lt;/em&gt; the tests pass, but only because they're skipped.&lt;/p&gt;

&lt;p&gt;For our experiment, we decided to narrow our focus to something specific and easy to fix: when FE changes a UI component, needing a &lt;strong&gt;&lt;em&gt;selector&lt;/em&gt;&lt;/strong&gt; change; and only &lt;strong&gt;smoke tests&lt;/strong&gt; would be used as the gate. I’ll give you more context below.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are selectors in UI testing?
&lt;/h3&gt;

&lt;p&gt;In frontend applications, we implement components that users interact with. These components are accessible - either by their text, input placeholder, role or by non-visible screen-reader aria-* attributes, falling back to &lt;code&gt;data-testid&lt;/code&gt;. These identifiers, &lt;strong&gt;selectors&lt;/strong&gt; (or locators, as some libraries call them), are used by testing libraries, such as Testing Library, Playwright or Cypress to conduct automated tests, which simulate user flows.&lt;/p&gt;

&lt;p&gt;Whenever a text, label, or whatever is used to identify the testing component is changed, the tests break. You may think this is a rare problem, but it can happen in some situations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the frontend is using a &lt;strong&gt;design system&lt;/strong&gt; and it’s moving to another one, this will likely break selectors. This will happen a lot during migrations.&lt;/li&gt;
&lt;li&gt;If the &lt;strong&gt;text&lt;/strong&gt; is changed and sometimes its &lt;strong&gt;position&lt;/strong&gt;, this will likely break as well.&lt;/li&gt;
&lt;li&gt;Any &lt;strong&gt;HTML change&lt;/strong&gt; can break the selector.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What are smoke tests?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Smoke tests are a small set of end-to-end tests that cover only the most critical flows of an application&lt;/strong&gt;. The idea is not to test everything, but to quickly answer one question: is the app still working? Because they are fewer and faster than the full suite, they are &lt;em&gt;cheaper&lt;/em&gt; to run on every feature branch - and for this experiment, that also meant fewer (and less flaky) failures for the agent to deal with. If you want to go deeper, &lt;a href="https://uilicious.com/blog/what-is-smoke-testing-how-to-automate-it/" rel="noopener noreferrer"&gt;this article&lt;/a&gt; is a good starting point.&lt;/p&gt;

&lt;h3&gt;
  
  
  Constraints
&lt;/h3&gt;

&lt;p&gt;This use case is &lt;strong&gt;&lt;em&gt;not&lt;/em&gt;&lt;/strong&gt; a one-size-fits-all solution, but it can give you some ideas. We had this specific setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Two separate codebases&lt;/strong&gt;: some projects keep the frontend and test automation suite in the same codebase. This is &lt;strong&gt;&lt;em&gt;not&lt;/em&gt;&lt;/strong&gt; the case for our project - there is one repository for the frontend and another for test automation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FE can’t be built locally in TA CI&lt;/strong&gt;: this means that the TA pipeline can’t test on the same conditions as FE does. FE runs on a local build and triggers smoke tests there. On the other hand, TA can’t - it can only run against some deployed QA environments. Bottom line: because of this, the same tests that fail in the FE pipeline can pass in TA (and vice-versa). This is why one of the agent's outcomes is &lt;em&gt;Not reproduced&lt;/em&gt; (more on that below).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Different CI/CD tools per repository&lt;/strong&gt;: FE runs its CI pipeline in Drone and its CD (deployments) in GitHub Actions (GA). TA only has a CI pipeline in Drone - it doesn't deploy anything, just some reports, which are managed by Drone as well.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Proposed Solution
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj5rp8n2wpfzitmt6pha1.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj5rp8n2wpfzitmt6pha1.jpg" alt="Flowchart showing cross-team loop - on the Frontend side, a pull request is created, tests A/B/C fail, and Drone sends a  raw `repository_dispatch` endraw  event to notify UI Test Automation. On the UI Test Automation side, the notification triggers a process where an automated agent (shown inside a dashed box labeled “automated”) updates broken tests, checks a decision point “Tests fixed?”, and if fixed opens a PR for review, after which a human reviews and merges the PR, feeding the result back so the overall pipeline can proceed. After it, back to Frontend, CI passes leading to the PR being merged." width="799" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The overall idea is that whenever there is a change in FE, the CI pipeline runs smoke tests, and if they fail, it would trigger a workflow agent in TA by sending a &lt;code&gt;repository_dispatch&lt;/code&gt; event to the TA repo (through the GitHub REST API), which starts a GitHub Actions workflow there. This agent would then try to fix the non-passing tests. If it is successful, it would create a PR for a human to check. If this PR was merged, then the FE CI pipeline would pass.&lt;/p&gt;

&lt;p&gt;Some notes to consider here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When TA PR is merged, FE CI is &lt;strong&gt;&lt;em&gt;not&lt;/em&gt;&lt;/strong&gt; triggered automatically. A user has to re-trigger the job in Drone. This could be tightened in the future, by adding another task to do it when the branch is merged to main in TA.&lt;/li&gt;
&lt;li&gt;Also when TA PR is merged to main, it will cause &lt;strong&gt;FE main and other branches to fail the tests&lt;/strong&gt; (because they would not up-to-date yet with the feature branch). This is a known trade-off, which is acceptable, considering we are speeding up the process, at the same time controlling the flow (because a human will need to push the button to merge TA PR into main).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After coming to this high-level design (after a lot of back and forth), I started to think about the details.&lt;/p&gt;

&lt;h3&gt;
  
  
  Designing the automation flow
&lt;/h3&gt;

&lt;p&gt;To implement this, some questions had to be answered:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Timing&lt;/strong&gt;: FE &lt;em&gt;can&lt;/em&gt; trigger the agent every time tests fail, but &lt;em&gt;should&lt;/em&gt; it?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform&lt;/strong&gt;: &lt;em&gt;Where&lt;/em&gt; should the agentic workflow run - &lt;em&gt;Drone&lt;/em&gt; or &lt;em&gt;GitHub Actions&lt;/em&gt;?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-repo/CI communication&lt;/strong&gt;: How would the &lt;em&gt;connection&lt;/em&gt; between FE and TA repositories happen?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outcomes&lt;/strong&gt;: What happens if the agent can’t fix the test? What if it can? What if it can’t reproduce (tests pass)?&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Timing: when should the agent run?
&lt;/h4&gt;

&lt;p&gt;Considering that every feature branch in FE triggers a CI pipeline and because of cost-efficiency and having more control, especially during the testing of this automation, I chose to trigger the agent under certain conditions. The feature branch changes would need to be deployed into a specific environment (we have more than 4 environments set up, so we can test multiple feature branches at the same time). Let’s call this environment the &lt;strong&gt;QA Env&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Having this environment is also needed because of the constraint that TA can only run smoke tests against deployed environments.&lt;/p&gt;

&lt;h4&gt;
  
  
  Platform: where should the agent run?
&lt;/h4&gt;

&lt;p&gt;There are multiple steps of the automation design, and one of them is to decide where to trigger the agentic flow. As mentioned in the Constraints, we were using both &lt;em&gt;Drone&lt;/em&gt; and &lt;em&gt;GA&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Our Drone was becoming crowded already - it was running processes on all branches, including main. And I realised that with GA we would have more control on when and how to trigger - their interface was more friendly for testing and manual triggering. Also, I could create a separated flow only for the agent, not mixing with other processes.&lt;/p&gt;

&lt;p&gt;For those reasons, I decided to go with GA for the agentic workflow, therefore the point of connection should be &lt;strong&gt;GA for triggering the agent&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Connecting the dots: how do the repositories communicate?
&lt;/h4&gt;

&lt;p&gt;The initiator of this automation is FE when two conditions are met: &lt;strong&gt;smoke tests fail &lt;em&gt;AND&lt;/em&gt; it is deployed to QA Env&lt;/strong&gt;. Since FE CI runs in Drone and FE CD runs in GA, these two conditions are checked in different places. I had two options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run smoke tests in GA after QA Env deployment, and then send the dispatch event to TA.&lt;/li&gt;
&lt;li&gt;In Drone, after smoke tests step, check if QA Env is deployed and then send the dispatch event to TA.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first option would be more costly - we would need to bring the smoke test step into the GA, and it would duplicate the step in both CI and CD pipelines.&lt;/p&gt;

&lt;p&gt;The second option, on the other hand, would be a simple check. That’s because we use GitHub labels to track which environment is being deployed. We already had a flow set up where adding a label to a PR triggers a deployment to that environment.&lt;/p&gt;

&lt;p&gt;Given that, we could check the GitHub label right after the Smoke tests step. If both smoke tests failed and the deployment label was set as QA Env, then &lt;strong&gt;Drone would send the dispatch event to TA&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Given that CI and CD happen in different environments, one trade-off here is that it is possible that Drone will receive and identify the label, but the GA deployment process can fail, or Drone check can happen before the branch is actually deployed. I accepted these trade-offs, because the worst that could happen is that the tests run against an environment &lt;em&gt;without&lt;/em&gt; FE change, triggering a &lt;em&gt;false negative&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;For the race condition, as soon as the label is added, usually the CD happens in 5 minutes. The entire CI pipeline usually takes 15 minutes, making it a comfortable pass, since the check would be as the last step of the CI pipeline.&lt;/p&gt;

&lt;h4&gt;
  
  
  Outcomes: what should the agent report?
&lt;/h4&gt;

&lt;p&gt;The FE created a new feature that broke smoke tests, it was deployed to QA Env, and the dispatch event was sent. Now what? What should the agent do?&lt;/p&gt;

&lt;p&gt;The entire goal is for the broken smoke tests to be fixed, by updating the selectors. For that, I took some decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We had more than &lt;em&gt;40&lt;/em&gt; written smoke tests in total. For the purposes of this automation, we &lt;em&gt;don’t&lt;/em&gt; need the entire suite to run. Therefore, there should be a payload carrying the information of &lt;strong&gt;which smoke tests have failed in the FE side&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;When attempting to fix the test, I see some outcomes, overall:

&lt;ul&gt;
&lt;li&gt;The agent &lt;strong&gt;is able&lt;/strong&gt; to fix the test.&lt;/li&gt;
&lt;li&gt;The agent &lt;strong&gt;is not able&lt;/strong&gt; to fix the test.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;tests pass&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, with three main outcomes, plus two edge cases we would have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If &lt;strong&gt;tests are fixed&lt;/strong&gt;, then a PR is created in TA side, and a comment is sent to FE PR with the message of &lt;strong&gt;“Already repaired”&lt;/strong&gt;, along with the &lt;em&gt;link&lt;/em&gt; to the TA PR.&lt;/li&gt;
&lt;li&gt;If &lt;strong&gt;tests were already fixed&lt;/strong&gt; by a prior run, then a comment is sent to FE PR with the message of &lt;strong&gt;“Already repaired”&lt;/strong&gt;, &lt;em&gt;without&lt;/em&gt; a link to a TA PR.&lt;/li&gt;
&lt;li&gt;If &lt;strong&gt;tests are not fixed&lt;/strong&gt;, then &lt;em&gt;no&lt;/em&gt; PR is created, but a message is sent back to FE PR with the message of &lt;strong&gt;“Possible regression”&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;If &lt;strong&gt;tests pass&lt;/strong&gt;, then a message is sent back to FE PR with the message of &lt;strong&gt;“Not reproduced”&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;If there is &lt;strong&gt;an environment failure&lt;/strong&gt; (e.g., QA Env is unreachable), the agent aborts and no message is sent back to FE PR - the error is only logged in the GA run.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although it is possible to see all the logs in GA workflows page, I wanted to make provide as much up front transparency as possible, by ending the flow in FE PR, with a message on what was the outcome of the entire process.&lt;/p&gt;

&lt;p&gt;Then, with most of the definitions set up, we can finally look at how the agent does this, and the harnesses around it, in the next sections.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up the prerequisites
&lt;/h3&gt;

&lt;p&gt;As you will see, there are multiple steps to implement the flow end-to-end.&lt;/p&gt;

&lt;h4&gt;
  
  
  Creating the agent harness
&lt;/h4&gt;

&lt;p&gt;A &lt;strong&gt;harness&lt;/strong&gt; is everything around the model that shapes how the agent works - instruction files, skills, commands, scripts and guardrails - so it behaves predictably without someone guiding it step by step.&lt;/p&gt;

&lt;p&gt;The initial state of the codebase was not ready for AI at all. To avoid increasing the blast radius of the project, I decided to just create enough harnesses to implement this POC. Two assets were created:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AGENTS.md&lt;/strong&gt;: with &lt;code&gt;CLAUDE.md&lt;/code&gt; pointing to it, which enables uses for both Claude and OpenAI models to read. Normal instructions file that is injected to every agent session (not going too deep into this as there's plenty of documentation on the web).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Selector conventions skill&lt;/strong&gt;: defines the &lt;strong&gt;guidelines on how to choose the selector&lt;/strong&gt;. There is a hierarchy the agent has to follow. For example, the first attempt should be with &lt;code&gt;getByRole&lt;/code&gt;, then by &lt;code&gt;getByLabel&lt;/code&gt; and so on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With those, I tested how it performed using my own agent (local Claude Code CLI).&lt;/p&gt;

&lt;p&gt;Other commands were created too, but they belong to the automation per se, so they will be specified in the following sections.&lt;/p&gt;

&lt;h4&gt;
  
  
  Making tests environment-agnostic
&lt;/h4&gt;

&lt;p&gt;Another problem was that the tests were not able to run against QA Env. Data was coupled into a specific environment, as well as some selectors. This isn’t related to AI nor to automation, but it would impact the ability to develop it. Therefore, I had an additional prep work: creating functions that would setup/create and teardown/delete data for every test run and making selectors environment agnostic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing the flow
&lt;/h3&gt;

&lt;p&gt;A visual diagram of the entire flow is shown below:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F99g75l4b03ps58lhcgno.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F99g75l4b03ps58lhcgno.jpg" alt="This flowchart illustrates an automated test selector repair system with two interconnected workflows. On the frontend side, after a PR is opened, and smoke test fails, the system then checks whether the code is deployed to the QA environment. If so, the system dispatches a " id="" width="800" height="721"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Frontend: dispatching the event
&lt;/h4&gt;

&lt;p&gt;As the flow starts in FE, naturally I had to start there, but the work was slim: I’ve added a new step in &lt;code&gt;drone.yml&lt;/code&gt;, after the Smoke test step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Smoke test&lt;/span&gt;
  &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="c1"&gt;# Smoke tests instructions&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dispatch selector repair&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;GITHUB_APP_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;from_secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GITHUB_APP_TOKEN&lt;/span&gt;
    &lt;span class="na"&gt;REPAIR_ENVIRONMENTS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;QA_Env&lt;/span&gt;
  &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sh ci/drone/dispatch-selector-repair.sh&lt;/span&gt;
  &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Smoke test&lt;/span&gt;
  &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;failure&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Note: this is an example piece of code and should not be used as it will not work).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As variables, a GitHub App token was needed to enable &lt;em&gt;cross-repository communication&lt;/em&gt; and the &lt;code&gt;REPAIR_ENVIRONMENTS&lt;/code&gt; is a mirror list of which environments are enabled. For now, only QA Env is enabled to trigger this automation.&lt;/p&gt;

&lt;p&gt;I could add all the instructions in the YAML file, but I had extracted them into &lt;code&gt;ci/drone/dispatch-selector-repair.sh&lt;/code&gt; to separate concerns and improve readability. In this script, a few things were done:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check if deployed environment is valid&lt;/li&gt;
&lt;li&gt;Check if GitHub token is valid&lt;/li&gt;
&lt;li&gt;Collect the failed tests. Exit if no JSON report of failed tests is found.&lt;/li&gt;
&lt;li&gt;Fetch commit SHA, PR id number&lt;/li&gt;
&lt;li&gt;Prepare payload and dispatch &lt;code&gt;frontend-smoke-failed&lt;/code&gt; event&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, TA would receive this event and payload n its end.&lt;/p&gt;

&lt;h4&gt;
  
  
  Test Automation: running the agent
&lt;/h4&gt;

&lt;p&gt;To receive the event, a GA workflow was created. &lt;code&gt;selector-repair.yml&lt;/code&gt; is triggered when the event is received:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;repository_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;frontend-smoke-failed&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It can also be trigger manually, although this was mostly enabled for testing purposes, when evaluating the performance of the agent.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;Runs &lt;strong&gt;deterministic checks&lt;/strong&gt; before calling the agent: validates the payload from the dispatcher (environment, FE PR, SHA and failed specs) with regex and against injection, checks API keys, re-checks the deployed environment and confirms portal access. &lt;strong&gt;Failing early here avoids unnecessary agent calls and adds a security layer.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;If everything’s ok, then &lt;strong&gt;installs OpenAI Codex CLI, for headless agent&lt;/strong&gt;. The chosen model was GPT-5.4. The goal is for the harness be good enough that we don’t need more capable/high reasoning (and more expensive) models, such as Opus and Astra.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Triggers the agent&lt;/strong&gt; to run repair, by using &lt;code&gt;codex exec&lt;/code&gt; command, which runs the &lt;strong&gt;Repair selector command&lt;/strong&gt; (described below, officially called &lt;code&gt;repair-selectors-ci&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Outputs &lt;strong&gt;repair summary&lt;/strong&gt; and uploads evidence (HTML files and images)&lt;/li&gt;
&lt;li&gt;Comments back in FE PR&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As you can see, the LLM is only involved in in one specific step. Before and after, deterministic scripts were written, both to reduce AI costs and improve quality. The agent is only triggered if it can actually run the tests in an enabled environment; and after the agent finishes, a script puts together the message body for the PR comment back to FE.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Repair selector command
&lt;/h4&gt;

&lt;p&gt;On top of the shared harness from the setup section, the CI run adds two commands. Namely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repair selector command&lt;/strong&gt;: the command that is triggered by automation itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open repair PR command&lt;/strong&gt;: a separate command that is triggered inside the Repair selector command.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Repair selector command is where the core agent logic lives. In summary, it does:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Reads inputs from environment variables&lt;/strong&gt;: which environment to test, which FE PR triggered the run, and a list of failed specs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Manages a persistent branch:&lt;/strong&gt; one branch per frontend PR, reused and appended to across multiple runs rather than recreated each time, as each commit in the same branch triggers a new run. It never rebases or force-pushes, since the commit history works as an audit log. Also, if there is a new commit from FE side, GA cancels the current run via &lt;code&gt;concurrency: cancel-in-progress&lt;/code&gt;, so it doesn’t keep working on outdated code. If the commit was already pushed, it stays there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Determines what to test:&lt;/strong&gt; either the specific failing specs or runs the whole smoke suite itself, in case of a manual run. If a previous repair run already committed a fix for it on the persistent branch, it's categorized as “Already repaired”.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Triages each failure into one of three buckets:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rot&lt;/strong&gt; - the element is still there, just addressed differently (changed role/name/DOM position) → safe to auto-fix.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regression&lt;/strong&gt; — the element is missing or the underlying data is wrong → escalate to a human, reported as &lt;em&gt;Possible regression&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment failure&lt;/strong&gt; — the portal itself was unreachable/broken, so failures aren't meaningful at all → abort triage entirely rather than misreporting every spec as broken. Nothing is sent back to FE; the error is only logged in the GA run.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. Repairs rot-classified selectors&lt;/strong&gt; by probing the live page for accessible signals (role, label, text, placeholder, etc.), picking the most precise selector that resolves to exactly one element, and verifying the fix actually turns the test green before keeping it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Runs quality gates&lt;/strong&gt; — lint/prettier checks, cleanup of any scratch/probe files — before allowing anything to be committed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Opens or updates a draft PR&lt;/strong&gt; (never merges) - this is the &lt;strong&gt;Open repair PR command.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. Writes a structured JSON summary&lt;/strong&gt; (&lt;code&gt;repair-summary.json&lt;/code&gt;) as its only communication channel back to the calling workflow — listing what was fixed, what was escalated as a regression, what couldn't be reproduced, what a prior run already fixed, and any environment error — with strict rules about which fields must stay empty in which scenarios, so the human-facing report is never misleading.&lt;/p&gt;

&lt;p&gt;After that, the payload is sent back to &lt;code&gt;selector-repair.yml&lt;/code&gt; to be picked up by the last step of "Comment back on FE PR". This is a deterministic code that executes the feedback to the FE repository as we already discussed in "Outcomes: what should the agent report?" section.&lt;/p&gt;

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

&lt;p&gt;Before this experiment, a single breaking UI change meant skipping tests, merging with gaps in coverage, and coordinating at least four engineers across two teams. With this flow, FE gets feedback directly in its PR, TA reviews a draft fix instead of writing it from scratch, and, when the agent is successful, tests no longer need to sit skipped on main.&lt;/p&gt;

&lt;p&gt;The biggest lesson for me was that the agent was the smallest part of the work. Most of the effort went into the pieces around it: deciding when it should run, connecting two repositories and two CI tools, making the tests run against a shared environment, and writing deterministic checks so the LLM is only called when it can actually help.&lt;/p&gt;

&lt;p&gt;There's still room to improve and I believe this can serve as a case study for autonomous systems that go beyond using AI in local development. The future is agents working in the cloud without a human driving each step - and for that, we need solid harnesses and guardrails, and to keep observing their output to improve quality, reduce costs and speed things up.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>testing</category>
      <category>frontend</category>
      <category>automation</category>
    </item>
    <item>
      <title>Building resilient large-scale applications</title>
      <dc:creator>Mellina Yonashiro</dc:creator>
      <pubDate>Mon, 20 Apr 2026 13:34:39 +0000</pubDate>
      <link>https://dev.to/yogmel/building-resilient-large-scale-applications-43ca</link>
      <guid>https://dev.to/yogmel/building-resilient-large-scale-applications-43ca</guid>
      <description>&lt;p&gt;The principles for writing software are the same, but the development process itself is quite different when doing it for small versus large applications. Reading, fixing, and building on a small codebase is easier and more forgiving than in large ones. I have worked on applications from small clients, greenfield applications as well as large-scale ones, serving thousands of users. The difference is that, when there is already a fully operational machine running, mistakes are more costly and downtimes harder to recover from. Therefore, processes, guardrails and constant alignment become part of everyday development.&lt;/p&gt;

&lt;p&gt;Most of the concepts I cover here are applicable to any part of web development, but as a frontend engineer, my focus and examples will stay in that domain, especially in the modularization topic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stating the challenge: multiple people involved on a very large codebase
&lt;/h3&gt;

&lt;p&gt;Having a disorganized codebase is a problem in itself, but the problem multiplies when there are dozens of people working on the same project, which is often the case for large-scale applications. It can be developers from the same team, engineers from other teams, external contributors, or cross-functional staff engineers. Without some level of coordination, the following problems tend to emerge:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Strategy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Regression&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A team ships a feature or refactors code that unknowingly breaks another team's work&lt;/td&gt;
&lt;td&gt;Unit tests for logic, integration tests for module contracts, and E2E for critical user journeys, all enforced in CI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Code duplication&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Similar features are implemented independently across teams, leading to diverging logic&lt;/td&gt;
&lt;td&gt;Shared modules for common components, hooks, and utilities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Inconsistency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Standards around formatting, code style, and tooling are not enforced uniformly&lt;/td&gt;
&lt;td&gt;CI guardrails: linters, formatters, shared tsconfig, path alias enforcement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Unintended Changes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A developer modifies another team's module without awareness, causing unintended behaviour changes&lt;/td&gt;
&lt;td&gt;CODEOWNERS and enforce review from the owning team on every change (and a responsive review process)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Obscurity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Onboarding, patterns, and code conventions are unclear or scattered across different places&lt;/td&gt;
&lt;td&gt;Central documentation covering setup, architecture decisions, and code style&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Staging overwrite&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A branch deployment to staging silently overrides another team's active deployment&lt;/td&gt;
&lt;td&gt;Per-branch preview environments instead of a shared staging instance&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is by no means an exhaustive list of all of the challenges of working on a large-scale application. These are some of the most common issues I personally have come across over the years, and some strategies that have worked. But, of course, as we know, everything in software development is a trade-off, so no solution is a 100% fit for every case.&lt;/p&gt;

&lt;p&gt;That said, I’ll discuss the issues and strategies on how to solve them in three groups: &lt;strong&gt;&lt;em&gt;modularization, documentation, CI guardrails and CD practices&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modularization
&lt;/h3&gt;

&lt;p&gt;In “&lt;em&gt;A Philosophy of Software Design&lt;/em&gt;”, Ousterhout calls two ways of programming: &lt;em&gt;strategic&lt;/em&gt; and &lt;em&gt;tactical&lt;/em&gt;. Simply put, tactical programming focuses on developing features and fixing bugs, and strategic focuses on protecting the “&lt;strong&gt;long-term structure of the system&lt;/strong&gt;”. These two ways of getting things done are widely used in the industry, but the author advocates that, although slower in the beginning, the strategic way of programming pays off, as tactical programming accumulates debt that slows teams down over the long term.&lt;/p&gt;

&lt;p&gt;Applying good and well-thought architecture is one of the principles of strategic programming, and one of the areas I chose to focus on is &lt;em&gt;modularization&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;What seems like a straightforward matter of developing features becomes hard when dozens of engineers and multiple teams are all committing to the same repository. Without proper coordination, this can cause issues such as &lt;em&gt;code duplication&lt;/em&gt;, &lt;em&gt;feature overwrite&lt;/em&gt;, &lt;em&gt;tightly coupled modules&lt;/em&gt;, and &lt;em&gt;regressions&lt;/em&gt; with each change.&lt;/p&gt;

&lt;p&gt;What's worth clarifying is that &lt;strong&gt;modules&lt;/strong&gt; in frontend look different from those in backend development. Usually, everything related to the application is in a &lt;strong&gt;single codebase&lt;/strong&gt;, as it needs to be bundled, compiled and run in the client. Libraries are loaded and bundled, and communication between modules happens via shared state, props, or event patterns at runtime. Common architecture choices such as layered, microservice, MVC are &lt;em&gt;adapted&lt;/em&gt; to the frontend.&lt;/p&gt;

&lt;p&gt;Our modules - not to be confused with UI components - &lt;strong&gt;are units of business logic&lt;/strong&gt;, each grouping all the code that belongs to a single domain concern. Clear module boundaries matter.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Modularization is the process of organizing your codebase into loosely coupled, self-contained pieces of code.&lt;/em&gt;&lt;/strong&gt; Total independence between them is impossible, so the goal is to keep dependencies explicit and minimal. It is worth noting that the word 'module' is overused in JavaScript - ES modules, npm packages, and CommonJS modules all use the same term. Here, I use it to mean a domain-ownership unit.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To understand how we apply it in frontend, let’s take an example.&lt;/p&gt;

&lt;p&gt;Consider this folder structure (modules by feature):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;src/
  features/
    checkout/
      components/
      hooks/
      utils/
      types/
      constants/
      index.ts
    search/
      components/
      hooks/
      utils/
      types/
      index.ts
  shared/
    components/
    hooks/
    utils/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this one (modules by type):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;src/
    components/
        checkout/
      search/
      shared/
  hooks/
      checkout/
      search/
      shared/
  utils/
        checkout/
      search/
      shared/
  types/
      checkout/
      search/
  constants/
      checkout/
      search/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although they look similar, the first makes the separation of responsibilities explicit in the file system (&lt;em&gt;Modules by Feature&lt;/em&gt;). If team A owns the Checkout flow, and team B owns the Search, they know which folder they can change and which folders require coordination with other teams. The second structure also allows for that (&lt;em&gt;Modules by Type&lt;/em&gt;), but feature-specific code is scattered across the codebase, which leads to more mistakes and slower cleanups.&lt;/p&gt;

&lt;p&gt;The diagram below illustrates the difference in how code is organized under each approach.&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%2Fov8eq6hionuzy26go66v.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%2Fov8eq6hionuzy26go66v.jpg" alt="Diagram comparing two frontend folder structures: Module by Feature (left) groups all code by domain — Checkout, Search, Shared — while Module by Type (right) groups by code type — Components, Hooks, Utils, Types — with feature subfolders inside each." width="800" height="772"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In Modules by Type, when a codebase grows to cover ten or twenty features, the &lt;code&gt;components/&lt;/code&gt; folder might contain hundreds of files. Some of those components belong to the checkout flow, some to the search experience, and some are shared across features. Finding everything related to a single feature requires jumping between &lt;code&gt;components/&lt;/code&gt;, &lt;code&gt;hooks/&lt;/code&gt;, &lt;code&gt;utils/&lt;/code&gt;, and &lt;code&gt;types/&lt;/code&gt; and knowing which files in each folder belong to which feature. &lt;/p&gt;

&lt;p&gt;In Modules by Feature, by contrast, finding everything related to checkout means opening one folder. Deleting a feature means deleting one folder. With this structure, it is also possible to set up &lt;em&gt;CODEOWNERS&lt;/em&gt;, which is important when there are multiple teams working in the same codebase. Whenever team A changes a piece of code under team B’s responsibility, they will be notified and required to be a reviewer of a PR, and will be prevented from merging (provided that &lt;em&gt;branch protection rules&lt;/em&gt; are configured to require code owner approval).&lt;/p&gt;

&lt;p&gt;On top of that, code duplication can be avoided by using &lt;em&gt;Shared Modules&lt;/em&gt; (utilities, components, or anything that might be useful for the entire system). But discovery is the hard part - which is why clear module ownership and documentation also matter here. That's where documentation becomes the connective tissue that makes modular structure legible at scale.&lt;/p&gt;

&lt;h3&gt;
  
  
  Documentation
&lt;/h3&gt;

&lt;p&gt;Everybody says we need good documentation, but the stakes are higher when there are hundreds of people looking at your code. In a large company, documentation is necessary. This &lt;strong&gt;decentralizes knowledge&lt;/strong&gt;, enables &lt;strong&gt;async work&lt;/strong&gt;, and &lt;strong&gt;reduces information silos&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Overall, the benefits it can bring are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Faster onboarding&lt;/strong&gt;: make newcomers more autonomous when running your project&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt;: keeping patterns, code conventions, and architecture decisions documented for future reference&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Centralization of information&lt;/strong&gt;: everything related to the project, Confluence pages, observability links, localization documents, etc., can be in the repository for easy access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI-readiness&lt;/strong&gt;: in addition to the “classic” benefits, agents can read through documents to implement features that comply with guidelines.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Documentation is, admittedly, tedious to write and tends to go stale quickly. For that reason, some advocate for less documentation, especially if the same intent can be expressed through the code itself. &lt;em&gt;Code should speak for itself through clear naming and structure&lt;/em&gt;. There are, however, things that code cannot document: processes like onboarding, releases, and deployments, or decisions that need to be more visible than what lives inside the codebase.&lt;/p&gt;

&lt;p&gt;Keeping documentation alive is a &lt;strong&gt;collaborative task&lt;/strong&gt;. There is no single responsible person: every team member needs to be proactive about keeping it current. When I start at a new company, I write down everything I need to get the project running - API keys, third-party access, software to install - and use it to update the onboarding documentation if it's outdated (and it usually is). I revisit it every time I help someone else get set up.&lt;/p&gt;

&lt;p&gt;As AI tools mature, &lt;strong&gt;documentation becomes a two-way investment&lt;/strong&gt;: repository-level documentation gives coding agents the &lt;em&gt;context&lt;/em&gt; they need, and AI-powered tools will be able to &lt;em&gt;speed up the documentation update process&lt;/em&gt;, like via Atlassian Automation or a GitHub Actions webhook.&lt;/p&gt;

&lt;h3&gt;
  
  
  CI guardrails and CD practices
&lt;/h3&gt;

&lt;p&gt;Modules give structure, and documentation makes that structure legible, but neither survives at scale without &lt;strong&gt;enforcement&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Verbal agreements matter, but &lt;em&gt;automated enforcement&lt;/em&gt; is what actually keeps things consistent. Agreements made in conversation don't stick - people forget. The volume is too high and context switches too frequent for people to reliably catch everything: &lt;em&gt;commit message conventions&lt;/em&gt;, &lt;em&gt;PR descriptions&lt;/em&gt;, &lt;em&gt;test coverage thresholds&lt;/em&gt;, &lt;em&gt;review processes&lt;/em&gt;, and so on. Because of that, we have several tools to automate this process, and we apply them by creating CI guardrails.&lt;/p&gt;

&lt;p&gt;For example, before a PR can merge to main, we can add a check that enforces a &lt;strong&gt;minimum overall coverage threshold&lt;/strong&gt;, and a d*&lt;em&gt;iff coverage&lt;/em&gt;* check to ensure newly introduced code is covered too. We can also run &lt;strong&gt;linter&lt;/strong&gt; checks - which catch potential bugs and enforce patterns - and &lt;strong&gt;formatter&lt;/strong&gt; checks for consistent code style. If those checks don’t pass, the change is not integrated into the main code. &lt;strong&gt;This ensures that every change meets a defined quality baseline.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With these automated checks, human review is no longer needed for mechanical steps - which is important at scale, where manual gatekeeping becomes a bottleneck. Instead of having to check formatting or whether it has enough tests, engineers can focus on business logic implementation and architectural decisions.&lt;/p&gt;

&lt;p&gt;After passing all the checks, it’s time to deploy to a staging environment. It shouldn’t be a problem to deploy to staging - or so it seems. In practice, when multiple teams are testing different features simultaneously, a &lt;em&gt;single shared environment becomes a source of constant conflict.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For that reason, when it comes to the CD part of the process, it is crucial to have multiple staging environments. What usually happens is a per-branch preview: you can deploy your branch, and it creates a unique URL. &lt;strong&gt;No shared staging instance means no overwrite&lt;/strong&gt;, no "who deployed what" confusion, and no blocked QA because two teams need to test at the same time.&lt;/p&gt;

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

&lt;p&gt;The practices covered here - modularization, documentation, and automated enforcement - are what make a large codebase workable in the long run.&lt;/p&gt;

&lt;p&gt;That said, the investment goes beyond tooling and processes. Large companies dedicate entire roles to it: platform engineers who ensure others can ship faster, and system architects who oversee architecture across domains. Beyond roles, there are dedicated processes - like writing and approving RFCs - to govern changes that affect multiple teams.&lt;/p&gt;

&lt;p&gt;The investment is ongoing and never fully finished. But the alternative, like teams stepping on each other, knowledge locked in people's heads, regressions shipping, costs far more.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>frontend</category>
      <category>softwareengineering</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>My AI Workflow as a Frontend Engineer</title>
      <dc:creator>Mellina Yonashiro</dc:creator>
      <pubDate>Fri, 27 Mar 2026 10:56:24 +0000</pubDate>
      <link>https://dev.to/yogmel/my-ai-workflow-as-a-frontend-engineer-4582</link>
      <guid>https://dev.to/yogmel/my-ai-workflow-as-a-frontend-engineer-4582</guid>
      <description>&lt;p&gt;I decided to compile and share all the ways I currently use AI for software development, because for some time, it was confusing to me how people were actually using it. I wouldn’t say there is a correct or more effective way to do it, as I also believe there is not a single way to do software engineering (there’s always the trade-off conversation when discussing engineering strategies). But feel free to take some ideas from here.&lt;/p&gt;

&lt;p&gt;So, in this article, I will write about the ways I use AI chatbots, agents, and other tools for my work and personal/hobby coding projects. For some more context on what I currently use - Claude code in the command line, Claude chatbot (with some integrations, such as Github and Notion), and some Gemini models in Roo Code (VS Code extension). I will be focusing on how I use them for engineering work, not so much for my personal life. Also, I will not discuss how companies may be implementing in their workflows.&lt;/p&gt;




&lt;h2&gt;
  
  
  Investigation &amp;amp; Debugging
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Reading and parsing error messages from logs&lt;/li&gt;
&lt;li&gt;Debugging frontend runtime errors via console log&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It was common practice for me to (try to) read the error message to identify where and why it occurred. If I didn’t understand, I’d paste the text in a search engine and dive deep into StackOverflow. Now, I paste the logs into a chat to speed up the research and solution process. This is especially valuable when the logs are long and hard for a human to read.&lt;/p&gt;

&lt;p&gt;The frontend debugging workflow works quite differently. Components break at runtime - for example, a state is not passed down correctly. It’s hard to catch in development, because the issue happens when interacting in the browser, when the code is already compiled and running. Then, when this happens, the agent, acting more like a coding partner, asks me to add (or have it add) console.log() calls in strategic places in the code, and it asks me what the dev tools console prints. It will then get more information and eventually understand where the problem is. This will proceed iteratively until we reach a solution.&lt;/p&gt;




&lt;h2&gt;
  
  
  Codebase Understanding &amp;amp; Quality
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Looking for inconsistencies in the codebase&lt;/li&gt;
&lt;li&gt;Writing unit tests and mocks&lt;/li&gt;
&lt;li&gt;Comparing changes with the main branch to catch regressions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Inconsistencies in a codebase are easy to miss and hard to prioritize. Using agentic coding that has access to the project makes it easy to find and make it more consistent. Most of the time, I don’t want it to act right away, as the codebase is huge - it can help with creating a plan, though. I make it read the project code, assess the opportunities to improve, and ask for a planning document. It’s important to keep patterns, naming conventions, and architecture consistent, for both bots and humans.&lt;/p&gt;

&lt;p&gt;Writing unit tests is a must, but a time-consuming task - especially writing mocks. In the frontend, we also have a hard time finding the right selector (finding a reference to a DOM element), as design systems create complex HTML structures for rendering components. It works wonderfully for those. AI can even completely write the test by scanning the function or component.&lt;/p&gt;

&lt;p&gt;Additionally to assisting with unit tests, I can compare my changes with the main branch, and have it check if there is something I wrote that can affect another part of the codebase (cause regression), or if it’s not consistent (as in code style or patterns).&lt;/p&gt;




&lt;h2&gt;
  
  
  Active Development
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Performing migrations and package upgrades&lt;/li&gt;
&lt;li&gt;Dealing with breaking changes&lt;/li&gt;
&lt;li&gt;Developing new features based on the current architecture&lt;/li&gt;
&lt;li&gt;Jira ticket descriptions to pull/merge requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Usually, projects have many dependencies, and these dependencies release updates very often. Dependabot (in GitHub), which has existed for a while now, already helped with minor package updates. Now, agentic code can help to deal with major upgrades and breaking changes. It can handle tedious tasks such as renaming parameters or replacing logic that has become obsolete. &lt;/p&gt;

&lt;p&gt;Moreover, one of the most powerful tools is to create new product features. It can take a feature description (or Jira ticket description) and implement it. Of course, agentic coding is better if your codebase is prepared for automation - if you’ve written proper documentation, context, and if it uses consistent patterns, architecture, and coding style. Plus, your instructions are clear (specifying which files to change, with example files).&lt;/p&gt;




&lt;h2&gt;
  
  
  Documentation &amp;amp; Communication
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Writing codebase documentation&lt;/li&gt;
&lt;li&gt;Creating Jira tickets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI is great at reading large chunks of data and summarizing. With that, I make it read the entire codebase and create documentation, separating by subject, such as security, observability, and accessibility. These are important to inform developers on which practices and patterns we are currently using, or where configurations are. There are also links to external resources if necessary, which may also be accessed by the agent.&lt;/p&gt;

&lt;p&gt;On a different direction, I use it to write comprehensive Jira tickets and PR descriptions for others to read. I usually have no problem writing them, but there are days when I’m just mentally tired. When this happens, I write some notes in draft format and ask AI to formulate a readable piece of text for humans (and machines) to understand.&lt;/p&gt;




&lt;h2&gt;
  
  
  Planning &amp;amp; Review
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Code reviews (GitHub-integrated)&lt;/li&gt;
&lt;li&gt;Brainstorming and looking for improvements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Having AI act as an additional reviewer, integrated into GitHub, for example, doesn't replace human review, but it catches things: inconsistencies, edge cases, logic that's technically correct but fragile, and not used parameters. Sometimes it is not correct, but most of the times it at least gives you some food for thought. What’s interesting in this interaction is that sometimes one model writes the feature code and another reviews it (for example, Claude writes the code, and Gemini reviews it). I’m looking forward to a future where I read some epic interaction between robots on what approach is the best to take.&lt;/p&gt;

&lt;p&gt;Brainstorming sessions are looser but useful. Sometimes you don't need a solution; you need to learn the pros and cons of it. For example, would it make sense to change from Yarn to Bun? What are the trade-offs in this case? Or, how could a certain for loop be more performative? These are real questions I have already asked (with more details, of course).&lt;/p&gt;




&lt;h2&gt;
  
  
  Experimentation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Me as a human learning new stuff&lt;/li&gt;
&lt;li&gt;Vibe coding (hobby projects)&lt;/li&gt;
&lt;li&gt;Agent development&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;People are using AI for many different things, and I am also doing some experimentation myself. I have been using it to keep me up to date on the latest technologies - frontend is a subject that is constantly transforming, and it is historically hard to keep up. At the same time that good practices are mostly the same, there are new tools everywhere - state management libraries, observability systems, frameworks that challenge the status quo.&lt;/p&gt;

&lt;p&gt;Also, there are some hobby projects I want to implement, but they are not my field of expertise. For those, I use the (in)famous vibe coding to implement projects from scratch. I’m pretty impressed by the outcome: using Python, we developed a web scraper while customizing it for my own needs.&lt;/p&gt;

&lt;p&gt;Lastly, I’m in the stage where I’m experimenting with implementing an agent myself. I’m still unsure in which direction I will take this, but I’m planning on something basic at first, just to understand the implementation process: token usage, integration, and how to connect to existing MCPs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I've Learned
&lt;/h2&gt;

&lt;p&gt;Information about how to use AI is all over the place, but overall, what I understood is that written communication is very important when prompting. I’ve always liked to write. I wrote journals, stories, on paper, on my computer, or on a notepad in my smartphone while on the tram. I don’t consider myself a writer, but I like doing it as a hobby. Because of that, I feel it is easier to write prompts and direct AI on what I want it to output. I can be direct when I want something specific, and I start a conversation with open questions when I want to do creative tasks. If you are good at communication or have some background knowledge on how these models work, you will most likely get better outcomes.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>frontend</category>
      <category>productivity</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Project Management for Engineers</title>
      <dc:creator>Mellina Yonashiro</dc:creator>
      <pubDate>Thu, 12 Mar 2026 12:03:40 +0000</pubDate>
      <link>https://dev.to/yogmel/project-management-for-engineers-43jb</link>
      <guid>https://dev.to/yogmel/project-management-for-engineers-43jb</guid>
      <description>&lt;p&gt;Project management is a diverse and complex topic, and each company handles it differently. To achieve better results, companies usually build a multi-skilled team with engineers, managers, product owners, and QA testers. With so many people, what is the actual role of software engineers in project management, then? How much involvement should non-technical and technical people have in each other's areas of influence? These are questions without a universal answer — but patterns do emerge, especially as AI tools make the line between "who decides what to build" and "who builds it" increasingly thin.&lt;/p&gt;

&lt;p&gt;This is my take on that, based on real experience across very different environments.&lt;/p&gt;




&lt;h2&gt;
  
  
  Some foundation - What I understand as Project Management?
&lt;/h2&gt;

&lt;p&gt;Project management is the practice of planning, organizing, and guiding work toward a specific goal, within constraints like time, budget, and scope. It's not just about Gantt charts and status updates, but making sure the right people are working on the right things at the right time, and that everyone actually knows what "done" looks like.&lt;/p&gt;

&lt;p&gt;Today, engineers are expected to participate in planning and project management, by estimate their own work, flag risks early, communicate progress, and sometimes even define the scope of what they're building.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Competencies: Where Engineers and Project Managers Overlap
&lt;/h2&gt;

&lt;p&gt;A project manager's core competencies typically include things like stakeholder communication, risk assessment, timeline planning, resource allocation, and scope management. A software engineer's core competencies include system design, implementation, debugging, code review, and technical decision-making.&lt;/p&gt;

&lt;p&gt;These seems like two distinct skill sets, but they are closer at some strategic points, and that intersection is where the most effective engineers tend to live.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Estimation&lt;/strong&gt; is one obvious overlap. Engineers estimate technical complexity and project managers translate that into timelines and resource plans. But a good engineer doesn't just throw numbers to story points: they understand why the timeline matters, what business decision depends on it, and how to communicate uncertainty in a way that actually helps the team plan.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependency mapping&lt;/strong&gt; is another. Knowing that your feature depends on a backend endpoint, a design decision, and a third-party API approval isn't just project management, it's engineering survival. Engineers who think in terms of dependencies and unblock themselves proactively are invaluable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Communication&lt;/strong&gt; might be the biggest one. Writing clear tickets, giving useful status updates, and knowing when to escalate a risk. These feel "soft," but they have very real consequences on project outcomes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The intersection, in short, is &lt;strong&gt;ownership thinking&lt;/strong&gt;: the ability to care about the outcome of your work, not just the output.&lt;/p&gt;




&lt;h2&gt;
  
  
  My Experience: What Different Companies Actually Expect From You
&lt;/h2&gt;

&lt;p&gt;I've worked across very different contexts, big tech companies, consultancies, and open-source projects. What "project management for engineers" means is different in each.&lt;/p&gt;

&lt;h3&gt;
  
  
  Big Company: Coordination as a Full-Time Sport
&lt;/h3&gt;

&lt;p&gt;In a large organization, the sheer number of moving parts means that getting anything done requires a surprising amount of coordination. Cross-team alignment, stakeholder approvals, dependency tracking, and navigating the tension between what engineering wants to build, between technical excellence and controlling tech debt, and what the roadmap says.&lt;/p&gt;

&lt;p&gt;In this environment, I worked closely with designers and product managers to figure out what could realistically be delivered and in what timeline. My job wasn't just to build, but also to make sure what I was building actually landed inside the system within the established procedures.&lt;/p&gt;

&lt;p&gt;This taught me a lot about bureaucracy as a communication problem. All that process exists because people need to stay aligned across a large, complex organization. The engineers who thrive learn to work with the process - and occasionally, strategically, to work around it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Consultancy: Being the Bridge
&lt;/h3&gt;

&lt;p&gt;Consulting is a different world entirely. The diversity of projects means you're constantly context-switching between different clients, tech stacks, and team cultures. But there's a common thread: you are always, in some way, the translator.&lt;/p&gt;

&lt;p&gt;In my time at a consultancy, the teams were large, the clients had their own ways of working, and the gap between "what the client asked for" and "what we understood them to ask for" was a constant source of risk. My role shifted toward being the bridge between our technical team and the client's expectations in some projects. That meant coordinating with my team on priorities, and constantly calibrating based on what signals were coming from the client side.&lt;/p&gt;

&lt;p&gt;What this context taught me, specifically, was how to read silence. Clients rarely tell you directly when something is off. You learn to catch it in how they phrase feedback, what they don't ask about, and when enthusiasm drops. That's a skill that doesn't show up in any job description, but it shapes how I communicate to this day.&lt;/p&gt;

&lt;h3&gt;
  
  
  Open Source: Full Ownership, Minimal Structure
&lt;/h3&gt;

&lt;p&gt;Working in open source is, in many ways, the purest form of engineering ownership I've experienced. There's no product manager telling you what to prioritize.&lt;/p&gt;

&lt;p&gt;In practice, this meant taking full ownership over what to build and how to build it, of course, within the broader directives of the project, but with significant autonomy. I decided what was most important based on my own findings when I talked to users. I shaped the roadmap, communicated it, and executed it. The bureaucratic overhead was almost nonexistent, and the trust was remarkably high.&lt;/p&gt;




&lt;h2&gt;
  
  
  My Personal Flavor: Design and User-Centered Perspective
&lt;/h2&gt;

&lt;p&gt;Most of what I developed came from being thrown into situations where it was needed, and from someone seeing something in me that I hadn't fully articulated for myself at those times.&lt;/p&gt;

&lt;p&gt;Looking back, what seems to resonate with the people I've worked with is a combination of things rooted in my background: I came into engineering with a &lt;strong&gt;design&lt;/strong&gt; background and have always been &lt;strong&gt;user-oriented&lt;/strong&gt;. Those two things shape how I approach technical problems: I think in terms of how things will be &lt;strong&gt;&lt;em&gt;experienced&lt;/em&gt;&lt;/strong&gt;, not just how they'll be implemented.&lt;/p&gt;

&lt;p&gt;One specific habit that keeps coming up: I like to &lt;strong&gt;create visual elements&lt;/strong&gt; — boards, diagrams, maps — as a way to &lt;em&gt;make sense of complex projects&lt;/em&gt;. I do this first for myself, to understand the shape of what I'm working on, but then I use those visuals when presenting to non-technical stakeholders. The goal is to give people an entry point into the technical conversation.&lt;/p&gt;

&lt;p&gt;I also bring a certain orientation toward education into how I communicate. I try to translate technical concepts without dumbing them down, which is a harder balance than it sounds. The goal is always to make the other person feel more informed and more capable, not more confused or dependent. That usually involves meeting people where they are, using analogies that fit their world, and then carefully building toward the technical layer.&lt;/p&gt;

&lt;p&gt;And maybe most importantly: I genuinely care about relationships. I want to know the people I'm working with, I want to talk to users. I want to understand who I'm building for, not just what I'm building. That sounds soft, but it has very practical consequences. When you understand who your users are, your technical decisions get sharper. You know what's worth over-engineering and what isn't. You know what "good enough" actually means.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion: It's Not a Separate Skill Set - It's an Extension
&lt;/h2&gt;

&lt;p&gt;If there's one thing I'd want you to take away from this, it's that project management isn't something you need to become a different kind of person to do. It's an extension of the curiosity and care you already bring to your technical work, just pointed outward, toward the team, the stakeholders, and the users.&lt;/p&gt;

&lt;p&gt;Different companies will need different things from you. But the underlying thread is the same: &lt;strong&gt;take ownership, communicate clearly and early, and care about the outcome, not just the output&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>career</category>
      <category>management</category>
      <category>productivity</category>
      <category>softwareengineering</category>
    </item>
  </channel>
</rss>
